* Parse input to extract completion context
(input: string, cursorOffset: number)
| 78 | * Parse input to extract completion context |
| 79 | */ |
| 80 | function parseInputContext(input: string, cursorOffset: number): InputContext { |
| 81 | const beforeCursor = input.slice(0, cursorOffset) |
| 82 | |
| 83 | // Check if it's a variable prefix, before expanding with shell-quote |
| 84 | const varMatch = beforeCursor.match(/\$[a-zA-Z_][a-zA-Z0-9_]*$/) |
| 85 | if (varMatch) { |
| 86 | return { prefix: varMatch[0], completionType: 'variable' } |
| 87 | } |
| 88 | |
| 89 | // Parse with shell-quote |
| 90 | const parseResult = tryParseShellCommand(beforeCursor) |
| 91 | if (!parseResult.success) { |
| 92 | // Fallback to simple parsing |
| 93 | const tokens = beforeCursor.split(/\s+/) |
| 94 | const prefix = tokens[tokens.length - 1] || '' |
| 95 | const isFirstToken = tokens.length === 1 && !beforeCursor.includes(' ') |
| 96 | const completionType = isFirstToken |
| 97 | ? 'command' |
| 98 | : getCompletionTypeFromPrefix(prefix) |
| 99 | return { prefix, completionType } |
| 100 | } |
| 101 | |
| 102 | // Extract current token |
| 103 | const lastToken = findLastStringToken(parseResult.tokens) |
| 104 | if (!lastToken) { |
| 105 | // No string token found - check if after operator |
| 106 | const lastParsedToken = parseResult.tokens[parseResult.tokens.length - 1] |
| 107 | const completionType = |
| 108 | lastParsedToken && isCommandOperator(lastParsedToken) |
| 109 | ? 'command' |
| 110 | : 'command' // Default to command at start |
| 111 | return { prefix: '', completionType } |
| 112 | } |
| 113 | |
| 114 | // If there's a trailing space, the user is starting a new argument |
| 115 | if (beforeCursor.endsWith(' ')) { |
| 116 | // After first token (command) with space = file argument expected |
| 117 | return { prefix: '', completionType: 'file' } |
| 118 | } |
| 119 | |
| 120 | // Determine completion type from context |
| 121 | const baseType = getCompletionTypeFromPrefix(lastToken.token) |
| 122 | |
| 123 | // If it's clearly a file or variable based on prefix, use that type |
| 124 | if (baseType === 'variable' || baseType === 'file') { |
| 125 | return { prefix: lastToken.token, completionType: baseType } |
| 126 | } |
| 127 | |
| 128 | // For command-like tokens, check context: are we starting a new command? |
| 129 | const completionType = isNewCommandContext( |
| 130 | parseResult.tokens, |
| 131 | lastToken.index, |
| 132 | ) |
| 133 | ? 'command' |
| 134 | : 'file' // Not after operator = file argument |
| 135 | |
| 136 | return { prefix: lastToken.token, completionType } |
| 137 | } |
no test coverage detected