From 8ac3879ee0d399f4bcaf89e0fdf44d1bc717a1e8 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 11 Sep 2024 09:49:17 +0200 Subject: [PATCH 01/25] WORKING only one insertion --- src/extension.ts | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 1106a6c..d2b801b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -75,7 +75,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo async (progress, progressCancellationToken) => { try { progress.report({ message: "Starting model..." }); - + let axiosCancelPost: () => void; const axiosCancelToken = new axios.CancelToken((c) => { axiosCancelPost = () => { @@ -85,7 +85,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo progressCancellationToken.onCancellationRequested(axiosCancelPost); vscode.workspace.onDidCloseTextDocument(axiosCancelPost); }); - + const response = await axios.post(apiEndpoint, { model: apiModel, prompt: fimPrompt, @@ -102,15 +102,15 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo 'Authorization': apiAuthentication } }); - + progress.report({ message: "Generating..." }); - + let completionText = response.data.response; completionText = completionText.replace(/||/g, '').trim(); - + // Split the completion text by new lines const lines = completionText.split('\n'); - + // Create a decoration for each line of the response const previewRanges = lines.map((line: string, idx: number) => { const linePos = new vscode.Position(position.line + idx, 0); @@ -126,20 +126,22 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo } }; }); - + // Apply the decorations for multiline preview textEditor.setDecorations(previewDecorationType, previewRanges); - + + let completionInserted = false; // Flag to track insertion + const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { if (event.document.uri.toString() === document.uri.toString()) { const change = event.contentChanges[0]; - + // Handle Backspace to decline the preview if (change && change.text === '' && change.rangeLength === 1) { textEditor.setDecorations(previewDecorationType, []); // Remove preview decorations disposable.dispose(); } - + // Handle Ctrl + Enter (or Cmd + Enter on macOS) to accept the preview const isCtrlOrCmdPressed = event.contentChanges.some( (change) => { @@ -148,28 +150,31 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo return isCtrlOrCmd; } ); - - if (isCtrlOrCmdPressed) { + + if (isCtrlOrCmdPressed && !completionInserted) { + // Ensure that we insert the completion text only once + completionInserted = true; + // Remove the preview decoration before applying the final completion textEditor.setDecorations(previewDecorationType, []); - + const edit = new vscode.WorkspaceEdit(); const insertPosition = new vscode.Position(position.line, 0); - - // Insert the completion only once + + // Avoid duplicating the completion text if (!document.getText().includes(completionText)) { edit.insert(document.uri, insertPosition, '\n' + completionText); await vscode.workspace.applyEdit(edit); } - + const newPosition = new vscode.Position(position.line + lines.length, lines[lines.length - 1].length); textEditor.selection = new vscode.Selection(newPosition, newPosition); - + disposable.dispose(); // Clean up the listener after accepting the completion } } }); - + } catch (err: any) { vscode.window.showErrorMessage( "Fabelous Autocoder encountered an error: " + err.message @@ -178,6 +183,8 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo } } ); + + } From 77e0dbc04898ef5b952bd3e65ac6ed1f21182cba Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 11 Sep 2024 09:54:28 +0200 Subject: [PATCH 02/25] not working multi line support --- src/extension.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index d2b801b..15fd0fe 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -110,11 +110,16 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo // Split the completion text by new lines const lines = completionText.split('\n'); - + // Create a decoration for each line of the response const previewRanges = lines.map((line: string, idx: number) => { - const linePos = new vscode.Position(position.line + idx, 0); - const range = new vscode.Range(linePos, linePos); // Set range at the start of each new line + // Determine the start and end positions for each line + const startPos = new vscode.Position(position.line + idx, 0); + const endPos = new vscode.Position(position.line + idx, line.length); + + // Create a range covering the whole line + const range = new vscode.Range(startPos, endPos); + return { range, renderOptions: { @@ -126,7 +131,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo } }; }); - + // Apply the decorations for multiline preview textEditor.setDecorations(previewDecorationType, previewRanges); From fb6e9a5d1f97a06dabbf1a1c3dd637c00d25a46b Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 11 Sep 2024 09:58:26 +0200 Subject: [PATCH 03/25] new approach --- src/extension.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 15fd0fe..4b30205 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -108,23 +108,23 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo let completionText = response.data.response; completionText = completionText.replace(/||/g, '').trim(); + // Split the completion text by new lines // Split the completion text by new lines const lines = completionText.split('\n'); // Create a decoration for each line of the response const previewRanges = lines.map((line: string, idx: number) => { - // Determine the start and end positions for each line + // Determine the start position for each line const startPos = new vscode.Position(position.line + idx, 0); - const endPos = new vscode.Position(position.line + idx, line.length); - - // Create a range covering the whole line - const range = new vscode.Range(startPos, endPos); + + // Create a range that decorates the line, spanning from start to start (empty range) + const range = new vscode.Range(startPos, startPos); return { range, renderOptions: { - before: { - contentText: line, + after: { + contentText: line, // Display the current line color: '#888888', fontStyle: 'italic', } @@ -134,6 +134,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo // Apply the decorations for multiline preview textEditor.setDecorations(previewDecorationType, previewRanges); + let completionInserted = false; // Flag to track insertion From ac7afb4b4ec7206784a4073c8a342716d735bff8 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 11 Sep 2024 11:28:55 +0200 Subject: [PATCH 04/25] step back --- src/extension.ts | 108 ++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 57 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 4b30205..319ee83 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -59,10 +59,13 @@ const previewDecorationType = vscode.window.createTextEditorDecorationType({ rangeBehavior: vscode.DecorationRangeBehavior.ClosedOpen, // Ensure proper handling of multiline decorations }); + + async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationToken?: vscode.CancellationToken) { const document = textEditor.document; const position = textEditor.selection.active; + // Get the context and create the FIM prompt const context = getContextLines(document, position); const fimPrompt = createFIMPrompt(context, document.languageId); @@ -85,7 +88,8 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo progressCancellationToken.onCancellationRequested(axiosCancelPost); vscode.workspace.onDidCloseTextDocument(axiosCancelPost); }); - + + // Make the API request const response = await axios.post(apiEndpoint, { model: apiModel, prompt: fimPrompt, @@ -107,80 +111,73 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo let completionText = response.data.response; completionText = completionText.replace(/||/g, '').trim(); - - // Split the completion text by new lines + // Split the completion text by new lines const lines = completionText.split('\n'); + + // Remove context lines and insert the preview + const startLine = Math.max(0, position.line - 1); // Start 1 line before the cursor + const endLine = position.line + 1; // End 1 line after the cursor + const rangeToReplace = new vscode.Range( + new vscode.Position(startLine, 0), + new vscode.Position(endLine, 0) + ); - // Create a decoration for each line of the response - const previewRanges = lines.map((line: string, idx: number) => { - // Determine the start position for each line - const startPos = new vscode.Position(position.line + idx, 0); - - // Create a range that decorates the line, spanning from start to start (empty range) - const range = new vscode.Range(startPos, startPos); - + // Apply grayed-out italic styling to the preview + const previewRanges = lines.map((line: string, index: number) => { + const linePos = new vscode.Position(startLine + index, 0); return { - range, + range: new vscode.Range(linePos, linePos), renderOptions: { - after: { - contentText: line, // Display the current line - color: '#888888', - fontStyle: 'italic', + before: { + contentText: line, + color: '#888888', // Grayed-out text + fontStyle: 'italic', // Italic text } } }; }); - // Apply the decorations for multiline preview + const previewDecorationType = vscode.window.createTextEditorDecorationType({ + color: '#888888', // Grayed-out color + fontStyle: 'italic', // Italic style + }); + + // Apply the preview as decoration textEditor.setDecorations(previewDecorationType, previewRanges); - - let completionInserted = false; // Flag to track insertion - + // Flag to ensure we only accept or dismiss once + let previewInserted = true; + + // Event handler to accept or dismiss the preview const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { if (event.document.uri.toString() === document.uri.toString()) { const change = event.contentChanges[0]; - - // Handle Backspace to decline the preview + + // Handle Backspace to dismiss the preview if (change && change.text === '' && change.rangeLength === 1) { - textEditor.setDecorations(previewDecorationType, []); // Remove preview decorations - disposable.dispose(); - } - - // Handle Ctrl + Enter (or Cmd + Enter on macOS) to accept the preview - const isCtrlOrCmdPressed = event.contentChanges.some( - (change) => { - const isMac = process.platform === 'darwin'; - const isCtrlOrCmd = isMac ? change.text.includes('\u0010') : change.text.includes('\n'); - return isCtrlOrCmd; - } - ); - - if (isCtrlOrCmdPressed && !completionInserted) { - // Ensure that we insert the completion text only once - completionInserted = true; - - // Remove the preview decoration before applying the final completion + // Remove the decoration preview textEditor.setDecorations(previewDecorationType, []); - + disposable.dispose(); // Clean up event listener + previewInserted = false; + } + + // Handle Enter to accept the preview + if (change && change.text === '\n' && previewInserted) { + // Remove the decoration preview and insert actual completion text + textEditor.setDecorations(previewDecorationType, []); // Remove decorations + const edit = new vscode.WorkspaceEdit(); - const insertPosition = new vscode.Position(position.line, 0); - - // Avoid duplicating the completion text - if (!document.getText().includes(completionText)) { - edit.insert(document.uri, insertPosition, '\n' + completionText); - await vscode.workspace.applyEdit(edit); - } - - const newPosition = new vscode.Position(position.line + lines.length, lines[lines.length - 1].length); - textEditor.selection = new vscode.Selection(newPosition, newPosition); - - disposable.dispose(); // Clean up the listener after accepting the completion + const acceptedText = completionText; + edit.replace(document.uri, rangeToReplace, acceptedText); // Insert actual completion + await vscode.workspace.applyEdit(edit); + + disposable.dispose(); // Clean up event listener + previewInserted = false; } } }); - + } catch (err: any) { vscode.window.showErrorMessage( "Fabelous Autocoder encountered an error: " + err.message @@ -189,12 +186,9 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo } } ); - - } - async function provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, cancellationToken: vscode.CancellationToken) { const item = new vscode.CompletionItem("Fabelous autocompletion"); item.insertText = new vscode.SnippetString('${1:}'); From 66c88a053b40c7c688e8f3cd97c486c42f57fae7 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Mon, 23 Sep 2024 09:13:03 +0200 Subject: [PATCH 05/25] having now switech to use tab to complete completion, but the preview itself is still a mess --- package.json | 2 +- src/extension.ts | 54 +++++++++++++++++++++--------------------------- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index 0f2c205..6b6d545 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fabelous-autocoder", - "version": "0.1.7", + "version": "0.2.49", "displayName": "Fabelous Autocoder", "description": "A simple to use Ollama autocompletion Plugin", "icon": "icon.png", diff --git a/src/extension.ts b/src/extension.ts index 319ee83..0d06f9c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -48,7 +48,6 @@ function getContextLines(document: vscode.TextDocument, position: vscode.Positio return lines.join("\n"); } - function createFIMPrompt(prefix: string, language: string): string { return `${prefix}${language}\n`; } @@ -59,8 +58,6 @@ const previewDecorationType = vscode.window.createTextEditorDecorationType({ rangeBehavior: vscode.DecorationRangeBehavior.ClosedOpen, // Ensure proper handling of multiline decorations }); - - async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationToken?: vscode.CancellationToken) { const document = textEditor.document; const position = textEditor.selection.active; @@ -78,7 +75,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo async (progress, progressCancellationToken) => { try { progress.report({ message: "Starting model..." }); - + let axiosCancelPost: () => void; const axiosCancelToken = new axios.CancelToken((c) => { axiosCancelPost = () => { @@ -106,24 +103,25 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo 'Authorization': apiAuthentication } }); - + progress.report({ message: "Generating..." }); - + let completionText = response.data.response; completionText = completionText.replace(/||/g, '').trim(); // Split the completion text by new lines const lines = completionText.split('\n'); - - // Remove context lines and insert the preview - const startLine = Math.max(0, position.line - 1); // Start 1 line before the cursor - const endLine = position.line + 1; // End 1 line after the cursor + + // Define the preview start and end lines + const startLine = Math.max(0, position.line); // Use cursor position directly + const endLine = position.line + lines.length; + const rangeToReplace = new vscode.Range( new vscode.Position(startLine, 0), new vscode.Position(endLine, 0) ); - // Apply grayed-out italic styling to the preview + // Apply the preview with multi-line support const previewRanges = lines.map((line: string, index: number) => { const linePos = new vscode.Position(startLine + index, 0); return { @@ -138,39 +136,33 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo }; }); - const previewDecorationType = vscode.window.createTextEditorDecorationType({ - color: '#888888', // Grayed-out color - fontStyle: 'italic', // Italic style - }); - - // Apply the preview as decoration textEditor.setDecorations(previewDecorationType, previewRanges); - // Flag to ensure we only accept or dismiss once let previewInserted = true; - // Event handler to accept or dismiss the preview + // Handle preview acceptance or dismissal with Tab and Backspace const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { if (event.document.uri.toString() === document.uri.toString()) { const change = event.contentChanges[0]; - // Handle Backspace to dismiss the preview + // Dismiss preview with Backspace if (change && change.text === '' && change.rangeLength === 1) { - // Remove the decoration preview - textEditor.setDecorations(previewDecorationType, []); + textEditor.setDecorations(previewDecorationType, []); // Clear preview disposable.dispose(); // Clean up event listener previewInserted = false; } - // Handle Enter to accept the preview - if (change && change.text === '\n' && previewInserted) { - // Remove the decoration preview and insert actual completion text - textEditor.setDecorations(previewDecorationType, []); // Remove decorations - + // Accept preview with Tab key + if (change && change.text === '\t' && previewInserted) { + textEditor.setDecorations(previewDecorationType, []); // Remove preview decorations const edit = new vscode.WorkspaceEdit(); - const acceptedText = completionText; - edit.replace(document.uri, rangeToReplace, acceptedText); // Insert actual completion + + // Replace the context with the actual completion + const acceptedText = completionText; + edit.replace(document.uri, rangeToReplace, acceptedText); + await vscode.workspace.applyEdit(edit); + await document.save(); // Optionally save after replacing disposable.dispose(); // Clean up event listener previewInserted = false; @@ -192,11 +184,11 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo async function provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, cancellationToken: vscode.CancellationToken) { const item = new vscode.CompletionItem("Fabelous autocompletion"); item.insertText = new vscode.SnippetString('${1:}'); - + if (responsePreview) { await new Promise(resolve => setTimeout(resolve, responsePreviewDelay * 1000)); if (cancellationToken.isCancellationRequested) { - return [ item ]; + return [item]; } const context = getContextLines(document, position); From 27571fcad8e60708fbbe453fcb7c7588d3fa9dd8 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Mon, 23 Sep 2024 13:25:12 +0200 Subject: [PATCH 06/25] still only single line --- src/extension.ts | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 0d06f9c..7088f95 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -112,31 +112,27 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo // Split the completion text by new lines const lines = completionText.split('\n'); - // Define the preview start and end lines - const startLine = Math.max(0, position.line); // Use cursor position directly - const endLine = position.line + lines.length; + // Define the preview start line + const startLine = position.line; - const rangeToReplace = new vscode.Range( - new vscode.Position(startLine, 0), - new vscode.Position(endLine, 0) - ); - - // Apply the preview with multi-line support + // Apply the preview line by line const previewRanges = lines.map((line: string, index: number) => { - const linePos = new vscode.Position(startLine + index, 0); + const linePos = new vscode.Position(startLine + index, 0); // Position each line on a new line + const range = new vscode.Range(linePos, linePos); // Create range for the line + return { - range: new vscode.Range(linePos, linePos), + range: range, // Range for the line renderOptions: { - before: { + after: { contentText: line, color: '#888888', // Grayed-out text - fontStyle: 'italic', // Italic text + fontStyle: 'italic', // Italic text for preview } } }; }); - textEditor.setDecorations(previewDecorationType, previewRanges); + textEditor.setDecorations(previewDecorationType, previewRanges); // Apply the decorations for the preview let previewInserted = true; @@ -159,6 +155,13 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo // Replace the context with the actual completion const acceptedText = completionText; + const endLine = startLine + lines.length; + + const rangeToReplace = new vscode.Range( + new vscode.Position(startLine, 0), + new vscode.Position(endLine, 0) + ); + edit.replace(document.uri, rangeToReplace, acceptedText); await vscode.workspace.applyEdit(edit); From 2a46e061d336a342ec60bff90dc5a716b0575fc1 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Mon, 23 Sep 2024 14:17:23 +0200 Subject: [PATCH 07/25] Updated to show now also the used contet in preview (small step progress) --- package.json | 2 +- src/extension.ts | 36 ++++++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 6b6d545..635a727 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fabelous-autocoder", - "version": "0.2.49", + "version": "0.2.58", "displayName": "Fabelous Autocoder", "description": "A simple to use Ollama autocompletion Plugin", "icon": "icon.png", diff --git a/src/extension.ts b/src/extension.ts index 7088f95..81d65e7 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -112,14 +112,27 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo // Split the completion text by new lines const lines = completionText.split('\n'); - // Define the preview start line - const startLine = position.line; + // Define the start line and end line based on context (adjusting the preview to the same lines) + let startLine = position.line - (context.split('\n').length - 1); + if (startLine < 0) startLine = 0; // Ensure startLine is not negative + + const endLine = Math.min(startLine + lines.length, document.lineCount); // Ensure endLine doesn't exceed document line count + + // Apply the preview line by line over the context range + const previewRanges: vscode.DecorationOptions[] = lines.map((line: string, index: number): vscode.DecorationOptions | null => { + const currentLine = startLine + index; + + // Ensure the current line is within the bounds of the document + if (currentLine >= document.lineCount) return null; + + const _ = new vscode.Position(currentLine, 0); // Position for each preview line + const endOfLine = document.lineAt(currentLine).text.length; // Full range for the current line + + const range = new vscode.Range( + new vscode.Position(currentLine, 0), + new vscode.Position(currentLine, endOfLine) + ); // Create range for each line - // Apply the preview line by line - const previewRanges = lines.map((line: string, index: number) => { - const linePos = new vscode.Position(startLine + index, 0); // Position each line on a new line - const range = new vscode.Range(linePos, linePos); // Create range for the line - return { range: range, // Range for the line renderOptions: { @@ -130,9 +143,8 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo } } }; - }); - - textEditor.setDecorations(previewDecorationType, previewRanges); // Apply the decorations for the preview + }).filter((range: vscode.DecorationOptions | null): range is vscode.DecorationOptions => range !== null); // Filter out any invalid ranges + textEditor.setDecorations(previewDecorationType, previewRanges as any); // Apply the decorations for the preview let previewInserted = true; @@ -155,11 +167,10 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo // Replace the context with the actual completion const acceptedText = completionText; - const endLine = startLine + lines.length; const rangeToReplace = new vscode.Range( new vscode.Position(startLine, 0), - new vscode.Position(endLine, 0) + new vscode.Position(endLine, document.lineAt(endLine - 1).text.length) ); edit.replace(document.uri, rangeToReplace, acceptedText); @@ -184,6 +195,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo } + async function provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, cancellationToken: vscode.CancellationToken) { const item = new vscode.CompletionItem("Fabelous autocompletion"); item.insertText = new vscode.SnippetString('${1:}'); From 8e72d08d53f079fc21bc5e73618450bf4074c641 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Mon, 23 Sep 2024 15:17:23 +0200 Subject: [PATCH 08/25] the preview is now working in one row, without the context --- package.json | 2 +- src/extension.ts | 69 ++++++++++++++++++++---------------------------- 2 files changed, 30 insertions(+), 41 deletions(-) diff --git a/package.json b/package.json index 635a727..23cdc39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fabelous-autocoder", - "version": "0.2.58", + "version": "0.2.6", "displayName": "Fabelous Autocoder", "description": "A simple to use Ollama autocompletion Plugin", "icon": "icon.png", diff --git a/src/extension.ts b/src/extension.ts index 81d65e7..8b6816a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -53,11 +53,14 @@ function createFIMPrompt(prefix: string, language: string): string { } const previewDecorationType = vscode.window.createTextEditorDecorationType({ - color: '#888888', // Grayed-out preview text - fontStyle: 'italic', - rangeBehavior: vscode.DecorationRangeBehavior.ClosedOpen, // Ensure proper handling of multiline decorations + after: { + color: '#888888', // Grayed-out preview text + fontStyle: 'italic', + }, + textDecoration: 'none; display: none;', // Hide the original text }); + async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationToken?: vscode.CancellationToken) { const document = textEditor.document; const position = textEditor.selection.active; @@ -112,73 +115,59 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo // Split the completion text by new lines const lines = completionText.split('\n'); - // Define the start line and end line based on context (adjusting the preview to the same lines) - let startLine = position.line - (context.split('\n').length - 1); - if (startLine < 0) startLine = 0; // Ensure startLine is not negative + // Define the start line based on the current position + const startLine = position.line; + const endLine = Math.min(startLine + lines.length, document.lineCount); - const endLine = Math.min(startLine + lines.length, document.lineCount); // Ensure endLine doesn't exceed document line count + // Apply the preview, replacing existing content + const previewRanges: vscode.DecorationOptions[] = []; + for (let i = 0; i < lines.length; i++) { + const currentLine = startLine + i; + if (currentLine >= document.lineCount) break; - // Apply the preview line by line over the context range - const previewRanges: vscode.DecorationOptions[] = lines.map((line: string, index: number): vscode.DecorationOptions | null => { - const currentLine = startLine + index; - - // Ensure the current line is within the bounds of the document - if (currentLine >= document.lineCount) return null; - - const _ = new vscode.Position(currentLine, 0); // Position for each preview line - const endOfLine = document.lineAt(currentLine).text.length; // Full range for the current line - - const range = new vscode.Range( - new vscode.Position(currentLine, 0), - new vscode.Position(currentLine, endOfLine) - ); // Create range for each line - - return { - range: range, // Range for the line + const range = document.lineAt(currentLine).range; + previewRanges.push({ + range, renderOptions: { after: { - contentText: line, - color: '#888888', // Grayed-out text - fontStyle: 'italic', // Italic text for preview + contentText: lines[i], } } - }; - }).filter((range: vscode.DecorationOptions | null): range is vscode.DecorationOptions => range !== null); // Filter out any invalid ranges - textEditor.setDecorations(previewDecorationType, previewRanges as any); // Apply the decorations for the preview + }); + } + + textEditor.setDecorations(previewDecorationType, previewRanges); let previewInserted = true; - // Handle preview acceptance or dismissal with Tab and Backspace + // Handle preview acceptance or dismissal const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { if (event.document.uri.toString() === document.uri.toString()) { const change = event.contentChanges[0]; // Dismiss preview with Backspace if (change && change.text === '' && change.rangeLength === 1) { - textEditor.setDecorations(previewDecorationType, []); // Clear preview - disposable.dispose(); // Clean up event listener + textEditor.setDecorations(previewDecorationType, []); + disposable.dispose(); previewInserted = false; } // Accept preview with Tab key if (change && change.text === '\t' && previewInserted) { - textEditor.setDecorations(previewDecorationType, []); // Remove preview decorations + textEditor.setDecorations(previewDecorationType, []); const edit = new vscode.WorkspaceEdit(); - // Replace the context with the actual completion - const acceptedText = completionText; - const rangeToReplace = new vscode.Range( new vscode.Position(startLine, 0), - new vscode.Position(endLine, document.lineAt(endLine - 1).text.length) + new vscode.Position(endLine, document.lineAt(endLine - 1).range.end.character) ); - edit.replace(document.uri, rangeToReplace, acceptedText); + edit.replace(document.uri, rangeToReplace, completionText); await vscode.workspace.applyEdit(edit); await document.save(); // Optionally save after replacing - disposable.dispose(); // Clean up event listener + disposable.dispose(); previewInserted = false; } } From 9109c199e656f0c09201d364d0bd814866e3e2e5 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 2 Oct 2024 15:40:11 +0300 Subject: [PATCH 09/25] so preview and complition is technially working, but it overwrites in preview mode the current code --- package.json | 2 +- src/extension.ts | 61 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 23cdc39..3744b0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fabelous-autocoder", - "version": "0.2.6", + "version": "0.2.61", "displayName": "Fabelous Autocoder", "description": "A simple to use Ollama autocompletion Plugin", "icon": "icon.png", diff --git a/src/extension.ts b/src/extension.ts index 8b6816a..f37b3df 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -48,6 +48,7 @@ function getContextLines(document: vscode.TextDocument, position: vscode.Positio return lines.join("\n"); } + function createFIMPrompt(prefix: string, language: string): string { return `${prefix}${language}\n`; } @@ -59,14 +60,26 @@ const previewDecorationType = vscode.window.createTextEditorDecorationType({ }, textDecoration: 'none; display: none;', // Hide the original text }); +// Generate extra lines for the preview +function generateExtraPreviewLines(document: vscode.TextDocument, position: vscode.Position, numLines: number): string[] { + const extraLines = []; + const startLine = position.line + 1; + const endLine = Math.min(document.lineCount - 1, startLine + numLines - 1); + for (let i = startLine; i <= endLine; i++) { + extraLines.push(document.lineAt(i).text); + } + + return extraLines; +} async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationToken?: vscode.CancellationToken) { const document = textEditor.document; const position = textEditor.selection.active; - - // Get the context and create the FIM prompt + const contextLines = 2; + const startLine = Math.max(0, position.line - contextLines); const context = getContextLines(document, position); + const fimPrompt = createFIMPrompt(context, document.languageId); vscode.window.withProgress( @@ -89,6 +102,9 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo vscode.workspace.onDidCloseTextDocument(axiosCancelPost); }); + // Increase the number of tokens to predict + const extendedNumPredict = numPredict * 2; // Adjust this multiplier as needed + // Make the API request const response = await axios.post(apiEndpoint, { model: apiModel, @@ -96,7 +112,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo stream: false, raw: true, options: { - num_predict: numPredict, + num_predict: extendedNumPredict, temperature: apiTemperature, stop: ["", "```"] } @@ -113,24 +129,25 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo completionText = completionText.replace(/||/g, '').trim(); // Split the completion text by new lines - const lines = completionText.split('\n'); + const newLines = completionText.split('\n'); - // Define the start line based on the current position - const startLine = position.line; - const endLine = Math.min(startLine + lines.length, document.lineCount); + // Calculate the number of new lines in the completion + const completionLineCount = newLines.length; - // Apply the preview, replacing existing content + // Ensure we have at least as many new lines as the completion, plus some extra + const extraLines = 1; // You can adjust this number + const totalNewLines = Math.max(completionLineCount + extraLines, position.line - startLine + 1); + + // Create preview decorations const previewRanges: vscode.DecorationOptions[] = []; - for (let i = 0; i < lines.length; i++) { - const currentLine = startLine + i; - if (currentLine >= document.lineCount) break; - - const range = document.lineAt(currentLine).range; + for (let i = 0; i < totalNewLines; i++) { + const lineContent = i < newLines.length ? newLines[i] : ''; + const range = new vscode.Range(startLine + i, 0, startLine + i + 1, 0); previewRanges.push({ range, renderOptions: { after: { - contentText: lines[i], + contentText: lineContent, } } }); @@ -157,15 +174,19 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo textEditor.setDecorations(previewDecorationType, []); const edit = new vscode.WorkspaceEdit(); - const rangeToReplace = new vscode.Range( - new vscode.Position(startLine, 0), - new vscode.Position(endLine, document.lineAt(endLine - 1).range.end.character) - ); + // Create the text to insert: completion text plus extra newlines + const insertText = completionText + '\n'.repeat(Math.max(0, totalNewLines - completionLineCount)); - edit.replace(document.uri, rangeToReplace, completionText); + // Replace the context with the new text + const replaceRange = new vscode.Range(startLine, 0, position.line + 1, 0); + edit.replace(document.uri, replaceRange, insertText); await vscode.workspace.applyEdit(edit); - await document.save(); // Optionally save after replacing + await document.save(); // Optionally save after inserting + + // Move the cursor to the end of the inserted completion + const newPosition = new vscode.Position(startLine + totalNewLines, 0); + textEditor.selection = new vscode.Selection(newPosition, newPosition); disposable.dispose(); previewInserted = false; From 0416896254bdb947b35f8e4e75a01f7903fb56a6 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 2 Oct 2024 15:48:08 +0300 Subject: [PATCH 10/25] fixed bug for showing header --- package.json | 2 +- src/extension.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3744b0b..10be8e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fabelous-autocoder", - "version": "0.2.61", + "version": "0.2.74", "displayName": "Fabelous Autocoder", "description": "A simple to use Ollama autocompletion Plugin", "icon": "icon.png", diff --git a/src/extension.ts b/src/extension.ts index f37b3df..ac30149 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -178,7 +178,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo const insertText = completionText + '\n'.repeat(Math.max(0, totalNewLines - completionLineCount)); // Replace the context with the new text - const replaceRange = new vscode.Range(startLine, 0, position.line + 1, 0); + const replaceRange = new vscode.Range(startLine, 0, position.line, 0); edit.replace(document.uri, replaceRange, insertText); await vscode.workspace.applyEdit(edit); From ffd22f7cc6fb642b3bc6346f06db074471f33ba8 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Thu, 3 Oct 2024 11:43:47 +0300 Subject: [PATCH 11/25] fixed --- src/extension.ts | 107 +++++++++++++++++++---------------------------- 1 file changed, 44 insertions(+), 63 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index ac30149..d44e9e3 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -60,18 +60,6 @@ const previewDecorationType = vscode.window.createTextEditorDecorationType({ }, textDecoration: 'none; display: none;', // Hide the original text }); -// Generate extra lines for the preview -function generateExtraPreviewLines(document: vscode.TextDocument, position: vscode.Position, numLines: number): string[] { - const extraLines = []; - const startLine = position.line + 1; - const endLine = Math.min(document.lineCount - 1, startLine + numLines - 1); - - for (let i = startLine; i <= endLine; i++) { - extraLines.push(document.lineAt(i).text); - } - - return extraLines; -} async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationToken?: vscode.CancellationToken) { const document = textEditor.document; @@ -102,9 +90,6 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo vscode.workspace.onDidCloseTextDocument(axiosCancelPost); }); - // Increase the number of tokens to predict - const extendedNumPredict = numPredict * 2; // Adjust this multiplier as needed - // Make the API request const response = await axios.post(apiEndpoint, { model: apiModel, @@ -112,7 +97,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo stream: false, raw: true, options: { - num_predict: extendedNumPredict, + num_predict: numPredict, temperature: apiTemperature, stop: ["", "```"] } @@ -131,12 +116,8 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo // Split the completion text by new lines const newLines = completionText.split('\n'); - // Calculate the number of new lines in the completion - const completionLineCount = newLines.length; - - // Ensure we have at least as many new lines as the completion, plus some extra - const extraLines = 1; // You can adjust this number - const totalNewLines = Math.max(completionLineCount + extraLines, position.line - startLine + 1); + // Calculate the number of new lines in the completion, plus 2 extra lines + const totalNewLines = newLines.length + 2; // Create preview decorations const previewRanges: vscode.DecorationOptions[] = []; @@ -158,54 +139,54 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo let previewInserted = true; // Handle preview acceptance or dismissal - const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { - if (event.document.uri.toString() === document.uri.toString()) { - const change = event.contentChanges[0]; + // Handle preview acceptance or dismissal + const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { + if (event.document.uri.toString() === document.uri.toString()) { + const change = event.contentChanges[0]; - // Dismiss preview with Backspace - if (change && change.text === '' && change.rangeLength === 1) { - textEditor.setDecorations(previewDecorationType, []); - disposable.dispose(); - previewInserted = false; - } - - // Accept preview with Tab key - if (change && change.text === '\t' && previewInserted) { - textEditor.setDecorations(previewDecorationType, []); - const edit = new vscode.WorkspaceEdit(); - - // Create the text to insert: completion text plus extra newlines - const insertText = completionText + '\n'.repeat(Math.max(0, totalNewLines - completionLineCount)); - - // Replace the context with the new text - const replaceRange = new vscode.Range(startLine, 0, position.line, 0); - edit.replace(document.uri, replaceRange, insertText); - - await vscode.workspace.applyEdit(edit); - await document.save(); // Optionally save after inserting - - // Move the cursor to the end of the inserted completion - const newPosition = new vscode.Position(startLine + totalNewLines, 0); - textEditor.selection = new vscode.Selection(newPosition, newPosition); - - disposable.dispose(); - previewInserted = false; - } + // Dismiss preview with Backspace + if (change && change.text === '' && change.rangeLength === 1) { + textEditor.setDecorations(previewDecorationType, []); + disposable.dispose(); + previewInserted = false; } - }); - } catch (err: any) { - vscode.window.showErrorMessage( - "Fabelous Autocoder encountered an error: " + err.message - ); - console.log(err); - } + // Accept preview with Tab key + if (change && change.text === '\t' && previewInserted) { + textEditor.setDecorations(previewDecorationType, []); + const edit = new vscode.WorkspaceEdit(); + + // Insert the completion text + const insertText = '\n'.repeat(1) + completionText + '\n'.repeat(1); // Add 2 extra newlines + + // Replace the entire range from the start of the context to the current position + const replaceRange = new vscode.Range(startLine, 0, position.line, position.character); + edit.replace(document.uri, replaceRange, insertText); + + await vscode.workspace.applyEdit(edit); + await document.save(); // Optionally save after inserting + + // Move the cursor to the end of the inserted completion + const newPosition = new vscode.Position(startLine + totalNewLines - 2, 0); + textEditor.selection = new vscode.Selection(newPosition, newPosition); + + disposable.dispose(); + previewInserted = false; + } + } + }); + + } catch (err: any) { + vscode.window.showErrorMessage( + "Fabelous Autocoder encountered an error: " + err.message + ); + console.log(err); } - ); + } +); } - async function provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, cancellationToken: vscode.CancellationToken) { const item = new vscode.CompletionItem("Fabelous autocompletion"); item.insertText = new vscode.SnippetString('${1:}'); From 6a953b7a126770f1229a87e4f6c78d62a37a3de1 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Thu, 3 Oct 2024 12:03:05 +0300 Subject: [PATCH 12/25] preview extra lines --- src/extension.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index d44e9e3..d233ffa 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -119,11 +119,21 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo // Calculate the number of new lines in the completion, plus 2 extra lines const totalNewLines = newLines.length + 2; - // Create preview decorations + // Create preview decorations and insert new lines const previewRanges: vscode.DecorationOptions[] = []; + const edit = new vscode.WorkspaceEdit(); + const document = textEditor.document; + for (let i = 0; i < totalNewLines; i++) { const lineContent = i < newLines.length ? newLines[i] : ''; - const range = new vscode.Range(startLine + i, 0, startLine + i + 1, 0); + const position = new vscode.Position(startLine + i, 0); + + // Insert a new line + edit.insert(document.uri, position, '\n'); + + // Create a range for the newly inserted line + const range = new vscode.Range(position, position.translate(1, 0)); + previewRanges.push({ range, renderOptions: { @@ -134,11 +144,14 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo }); } + // Apply the edit to insert new lines + await vscode.workspace.applyEdit(edit); + + // Set decorations on the newly inserted lines textEditor.setDecorations(previewDecorationType, previewRanges); let previewInserted = true; - // Handle preview acceptance or dismissal // Handle preview acceptance or dismissal const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { if (event.document.uri.toString() === document.uri.toString()) { From 77e328fceef0b9f056502067576293ab6fc75efb Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Thu, 3 Oct 2024 12:20:38 +0300 Subject: [PATCH 13/25] near final --- src/extension.ts | 170 +++++++++++++++++++++++++---------------------- 1 file changed, 91 insertions(+), 79 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index d233ffa..0e417f5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -61,6 +61,7 @@ const previewDecorationType = vscode.window.createTextEditorDecorationType({ textDecoration: 'none; display: none;', // Hide the original text }); + async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationToken?: vscode.CancellationToken) { const document = textEditor.document; const position = textEditor.selection.active; @@ -113,90 +114,101 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo let completionText = response.data.response; completionText = completionText.replace(/||/g, '').trim(); - // Split the completion text by new lines - const newLines = completionText.split('\n'); + let previewInserted = false; + let originalContent: string; + let previewStartLine: number; - // Calculate the number of new lines in the completion, plus 2 extra lines - const totalNewLines = newLines.length + 2; + // Store the original content and insert preview +const storeAndInsertPreview = async () => { + previewStartLine = startLine; + const endLine = document.lineCount - 1; + const endCharacter = document.lineAt(endLine).text.length; + const fullRange = new vscode.Range(startLine, 0, endLine, endCharacter); + originalContent = document.getText(fullRange); - // Create preview decorations and insert new lines - const previewRanges: vscode.DecorationOptions[] = []; - const edit = new vscode.WorkspaceEdit(); - const document = textEditor.document; + const previewContent = completionText + '\n'.repeat(1); + const edit = new vscode.WorkspaceEdit(); + // Overwrite the context by replacing instead of inserting + edit.replace(document.uri, fullRange, previewContent); + await vscode.workspace.applyEdit(edit); - for (let i = 0; i < totalNewLines; i++) { - const lineContent = i < newLines.length ? newLines[i] : ''; - const position = new vscode.Position(startLine + i, 0); - - // Insert a new line - edit.insert(document.uri, position, '\n'); - - // Create a range for the newly inserted line - const range = new vscode.Range(position, position.translate(1, 0)); - - previewRanges.push({ - range, - renderOptions: { - after: { - contentText: lineContent, - } - } - }); + // Set decorations on the newly inserted lines + const previewRanges: vscode.DecorationOptions[] = []; + const previewLines = previewContent.split('\n'); + for (let i = 0; i < previewLines.length; i++) { + const range = new vscode.Range(startLine + i, 0, startLine + i + 1, 0); + previewRanges.push({ + range, + renderOptions: { + after: { + contentText: previewLines[i], } - - // Apply the edit to insert new lines - await vscode.workspace.applyEdit(edit); - - // Set decorations on the newly inserted lines - textEditor.setDecorations(previewDecorationType, previewRanges); - - let previewInserted = true; - - // Handle preview acceptance or dismissal - const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { - if (event.document.uri.toString() === document.uri.toString()) { - const change = event.contentChanges[0]; - - // Dismiss preview with Backspace - if (change && change.text === '' && change.rangeLength === 1) { - textEditor.setDecorations(previewDecorationType, []); - disposable.dispose(); - previewInserted = false; - } - - // Accept preview with Tab key - if (change && change.text === '\t' && previewInserted) { - textEditor.setDecorations(previewDecorationType, []); - const edit = new vscode.WorkspaceEdit(); - - // Insert the completion text - const insertText = '\n'.repeat(1) + completionText + '\n'.repeat(1); // Add 2 extra newlines - - // Replace the entire range from the start of the context to the current position - const replaceRange = new vscode.Range(startLine, 0, position.line, position.character); - edit.replace(document.uri, replaceRange, insertText); - - await vscode.workspace.applyEdit(edit); - await document.save(); // Optionally save after inserting - - // Move the cursor to the end of the inserted completion - const newPosition = new vscode.Position(startLine + totalNewLines - 2, 0); - textEditor.selection = new vscode.Selection(newPosition, newPosition); - - disposable.dispose(); - previewInserted = false; - } - } - }); - - } catch (err: any) { - vscode.window.showErrorMessage( - "Fabelous Autocoder encountered an error: " + err.message - ); - console.log(err); - } + } + }); } -); + textEditor.setDecorations(previewDecorationType, previewRanges); + previewInserted = true; +}; + + + // Handle preview acceptance or dismissal + const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { + if (event.document.uri.toString() === document.uri.toString() && previewInserted) { + const change = event.contentChanges[0]; + + // Dismiss preview with Backspace + if (change && change.text === '' && change.rangeLength === 1) { + await restoreOriginalContent(); + } + + // Accept preview with Tab key + if (change && change.text === '\t') { + await acceptPreview(); + } + } + }); + + const restoreOriginalContent = async () => { + const edit = new vscode.WorkspaceEdit(); + const endLine = document.lineCount - 1; + const endCharacter = document.lineAt(endLine).text.length; + const fullRange = new vscode.Range(previewStartLine, 0, endLine, endCharacter); + edit.replace(document.uri, fullRange, originalContent); + await vscode.workspace.applyEdit(edit); + textEditor.setDecorations(previewDecorationType, []); + previewInserted = false; + disposable.dispose(); + }; + + const acceptPreview = async () => { + textEditor.setDecorations(previewDecorationType, []); + const edit = new vscode.WorkspaceEdit(); + const endLine = document.lineCount - 1; + const endCharacter = document.lineAt(endLine).text.length; + const fullRange = new vscode.Range(previewStartLine, 0, endLine, endCharacter); + edit.replace(document.uri, fullRange, completionText); + await vscode.workspace.applyEdit(edit); + await document.save(); + + // Move the cursor to the end of the inserted completion + const newPosition = new vscode.Position(previewStartLine + completionText.split('\n').length - 1, 0); + textEditor.selection = new vscode.Selection(newPosition, newPosition); + + previewInserted = false; + disposable.dispose(); + }; + + // Call this function to initiate the preview + await storeAndInsertPreview(); + + } catch (err: any) { + vscode.window.showErrorMessage( + "Fabelous Autocoder encountered an error: " + err.message + ); + console.log(err); + } + } + ); } From ccd35283253f0e20a298f03f3a8c62a09331264b Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Thu, 3 Oct 2024 12:41:27 +0300 Subject: [PATCH 14/25] minor incosnitentcy with removing --- src/extension.ts | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 0e417f5..1ec7535 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -119,6 +119,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo let previewStartLine: number; // Store the original content and insert preview +// Store the original content and insert preview const storeAndInsertPreview = async () => { previewStartLine = startLine; const endLine = document.lineCount - 1; @@ -128,15 +129,18 @@ const storeAndInsertPreview = async () => { const previewContent = completionText + '\n'.repeat(1); const edit = new vscode.WorkspaceEdit(); - // Overwrite the context by replacing instead of inserting - edit.replace(document.uri, fullRange, previewContent); + edit.replace(document.uri, fullRange, previewContent); // Overwrite the context await vscode.workspace.applyEdit(edit); + // Move the cursor to the start of the inserted content for easy dismissal + const newPosition = new vscode.Position(previewStartLine, 0); + textEditor.selection = new vscode.Selection(newPosition, newPosition); + // Set decorations on the newly inserted lines const previewRanges: vscode.DecorationOptions[] = []; const previewLines = previewContent.split('\n'); for (let i = 0; i < previewLines.length; i++) { - const range = new vscode.Range(startLine + i, 0, startLine + i + 1, 0); + const range = new vscode.Range(previewStartLine + i, 0, previewStartLine + i + 1, 0); previewRanges.push({ range, renderOptions: { @@ -160,7 +164,6 @@ const storeAndInsertPreview = async () => { if (change && change.text === '' && change.rangeLength === 1) { await restoreOriginalContent(); } - // Accept preview with Tab key if (change && change.text === '\t') { await acceptPreview(); @@ -178,7 +181,15 @@ const storeAndInsertPreview = async () => { textEditor.setDecorations(previewDecorationType, []); previewInserted = false; disposable.dispose(); + + // Move the cursor to the end of the original content + const originalLines = originalContent.split('\n'); + const lastLine = previewStartLine + originalLines.length - 1; + const lastLineLength = originalLines[originalLines.length - 1].length; + const newPosition = new vscode.Position(lastLine, lastLineLength); + textEditor.selection = new vscode.Selection(newPosition, newPosition); }; + const acceptPreview = async () => { textEditor.setDecorations(previewDecorationType, []); @@ -191,13 +202,17 @@ const storeAndInsertPreview = async () => { await document.save(); // Move the cursor to the end of the inserted completion - const newPosition = new vscode.Position(previewStartLine + completionText.split('\n').length - 1, 0); + const completionLines = completionText.split('\n'); + const lastLine = previewStartLine + completionLines.length - 1; + const lastLineLength = completionLines[completionLines.length - 1].length; + const newPosition = new vscode.Position(lastLine, lastLineLength); textEditor.selection = new vscode.Selection(newPosition, newPosition); previewInserted = false; disposable.dispose(); }; + // Call this function to initiate the preview await storeAndInsertPreview(); From b6241855fc02d2e8b7161658193fb5bbaee56fb4 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Fri, 4 Oct 2024 13:00:14 +0300 Subject: [PATCH 15/25] bug in approving --- src/extension.ts | 93 +++++++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 1ec7535..cfa1354 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -118,8 +118,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo let originalContent: string; let previewStartLine: number; - // Store the original content and insert preview -// Store the original content and insert preview + // Store the original content and insert preview const storeAndInsertPreview = async () => { previewStartLine = startLine; const endLine = document.lineCount - 1; @@ -132,13 +131,15 @@ const storeAndInsertPreview = async () => { edit.replace(document.uri, fullRange, previewContent); // Overwrite the context await vscode.workspace.applyEdit(edit); - // Move the cursor to the start of the inserted content for easy dismissal - const newPosition = new vscode.Position(previewStartLine, 0); + // Move the cursor to the end of the inserted content + const previewLines = previewContent.split('\n'); + const lastPreviewLineIndex = previewStartLine + previewLines.length - 1; + const lastPreviewLine = previewLines[previewLines.length - 1]; + const newPosition = new vscode.Position(lastPreviewLineIndex, lastPreviewLine.length); textEditor.selection = new vscode.Selection(newPosition, newPosition); // Set decorations on the newly inserted lines const previewRanges: vscode.DecorationOptions[] = []; - const previewLines = previewContent.split('\n'); for (let i = 0; i < previewLines.length; i++) { const range = new vscode.Range(previewStartLine + i, 0, previewStartLine + i + 1, 0); previewRanges.push({ @@ -154,62 +155,74 @@ const storeAndInsertPreview = async () => { previewInserted = true; }; + const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { + if (event.document.uri.toString() === document.uri.toString() && previewInserted) { + console.log("URI matches and preview inserted"); + if (event.contentChanges.length > 0) { + const change = event.contentChanges[0]; + console.log("Change:", change); - // Handle preview acceptance or dismissal - const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { - if (event.document.uri.toString() === document.uri.toString() && previewInserted) { - const change = event.contentChanges[0]; - - // Dismiss preview with Backspace - if (change && change.text === '' && change.rangeLength === 1) { - await restoreOriginalContent(); - } - // Accept preview with Tab key - if (change && change.text === '\t') { - await acceptPreview(); - } - } - }); + // Accept preview with Tab key + if (change && change.text === '\t' && previewInserted) { + console.log("Tab key pressed, accepting preview"); + await acceptPreview(textEditor, document, startLine, position, completionText); + } + // Keep the existing backspace functionality + if (change.text === '' && change.range.start.line >= previewStartLine) { + console.log("Backspace detected"); + await restoreOriginalContent(); + } + } + } + }); const restoreOriginalContent = async () => { - const edit = new vscode.WorkspaceEdit(); + if (!previewInserted) return; + const endLine = document.lineCount - 1; - const endCharacter = document.lineAt(endLine).text.length; - const fullRange = new vscode.Range(previewStartLine, 0, endLine, endCharacter); + const fullRange = new vscode.Range(previewStartLine, 0, endLine, document.lineAt(endLine).text.length); + const edit = new vscode.WorkspaceEdit(); + edit.replace(document.uri, fullRange, originalContent); await vscode.workspace.applyEdit(edit); + textEditor.setDecorations(previewDecorationType, []); previewInserted = false; disposable.dispose(); - - // Move the cursor to the end of the original content - const originalLines = originalContent.split('\n'); - const lastLine = previewStartLine + originalLines.length - 1; - const lastLineLength = originalLines[originalLines.length - 1].length; - const newPosition = new vscode.Position(lastLine, lastLineLength); + + // Move cursor back to the end of the original content + const lastOriginalLineNumber = previewStartLine + originalContent.split('\n').length - 1; + const lastLineLength = originalContent.split('\n').pop()?.length || 0; + const newPosition = new vscode.Position(lastOriginalLineNumber, lastLineLength); textEditor.selection = new vscode.Selection(newPosition, newPosition); }; + - const acceptPreview = async () => { + // Modify the acceptPreview function to match the working version + const acceptPreview = async (textEditor: vscode.TextEditor, document: vscode.TextDocument, startLine: number, position: vscode.Position, completionText: string) => { + console.log("Accepting preview"); textEditor.setDecorations(previewDecorationType, []); const edit = new vscode.WorkspaceEdit(); - const endLine = document.lineCount - 1; - const endCharacter = document.lineAt(endLine).text.length; - const fullRange = new vscode.Range(previewStartLine, 0, endLine, endCharacter); - edit.replace(document.uri, fullRange, completionText); + + // Insert the completion text + const insertText = '\n'.repeat(1) + completionText + '\n'.repeat(1); // Add 2 extra newlines + + // Replace the entire range from the start of the context to the current position + const replaceRange = new vscode.Range(startLine, 0, position.line, position.character); + edit.replace(document.uri, replaceRange, insertText); + await vscode.workspace.applyEdit(edit); - await document.save(); + await document.save(); // Optionally save after inserting // Move the cursor to the end of the inserted completion - const completionLines = completionText.split('\n'); - const lastLine = previewStartLine + completionLines.length - 1; - const lastLineLength = completionLines[completionLines.length - 1].length; - const newPosition = new vscode.Position(lastLine, lastLineLength); + const totalNewLines = insertText.split('\n').length; + const newPosition = new vscode.Position(startLine + totalNewLines - 2, 0); textEditor.selection = new vscode.Selection(newPosition, newPosition); - previewInserted = false; disposable.dispose(); + previewInserted = false; + console.log("Preview accepted"); }; From d8e97c2b4efb9944e89134f3365f1531c19fbb93 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Fri, 4 Oct 2024 18:26:01 +0300 Subject: [PATCH 16/25] stable version 0.2.0, now providing preview pressing double tab to accept preview --- package.json | 9 ++- src/extension.ts | 140 +++++++++++++++++++------------------ src/test.py | 175 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 252 insertions(+), 72 deletions(-) create mode 100644 src/test.py diff --git a/package.json b/package.json index 10be8e5..53d6a68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fabelous-autocoder", - "version": "0.2.74", + "version": "0.2.0", "displayName": "Fabelous Autocoder", "description": "A simple to use Ollama autocompletion Plugin", "icon": "icon.png", @@ -110,10 +110,9 @@ }, "commands": [ { - "command": "fabelous-autocoder.autocomplete", - "title": "Fabelous autocompletion" - } - ] + "command": "fabelous-autocoder.autocomplete", + "title": "Fabelous Autocompletion" + }] }, "scripts": { "vscode:prepublish": "npm run compile", diff --git a/src/extension.ts b/src/extension.ts index cfa1354..acaa133 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -61,7 +61,6 @@ const previewDecorationType = vscode.window.createTextEditorDecorationType({ textDecoration: 'none; display: none;', // Hide the original text }); - async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationToken?: vscode.CancellationToken) { const document = textEditor.document; const position = textEditor.selection.active; @@ -117,65 +116,74 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo let previewInserted = false; let originalContent: string; let previewStartLine: number; + const storeAndInsertPreview = async () => { + previewStartLine = startLine; + const endLine = document.lineCount - 1; + const endCharacter = document.lineAt(endLine).text.length; + const fullRange = new vscode.Range(startLine, 0, endLine, endCharacter); + originalContent = document.getText(fullRange); + + const previewContent = completionText + '\n'.repeat(1); + const edit = new vscode.WorkspaceEdit(); + edit.replace(document.uri, fullRange, previewContent); // Overwrite the content + await vscode.workspace.applyEdit(edit); + + // Split the preview content into lines + const previewLines = previewContent.split('\n'); + + // Calculate the exact position of the last character of the preview content + const lastPreviewLineIndex = previewStartLine + previewLines.length - 1; + const lastPreviewLine = previewLines[previewLines.length - 2]; // Second to last line (last line is newline) + const newPosition = new vscode.Position(lastPreviewLineIndex - 1, lastPreviewLine.length); // Place cursor at end of last line + + // Set the new cursor position after inserting preview + textEditor.selection = new vscode.Selection(newPosition, newPosition); + + // Explicitly scroll to reveal the cursor position at the end of the preview + textEditor.revealRange(new vscode.Range(newPosition, newPosition), vscode.TextEditorRevealType.InCenter); + + // Set decorations on the newly inserted lines + const previewRanges: vscode.DecorationOptions[] = []; + for (let i = 0; i < previewLines.length; i++) { + const range = new vscode.Range(previewStartLine + i, 0, previewStartLine + i, previewLines[i].length); + previewRanges.push({ + range, + renderOptions: { + after: { + contentText: previewLines[i], + } + } + }); + } + textEditor.setDecorations(previewDecorationType, previewRanges); + previewInserted = true; + }; + + const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor || event.document.uri.toString() !== textEditor.document.uri.toString() || !previewInserted) { + return; + } + + // Determine tab size setting + const tabSize = textEditor.options.tabSize as number || 4; + + for (const change of event.contentChanges) { + const changeStartLine = change.range.start.line; + + // Detect Backspace by a single-character deletion + if (change.text === '' && change.rangeLength === 1 && changeStartLine >= previewStartLine) { + await restoreOriginalContent(); + } + + // Detect Tab by checking if the change is a multiple of spaces equal to the tab size + if (/^[ ]+$/.test(change.text) && change.text.length === tabSize && changeStartLine >= previewStartLine) { + await acceptPreview(textEditor, document, startLine, position, completionText); + } + } + }); - // Store the original content and insert preview -const storeAndInsertPreview = async () => { - previewStartLine = startLine; - const endLine = document.lineCount - 1; - const endCharacter = document.lineAt(endLine).text.length; - const fullRange = new vscode.Range(startLine, 0, endLine, endCharacter); - originalContent = document.getText(fullRange); - - const previewContent = completionText + '\n'.repeat(1); - const edit = new vscode.WorkspaceEdit(); - edit.replace(document.uri, fullRange, previewContent); // Overwrite the context - await vscode.workspace.applyEdit(edit); - - // Move the cursor to the end of the inserted content - const previewLines = previewContent.split('\n'); - const lastPreviewLineIndex = previewStartLine + previewLines.length - 1; - const lastPreviewLine = previewLines[previewLines.length - 1]; - const newPosition = new vscode.Position(lastPreviewLineIndex, lastPreviewLine.length); - textEditor.selection = new vscode.Selection(newPosition, newPosition); - - // Set decorations on the newly inserted lines - const previewRanges: vscode.DecorationOptions[] = []; - for (let i = 0; i < previewLines.length; i++) { - const range = new vscode.Range(previewStartLine + i, 0, previewStartLine + i + 1, 0); - previewRanges.push({ - range, - renderOptions: { - after: { - contentText: previewLines[i], - } - } - }); - } - textEditor.setDecorations(previewDecorationType, previewRanges); - previewInserted = true; -}; - - const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { - if (event.document.uri.toString() === document.uri.toString() && previewInserted) { - console.log("URI matches and preview inserted"); - if (event.contentChanges.length > 0) { - const change = event.contentChanges[0]; - console.log("Change:", change); - - // Accept preview with Tab key - if (change && change.text === '\t' && previewInserted) { - console.log("Tab key pressed, accepting preview"); - await acceptPreview(textEditor, document, startLine, position, completionText); - } - - // Keep the existing backspace functionality - if (change.text === '' && change.range.start.line >= previewStartLine) { - console.log("Backspace detected"); - await restoreOriginalContent(); - } - } - } - }); + const restoreOriginalContent = async () => { if (!previewInserted) return; @@ -196,36 +204,33 @@ const storeAndInsertPreview = async () => { const newPosition = new vscode.Position(lastOriginalLineNumber, lastLineLength); textEditor.selection = new vscode.Selection(newPosition, newPosition); }; - - // Modify the acceptPreview function to match the working version const acceptPreview = async (textEditor: vscode.TextEditor, document: vscode.TextDocument, startLine: number, position: vscode.Position, completionText: string) => { console.log("Accepting preview"); textEditor.setDecorations(previewDecorationType, []); const edit = new vscode.WorkspaceEdit(); - + // Insert the completion text const insertText = '\n'.repeat(1) + completionText + '\n'.repeat(1); // Add 2 extra newlines - + // Replace the entire range from the start of the context to the current position const replaceRange = new vscode.Range(startLine, 0, position.line, position.character); edit.replace(document.uri, replaceRange, insertText); - + await vscode.workspace.applyEdit(edit); await document.save(); // Optionally save after inserting - + // Move the cursor to the end of the inserted completion const totalNewLines = insertText.split('\n').length; const newPosition = new vscode.Position(startLine + totalNewLines - 2, 0); textEditor.selection = new vscode.Selection(newPosition, newPosition); - + disposable.dispose(); previewInserted = false; console.log("Preview accepted"); }; - // Call this function to initiate the preview await storeAndInsertPreview(); @@ -240,6 +245,7 @@ const storeAndInsertPreview = async () => { } + async function provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, cancellationToken: vscode.CancellationToken) { const item = new vscode.CompletionItem("Fabelous autocompletion"); item.insertText = new vscode.SnippetString('${1:}'); diff --git a/src/test.py b/src/test.py new file mode 100644 index 0000000..8dfb3f9 --- /dev/null +++ b/src/test.py @@ -0,0 +1,175 @@ + + + + + +def quicksort(arr): + if len(arr) <= 1: + return arr + else: + pivot = arr[len(arr)//2] # choose the middle element as the pivot + left_partition = [i for i in arr if i < pivot] + right_partition = [i for i in arr if i > pivot] + + return quicksort(left_partition) + [pivot] + quicksort(right_partition) + + +def quicksort(arr): + if len(arr) <= 1: + return arr + mid = int(len(arr)/2) + left = [item for i, item in enumerate(arr) if i=mid] + + return quicksort(left)+[arr[mid]]+quicksort(right) +def is_fibunccai(num): + num = str(num) + size = len(num) + + # Create 1s array for DP table to store last 10 digits of Fibonacci sequence + fib = [1] * 10 + + # Initialize last two numbers in Fibonacci sequence as 1 and 2 +def binary_search(arr, num) : + left = 0; right = len(arr) - 1 + + while (left <= right) : + mid = int((right + left) / 2) + + if arr[mid] == num : + return mid + + elif arr[mid] > num : + right = mid - 1 + + else: + left = mid + 1 + + return -1 + +arr = [2, 4, 5, 7, 8, 9, 10, 11, 12] +num = 9 +index = binary_search(arr, num) + +if index!= -1: + print("Element is present at ", index) +else: + print("Element is not present in array") + +def quicksort(arr): + if len(arr) <= 1: + return arr + + pivot = arr[len(arr)//2] + left_partition, right_partition = [], [] + + for i in range(len(arr)): + if arr[i] < pivot: + left_partition.append(arr[i]) + else: + right_partition.append(arr[i]) + + return quicksort(left_partition) + [pivot] + quicksort(right_partition) + +def merge_sort(arr): + if len(arr) > 1: + mid = len(arr) // 2 + left_half = arr[:mid] + right_half = arr[mid:] + + # Recursive call for sorting the two halves + merge_sort(left_half) + merge_sort(right_half) + + i = j = k = 0 + + while i < len(left_half) and j < len(right_half): + if left_half[i] < right_half[j]: + arr[k] = left_half[i] + i += 1 + else: + arr[k] = right_half[j] + j += 1 + k += 1 + + # Checking if any element was left + while i < len(left_half): + arr[k] = left_half[i] + i += 1 + k += 1 + + while j < len(right_half): + arr[k] = right_half[j] + j += 1 + k += 1 +# Driver code to test above +arr = [24, 32, 10, 67, 55, 38, 19] +print(arr) +merge_sort(arr) + +print("Sorted array is:") +print(arr) +r) // 2 + left_half = arr[:mid] + right_half = arr[mid:] + + # Recursive call for sorting the two halves + merge_sort(left_half) + merge_sort(right_half) + + i = j = k = 0 + + while i < len(left_half) and j < len(right_half): + if left_half[i] < right_half[j]: + arr[k] = left_half[i] + i += 1 + else: + arr[k] = right_half[j] + j += 1 + k += 1 + + # Checking if any element was left + while i < len(left_half): + arr[k] = left_half[i] + i += 1 + k += 1 + + while j < len(right_half): + arr[k] = right_half[j] + j += 1 + k += 1 +# Driver code to test above +arr = [24, 32, 10, 67, 55, 38, 19] +print(arr) +merge_sort(arr) + +print("Sorted array is:") +print(arr) + +def is_fibuncacci(num): + if (num < 2): + return False + + # Base case + if num == 1: + return True + + for i in range(2, num + 1): + if is_fibonacci(i) and is_fibonacci(num - i): + return True + + # If we reach here, then n + + + # Base case + if num == 1: + return True + + for i in range(2, num + 1): + if is_fibonacci(i) and is_fibonacci(num - i): + return True + + # If we reach here, then n + + +def quicksort(arr) \ No newline at end of file From 4c5eb334dfd9955e1c937c15b45e39563f8c7bfc Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Fri, 4 Oct 2024 18:26:16 +0300 Subject: [PATCH 17/25] remove test file --- src/test.py | 175 ---------------------------------------------------- 1 file changed, 175 deletions(-) delete mode 100644 src/test.py diff --git a/src/test.py b/src/test.py deleted file mode 100644 index 8dfb3f9..0000000 --- a/src/test.py +++ /dev/null @@ -1,175 +0,0 @@ - - - - - -def quicksort(arr): - if len(arr) <= 1: - return arr - else: - pivot = arr[len(arr)//2] # choose the middle element as the pivot - left_partition = [i for i in arr if i < pivot] - right_partition = [i for i in arr if i > pivot] - - return quicksort(left_partition) + [pivot] + quicksort(right_partition) - - -def quicksort(arr): - if len(arr) <= 1: - return arr - mid = int(len(arr)/2) - left = [item for i, item in enumerate(arr) if i=mid] - - return quicksort(left)+[arr[mid]]+quicksort(right) -def is_fibunccai(num): - num = str(num) - size = len(num) - - # Create 1s array for DP table to store last 10 digits of Fibonacci sequence - fib = [1] * 10 - - # Initialize last two numbers in Fibonacci sequence as 1 and 2 -def binary_search(arr, num) : - left = 0; right = len(arr) - 1 - - while (left <= right) : - mid = int((right + left) / 2) - - if arr[mid] == num : - return mid - - elif arr[mid] > num : - right = mid - 1 - - else: - left = mid + 1 - - return -1 - -arr = [2, 4, 5, 7, 8, 9, 10, 11, 12] -num = 9 -index = binary_search(arr, num) - -if index!= -1: - print("Element is present at ", index) -else: - print("Element is not present in array") - -def quicksort(arr): - if len(arr) <= 1: - return arr - - pivot = arr[len(arr)//2] - left_partition, right_partition = [], [] - - for i in range(len(arr)): - if arr[i] < pivot: - left_partition.append(arr[i]) - else: - right_partition.append(arr[i]) - - return quicksort(left_partition) + [pivot] + quicksort(right_partition) - -def merge_sort(arr): - if len(arr) > 1: - mid = len(arr) // 2 - left_half = arr[:mid] - right_half = arr[mid:] - - # Recursive call for sorting the two halves - merge_sort(left_half) - merge_sort(right_half) - - i = j = k = 0 - - while i < len(left_half) and j < len(right_half): - if left_half[i] < right_half[j]: - arr[k] = left_half[i] - i += 1 - else: - arr[k] = right_half[j] - j += 1 - k += 1 - - # Checking if any element was left - while i < len(left_half): - arr[k] = left_half[i] - i += 1 - k += 1 - - while j < len(right_half): - arr[k] = right_half[j] - j += 1 - k += 1 -# Driver code to test above -arr = [24, 32, 10, 67, 55, 38, 19] -print(arr) -merge_sort(arr) - -print("Sorted array is:") -print(arr) -r) // 2 - left_half = arr[:mid] - right_half = arr[mid:] - - # Recursive call for sorting the two halves - merge_sort(left_half) - merge_sort(right_half) - - i = j = k = 0 - - while i < len(left_half) and j < len(right_half): - if left_half[i] < right_half[j]: - arr[k] = left_half[i] - i += 1 - else: - arr[k] = right_half[j] - j += 1 - k += 1 - - # Checking if any element was left - while i < len(left_half): - arr[k] = left_half[i] - i += 1 - k += 1 - - while j < len(right_half): - arr[k] = right_half[j] - j += 1 - k += 1 -# Driver code to test above -arr = [24, 32, 10, 67, 55, 38, 19] -print(arr) -merge_sort(arr) - -print("Sorted array is:") -print(arr) - -def is_fibuncacci(num): - if (num < 2): - return False - - # Base case - if num == 1: - return True - - for i in range(2, num + 1): - if is_fibonacci(i) and is_fibonacci(num - i): - return True - - # If we reach here, then n - - - # Base case - if num == 1: - return True - - for i in range(2, num + 1): - if is_fibonacci(i) and is_fibonacci(num - i): - return True - - # If we reach here, then n - - -def quicksort(arr) \ No newline at end of file From 804a3113e45d3a70a28e80ed8c37322f68c1496f Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Fri, 4 Oct 2024 19:00:47 +0300 Subject: [PATCH 18/25] [wip] preview is semi working, i need to jump to the end off the inserted content and extra lines needs to be generated --- src/extension.ts | 74 +++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index acaa133..cdf30b8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -67,7 +67,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo const contextLines = 2; const startLine = Math.max(0, position.line - contextLines); const context = getContextLines(document, position); - + let isHandlingChange = false; const fimPrompt = createFIMPrompt(context, document.languageId); vscode.window.withProgress( @@ -131,16 +131,6 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo // Split the preview content into lines const previewLines = previewContent.split('\n'); - // Calculate the exact position of the last character of the preview content - const lastPreviewLineIndex = previewStartLine + previewLines.length - 1; - const lastPreviewLine = previewLines[previewLines.length - 2]; // Second to last line (last line is newline) - const newPosition = new vscode.Position(lastPreviewLineIndex - 1, lastPreviewLine.length); // Place cursor at end of last line - - // Set the new cursor position after inserting preview - textEditor.selection = new vscode.Selection(newPosition, newPosition); - - // Explicitly scroll to reveal the cursor position at the end of the preview - textEditor.revealRange(new vscode.Range(newPosition, newPosition), vscode.TextEditorRevealType.InCenter); // Set decorations on the newly inserted lines const previewRanges: vscode.DecorationOptions[] = []; @@ -161,29 +151,33 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { const textEditor = vscode.window.activeTextEditor; - if (!textEditor || event.document.uri.toString() !== textEditor.document.uri.toString() || !previewInserted) { + if (!textEditor || event.document.uri.toString() !== textEditor.document.uri.toString() || !previewInserted || isHandlingChange) { return; } - // Determine tab size setting - const tabSize = textEditor.options.tabSize as number || 4; + isHandlingChange = true; - for (const change of event.contentChanges) { - const changeStartLine = change.range.start.line; + try { + for (const change of event.contentChanges) { + const changeStartLine = change.range.start.line; - // Detect Backspace by a single-character deletion - if (change.text === '' && change.rangeLength === 1 && changeStartLine >= previewStartLine) { - await restoreOriginalContent(); - } - - // Detect Tab by checking if the change is a multiple of spaces equal to the tab size - if (/^[ ]+$/.test(change.text) && change.text.length === tabSize && changeStartLine >= previewStartLine) { - await acceptPreview(textEditor, document, startLine, position, completionText); + // Detect Backspace by a single-character deletion + if (change.text === '' && change.rangeLength === 1 && changeStartLine >= previewStartLine) { + await restoreOriginalContent(); + } + + // Detect Tab key press + if (change.text === '\t' && changeStartLine >= previewStartLine) { + await acceptPreview(textEditor, event.document, startLine, textEditor.selection.active, completionText); + } } + } finally { + isHandlingChange = false; } }); - + + const restoreOriginalContent = async () => { if (!previewInserted) return; @@ -197,35 +191,25 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo textEditor.setDecorations(previewDecorationType, []); previewInserted = false; disposable.dispose(); - - // Move cursor back to the end of the original content - const lastOriginalLineNumber = previewStartLine + originalContent.split('\n').length - 1; - const lastLineLength = originalContent.split('\n').pop()?.length || 0; - const newPosition = new vscode.Position(lastOriginalLineNumber, lastLineLength); - textEditor.selection = new vscode.Selection(newPosition, newPosition); }; // Modify the acceptPreview function to match the working version + const acceptPreview = async (textEditor: vscode.TextEditor, document: vscode.TextDocument, startLine: number, position: vscode.Position, completionText: string) => { - console.log("Accepting preview"); + console.log("Accepting preview with completion text:", completionText); textEditor.setDecorations(previewDecorationType, []); const edit = new vscode.WorkspaceEdit(); - - // Insert the completion text - const insertText = '\n'.repeat(1) + completionText + '\n'.repeat(1); // Add 2 extra newlines - - // Replace the entire range from the start of the context to the current position + + // Adjust the insertion logic to avoid duplicate newlines + const insertText = completionText; + + // Replace the range from the start of the context to the current position const replaceRange = new vscode.Range(startLine, 0, position.line, position.character); edit.replace(document.uri, replaceRange, insertText); - + await vscode.workspace.applyEdit(edit); - await document.save(); // Optionally save after inserting - - // Move the cursor to the end of the inserted completion - const totalNewLines = insertText.split('\n').length; - const newPosition = new vscode.Position(startLine + totalNewLines - 2, 0); - textEditor.selection = new vscode.Selection(newPosition, newPosition); - + await document.save(); + disposable.dispose(); previewInserted = false; console.log("Preview accepted"); From f122d99ba1de2fa162a6e2ca47fd493a35a02223 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Sat, 5 Oct 2024 09:18:22 +0300 Subject: [PATCH 19/25] code now works with everything except tab --- src/extension.ts | 62 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index cdf30b8..ff82814 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -116,6 +116,8 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo let previewInserted = false; let originalContent: string; let previewStartLine: number; + + const storeAndInsertPreview = async () => { previewStartLine = startLine; const endLine = document.lineCount - 1; @@ -148,8 +150,33 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo textEditor.setDecorations(previewDecorationType, previewRanges); previewInserted = true; }; - - const disposable = vscode.workspace.onDidChangeTextDocument(async (event) => { + const disposable = vscode.window.onDidChangeTextEditorSelection(async (event) => { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor || !previewInserted || isHandlingChange) { + return; + } + + isHandlingChange = true; + + try { + const activeSelection = textEditor.selection; + const changeStartLine = activeSelection.active.line; + + // Detect Tab key press by checking the active selection + if (event.kind === vscode.TextEditorSelectionChangeKind.Keyboard && changeStartLine >= previewStartLine) { + const changeText = textEditor.document.getText(activeSelection); + + if (changeText === '') { + // Tab key (empty selection) -> Accept the preview + await acceptPreview(textEditor, textEditor.document, startLine, activeSelection.active, completionText); + } + } + } finally { + isHandlingChange = false; + } + }); + + vscode.workspace.onDidChangeTextDocument(async (event) => { const textEditor = vscode.window.activeTextEditor; if (!textEditor || event.document.uri.toString() !== textEditor.document.uri.toString() || !previewInserted || isHandlingChange) { return; @@ -166,7 +193,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo await restoreOriginalContent(); } - // Detect Tab key press + // Detect Tab key press by checking if the inserted text is a tab character if (change.text === '\t' && changeStartLine >= previewStartLine) { await acceptPreview(textEditor, event.document, startLine, textEditor.selection.active, completionText); } @@ -175,26 +202,24 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo isHandlingChange = false; } }); - - - + + // Restore original content if Backspace is pressed (decline the preview) const restoreOriginalContent = async () => { if (!previewInserted) return; - + const endLine = document.lineCount - 1; const fullRange = new vscode.Range(previewStartLine, 0, endLine, document.lineAt(endLine).text.length); const edit = new vscode.WorkspaceEdit(); - + edit.replace(document.uri, fullRange, originalContent); await vscode.workspace.applyEdit(edit); - + textEditor.setDecorations(previewDecorationType, []); previewInserted = false; - disposable.dispose(); + disposable.dispose(); // Cancel listener when preview is discarded }; - - // Modify the acceptPreview function to match the working version - + + // Accept the preview when Tab is pressed const acceptPreview = async (textEditor: vscode.TextEditor, document: vscode.TextDocument, startLine: number, position: vscode.Position, completionText: string) => { console.log("Accepting preview with completion text:", completionText); textEditor.setDecorations(previewDecorationType, []); @@ -202,21 +227,24 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo // Adjust the insertion logic to avoid duplicate newlines const insertText = completionText; - + // Replace the range from the start of the context to the current position const replaceRange = new vscode.Range(startLine, 0, position.line, position.character); edit.replace(document.uri, replaceRange, insertText); - + await vscode.workspace.applyEdit(edit); await document.save(); - disposable.dispose(); + disposable.dispose(); // Cancel listener when preview is accepted previewInserted = false; console.log("Preview accepted"); }; - + // Call this function to initiate the preview await storeAndInsertPreview(); + + + } catch (err: any) { vscode.window.showErrorMessage( From f6ea8494a72e4884c955b2d9ab35df2e4f44c8d3 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Mon, 7 Oct 2024 09:22:02 +0200 Subject: [PATCH 20/25] accept and decline is working --- src/extension.ts | 52 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index ff82814..53614c6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -176,32 +176,62 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo } }); + // Handles Enter key separately vscode.workspace.onDidChangeTextDocument(async (event) => { const textEditor = vscode.window.activeTextEditor; if (!textEditor || event.document.uri.toString() !== textEditor.document.uri.toString() || !previewInserted || isHandlingChange) { return; } - + isHandlingChange = true; - + try { for (const change of event.contentChanges) { const changeStartLine = change.range.start.line; - - // Detect Backspace by a single-character deletion - if (change.text === '' && change.rangeLength === 1 && changeStartLine >= previewStartLine) { - await restoreOriginalContent(); + + if (change.text.includes('\n') && changeStartLine >= previewStartLine) { + // Accept the preview and move to the next line + await acceptPreview(textEditor, textEditor.document, startLine, textEditor.selection.active, completionText); + await vscode.commands.executeCommand('default:type', { text: '\n' }); + break; } - - // Detect Tab key press by checking if the inserted text is a tab character - if (change.text === '\t' && changeStartLine >= previewStartLine) { - await acceptPreview(textEditor, event.document, startLine, textEditor.selection.active, completionText); + + // Handle Backspace + if (change.text === '' && change.rangeLength === 1 && changeStartLine >= previewStartLine) { + // Discard the preview if Backspace is pressed + await restoreOriginalContent(); + break; } } } finally { isHandlingChange = false; } }); + + vscode.window.onDidChangeTextEditorSelection(async (event) => { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor || !previewInserted || isHandlingChange) { + return; + } + + isHandlingChange = true; + + try { + // Handle arrow keys or any other navigation keys + const currentSelection = event.selections[0]; + const { document } = textEditor; + + // Detect unwanted acceptance from simple navigation + if (currentSelection.start.line < previewStartLine) { + await restoreOriginalContent(); + } + } finally { + isHandlingChange = false; + } + }); + + + // Restore original content if Backspace is pressed (decline the preview) const restoreOriginalContent = async () => { @@ -221,7 +251,6 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo // Accept the preview when Tab is pressed const acceptPreview = async (textEditor: vscode.TextEditor, document: vscode.TextDocument, startLine: number, position: vscode.Position, completionText: string) => { - console.log("Accepting preview with completion text:", completionText); textEditor.setDecorations(previewDecorationType, []); const edit = new vscode.WorkspaceEdit(); @@ -237,7 +266,6 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo disposable.dispose(); // Cancel listener when preview is accepted previewInserted = false; - console.log("Preview accepted"); }; // Call this function to initiate the preview From dcb5a3bbdf8c4a85c40a24e3a6407488d875ec33 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Tue, 8 Oct 2024 11:17:17 +0200 Subject: [PATCH 21/25] not enough lines between preview and final --- src/extension.ts | 128 ++++++++++++++++++----------------------------- 1 file changed, 50 insertions(+), 78 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 53614c6..0b3c6b4 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -61,6 +61,7 @@ const previewDecorationType = vscode.window.createTextEditorDecorationType({ textDecoration: 'none; display: none;', // Hide the original text }); + async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationToken?: vscode.CancellationToken) { const document = textEditor.document; const position = textEditor.selection.active; @@ -116,40 +117,42 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo let previewInserted = false; let originalContent: string; let previewStartLine: number; - + let previewEndLine: number; const storeAndInsertPreview = async () => { - previewStartLine = startLine; - const endLine = document.lineCount - 1; - const endCharacter = document.lineAt(endLine).text.length; - const fullRange = new vscode.Range(startLine, 0, endLine, endCharacter); + const currentLine = position.line; + previewStartLine = currentLine; + const previewLines = completionText.split('\n'); + previewEndLine = previewStartLine + previewLines.length; + + // Ensure the previewEndLine doesn't exceed the document's line count + const documentEndLine = document.lineCount - 1; + previewEndLine = Math.min(previewEndLine, documentEndLine + 1); + + // Store original content + const fullRange = new vscode.Range(previewStartLine, 0, previewEndLine, document.lineAt(previewEndLine - 1).text.length); originalContent = document.getText(fullRange); - - const previewContent = completionText + '\n'.repeat(1); + + // Prepare the new content + const newContent = previewLines.join('\n'); + const edit = new vscode.WorkspaceEdit(); - edit.replace(document.uri, fullRange, previewContent); // Overwrite the content + edit.replace(document.uri, fullRange, newContent); await vscode.workspace.applyEdit(edit); - - // Split the preview content into lines - const previewLines = previewContent.split('\n'); - - - // Set decorations on the newly inserted lines - const previewRanges: vscode.DecorationOptions[] = []; - for (let i = 0; i < previewLines.length; i++) { - const range = new vscode.Range(previewStartLine + i, 0, previewStartLine + i, previewLines[i].length); - previewRanges.push({ - range, - renderOptions: { - after: { - contentText: previewLines[i], - } + + // Highlight only the new lines + const previewRanges: vscode.DecorationOptions[] = previewLines.map((line: string, index: number) => ({ + range: new vscode.Range(previewStartLine + index, 0, previewStartLine + index, line.length), + renderOptions: { + after: { + contentText: line, } - }); - } + } + })); textEditor.setDecorations(previewDecorationType, previewRanges); previewInserted = true; }; + const disposable = vscode.window.onDidChangeTextEditorSelection(async (event) => { const textEditor = vscode.window.activeTextEditor; if (!textEditor || !previewInserted || isHandlingChange) { @@ -162,7 +165,6 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo const activeSelection = textEditor.selection; const changeStartLine = activeSelection.active.line; - // Detect Tab key press by checking the active selection if (event.kind === vscode.TextEditorSelectionChangeKind.Keyboard && changeStartLine >= previewStartLine) { const changeText = textEditor.document.getText(activeSelection); @@ -176,7 +178,6 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo } }); - // Handles Enter key separately vscode.workspace.onDidChangeTextDocument(async (event) => { const textEditor = vscode.window.activeTextEditor; if (!textEditor || event.document.uri.toString() !== textEditor.document.uri.toString() || !previewInserted || isHandlingChange) { @@ -190,15 +191,12 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo const changeStartLine = change.range.start.line; if (change.text.includes('\n') && changeStartLine >= previewStartLine) { - // Accept the preview and move to the next line await acceptPreview(textEditor, textEditor.document, startLine, textEditor.selection.active, completionText); await vscode.commands.executeCommand('default:type', { text: '\n' }); break; } - // Handle Backspace if (change.text === '' && change.rangeLength === 1 && changeStartLine >= previewStartLine) { - // Discard the preview if Backspace is pressed await restoreOriginalContent(); break; } @@ -208,72 +206,46 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationTo } }); - vscode.window.onDidChangeTextEditorSelection(async (event) => { - const textEditor = vscode.window.activeTextEditor; - if (!textEditor || !previewInserted || isHandlingChange) { - return; - } - - isHandlingChange = true; - - try { - // Handle arrow keys or any other navigation keys - const currentSelection = event.selections[0]; - const { document } = textEditor; - - // Detect unwanted acceptance from simple navigation - if (currentSelection.start.line < previewStartLine) { - await restoreOriginalContent(); - } - } finally { - isHandlingChange = false; - } - }); - - - - - // Restore original content if Backspace is pressed (decline the preview) const restoreOriginalContent = async () => { if (!previewInserted) return; - - const endLine = document.lineCount - 1; - const fullRange = new vscode.Range(previewStartLine, 0, endLine, document.lineAt(endLine).text.length); + + const fullRange = new vscode.Range(previewStartLine, 0, previewEndLine, 0); const edit = new vscode.WorkspaceEdit(); - + edit.replace(document.uri, fullRange, originalContent); await vscode.workspace.applyEdit(edit); - + textEditor.setDecorations(previewDecorationType, []); previewInserted = false; disposable.dispose(); // Cancel listener when preview is discarded }; - - // Accept the preview when Tab is pressed + const acceptPreview = async (textEditor: vscode.TextEditor, document: vscode.TextDocument, startLine: number, position: vscode.Position, completionText: string) => { textEditor.setDecorations(previewDecorationType, []); const edit = new vscode.WorkspaceEdit(); - - // Adjust the insertion logic to avoid duplicate newlines - const insertText = completionText; - - // Replace the range from the start of the context to the current position - const replaceRange = new vscode.Range(startLine, 0, position.line, position.character); - edit.replace(document.uri, replaceRange, insertText); - + + const previewLines = completionText.split('\n'); + const endLine = startLine + previewLines.length; + + // Ensure that endLine does not exceed document bounds + const documentEndLine = document.lineCount - 1; + const finalEndLine = Math.min(endLine, documentEndLine + 1); + + // Prepare the new content + const newContent = previewLines.join('\n'); + + // Replace the range with the new content + const replaceRange = new vscode.Range(startLine, 0, finalEndLine, document.lineAt(finalEndLine - 1).text.length); + edit.replace(document.uri, replaceRange, newContent.trim()); + await vscode.workspace.applyEdit(edit); await document.save(); - + disposable.dispose(); // Cancel listener when preview is accepted previewInserted = false; }; - - // Call this function to initiate the preview + await storeAndInsertPreview(); - - - - } catch (err: any) { vscode.window.showErrorMessage( "Fabelous Autocoder encountered an error: " + err.message From 424cc124b2d1cf5b2da59ce922742eab70812907 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Tue, 8 Oct 2024 19:07:50 +0200 Subject: [PATCH 22/25] omg this is working --- src/extension.ts | 451 ++++++++++++++++++----------------------------- 1 file changed, 168 insertions(+), 283 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 0b3c6b4..1291669 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,269 +1,179 @@ -import * as vscode from "vscode"; -import axios from "axios"; +import * as vscode from 'vscode'; +import axios from 'axios'; -let VSConfig: vscode.WorkspaceConfiguration; -let apiEndpoint: string; -let apiAuthentication: string; -let apiModel: string; -let apiTemperature: number; -let numPredict: number; -let promptWindowSize: number; -let completionKeys: string; -let responsePreview: boolean | undefined; -let responsePreviewMaxTokens: number; -let responsePreviewDelay: number; -let continueInline: boolean | undefined; -let keepAlive: number | undefined; -let topP: number | undefined; +let config: { + apiEndpoint: string; + apiAuthentication: string; + apiModel: string; + apiTemperature: number; + numPredict: number; + promptWindowSize: number; + completionKeys: string[]; + responsePreview: boolean; + responsePreviewMaxTokens: number; + responsePreviewDelay: number; + continueInline: boolean; + keepAlive: number; + topP: number; +}; -function updateVSConfig() { - VSConfig = vscode.workspace.getConfiguration("fabelous-autocoder"); - apiEndpoint = VSConfig.get("endpoint") || "http://localhost:11434/api/generate"; - apiAuthentication = VSConfig.get("authentication") || ""; - apiModel = VSConfig.get("model") || "fabelous-coder:latest"; - numPredict = VSConfig.get("max tokens predicted") || 1000; - promptWindowSize = VSConfig.get("prompt window size") || 2000; - completionKeys = VSConfig.get("completion keys") || " "; - responsePreview = VSConfig.get("response preview"); - responsePreviewMaxTokens = VSConfig.get("preview max tokens") || 50; - responsePreviewDelay = VSConfig.get("preview delay") || 0; - continueInline = VSConfig.get("continue inline"); - apiTemperature = VSConfig.get("temperature") || 0.7; - keepAlive = VSConfig.get("keep alive") || 30; - topP = VSConfig.get("top p") || 1; +let previewDecorationType: vscode.TextEditorDecorationType; + +function updateConfig() { + const vsConfig = vscode.workspace.getConfiguration('fabelous-autocoder'); + config = { + apiEndpoint: vsConfig.get('endpoint') || 'http://localhost:11434/api/generate', + apiAuthentication: vsConfig.get('authentication') || '', + apiModel: vsConfig.get('model') || 'fabelous-coder:latest', + apiTemperature: vsConfig.get('temperature') || 0.7, + numPredict: vsConfig.get('max tokens predicted') || 1000, + promptWindowSize: vsConfig.get('prompt window size') || 2000, + completionKeys: (vsConfig.get('completion keys') as string || ' ').split(''), + responsePreview: vsConfig.get('response preview') || false, + responsePreviewMaxTokens: vsConfig.get('preview max tokens') || 50, + responsePreviewDelay: vsConfig.get('preview delay') || 0, + continueInline: vsConfig.get('continue inline') || false, + keepAlive: vsConfig.get('keep alive') || 30, + topP: vsConfig.get('top p') || 1, + }; } -updateVSConfig(); -vscode.workspace.onDidChangeConfiguration(updateVSConfig); +function createPreviewDecorationType() { + previewDecorationType = vscode.window.createTextEditorDecorationType({ + after: { + color: '#888888', + fontStyle: 'italic', + }, + textDecoration: 'none; display: none;', + }); +} function getContextLines(document: vscode.TextDocument, position: vscode.Position): string { - const lines = []; const startLine = Math.max(0, position.line - 1); const endLine = position.line; - - for (let i = startLine; i <= endLine; i++) { - lines.push(document.lineAt(i).text); - } - - return lines.join("\n"); + return document.getText(new vscode.Range(startLine, 0, endLine, position.character)); } - function createFIMPrompt(prefix: string, language: string): string { return `${prefix}${language}\n`; } -const previewDecorationType = vscode.window.createTextEditorDecorationType({ - after: { - color: '#888888', // Grayed-out preview text - fontStyle: 'italic', - }, - textDecoration: 'none; display: none;', // Hide the original text -}); +async function generateCompletion(prompt: string, cancellationToken: vscode.CancellationToken): Promise { + const axiosCancelToken = new axios.CancelToken((c) => { + cancellationToken.onCancellationRequested(() => c('Request cancelled')); + }); - -async function autocompleteCommand(textEditor: vscode.TextEditor, cancellationToken?: vscode.CancellationToken) { - const document = textEditor.document; - const position = textEditor.selection.active; - const contextLines = 2; - const startLine = Math.max(0, position.line - contextLines); - const context = getContextLines(document, position); - let isHandlingChange = false; - const fimPrompt = createFIMPrompt(context, document.languageId); - - vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: "Fabelous Autocoder", - cancellable: true, - }, - async (progress, progressCancellationToken) => { - try { - progress.report({ message: "Starting model..." }); - - let axiosCancelPost: () => void; - const axiosCancelToken = new axios.CancelToken((c) => { - axiosCancelPost = () => { - c("Autocompletion request terminated by user cancel"); - }; - if (cancellationToken) cancellationToken.onCancellationRequested(axiosCancelPost); - progressCancellationToken.onCancellationRequested(axiosCancelPost); - vscode.workspace.onDidCloseTextDocument(axiosCancelPost); - }); - - // Make the API request - const response = await axios.post(apiEndpoint, { - model: apiModel, - prompt: fimPrompt, - stream: false, - raw: true, - options: { - num_predict: numPredict, - temperature: apiTemperature, - stop: ["", "```"] - } - }, { - cancelToken: axiosCancelToken, - headers: { - 'Authorization': apiAuthentication - } - }); - - progress.report({ message: "Generating..." }); - - let completionText = response.data.response; - completionText = completionText.replace(/||/g, '').trim(); - - let previewInserted = false; - let originalContent: string; - let previewStartLine: number; - let previewEndLine: number; - - const storeAndInsertPreview = async () => { - const currentLine = position.line; - previewStartLine = currentLine; - const previewLines = completionText.split('\n'); - previewEndLine = previewStartLine + previewLines.length; - - // Ensure the previewEndLine doesn't exceed the document's line count - const documentEndLine = document.lineCount - 1; - previewEndLine = Math.min(previewEndLine, documentEndLine + 1); - - // Store original content - const fullRange = new vscode.Range(previewStartLine, 0, previewEndLine, document.lineAt(previewEndLine - 1).text.length); - originalContent = document.getText(fullRange); - - // Prepare the new content - const newContent = previewLines.join('\n'); - - const edit = new vscode.WorkspaceEdit(); - edit.replace(document.uri, fullRange, newContent); - await vscode.workspace.applyEdit(edit); - - // Highlight only the new lines - const previewRanges: vscode.DecorationOptions[] = previewLines.map((line: string, index: number) => ({ - range: new vscode.Range(previewStartLine + index, 0, previewStartLine + index, line.length), - renderOptions: { - after: { - contentText: line, - } - } - })); - textEditor.setDecorations(previewDecorationType, previewRanges); - previewInserted = true; - }; - - const disposable = vscode.window.onDidChangeTextEditorSelection(async (event) => { - const textEditor = vscode.window.activeTextEditor; - if (!textEditor || !previewInserted || isHandlingChange) { - return; - } - - isHandlingChange = true; - - try { - const activeSelection = textEditor.selection; - const changeStartLine = activeSelection.active.line; - - if (event.kind === vscode.TextEditorSelectionChangeKind.Keyboard && changeStartLine >= previewStartLine) { - const changeText = textEditor.document.getText(activeSelection); - - if (changeText === '') { - // Tab key (empty selection) -> Accept the preview - await acceptPreview(textEditor, textEditor.document, startLine, activeSelection.active, completionText); - } - } - } finally { - isHandlingChange = false; - } - }); - - vscode.workspace.onDidChangeTextDocument(async (event) => { - const textEditor = vscode.window.activeTextEditor; - if (!textEditor || event.document.uri.toString() !== textEditor.document.uri.toString() || !previewInserted || isHandlingChange) { - return; - } - - isHandlingChange = true; - - try { - for (const change of event.contentChanges) { - const changeStartLine = change.range.start.line; - - if (change.text.includes('\n') && changeStartLine >= previewStartLine) { - await acceptPreview(textEditor, textEditor.document, startLine, textEditor.selection.active, completionText); - await vscode.commands.executeCommand('default:type', { text: '\n' }); - break; - } - - if (change.text === '' && change.rangeLength === 1 && changeStartLine >= previewStartLine) { - await restoreOriginalContent(); - break; - } - } - } finally { - isHandlingChange = false; - } - }); - - const restoreOriginalContent = async () => { - if (!previewInserted) return; - - const fullRange = new vscode.Range(previewStartLine, 0, previewEndLine, 0); - const edit = new vscode.WorkspaceEdit(); - - edit.replace(document.uri, fullRange, originalContent); - await vscode.workspace.applyEdit(edit); - - textEditor.setDecorations(previewDecorationType, []); - previewInserted = false; - disposable.dispose(); // Cancel listener when preview is discarded - }; - - const acceptPreview = async (textEditor: vscode.TextEditor, document: vscode.TextDocument, startLine: number, position: vscode.Position, completionText: string) => { - textEditor.setDecorations(previewDecorationType, []); - const edit = new vscode.WorkspaceEdit(); - - const previewLines = completionText.split('\n'); - const endLine = startLine + previewLines.length; - - // Ensure that endLine does not exceed document bounds - const documentEndLine = document.lineCount - 1; - const finalEndLine = Math.min(endLine, documentEndLine + 1); - - // Prepare the new content - const newContent = previewLines.join('\n'); - - // Replace the range with the new content - const replaceRange = new vscode.Range(startLine, 0, finalEndLine, document.lineAt(finalEndLine - 1).text.length); - edit.replace(document.uri, replaceRange, newContent.trim()); - - await vscode.workspace.applyEdit(edit); - await document.save(); - - disposable.dispose(); // Cancel listener when preview is accepted - previewInserted = false; - }; - - await storeAndInsertPreview(); - } catch (err: any) { - vscode.window.showErrorMessage( - "Fabelous Autocoder encountered an error: " + err.message - ); - console.log(err); - } + const response = await axios.post(config.apiEndpoint, { + model: config.apiModel, + prompt: prompt, + stream: false, + raw: true, + options: { + num_predict: config.numPredict + 100, // Generate extra lines for preview + temperature: config.apiTemperature, + stop: ['', '```'], + keep_alive: config.keepAlive, + top_p: config.topP, } - ); + }, { + cancelToken: axiosCancelToken, + headers: { + 'Authorization': config.apiAuthentication + } + }); + + const fullCompletion = response.data.response.replace(/||/g, '').trim(); + const completionLines = fullCompletion.split('\n'); + const usedContextLines = prompt.split('\n').length; + + // Remove used context and take extra lines for preview + return completionLines.slice(usedContextLines, usedContextLines + config.numPredict).join('\n'); } +class CompletionManager { + private textEditor: vscode.TextEditor; + private document: vscode.TextDocument; + private startPosition: vscode.Position; + private completionText: string; + private previewLines: string[]; + constructor(textEditor: vscode.TextEditor, startPosition: vscode.Position, completionText: string) { + this.textEditor = textEditor; + this.document = textEditor.document; + this.startPosition = startPosition; + this.completionText = completionText; + this.previewLines = completionText.split('\n'); + } + + public async showPreview() { + const previewRanges: vscode.DecorationOptions[] = this.previewLines.map((line, index) => ({ + range: new vscode.Range(this.startPosition.translate(index, 0), this.startPosition.translate(index, 0)), + renderOptions: { + after: { + contentText: line, + } + } + })); + this.textEditor.setDecorations(previewDecorationType, previewRanges); + } + + public async acceptCompletion() { + const edit = new vscode.WorkspaceEdit(); + edit.insert(this.document.uri, this.startPosition, this.completionText); + await vscode.workspace.applyEdit(edit); + this.clearPreview(); + } + + public clearPreview() { + this.textEditor.setDecorations(previewDecorationType, []); + } +} + +async function autocompleteCommand(textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit, ...args: any[]) { + const cancellationTokenSource = new vscode.CancellationTokenSource(); + const cancellationToken = cancellationTokenSource.token; + + try { + const document = textEditor.document; + const position = textEditor.selection.active; + const context = getContextLines(document, position); + const fimPrompt = createFIMPrompt(context, document.languageId); + + const completionText = await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: 'Fabelous Autocoder', + cancellable: true, + }, async (progress, progressCancellationToken) => { + progress.report({ message: 'Generating...' }); + return await generateCompletion(fimPrompt, progressCancellationToken); + }); + + const completionManager = new CompletionManager(textEditor, position, completionText); + await completionManager.showPreview(); + + const disposable = vscode.window.onDidChangeTextEditorSelection(async (event) => { + if (event.textEditor !== textEditor) return; + + if (event.kind === vscode.TextEditorSelectionChangeKind.Keyboard) { + await completionManager.acceptCompletion(); + disposable.dispose(); + } + }); + } catch (err: any) { + vscode.window.showErrorMessage(`Fabelous Autocoder encountered an error: ${err.message}`); + console.error(err); + } finally { + cancellationTokenSource.dispose(); + } +} async function provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, cancellationToken: vscode.CancellationToken) { - const item = new vscode.CompletionItem("Fabelous autocompletion"); + const item = new vscode.CompletionItem('Fabelous autocompletion'); item.insertText = new vscode.SnippetString('${1:}'); + item.documentation = new vscode.MarkdownString('Press `Enter` to get an autocompletion from Fabelous Autocoder'); - if (responsePreview) { - await new Promise(resolve => setTimeout(resolve, responsePreviewDelay * 1000)); + if (config.responsePreview) { + await new Promise(resolve => setTimeout(resolve, config.responsePreviewDelay * 1000)); if (cancellationToken.isCancellationRequested) { return [item]; } @@ -272,58 +182,33 @@ async function provideCompletionItems(document: vscode.TextDocument, position: v const fimPrompt = createFIMPrompt(context, document.languageId); try { - const response_preview = await axios.post(apiEndpoint, { - model: apiModel, - prompt: fimPrompt, - stream: false, - raw: true, - options: { - num_predict: responsePreviewMaxTokens, - temperature: apiTemperature, - stop: ['', '\n', '```'], - ...(keepAlive && { keep_alive: keepAlive }), - ...(topP && { top_p: topP }), - } - }, { - cancelToken: new axios.CancelToken((c) => { - cancellationToken.onCancellationRequested(() => c("Autocompletion request terminated by completion cancel")); - }) - }); + const previewText = await generateCompletion(fimPrompt, cancellationToken); + item.detail = previewText.split('\n')[0]; // Show first line as preview } catch (error) { - console.error("Error fetching preview:", error); + console.error('Error fetching preview:', error); } } - item.documentation = new vscode.MarkdownString('Press `Enter` to get an autocompletion from Fabelous Autocoder'); - if (continueInline || !responsePreview) { + if (config.continueInline || !config.responsePreview) { item.command = { command: 'fabelous-autocoder.autocomplete', title: 'Fabelous Autocomplete', - arguments: [cancellationToken] + arguments: [] }; } + return [item]; } -function activate(context: vscode.ExtensionContext) { - const completionProvider = vscode.languages.registerCompletionItemProvider("*", { - provideCompletionItems - }, - ...completionKeys.split("") +export function activate(context: vscode.ExtensionContext) { + updateConfig(); + createPreviewDecorationType(); + + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration(updateConfig), + vscode.languages.registerCompletionItemProvider('*', { provideCompletionItems }, ...config.completionKeys), + vscode.commands.registerTextEditorCommand('fabelous-autocoder.autocomplete', autocompleteCommand) ); - const externalAutocompleteCommand = vscode.commands.registerTextEditorCommand( - "fabelous-autocoder.autocomplete", - (textEditor, _, cancellationToken?) => { - autocompleteCommand(textEditor, cancellationToken); - } - ); - context.subscriptions.push(completionProvider); - context.subscriptions.push(externalAutocompleteCommand); } -function deactivate() { } - -module.exports = { - activate, - deactivate, -}; +export function deactivate() {} From 96a3971b714811fe12cfbd2aeb77380c02202917 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 9 Oct 2024 08:10:02 +0200 Subject: [PATCH 23/25] preview added --- src/extension.ts | 105 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 25 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 1291669..86841ec 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -69,7 +69,7 @@ async function generateCompletion(prompt: string, cancellationToken: vscode.Canc stream: false, raw: true, options: { - num_predict: config.numPredict + 100, // Generate extra lines for preview + num_predict: config.numPredict, temperature: config.apiTemperature, stop: ['', '```'], keep_alive: config.keepAlive, @@ -82,12 +82,7 @@ async function generateCompletion(prompt: string, cancellationToken: vscode.Canc } }); - const fullCompletion = response.data.response.replace(/||/g, '').trim(); - const completionLines = fullCompletion.split('\n'); - const usedContextLines = prompt.split('\n').length; - - // Remove used context and take extra lines for preview - return completionLines.slice(usedContextLines, usedContextLines + config.numPredict).join('\n'); + return response.data.response.replace(/||/g, '').trim(); } class CompletionManager { @@ -95,38 +90,58 @@ class CompletionManager { private document: vscode.TextDocument; private startPosition: vscode.Position; private completionText: string; - private previewLines: string[]; constructor(textEditor: vscode.TextEditor, startPosition: vscode.Position, completionText: string) { this.textEditor = textEditor; this.document = textEditor.document; this.startPosition = startPosition; this.completionText = completionText; - this.previewLines = completionText.split('\n'); } public async showPreview() { - const previewRanges: vscode.DecorationOptions[] = this.previewLines.map((line, index) => ({ - range: new vscode.Range(this.startPosition.translate(index, 0), this.startPosition.translate(index, 0)), - renderOptions: { - after: { - contentText: line, + const completionLines = this.completionText.split('\n'); + const previewLines = [ + '', // Empty line before + ...completionLines, + '' // Empty line after + ]; + + const previewRanges: vscode.DecorationOptions[] = previewLines.map((line, index) => { + const lineNumber = Math.max(0, this.startPosition.line + index - 1); + return { + range: new vscode.Range( + new vscode.Position(lineNumber, 0), + new vscode.Position(lineNumber, Number.MAX_VALUE) + ), + renderOptions: { + after: { + contentText: line, + } } - } - })); + }; + }); this.textEditor.setDecorations(previewDecorationType, previewRanges); } + public async acceptCompletion() { const edit = new vscode.WorkspaceEdit(); - edit.insert(this.document.uri, this.startPosition, this.completionText); + const startLine = Math.max(0, this.startPosition.line - 1); + const range = new vscode.Range( + new vscode.Position(startLine, 0), + this.startPosition.translate(0, Number.MAX_VALUE) + ); + edit.replace(this.document.uri, range, this.completionText); await vscode.workspace.applyEdit(edit); this.clearPreview(); } - + public clearPreview() { this.textEditor.setDecorations(previewDecorationType, []); } + public declineCompletion() { + this.clearPreview(); + } } async function autocompleteCommand(textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit, ...args: any[]) { @@ -148,20 +163,57 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, edit: vscode.T return await generateCompletion(fimPrompt, progressCancellationToken); }); + console.log('Completion generated:', completionText); + const completionManager = new CompletionManager(textEditor, position, completionText); await completionManager.showPreview(); - const disposable = vscode.window.onDidChangeTextEditorSelection(async (event) => { - if (event.textEditor !== textEditor) return; + let isDisposed = false; - if (event.kind === vscode.TextEditorSelectionChangeKind.Keyboard) { - await completionManager.acceptCompletion(); + const dispose = () => { + if (!isDisposed) { + console.log('Disposing listeners'); disposable.dispose(); + declineDisposable.dispose(); + isDisposed = true; + } + }; + + const disposable = vscode.Disposable.from( + vscode.window.onDidChangeTextEditorSelection(async (event) => { + if (event.textEditor !== textEditor) return; + + if (event.kind === vscode.TextEditorSelectionChangeKind.Keyboard) { + console.log('Accepting completion'); + await completionManager.acceptCompletion(); + dispose(); + } + }), + vscode.window.onDidChangeActiveTextEditor(() => { + console.log('Active editor changed, clearing preview'); + completionManager.clearPreview(); + dispose(); + }), + vscode.workspace.onDidChangeTextDocument((event) => { + if (event.document === document) { + console.log('Document changed, clearing preview'); + completionManager.clearPreview(); + dispose(); + } + }) + ); + + const declineDisposable = vscode.commands.registerCommand('type', async (args) => { + if (args.text === '\b') { // Backspace key + console.log('Declining completion'); + completionManager.declineCompletion(); + dispose(); } }); + } catch (err: any) { + console.error('Error in autocompleteCommand:', err); vscode.window.showErrorMessage(`Fabelous Autocoder encountered an error: ${err.message}`); - console.error(err); } finally { cancellationTokenSource.dispose(); } @@ -182,8 +234,11 @@ async function provideCompletionItems(document: vscode.TextDocument, position: v const fimPrompt = createFIMPrompt(context, document.languageId); try { - const previewText = await generateCompletion(fimPrompt, cancellationToken); - item.detail = previewText.split('\n')[0]; // Show first line as preview + const result = await generateCompletion(fimPrompt, cancellationToken); + const preview = (result as any).preview; + if (preview) { + item.detail = preview.split('\n')[0]; + } } catch (error) { console.error('Error fetching preview:', error); } From a56d47747d2d55f25efe82aa8b7b1fa1967c57bc Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 9 Oct 2024 14:18:33 +0200 Subject: [PATCH 24/25] only rows are not working yet --- src/extension.ts | 58 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 86841ec..facecec 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -18,6 +18,7 @@ let config: { }; let previewDecorationType: vscode.TextEditorDecorationType; +let activeCompletionManager: CompletionManager | null = null; function updateConfig() { const vsConfig = vscode.workspace.getConfiguration('fabelous-autocoder'); @@ -100,14 +101,13 @@ class CompletionManager { public async showPreview() { const completionLines = this.completionText.split('\n'); - const previewLines = [ - '', // Empty line before - ...completionLines, - '' // Empty line after - ]; - + const emptyLine = ''; // Empty line for spacing + const previewLines = [emptyLine, ...completionLines, emptyLine]; + const previewRanges: vscode.DecorationOptions[] = previewLines.map((line, index) => { - const lineNumber = Math.max(0, this.startPosition.line + index - 1); + const actualLineNumber = this.startPosition.line + index; + const totalLines = this.textEditor.document.lineCount; + const lineNumber = Math.min(totalLines - 1, actualLineNumber); return { range: new vscode.Range( new vscode.Position(lineNumber, 0), @@ -115,14 +115,14 @@ class CompletionManager { ), renderOptions: { after: { - contentText: line, - } - } + contentText: line.length > 0 ? ` ${line}` : '', + }, + }, }; }); + this.textEditor.setDecorations(previewDecorationType, previewRanges); } - public async acceptCompletion() { const edit = new vscode.WorkspaceEdit(); @@ -139,6 +139,7 @@ class CompletionManager { public clearPreview() { this.textEditor.setDecorations(previewDecorationType, []); } + public declineCompletion() { this.clearPreview(); } @@ -167,6 +168,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, edit: vscode.T const completionManager = new CompletionManager(textEditor, position, completionText); await completionManager.showPreview(); + activeCompletionManager = completionManager; let isDisposed = false; @@ -174,7 +176,8 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, edit: vscode.T if (!isDisposed) { console.log('Disposing listeners'); disposable.dispose(); - declineDisposable.dispose(); + typeDisposable.dispose(); + activeCompletionManager = null; isDisposed = true; } }; @@ -203,7 +206,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, edit: vscode.T }) ); - const declineDisposable = vscode.commands.registerCommand('type', async (args) => { + const typeDisposable = vscode.commands.registerCommand('type', async (args) => { if (args.text === '\b') { // Backspace key console.log('Declining completion'); completionManager.declineCompletion(); @@ -219,6 +222,27 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, edit: vscode.T } } +async function acceptCompletion() { + if (activeCompletionManager) { + await activeCompletionManager.acceptCompletion(); + const editor = vscode.window.activeTextEditor; + if (editor) { + const lastLine = editor.document.lineAt(editor.document.lineCount - 1); + const newPosition = new vscode.Position(lastLine.lineNumber, lastLine.text.length); + editor.selection = new vscode.Selection(newPosition, newPosition); + } + activeCompletionManager = null; + } +} + +async function handleTab() { + if (activeCompletionManager) { + await acceptCompletion(); + } else { + await vscode.commands.executeCommand('tab'); + } +} + async function provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, cancellationToken: vscode.CancellationToken) { const item = new vscode.CompletionItem('Fabelous autocompletion'); item.insertText = new vscode.SnippetString('${1:}'); @@ -237,7 +261,7 @@ async function provideCompletionItems(document: vscode.TextDocument, position: v const result = await generateCompletion(fimPrompt, cancellationToken); const preview = (result as any).preview; if (preview) { - item.detail = preview.split('\n')[0]; + item.detail = preview.split('\n')[0]; } } catch (error) { console.error('Error fetching preview:', error); @@ -254,7 +278,6 @@ async function provideCompletionItems(document: vscode.TextDocument, position: v return [item]; } - export function activate(context: vscode.ExtensionContext) { updateConfig(); createPreviewDecorationType(); @@ -262,8 +285,11 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push( vscode.workspace.onDidChangeConfiguration(updateConfig), vscode.languages.registerCompletionItemProvider('*', { provideCompletionItems }, ...config.completionKeys), - vscode.commands.registerTextEditorCommand('fabelous-autocoder.autocomplete', autocompleteCommand) + vscode.commands.registerTextEditorCommand('fabelous-autocoder.autocomplete', autocompleteCommand), + vscode.commands.registerCommand('fabelous-autocoder.acceptCompletion', acceptCompletion), + vscode.commands.registerCommand('fabelous-autocoder.handleTab', handleTab) ); } + export function deactivate() {} From 439a538c7e2ffec71f1652f9e065979bfad9c830 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 9 Oct 2024 14:24:53 +0200 Subject: [PATCH 25/25] bug fix for lines add extra lines before inserting --- src/extension.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/extension.ts b/src/extension.ts index facecec..3790c1b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -100,7 +100,9 @@ class CompletionManager { } public async showPreview() { + this.completionText = '\n' + this.completionText; const completionLines = this.completionText.split('\n'); + const emptyLine = ''; // Empty line for spacing const previewLines = [emptyLine, ...completionLines, emptyLine]; @@ -166,6 +168,7 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, edit: vscode.T console.log('Completion generated:', completionText); + const completionManager = new CompletionManager(textEditor, position, completionText); await completionManager.showPreview(); activeCompletionManager = completionManager;