From 621db41722e3a2960d1b81069dfe1ed5337ce8a6 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 9 Oct 2024 15:22:51 +0200 Subject: [PATCH 1/4] updated to version 2.0 --- package.json | 285 +++++++++++++++++++++++++++------------------------ 1 file changed, 151 insertions(+), 134 deletions(-) diff --git a/package.json b/package.json index 53d6a68..bf85831 100644 --- a/package.json +++ b/package.json @@ -1,135 +1,152 @@ { - "name": "fabelous-autocoder", - "version": "0.2.0", - "displayName": "Fabelous Autocoder", - "description": "A simple to use Ollama autocompletion Plugin", - "icon": "icon.png", - "publisher": "fabel", - "license": "CC BY-ND 4.0", - "bugs": { - "url": "https://gitea.fabelous.app/fabel/Fabelous-Autocoder/issues" - }, - "repository": { - "type": "git", - "url": "https://gitea.fabelous.app/fabel/Fabelous-Autocoder.git" - }, - "engines": { - "vscode": "^1.89.0" - }, - "categories": [ - "Machine Learning", - "Snippets", - "Programming Languages" - ], - "keywords": [ - "ollama", - "coding", - "autocomplete", - "open source", - "assistant", - "ai", - "llm" - ], - "galleryBanner": { - "color": "#133773" - }, - "activationEvents": [ - "onStartupFinished" - ], - "main": "./out/extension.js", - "contributes": { - "configuration": { - "title": "Fabelous Autocoder", - "properties": { - "fabelous-autocoder.endpoint": { - "type": "string", - "default": "http://localhost:11434/api/generate", - "description": "The endpoint of the ollama REST API" - }, - "fabelous-autocoder.authentication": { - "type": "string", - "default": "", - "description": "Authorization Token for Ollama" - }, - "fabelous-autocoder.model": { - "type": "string", - "default": "", - "description": "The model to use for generating completions" - }, - "fabelous-autocoder.max tokens predicted": { - "type": "integer", - "default": 1000, - "description": "The maximum number of tokens generated by the model." - }, - "fabelous-autocoder.prompt window size": { - "type": "integer", - "default": 2000, - "description": "The size of the prompt in characters. NOT tokens, so can be set about 1.5-2x the max tokens of the model (varies)." - }, - "fabelous-autocoder.completion keys": { - "type": "string", - "default": " ", - "description": "Character that the autocompletion item provider appear on. Multiple characters will be treated as different entries. REQUIRES RELOAD" - }, - "fabelous-autocoder.response preview": { - "type": "boolean", - "default": true, - "description": "Inline completion label will be the first line of response. Max is 10 tokens, but this is unlikely to be reached. If the first line is empty, the default label will be used. Not streamable, disable on slow devices." - }, - "fabelous-autocoder.preview max tokens": { - "type": "integer", - "default": 50, - "description": "The maximum number of tokens generated by the model for the response preview. Typically not reached as the preview stops on newline. Recommended to keep very low due to computational cost." - }, - "fabelous-autocoder.preview delay": { - "type": "number", - "default": 1, - "description": "Time to wait in seconds before starting inline preview generation. Prevents Ollama server from running briefly every time the completion key is pressed, which causes unnecessary compute usage. If you are not on a battery powered device, set this to 0 for a more responsive experience." - }, - "fabelous-autocoder.continue inline": { - "type": "boolean", - "default": true, - "description": "Ollama continues autocompletion after what is previewed inline. Disabling disables that feature as some may find it irritating. Multiline completion is still accessible through the shortcut even after disabling." - - }, - "fabelous-autocoder.temperature": { - "type": "number", - "default": 0.5, - "description": "Temperature of the model. It is recommended to set it lower than you would for dialogue." - }, - "fabelous-autocoder.keep alive": { - "type": "number", - "default": 10, - "description": "Time in minutes before Ollama unloads the model." - }, - "fabelous-autocoder.top p": { - "type": "number", - "description": "Top p sampling for the model." - } - } - }, - "commands": [ - { - "command": "fabelous-autocoder.autocomplete", - "title": "Fabelous Autocompletion" - }] - }, - "scripts": { - "vscode:prepublish": "npm run compile", - "compile": "tsc --skipLibCheck -p ./", - "package": "npm run compile && vsce package", - "lint": "eslint \"src/**/*.ts\"", - "watch": "tsc --skipLibCheck -watch -p ./" - }, - "devDependencies": { - "@types/node": "^20.12.8", - "@types/vscode": "^1.89.0", - "@typescript-eslint/eslint-plugin": "^7.8.0", - "@typescript-eslint/parser": "^7.8.0", - "eslint": "^8.57.0", - "typescript": "^5.4.5" - }, - "dependencies": { - "axios": "^1.6.8" - } -} \ No newline at end of file + "name": "fabelous-autocoder", + "version": "0.2.0", + "displayName": "Fabelous Autocoder", + "description": "A simple to use Ollama autocompletion Plugin", + "icon": "icon.png", + "publisher": "fabel", + "license": "CC BY-ND 4.0", + "bugs": { + "url": "https://gitea.fabelous.app/fabel/Fabelous-Autocoder/issues" + }, + "repository": { + "type": "git", + "url": "https://gitea.fabelous.app/fabel/Fabelous-Autocoder.git" + }, + "engines": { + "vscode": "^1.89.0" + }, + "categories": [ + "Machine Learning", + "Snippets", + "Programming Languages" + ], + "keywords": [ + "ollama", + "coding", + "autocomplete", + "open source", + "assistant", + "ai", + "llm" + ], + "galleryBanner": { + "color": "#133773" + }, + "activationEvents": [ + "onStartupFinished" + ], + "main": "./out/extension.js", + "contributes": { + "configuration": { + "title": "Fabelous Autocoder", + "properties": { + "fabelous-autocoder.endpoint": { + "type": "string", + "default": "http://localhost:11434/api/generate", + "description": "The endpoint of the ollama REST API" + }, + "fabelous-autocoder.authentication": { + "type": "string", + "default": "", + "description": "Authorization Token for Ollama" + }, + "fabelous-autocoder.model": { + "type": "string", + "default": "", + "description": "The model to use for generating completions" + }, + "fabelous-autocoder.max tokens predicted": { + "type": "integer", + "default": 1000, + "description": "The maximum number of tokens generated by the model." + }, + "fabelous-autocoder.prompt window size": { + "type": "integer", + "default": 2000, + "description": "The size of the prompt in characters. NOT tokens, so can be set about 1.5-2x the max tokens of the model (varies)." + }, + "fabelous-autocoder.completion keys": { + "type": "string", + "default": " ", + "description": "Character that the autocompletion item provider appear on. Multiple characters will be treated as different entries. REQUIRES RELOAD" + }, + "fabelous-autocoder.response preview": { + "type": "boolean", + "default": true, + "description": "Inline completion label will be the first line of response. Max is 10 tokens, but this is unlikely to be reached. If the first line is empty, the default label will be used. Not streamable, disable on slow devices." + }, + "fabelous-autocoder.preview max tokens": { + "type": "integer", + "default": 50, + "description": "The maximum number of tokens generated by the model for the response preview. Typically not reached as the preview stops on newline. Recommended to keep very low due to computational cost." + }, + "fabelous-autocoder.preview delay": { + "type": "number", + "default": 1, + "description": "Time to wait in seconds before starting inline preview generation. Prevents Ollama server from running briefly every time the completion key is pressed, which causes unnecessary compute usage. If you are not on a battery powered device, set this to 0 for a more responsive experience." + }, + "fabelous-autocoder.continue inline": { + "type": "boolean", + "default": true, + "description": "Ollama continues autocompletion after what is previewed inline. Disabling disables that feature as some may find it irritating. Multiline completion is still accessible through the shortcut even after disabling." + }, + "fabelous-autocoder.temperature": { + "type": "number", + "default": 0.5, + "description": "Temperature of the model. It is recommended to set it lower than you would for dialogue." + }, + "fabelous-autocoder.keep alive": { + "type": "number", + "default": 10, + "description": "Time in minutes before Ollama unloads the model." + }, + "fabelous-autocoder.top p": { + "type": "number", + "default": 1, + "description": "Top p sampling for the model." + }, + "fabelous-autocoder.enableLineByLineAcceptance": { + "type": "boolean", + "default": false, + "description": "Enable line-by-line acceptance of the generated code." + } + } + }, + "keybindings": [ + { + "command": "fabelous-autocoder.handleTab", + "key": "tab", + "when": "editorTextFocus && !editorTabMovesFocus" + } + ], + "commands": [ + { + "command": "fabelous-autocoder.autocomplete", + "title": "Fabelous Autocompletion" + }, + { + "command": "fabelous-autocoder.handleTab", + "title": "Handle Tab" + } + ] + }, + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc --skipLibCheck -p ./", + "package": "npm run compile && vsce package", + "lint": "eslint \"src/**/*.ts\"", + "watch": "tsc --skipLibCheck -watch -p ./" + }, + "devDependencies": { + "@types/node": "^20.12.8", + "@types/vscode": "^1.89.0", + "@typescript-eslint/eslint-plugin": "^7.8.0", + "@typescript-eslint/parser": "^7.8.0", + "eslint": "^8.57.0", + "typescript": "^5.4.5" + }, + "dependencies": { + "axios": "^1.6.8" + } +} -- 2.34.1 From e02296c5a718a7d5dff4af0c602232cc69641c87 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 9 Oct 2024 16:05:40 +0200 Subject: [PATCH 2/4] only ehader is to much --- src/extension.ts | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 3790c1b..49926c6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -85,7 +85,6 @@ async function generateCompletion(prompt: string, cancellationToken: vscode.Canc return response.data.response.replace(/||/g, '').trim(); } - class CompletionManager { private textEditor: vscode.TextEditor; private document: vscode.TextDocument; @@ -100,24 +99,27 @@ class CompletionManager { } public async showPreview() { - this.completionText = '\n' + this.completionText; - const completionLines = this.completionText.split('\n'); + // Calculate lines in the completionText + const completionLines = this.completionText.split('\n').length; - const emptyLine = ''; // Empty line for spacing - const previewLines = [emptyLine, ...completionLines, emptyLine]; + // Insert empty lines to make space for completion preview + const edit = new vscode.WorkspaceEdit(); + const linePadding = '\n'.repeat(completionLines); // Adjust padding based on lines in completion + const range = new vscode.Range(this.startPosition, this.startPosition); + + edit.insert(this.document.uri, this.startPosition, linePadding); + await vscode.workspace.applyEdit(edit); - const previewRanges: vscode.DecorationOptions[] = previewLines.map((line, index) => { + const previewRanges: vscode.DecorationOptions[] = this.completionText.split('\n').map((line, index) => { 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), - new vscode.Position(lineNumber, Number.MAX_VALUE) + new vscode.Position(actualLineNumber, 0), + new vscode.Position(actualLineNumber, 0) // Positions should be zero-length for insertions ), renderOptions: { after: { - contentText: line.length > 0 ? ` ${line}` : '', + contentText: ` ${line}`.trim(), }, }, }; @@ -128,18 +130,25 @@ class CompletionManager { public async acceptCompletion() { const edit = new vscode.WorkspaceEdit(); - 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) + + // Prepare to insert completion text + const endLine = this.startPosition.line + this.completionText.split('\n').length; + + // Replace the preview lines with actual completion text + const rangeToReplace = new vscode.Range( + this.startPosition, + new vscode.Position(endLine, 0) // Position for line end should just reach intended insert ); - edit.replace(this.document.uri, range, this.completionText); + + edit.replace(this.document.uri, rangeToReplace, this.completionText); + await vscode.workspace.applyEdit(edit); + this.clearPreview(); } - + public clearPreview() { - this.textEditor.setDecorations(previewDecorationType, []); + this.textEditor.setDecorations(previewDecorationType, []); // Clear only the decorations } public declineCompletion() { @@ -147,6 +156,7 @@ class CompletionManager { } } + async function autocompleteCommand(textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit, ...args: any[]) { const cancellationTokenSource = new vscode.CancellationTokenSource(); const cancellationToken = cancellationTokenSource.token; -- 2.34.1 From c1d0a53720c95ee940ed5393932277fccc88dcb7 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 9 Oct 2024 19:51:12 +0200 Subject: [PATCH 3/4] oonly remaining bug: backspace is sometimes still triggerable --- src/extension.ts | 181 +++++++++++++++++++++++++---------------------- 1 file changed, 96 insertions(+), 85 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 49926c6..1026b12 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -85,11 +85,13 @@ async function generateCompletion(prompt: string, cancellationToken: vscode.Canc return response.data.response.replace(/||/g, '').trim(); } + class CompletionManager { private textEditor: vscode.TextEditor; private document: vscode.TextDocument; private startPosition: vscode.Position; private completionText: string; + private insertedLineCount: number = 0; // Track the number of inserted lines constructor(textEditor: vscode.TextEditor, startPosition: vscode.Position, completionText: string) { this.textEditor = textEditor; @@ -99,64 +101,120 @@ class CompletionManager { } public async showPreview() { - // Calculate lines in the completionText + if (!previewDecorationType) { + createPreviewDecorationType(); + } + const completionLines = this.completionText.split('\n').length; - - // Insert empty lines to make space for completion preview + + // Adjust the start position to line after the original start position + const adjustedStartPosition = this.startPosition.translate(0, 0); + + // Step 1: Insert blank lines to make space for the preview const edit = new vscode.WorkspaceEdit(); - const linePadding = '\n'.repeat(completionLines); // Adjust padding based on lines in completion - const range = new vscode.Range(this.startPosition, this.startPosition); - - edit.insert(this.document.uri, this.startPosition, linePadding); + const linePadding = '\n'.repeat(completionLines + 1); // Include extra line break for visual separation + edit.insert(this.document.uri, adjustedStartPosition, linePadding); await vscode.workspace.applyEdit(edit); - + + this.insertedLineCount = completionLines + 1; + + // Step 2: Apply decorations const previewRanges: vscode.DecorationOptions[] = this.completionText.split('\n').map((line, index) => { - const actualLineNumber = this.startPosition.line + index; + const lineNumber = adjustedStartPosition.line + index + 1; // Start preview one line later return { range: new vscode.Range( - new vscode.Position(actualLineNumber, 0), - new vscode.Position(actualLineNumber, 0) // Positions should be zero-length for insertions + new vscode.Position(lineNumber, 0), + new vscode.Position(lineNumber, 0) ), renderOptions: { after: { - contentText: ` ${line}`.trim(), + contentText: line, + color: '#888888', + fontStyle: 'italic', }, }, }; }); - + this.textEditor.setDecorations(previewDecorationType, previewRanges); } + public async acceptCompletion() { const edit = new vscode.WorkspaceEdit(); - - // Prepare to insert completion text - const endLine = this.startPosition.line + this.completionText.split('\n').length; - - // Replace the preview lines with actual completion text + const completionLines = this.completionText.split('\n'); + const numberOfLines = completionLines.length; + + // Ensure the start position is never negative + const safeStartPosition = new vscode.Position(Math.max(0, this.startPosition.line - 1), 0); + + // Prepare the range to replace const rangeToReplace = new vscode.Range( - this.startPosition, - new vscode.Position(endLine, 0) // Position for line end should just reach intended insert + safeStartPosition, + this.startPosition.translate(numberOfLines, 0) ); - - edit.replace(this.document.uri, rangeToReplace, this.completionText); - + + // Construct the content to insert + const contentToInsert = (safeStartPosition.line === 0 ? '' : '\n') + this.completionText + '\n'; + edit.replace(this.document.uri, rangeToReplace, contentToInsert); + await vscode.workspace.applyEdit(edit); - - this.clearPreview(); + this.clearPreview(); // Clear the preview decorations + + // Calculate the new cursor position from the inserted content + const lastCompletionLine = completionLines[completionLines.length - 1]; + const newPosition = new vscode.Position(this.startPosition.line + numberOfLines - 1, lastCompletionLine.length); + + // Set the new cursor position without any additional move + this.textEditor.selection = new vscode.Selection(newPosition, newPosition); } + + public clearPreview() { - this.textEditor.setDecorations(previewDecorationType, []); // Clear only the decorations + this.textEditor.setDecorations(previewDecorationType, []); // Remove all preview decorations } - public declineCompletion() { - this.clearPreview(); + public async declineCompletion() { + this.clearPreview(); // Clear the preview decorations + + try { + const document = this.textEditor.document; + const currentPosition = this.textEditor.selection.active; + + // Calculate the range of lines to remove + const startLine = this.startPosition.line + 1; + const endLine = currentPosition.line; + if (endLine > startLine) { + const workspaceEdit = new vscode.WorkspaceEdit(); + + // Create a range from start of startLine to end of endLine + const range = new vscode.Range( + new vscode.Position(startLine, 0), + new vscode.Position(endLine, document.lineAt(endLine).text.length) + ); + + // Delete the range + workspaceEdit.delete(document.uri, range); + + // Apply the edit + await vscode.workspace.applyEdit(workspaceEdit); + + // Move the cursor back to the original position + this.textEditor.selection = new vscode.Selection(this.startPosition, this.startPosition); + + console.log(`Lines ${startLine + 1} to ${endLine + 1} removed successfully`); + } else { + console.log('No lines to remove'); + } + } catch (error) { + console.error('Error declining completion:', error); + vscode.window.showErrorMessage(`Error removing lines: ${error}`); + } } } - + async function autocompleteCommand(textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit, ...args: any[]) { const cancellationTokenSource = new vscode.CancellationTokenSource(); const cancellationToken = cancellationTokenSource.token; @@ -183,49 +241,6 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, edit: vscode.T await completionManager.showPreview(); activeCompletionManager = completionManager; - let isDisposed = false; - - const dispose = () => { - if (!isDisposed) { - console.log('Disposing listeners'); - disposable.dispose(); - typeDisposable.dispose(); - activeCompletionManager = null; - 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 typeDisposable = 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); @@ -235,24 +250,19 @@ async function autocompleteCommand(textEditor: vscode.TextEditor, edit: vscode.T } } -async function acceptCompletion() { +async function handleTab() { 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; + } else { + await vscode.commands.executeCommand('tab'); } } -async function handleTab() { +async function handleBackspace() { if (activeCompletionManager) { - await acceptCompletion(); + await activeCompletionManager.declineCompletion(); } else { - await vscode.commands.executeCommand('tab'); + await vscode.commands.executeCommand('deleteLeft'); } } @@ -299,10 +309,11 @@ export function activate(context: vscode.ExtensionContext) { vscode.workspace.onDidChangeConfiguration(updateConfig), vscode.languages.registerCompletionItemProvider('*', { provideCompletionItems }, ...config.completionKeys), vscode.commands.registerTextEditorCommand('fabelous-autocoder.autocomplete', autocompleteCommand), - vscode.commands.registerCommand('fabelous-autocoder.acceptCompletion', acceptCompletion), - vscode.commands.registerCommand('fabelous-autocoder.handleTab', handleTab) + vscode.commands.registerCommand('fabelous-autocoder.handleTab', handleTab), + vscode.commands.registerCommand('fabelous-autocoder.handleBackspace', handleBackspace) // Add this line ); } + export function deactivate() {} -- 2.34.1 From a3bf2f93bbe9941f930af28c087554397be37b17 Mon Sep 17 00:00:00 2001 From: Falko Habel Date: Wed, 9 Oct 2024 19:58:21 +0200 Subject: [PATCH 4/4] =?UTF-8?q?Final=20Version.=20Everything=20is=20workin?= =?UTF-8?q?g;=20atleast=20for=20Python=F0=9F=A5=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 17 +++++++++++------ src/extension.ts | 29 +++++++++++++++++++---------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index bf85831..a3c4aef 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "displayName": "Fabelous Autocoder", "description": "A simple to use Ollama autocompletion Plugin", "icon": "icon.png", - "publisher": "fabel", + "publisher": "Falko Habel", "license": "CC BY-ND 4.0", "bugs": { "url": "https://gitea.fabelous.app/fabel/Fabelous-Autocoder/issues" @@ -114,11 +114,16 @@ } }, "keybindings": [ - { - "command": "fabelous-autocoder.handleTab", - "key": "tab", - "when": "editorTextFocus && !editorTabMovesFocus" - } + { + "command": "fabelous-autocoder.handleTab", + "key": "tab", + "when": "editorTextFocus && !editorTabMovesFocus" + }, + { + "command": "fabelous-autocoder.handleBackspace", + "key": "backspace", + "when": "editorTextFocus" + } ], "commands": [ { diff --git a/src/extension.ts b/src/extension.ts index 1026b12..0bbacc8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -144,31 +144,39 @@ class CompletionManager { const edit = new vscode.WorkspaceEdit(); const completionLines = this.completionText.split('\n'); const numberOfLines = completionLines.length; - + // Ensure the start position is never negative const safeStartPosition = new vscode.Position(Math.max(0, this.startPosition.line - 1), 0); - + // Prepare the range to replace const rangeToReplace = new vscode.Range( safeStartPosition, this.startPosition.translate(numberOfLines, 0) ); - + // Construct the content to insert const contentToInsert = (safeStartPosition.line === 0 ? '' : '\n') + this.completionText + '\n'; edit.replace(this.document.uri, rangeToReplace, contentToInsert); - + await vscode.workspace.applyEdit(edit); - this.clearPreview(); // Clear the preview decorations - + + // Clear the preview decorations + this.clearPreview(); + + // Set activeCompletionManager to null + activeCompletionManager = null; + // Calculate the new cursor position from the inserted content const lastCompletionLine = completionLines[completionLines.length - 1]; - const newPosition = new vscode.Position(this.startPosition.line + numberOfLines - 1, lastCompletionLine.length); - - // Set the new cursor position without any additional move + const newPosition = new vscode.Position( + this.startPosition.line + numberOfLines - 1, + lastCompletionLine.length + ); + + // Set the new cursor position this.textEditor.selection = new vscode.Selection(newPosition, newPosition); } - + public clearPreview() { @@ -204,6 +212,7 @@ class CompletionManager { this.textEditor.selection = new vscode.Selection(this.startPosition, this.startPosition); console.log(`Lines ${startLine + 1} to ${endLine + 1} removed successfully`); + activeCompletionManager = null; } else { console.log('No lines to remove'); } -- 2.34.1