(char: string, skipUndoCoalescing?: boolean)
| 1188 | |
| 1189 | // All the editor methods from before... |
| 1190 | private insertCharacter(char: string, skipUndoCoalescing?: boolean): void { |
| 1191 | this.exitHistoryBrowsing(); |
| 1192 | |
| 1193 | // Undo coalescing (fish-style): |
| 1194 | // - Consecutive word chars coalesce into one undo unit |
| 1195 | // - Space captures state before itself (so undo removes space+following word together) |
| 1196 | // - Each space is separately undoable |
| 1197 | // Skip coalescing when called from atomic operations (e.g., handlePaste) |
| 1198 | if (!skipUndoCoalescing) { |
| 1199 | if (isWhitespaceChar(char) || this.lastAction !== "type-word") { |
| 1200 | this.pushUndoSnapshot(); |
| 1201 | } |
| 1202 | this.lastAction = "type-word"; |
| 1203 | } |
| 1204 | |
| 1205 | const line = this.state.lines[this.state.cursorLine] || ""; |
| 1206 | |
| 1207 | const before = line.slice(0, this.state.cursorCol); |
| 1208 | const after = line.slice(this.state.cursorCol); |
| 1209 | |
| 1210 | this.state.lines[this.state.cursorLine] = before + char + after; |
| 1211 | this.setCursorCol(this.state.cursorCol + char.length); |
| 1212 | |
| 1213 | if (this.onChange) { |
| 1214 | this.onChange(this.getText()); |
| 1215 | } |
| 1216 | |
| 1217 | // Check if we should trigger or update autocomplete |
| 1218 | if (!this.autocompleteState) { |
| 1219 | // Auto-trigger for "/" at the start of a line (slash commands) |
| 1220 | if (char === "/" && this.isAtStartOfMessage()) { |
| 1221 | this.tryTriggerAutocomplete(); |
| 1222 | } |
| 1223 | // Auto-trigger for symbol-based completion like @, #, or provider triggers at token boundaries |
| 1224 | else if (this.autocompleteTriggerCharacters.includes(char)) { |
| 1225 | const currentLine = this.state.lines[this.state.cursorLine] || ""; |
| 1226 | const textBeforeCursor = currentLine.slice(0, this.state.cursorCol); |
| 1227 | const charBeforeSymbol = textBeforeCursor[textBeforeCursor.length - 2]; |
| 1228 | if (textBeforeCursor.length === 1 || charBeforeSymbol === " " || charBeforeSymbol === "\t") { |
| 1229 | this.tryTriggerAutocomplete(); |
| 1230 | } |
| 1231 | } |
| 1232 | // Also auto-trigger when typing letters in a slash command or symbol completion context |
| 1233 | else if (/[a-zA-Z0-9.\-_]/.test(char)) { |
| 1234 | const currentLine = this.state.lines[this.state.cursorLine] || ""; |
| 1235 | const textBeforeCursor = currentLine.slice(0, this.state.cursorCol); |
| 1236 | // Check if we're in a slash command (with or without space for arguments) |
| 1237 | if (this.isInSlashCommandContext(textBeforeCursor)) { |
| 1238 | this.tryTriggerAutocomplete(); |
| 1239 | } |
| 1240 | // Check if we're in a symbol-based completion context like @, #, or provider triggers |
| 1241 | else if (this.autocompleteTriggerPattern.test(textBeforeCursor)) { |
| 1242 | this.tryTriggerAutocomplete(); |
| 1243 | } |
| 1244 | } |
| 1245 | } else { |
| 1246 | this.updateAutocomplete(); |
| 1247 | } |
no test coverage detected