Archived
Improve web file completion continuation
This commit is contained in:
@@ -107,13 +107,25 @@ export class PromptEditor extends LitElement {
|
||||
if (version !== this.requestVersion) return;
|
||||
this.completions = files
|
||||
.slice(0, 12)
|
||||
.map((file) => ({ kind: "file", replaceFrom: trigger.from, replaceTo: trigger.to, insertText: `${trigger.fileMode === "path" ? "" : "@"}${file.path}`, detail: file.kind }));
|
||||
.map((file) => {
|
||||
const insertText = fileInsertText(file.path, trigger.fileMode === "path", trigger.quoted === true);
|
||||
return {
|
||||
kind: "file",
|
||||
replaceFrom: trigger.from,
|
||||
replaceTo: trigger.to,
|
||||
insertText,
|
||||
detail: file.kind,
|
||||
...(file.path.endsWith("/") && insertText.endsWith("\"") ? { cursorOffset: insertText.length - 1 } : {}),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private currentTrigger(): { kind: "command" | "file"; query: string; from: number; to: number; fileKind?: FileSuggestion["kind"]; fileMode?: "file" | "path" } | undefined {
|
||||
private currentTrigger(): { kind: "command" | "file"; query: string; from: number; to: number; fileKind?: FileSuggestion["kind"]; fileMode?: "file" | "path"; quoted?: boolean } | undefined {
|
||||
const cursor = this.textarea?.selectionStart ?? this.draft.length;
|
||||
const beforeCursor = this.draft.slice(0, cursor);
|
||||
const quotedTrigger = this.currentQuotedTrigger(beforeCursor, cursor);
|
||||
if (quotedTrigger !== undefined) return quotedTrigger;
|
||||
|
||||
const tokenStart = Math.max(beforeCursor.lastIndexOf(" "), beforeCursor.lastIndexOf("\n")) + 1;
|
||||
const token = beforeCursor.slice(tokenStart);
|
||||
@@ -124,6 +136,15 @@ export class PromptEditor extends LitElement {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private currentQuotedTrigger(beforeCursor: string, cursor: number): { kind: "file"; query: string; from: number; to: number; fileMode?: "file" | "path"; quoted: true } | undefined {
|
||||
const quoteStart = beforeCursor.lastIndexOf("\"");
|
||||
if (quoteStart === -1) return undefined;
|
||||
const prefix = beforeCursor.slice(0, quoteStart);
|
||||
if (prefix.endsWith("@")) return { kind: "file", query: beforeCursor.slice(quoteStart + 1), from: prefix.length - 1, to: cursor, quoted: true };
|
||||
if (prefix.endsWith("@ ")) return { kind: "file", query: beforeCursor.slice(quoteStart + 1), from: quoteStart, to: cursor, fileMode: "path", quoted: true };
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private handleKeyDown(event: KeyboardEvent) {
|
||||
if (this.completions.length) {
|
||||
if (event.key === "ArrowDown") {
|
||||
@@ -148,6 +169,14 @@ export class PromptEditor extends LitElement {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (event.key === "Tab") {
|
||||
const trigger = this.currentTrigger();
|
||||
if (trigger?.kind === "file") {
|
||||
event.preventDefault();
|
||||
void this.refreshCompletions();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
this.send(this.canSteer || this.isCompacting ? "followUp" : undefined);
|
||||
@@ -155,10 +184,13 @@ export class PromptEditor extends LitElement {
|
||||
}
|
||||
|
||||
private pick(item: CompletionItem) {
|
||||
const suffix = item.kind === "file" && item.insertText.endsWith("/") ? "" : " ";
|
||||
this.draft = `${this.draft.slice(0, item.replaceFrom)}${item.insertText}${suffix}${this.draft.slice(item.replaceTo)}`;
|
||||
const suffix = item.kind === "file" && (item.insertText.endsWith("/") || item.cursorOffset !== undefined) ? "" : " ";
|
||||
const cursor = item.replaceFrom + (item.cursorOffset ?? item.insertText.length) + suffix.length;
|
||||
const after = item.insertText.endsWith("\"") && this.draft.slice(item.replaceTo).startsWith("\"") ? this.draft.slice(item.replaceTo + 1) : this.draft.slice(item.replaceTo);
|
||||
this.draft = `${this.draft.slice(0, item.replaceFrom)}${item.insertText}${suffix}${after}`;
|
||||
if (this.sessionId !== undefined && this.sessionId !== "") saveDraft(this.sessionId, this.draft);
|
||||
this.completions = [];
|
||||
void this.updateComplete.then(() => this.textarea?.setSelectionRange(cursor, cursor));
|
||||
}
|
||||
|
||||
private send(streamingBehavior?: "steer" | "followUp") {
|
||||
@@ -173,6 +205,12 @@ export class PromptEditor extends LitElement {
|
||||
static override styles = promptEditorStyles;
|
||||
}
|
||||
|
||||
function fileInsertText(path: string, pathMode: boolean, quoted: boolean): string {
|
||||
const prefix = pathMode ? "" : "@";
|
||||
if (!quoted && !path.includes(" ")) return `${prefix}${path}`;
|
||||
return `${prefix}\"${path}\"`;
|
||||
}
|
||||
|
||||
function emptySlashCommands(): SlashCommand[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface CompletionItem {
|
||||
insertText: string;
|
||||
detail: string;
|
||||
description?: string;
|
||||
cursorOffset?: number;
|
||||
}
|
||||
|
||||
export const appStyles = css`
|
||||
|
||||
@@ -25,5 +25,9 @@ function isFileCompletionContext(draft: string): boolean {
|
||||
const token = currentToken(draft);
|
||||
if (token.startsWith("@")) return true;
|
||||
const tokenStart = draft.length - token.length;
|
||||
return draft.slice(0, tokenStart).endsWith("@ ");
|
||||
if (draft.slice(0, tokenStart).endsWith("@ ")) return true;
|
||||
const quoteStart = draft.lastIndexOf("\"");
|
||||
if (quoteStart === -1) return false;
|
||||
const prefix = draft.slice(0, quoteStart);
|
||||
return prefix.endsWith("@") || prefix.endsWith("@ ");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user