Welcome to the Suggestions project! In this first chapter, we are going to look at the "brain" of our slash command system.
Imagine you walk into a fancy hotel and you want your room cleaned. You might walk up to the concierge and say:
A bad computer system would only respond if you said the exact, official term: "INITIATE_HOUSEKEEPING_PROTOCOL". If you made a typo like "Houskeeping", it would say "Command Not Found."
A smart system understands your intent. It knows that "room clean" maps to the Housekeeping service. It understands that "hlp" probably means Help.
This is Fuzzy Command Dispatch. It is the engine that allows users to type imperfectly but still find exactly what they need.
To achieve this, we use a technique called Fuzzy Search. Unlike a strict equality check (input === command), fuzzy search looks for approximate matches.
We utilize a library called Fuse.js. It assigns a "score" to how well a user's input matches our list of available commands.
When a user types something, we don't treat all matches equally. We prioritize them in this order:
/help./? (which is an alias for help)./hel./support (which appears in the description of the help command).
Here is what happens the moment a user types a character after a slash /:
Let's look at how we build this in commandSuggestions.ts.
Before we can search, we need to transform our raw commands into a format that the search engine understands. We create a CommandSearchItem.
We strip out special characters to make matching easier.
// We map our commands to a searchable format
const commandData = commands
.filter(cmd => !cmd.isHidden) // Only show visible commands
.map(cmd => ({
commandName: getCommandName(cmd),
// Split description into searchable words
descriptionKey: (cmd.description ?? '').split(' '),
aliasKey: cmd.aliases,
command: cmd,
}))
This is the most critical part. We need to tell the search engine which parts of a command are most important. We don't want a match in a long description to rank higher than a match in the command's actual name.
const fuse = new Fuse(commandData, {
threshold: 0.3, // 0.0 is perfect match, 1.0 is match anything
keys: [
{ name: 'commandName', weight: 3 }, // Highest priority!
{ name: 'aliasKey', weight: 2 }, // High priority
{ name: 'descriptionKey', weight: 0.5 }, // Low priority
],
})
commandName is weighted significantly higher (3) than descriptionKey (0.5).When the user types, we run the search. However, simply getting results isn't enough. We perform a custom sort to ensure the "Smart Concierge" logic holds up (e.g., favoring exact matches over fuzzy ones).
// 1. Get raw fuzzy results
const searchResults = fuse.search(query)
// 2. Custom sorting logic (Simplified)
const sortedResults = searchResults.sort((a, b) => {
// If 'a' is an exact name match, it always wins
if (a.name === query) return -1
// If 'a' starts with the query (prefix), it beats fuzzy matches
if (a.name.startsWith(query)) return -1
// Otherwise, trust the fuzzy score
return a.score - b.score
})
Sometimes, a user doesn't start a line with a command. They might be writing a sentence and decide to insert a command, like:
"I need to /check the logs."
The system needs to detect that slash even if it's not at the very beginning.
export function findMidInputSlashCommand(
input: string,
cursorOffset: number
): MidInputSlashCommand | null {
// Look backwards from cursor for a space followed by a slash
// e.g., " " + "/"
const beforeCursor = input.slice(0, cursorOffset)
const match = beforeCursor.match(/\s\/([a-zA-Z0-9_:-]*)$/)
if (!match) return null
// Return the token found, e.g., "/check"
return {
token: '/' + match[1],
startPos: match.index + 1, // Adjust for the space
partialCommand: match[1] // "check"
}
}
You might wonder: "If we have hundreds of commands, doesn't rebuilding this search index every time I type a letter slow things down?"
Yes, it would! To solve this, we use Caching. We only build the expensive Fuse index once. We save it in a variable called fuseCache and reuse it until the list of commands actually changes (like if a plugin is loaded).
// Simple caching mechanism
let fuseCache = null
function getCommandFuse(commands) {
// If we have a cache and commands haven't changed, return it
if (fuseCache?.commands === commands) {
return fuseCache.fuse
}
// Otherwise, build a new index...
}
Note: We will dive deeper into performance strategies in Performance Caching Layer.
In this chapter, we built the front door of our command system.
Now that the system knows what command the user wants, the next challenge is helping the user find files and resources to use with that command.
Next Chapter: Filesystem Navigation & Discovery
Generated by Code IQ