From 74ec33cf8ba8fe2f03ed85a5beac4f570a69c84e Mon Sep 17 00:00:00 2001 From: David Space Date: Sun, 20 Apr 2025 18:30:24 -0400 Subject: [PATCH 01/33] registered a test refractor --- package.json | 2 +- src/extension.ts | 11 +++++++- src/providers/refractor_provider.ts | 42 +++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/providers/refractor_provider.ts diff --git a/package.json b/package.json index 79cb94a8e..855006495 100644 --- a/package.json +++ b/package.json @@ -920,4 +920,4 @@ "ws": "^8.17.1", "ya-bbcode": "^4.0.0" } -} +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 1dcd1d7ba..ae5c2b035 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -31,6 +31,7 @@ import { } from "./utils"; import { prompt_for_godot_executable } from "./utils/prompts"; import { killSubProcesses, subProcess } from "./utils/subspawn"; +import { RefactorCodeActionProvider } from "./providers/refractor_provider"; interface Extension { context?: vscode.ExtensionContext; @@ -76,6 +77,13 @@ export function activate(context: vscode.ExtensionContext) { register_command("listGodotClasses", list_classes), register_command("switchSceneScript", switch_scene_script), register_command("getGodotPath", get_godot_path), + vscode.languages.registerCodeActionsProvider( + { language: 'gdscript' }, + new RefactorCodeActionProvider(), + { + providedCodeActionKinds: RefactorCodeActionProvider.providedCodeActionKinds, + } + ) ); set_context("godotFiles", ["gdscript", "gdscene", "gdresource", "gdshader"]); @@ -228,6 +236,7 @@ async function open_godot_editor_settings() { }); } + /** * Returns the executable path for Godot based on the current project's version. * Created to allow other extensions to get the path without having to go @@ -250,7 +259,7 @@ class GodotEditorTerminal implements vscode.Pseudoterminal { private closeEmitter = new vscode.EventEmitter(); onDidClose?: vscode.Event = this.closeEmitter.event; - constructor(private command: string) {} + constructor(private command: string) { } open(initialDimensions: vscode.TerminalDimensions | undefined): void { const proc = subProcess("GodotEditor", this.command, { shell: true, detached: true }); diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts new file mode 100644 index 000000000..78ccfb4cb --- /dev/null +++ b/src/providers/refractor_provider.ts @@ -0,0 +1,42 @@ +import * as vscode from "vscode"; + +export class RefactorCodeActionProvider implements vscode.CodeActionProvider { + // Define the kind of code actions this provider offers + public static readonly providedCodeActionKinds = [ + vscode.CodeActionKind.Refactor, + ]; + + public provideCodeActions( + document: vscode.TextDocument, + range: vscode.Range, + context: vscode.CodeActionContext, + token: vscode.CancellationToken + ): vscode.ProviderResult { + // Get the line where the cursor is located + const startLine = document.lineAt(range.start.line); + const lineText = startLine.text.trim(); + + // Only show the action if the line starts with "var" + if (!lineText.startsWith('var ')) { + return undefined; + } + + // Create the refactor action + const addExportToVariable = new vscode.CodeAction( + 'Add @export to var', + vscode.CodeActionKind.Refactor + ); + + // Specify the edits to be applied + addExportToVariable.edit = new vscode.WorkspaceEdit(); + const updatedText = lineText.replace(/^var /, '@export var ${1:}'); + + addExportToVariable.edit.replace( + document.uri, + startLine.range, + updatedText + ); + + return [addExportToVariable]; + } +} From b6e74da35780d747333f5b71d9e22adbd76e8a59 Mon Sep 17 00:00:00 2001 From: David Space Date: Sun, 20 Apr 2025 19:07:15 -0400 Subject: [PATCH 02/33] added addRangeExportToVariable --- src/providers/refractor_provider.ts | 57 ++++++++++++++++------- syntaxes/examples/code_actions_example.gd | 3 ++ 2 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 syntaxes/examples/code_actions_example.gd diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index 78ccfb4cb..066942a61 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -12,31 +12,54 @@ export class RefactorCodeActionProvider implements vscode.CodeActionProvider { context: vscode.CodeActionContext, token: vscode.CancellationToken ): vscode.ProviderResult { - // Get the line where the cursor is located const startLine = document.lineAt(range.start.line); const lineText = startLine.text.trim(); // Only show the action if the line starts with "var" - if (!lineText.startsWith('var ')) { + if (!lineText.startsWith("var ")) { return undefined; } - // Create the refactor action - const addExportToVariable = new vscode.CodeAction( - 'Add @export to var', - vscode.CodeActionKind.Refactor - ); - // Specify the edits to be applied - addExportToVariable.edit = new vscode.WorkspaceEdit(); - const updatedText = lineText.replace(/^var /, '@export var ${1:}'); - - addExportToVariable.edit.replace( - document.uri, - startLine.range, - updatedText - ); + var _addExportToVariable = addExportToVariable(lineText, document, startLine); + var _addRageExportToVariable = addRangeExportToVariable(lineText, document, startLine); - return [addExportToVariable]; + return [_addExportToVariable, _addRageExportToVariable]; } } + +function addExportToVariable(lineText: string, document: vscode.TextDocument, startLine: vscode.TextLine): vscode.CodeAction { + const codeAction = new vscode.CodeAction( + "Export this variable", + vscode.CodeActionKind.RefactorInline + ); + + codeAction.edit = new vscode.WorkspaceEdit(); + + const updatedText = lineText.replace(/^var/, "@export var"); + + codeAction.edit.replace( + document.uri, + startLine.range, + updatedText + ); + + return codeAction; +} + +function addRangeExportToVariable(lineText: string, document: vscode.TextDocument, startLine: vscode.TextLine): vscode.CodeAction { + const codeAction = new vscode.CodeAction( + "Export as a range", + vscode.CodeActionKind.RefactorInline + ); + codeAction.edit = new vscode.WorkspaceEdit(); + + const updatedText = lineText.replace(/^var\s*(\w+)\s*:\s*(float|int)/, "@export_range(0, 1) var $1: $2"); + + codeAction.edit.replace( + document.uri, + startLine.range, + updatedText + ); + return codeAction +} diff --git a/syntaxes/examples/code_actions_example.gd b/syntaxes/examples/code_actions_example.gd new file mode 100644 index 000000000..cecd7c68a --- /dev/null +++ b/syntaxes/examples/code_actions_example.gd @@ -0,0 +1,3 @@ +var simple_export +var range_export: float +var range_export_integer: int From 460116375aacba54ba6f1649f642f978a9c1f19f Mon Sep 17 00:00:00 2001 From: David Space Date: Sun, 20 Apr 2025 23:18:13 -0400 Subject: [PATCH 03/33] tried and failed to make a snippet --- src/providers/refractor_provider.ts | 64 +++++++++++++++++++---- syntaxes/examples/code_actions_example.gd | 8 ++- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index 066942a61..040f8330b 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -12,27 +12,28 @@ export class RefactorCodeActionProvider implements vscode.CodeActionProvider { context: vscode.CodeActionContext, token: vscode.CancellationToken ): vscode.ProviderResult { - const startLine = document.lineAt(range.start.line); - const lineText = startLine.text.trim(); - - // Only show the action if the line starts with "var" - if (!lineText.startsWith("var ")) { - return undefined; - } // Specify the edits to be applied - var _addExportToVariable = addExportToVariable(lineText, document, startLine); - var _addRageExportToVariable = addRangeExportToVariable(lineText, document, startLine); + var _addExportToVariable = addExportToVariable(range, document); + var _addRageExportToVariable = addRangeExportToVariable(range, document); return [_addExportToVariable, _addRageExportToVariable]; } + + } -function addExportToVariable(lineText: string, document: vscode.TextDocument, startLine: vscode.TextLine): vscode.CodeAction { +function addExportToVariable(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction { const codeAction = new vscode.CodeAction( "Export this variable", vscode.CodeActionKind.RefactorInline ); + const startLine = document.lineAt(range.start.line); + const lineText = startLine.text.trim(); + + if (!lineText.startsWith("var ")) { + return undefined; + } codeAction.edit = new vscode.WorkspaceEdit(); @@ -47,13 +48,53 @@ function addExportToVariable(lineText: string, document: vscode.TextDocument, st return codeAction; } -function addRangeExportToVariable(lineText: string, document: vscode.TextDocument, startLine: vscode.TextLine): vscode.CodeAction { +function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction { + const editor = vscode.window.activeTextEditor; + const REGEX: RegExp = /^var\s*(\w+)\s*:\s*(float|int)/ + const startLine = document.lineAt(range.start.line); + const lineText = startLine.text.trim(); + + const exec: RegExpExecArray | null = REGEX.exec(lineText) + + + if (!exec) { + return undefined + } + const codeAction = new vscode.CodeAction( "Export as a range", vscode.CodeActionKind.RefactorInline ); codeAction.edit = new vscode.WorkspaceEdit(); + + const updatedText = lineText.replace(REGEX, "@export_range(0, 1) var $1: $2"); + // const snipperString = new vscode.SnippetString(updatedText.replace("export_range(0, 1)", "@export_range(${1:0}, ${2:0})")) + + codeAction.edit.replace( + document.uri, + startLine.range, + updatedText + ); + // editor.insertSnippet(snipperString, startLine.range) + // Move the cursor to the specified position + // const newPosition = new vscode.Position(startLine.lineNumber, character); + // editor.selection = new vscode.Selection(newPosition, newPosition); + // editor.revealRange(new vscode.Range(newPosition, newPosition)); + + return codeAction +} + +function groupExportedProperties(lineText: string, document: vscode.TextDocument, startLine: vscode.TextLine): vscode.CodeAction { + const codeAction = new vscode.CodeAction( + "Group exports", + vscode.CodeActionKind.RefactorInline + ); + var editor = vscode.window.activeTextEditor; + const selectedText = editor.document.getText(editor.selection); + + codeAction.edit = new vscode.WorkspaceEdit(); + const updatedText = lineText.replace(/^var\s*(\w+)\s*:\s*(float|int)/, "@export_range(0, 1) var $1: $2"); codeAction.edit.replace( @@ -63,3 +104,4 @@ function addRangeExportToVariable(lineText: string, document: vscode.TextDocumen ); return codeAction } + diff --git a/syntaxes/examples/code_actions_example.gd b/syntaxes/examples/code_actions_example.gd index cecd7c68a..822caa1c3 100644 --- a/syntaxes/examples/code_actions_example.gd +++ b/syntaxes/examples/code_actions_example.gd @@ -1,3 +1,9 @@ var simple_export -var range_export: float +@@export_range(0, 0) var range_export: float var range_export_integer: int + +@export var ex +@export var ex +@export var ex +@export var ex +@export var ex From c524864a8ae61a707c0457ac1490d6d8e82c6708 Mon Sep 17 00:00:00 2001 From: David Space Date: Mon, 21 Apr 2025 21:30:19 -0400 Subject: [PATCH 04/33] added export these variables --- src/providers/refractor_provider.ts | 42 +++++++++++++++++------ syntaxes/examples/code_actions_example.gd | 12 +++---- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index 040f8330b..38baf784f 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -16,8 +16,9 @@ export class RefactorCodeActionProvider implements vscode.CodeActionProvider { // Specify the edits to be applied var _addExportToVariable = addExportToVariable(range, document); var _addRageExportToVariable = addRangeExportToVariable(range, document); + var _exportTheseVariables = exportTheseVariables(range, document) - return [_addExportToVariable, _addRageExportToVariable]; + return [_addExportToVariable, _addRageExportToVariable, _exportTheseVariables]; } @@ -85,23 +86,42 @@ function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocu return codeAction } -function groupExportedProperties(lineText: string, document: vscode.TextDocument, startLine: vscode.TextLine): vscode.CodeAction { +function exportTheseVariables(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction { const codeAction = new vscode.CodeAction( - "Group exports", - vscode.CodeActionKind.RefactorInline + "Export these variables", + vscode.CodeActionKind.RefactorRewrite, ); - var editor = vscode.window.activeTextEditor; - const selectedText = editor.document.getText(editor.selection); + const editor = vscode.window.activeTextEditor; + const REGEX = /^\s*(?:@.*?)+(.*)$/gm + const selectedText = document.getText(editor.selection); - codeAction.edit = new vscode.WorkspaceEdit(); + if (selectedText == "") return undefined; + + const individualLines = selectedText.split("\n"); + + var updatedText: String[] = []; + + for (let i = 0; i < individualLines.length; i++) { + const element = individualLines[i]; + console.log(element); - const updatedText = lineText.replace(/^var\s*(\w+)\s*:\s*(float|int)/, "@export_range(0, 1) var $1: $2"); + if (!element.startsWith("var ")) { + updatedText = updatedText.concat(element); + continue; + } + updatedText = updatedText.concat(element.replace(/^var/, "@export var")); + } + console.log(updatedText); + + if (updatedText.length <= 0) return undefined; + var newText: string = updatedText.join("\n"); + codeAction.edit = new vscode.WorkspaceEdit(); codeAction.edit.replace( document.uri, - startLine.range, - updatedText - ); + editor.selection, + newText, + ) return codeAction } diff --git a/syntaxes/examples/code_actions_example.gd b/syntaxes/examples/code_actions_example.gd index 822caa1c3..d616676f4 100644 --- a/syntaxes/examples/code_actions_example.gd +++ b/syntaxes/examples/code_actions_example.gd @@ -1,9 +1,9 @@ var simple_export -@@export_range(0, 0) var range_export: float +var range_export: float var range_export_integer: int -@export var ex -@export var ex -@export var ex -@export var ex -@export var ex +var ex +var ex +const ex +var ex +var ex From 49dca75ba3783339e7d6f2c91e27bb6f7887aab5 Mon Sep 17 00:00:00 2001 From: David Space Date: Mon, 21 Apr 2025 21:54:15 -0400 Subject: [PATCH 05/33] addRangeExportToVariable now discerns between a float and a int --- src/providers/refractor_provider.ts | 25 ++++++++++++++--------- syntaxes/examples/code_actions_example.gd | 4 ++-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index 38baf784f..6af6a9995 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -1,5 +1,12 @@ import * as vscode from "vscode"; +/** + * @param $1 The variable name, + * @param $2 The variable type, + * @param $3 Everything else after the type + */ +const VARIABLE_REGEXP: RegExp = /var\s*(\w+:?)\s*\s*(\w*)([\s\S\w\W]*)/ + export class RefactorCodeActionProvider implements vscode.CodeActionProvider { // Define the kind of code actions this provider offers public static readonly providedCodeActionKinds = [ @@ -38,7 +45,7 @@ function addExportToVariable(range: vscode.Range, document: vscode.TextDocument) codeAction.edit = new vscode.WorkspaceEdit(); - const updatedText = lineText.replace(/^var/, "@export var"); + const updatedText = lineText.replace(VARIABLE_REGEXP, "@export var $1 $2 $3"); codeAction.edit.replace( document.uri, @@ -50,7 +57,6 @@ function addExportToVariable(range: vscode.Range, document: vscode.TextDocument) } function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction { - const editor = vscode.window.activeTextEditor; const REGEX: RegExp = /^var\s*(\w+)\s*:\s*(float|int)/ const startLine = document.lineAt(range.start.line); const lineText = startLine.text.trim(); @@ -67,22 +73,21 @@ function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocu vscode.CodeActionKind.RefactorInline ); codeAction.edit = new vscode.WorkspaceEdit(); + var updatedText = ""; + if (exec[2].trim() == "float") { + updatedText = lineText.replace(REGEX, "@export_range(0.0, 1.0) var $1: $2"); + } else { + updatedText = lineText.replace(REGEX, "@export_range(0, 1) var $1: $2"); + } - const updatedText = lineText.replace(REGEX, "@export_range(0, 1) var $1: $2"); - // const snipperString = new vscode.SnippetString(updatedText.replace("export_range(0, 1)", "@export_range(${1:0}, ${2:0})")) codeAction.edit.replace( document.uri, startLine.range, updatedText ); - // editor.insertSnippet(snipperString, startLine.range) - // Move the cursor to the specified position - // const newPosition = new vscode.Position(startLine.lineNumber, character); - // editor.selection = new vscode.Selection(newPosition, newPosition); - // editor.revealRange(new vscode.Range(newPosition, newPosition)); - + codeAction.isPreferred = true return codeAction } diff --git a/syntaxes/examples/code_actions_example.gd b/syntaxes/examples/code_actions_example.gd index d616676f4..98392f4d4 100644 --- a/syntaxes/examples/code_actions_example.gd +++ b/syntaxes/examples/code_actions_example.gd @@ -1,6 +1,6 @@ var simple_export -var range_export: float -var range_export_integer: int +var range_export: float = 0.0 +var range_export_integer: int = 0 var ex var ex From 4da75dd7fcb968050a8eff0e04321c34c0e23d66 Mon Sep 17 00:00:00 2001 From: David Space Date: Mon, 21 Apr 2025 21:55:25 -0400 Subject: [PATCH 06/33] added todo --- src/providers/refractor_provider.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index 6af6a9995..4115a5a03 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -91,6 +91,7 @@ function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocu return codeAction } +// TODO: When everything is done, this should export them to their designated exports function exportTheseVariables(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction { const codeAction = new vscode.CodeAction( "Export these variables", From 108b6a0fe50a6aa3ecf825cff5defea3ff3cf315 Mon Sep 17 00:00:00 2001 From: David Space Date: Tue, 22 Apr 2025 17:32:50 -0400 Subject: [PATCH 07/33] refractored a bit --- src/providers/refractor_provider.ts | 73 +++++++++++++++-------- syntaxes/examples/code_actions_example.gd | 2 + 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index 4115a5a03..077877efa 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -1,7 +1,7 @@ import * as vscode from "vscode"; /** - * @param $1 The variable name, + * @param $1 The variable name (including the : if it has it), * @param $2 The variable type, * @param $3 Everything else after the type */ @@ -21,11 +21,10 @@ export class RefactorCodeActionProvider implements vscode.CodeActionProvider { ): vscode.ProviderResult { // Specify the edits to be applied - var _addExportToVariable = addExportToVariable(range, document); - var _addRageExportToVariable = addRangeExportToVariable(range, document); - var _exportTheseVariables = exportTheseVariables(range, document) + var _exportTheseVariables = exportTheseVariables(range, document); + var _handleDifferentVariableExports = handleDifferentTypedVariableExports(range, document); - return [_addExportToVariable, _addRageExportToVariable, _exportTheseVariables]; + return [..._handleDifferentVariableExports, _exportTheseVariables]; } @@ -39,10 +38,6 @@ function addExportToVariable(range: vscode.Range, document: vscode.TextDocument) const startLine = document.lineAt(range.start.line); const lineText = startLine.text.trim(); - if (!lineText.startsWith("var ")) { - return undefined; - } - codeAction.edit = new vscode.WorkspaceEdit(); const updatedText = lineText.replace(VARIABLE_REGEXP, "@export var $1 $2 $3"); @@ -56,32 +51,31 @@ function addExportToVariable(range: vscode.Range, document: vscode.TextDocument) return codeAction; } -function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction { - const REGEX: RegExp = /^var\s*(\w+)\s*:\s*(float|int)/ +function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocument, + name: string, + type: string, + body: string, +): vscode.CodeAction { const startLine = document.lineAt(range.start.line); const lineText = startLine.text.trim(); - const exec: RegExpExecArray | null = REGEX.exec(lineText) - - - if (!exec) { - return undefined - } + const is_number = type.trim() === "float" || type.trim() === "int"; + if (!is_number) return; const codeAction = new vscode.CodeAction( "Export as a range", vscode.CodeActionKind.RefactorInline ); + codeAction.edit = new vscode.WorkspaceEdit(); var updatedText = ""; - if (exec[2].trim() == "float") { - updatedText = lineText.replace(REGEX, "@export_range(0.0, 1.0) var $1: $2"); + if (type.trim() == "float") { + updatedText = lineText.replace(VARIABLE_REGEXP, `@export_range(0.0, 1.0) var ${name} ${type} ${body}`); } else { - updatedText = lineText.replace(REGEX, "@export_range(0, 1) var $1: $2"); + updatedText = lineText.replace(VARIABLE_REGEXP, `@export_range(0, 1) var ${name} ${type} ${body}`); } - codeAction.edit.replace( document.uri, startLine.range, @@ -91,6 +85,36 @@ function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocu return codeAction } + +/** + * For Variables that only checks for the type + * @param range + * @param document + * @returns + */ +function handleDifferentTypedVariableExports(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction[] { + var actions: vscode.CodeAction[] = []; + + const startLine = document.lineAt(range.start.line); + const lineText = startLine.text.trim(); + + const exec: RegExpExecArray | null = VARIABLE_REGEXP.exec(lineText) + if (!exec) { + return undefined + } + + const [_, name, type, body] = exec; + + var _addExportToVariable = addExportToVariable(range, document); + var _addRageExportToVariable = addRangeExportToVariable(range, document, name, type, body); + + actions.push(_addExportToVariable, _addRageExportToVariable) + + + + return actions; +} + // TODO: When everything is done, this should export them to their designated exports function exportTheseVariables(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction { const codeAction = new vscode.CodeAction( @@ -98,10 +122,9 @@ function exportTheseVariables(range: vscode.Range, document: vscode.TextDocument vscode.CodeActionKind.RefactorRewrite, ); const editor = vscode.window.activeTextEditor; - const REGEX = /^\s*(?:@.*?)+(.*)$/gm const selectedText = document.getText(editor.selection); - if (selectedText == "") return undefined; + if (selectedText === "") return undefined; const individualLines = selectedText.split("\n"); @@ -109,7 +132,6 @@ function exportTheseVariables(range: vscode.Range, document: vscode.TextDocument for (let i = 0; i < individualLines.length; i++) { const element = individualLines[i]; - console.log(element); if (!element.startsWith("var ")) { updatedText = updatedText.concat(element); @@ -117,7 +139,6 @@ function exportTheseVariables(range: vscode.Range, document: vscode.TextDocument } updatedText = updatedText.concat(element.replace(/^var/, "@export var")); } - console.log(updatedText); if (updatedText.length <= 0) return undefined; @@ -128,6 +149,6 @@ function exportTheseVariables(range: vscode.Range, document: vscode.TextDocument editor.selection, newText, ) + codeAction.isPreferred = true; return codeAction } - diff --git a/syntaxes/examples/code_actions_example.gd b/syntaxes/examples/code_actions_example.gd index 98392f4d4..64d493c46 100644 --- a/syntaxes/examples/code_actions_example.gd +++ b/syntaxes/examples/code_actions_example.gd @@ -1,6 +1,8 @@ var simple_export var range_export: float = 0.0 var range_export_integer: int = 0 +var range_export_integer: int +var range_export_float: float var ex var ex From faa2517e1a3106c8df3918de820f81dfb3f29ae5 Mon Sep 17 00:00:00 2001 From: David Space Date: Tue, 22 Apr 2025 18:16:30 -0400 Subject: [PATCH 08/33] added documentation to one --- src/providers/refractor_provider.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index 077877efa..a458b68e9 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -87,7 +87,9 @@ function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocu /** - * For Variables that only checks for the type + * For Variables that only checks for the type. + * It makes a single execution and passes down the results, furthermore , + * since the functions are only in variables, we exit early if the line is NOT a variable * @param range * @param document * @returns @@ -110,8 +112,6 @@ function handleDifferentTypedVariableExports(range: vscode.Range, document: vsco actions.push(_addExportToVariable, _addRageExportToVariable) - - return actions; } From 6103431558cceabf9fb674536e0bb0618a03ac1f Mon Sep 17 00:00:00 2001 From: David Space Date: Tue, 22 Apr 2025 18:22:27 -0400 Subject: [PATCH 09/33] tempted to make exportColorNoAlpha --- src/providers/refractor_provider.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index a458b68e9..1dfbf9644 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -21,7 +21,7 @@ export class RefactorCodeActionProvider implements vscode.CodeActionProvider { ): vscode.ProviderResult { // Specify the edits to be applied - var _exportTheseVariables = exportTheseVariables(range, document); + var _exportTheseVariables = exportTheseVariables(document); var _handleDifferentVariableExports = handleDifferentTypedVariableExports(range, document); return [..._handleDifferentVariableExports, _exportTheseVariables]; @@ -115,8 +115,26 @@ function handleDifferentTypedVariableExports(range: vscode.Range, document: vsco return actions; } +// function exportColorNoAlpha( +// range: vscode.Range, +// document: vscode.TextDocument, +// name: string, +// type: string, +// body: string, +// ): vscode.CodeAction { +// if (type !== "Color") return; +// const codeAction = new vscode.CodeAction( +// "Export as Color No Alpha", +// vscode.CodeActionKind.RefactorRewrite, +// ); + + + +// return; +// } + // TODO: When everything is done, this should export them to their designated exports -function exportTheseVariables(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction { +function exportTheseVariables(document: vscode.TextDocument): vscode.CodeAction { const codeAction = new vscode.CodeAction( "Export these variables", vscode.CodeActionKind.RefactorRewrite, From 33ee2019711e78aed1ecf27bc5072d92d58acbdd Mon Sep 17 00:00:00 2001 From: David Space Date: Tue, 22 Apr 2025 18:45:46 -0400 Subject: [PATCH 10/33] added mockup refractor --- package.json | 5 +++ src/extension.ts | 3 +- src/providers/refractor_provider.ts | 53 +++++++++++++++++++++++ syntaxes/examples/code_actions_example.gd | 14 ++++++ 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 855006495..3c7c08a6f 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,11 @@ } ], "commands": [ + { + "category": "Godot Tools", + "command": "godotTools.extractFunction", + "title": "Extract the selected code as a function" + }, { "category": "Godot Tools", "command": "godotTools.openEditor", diff --git a/src/extension.ts b/src/extension.ts index ae5c2b035..2c1a007ff 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -31,7 +31,7 @@ import { } from "./utils"; import { prompt_for_godot_executable } from "./utils/prompts"; import { killSubProcesses, subProcess } from "./utils/subspawn"; -import { RefactorCodeActionProvider } from "./providers/refractor_provider"; +import { extractFunctionCommand, RefactorCodeActionProvider } from "./providers/refractor_provider"; interface Extension { context?: vscode.ExtensionContext; @@ -77,6 +77,7 @@ export function activate(context: vscode.ExtensionContext) { register_command("listGodotClasses", list_classes), register_command("switchSceneScript", switch_scene_script), register_command("getGodotPath", get_godot_path), + register_command("extractFunction", extractFunctionCommand), vscode.languages.registerCodeActionsProvider( { language: 'gdscript' }, new RefactorCodeActionProvider(), diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index 1dfbf9644..5feb551ca 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -170,3 +170,56 @@ function exportTheseVariables(document: vscode.TextDocument): vscode.CodeAction codeAction.isPreferred = true; return codeAction } + + +export async function extractFunctionCommand(): Promise { + const editor = vscode.window.activeTextEditor; + + if (!editor) { + vscode.window.showErrorMessage('No active editor!'); + return; + } + + const selection = editor.selection; + const selectedText = editor.document.getText(selection); + + if (!selectedText) { + vscode.window.showErrorMessage('No code selected!'); + return; + } + + // Prompt for function name + const functionName = await vscode.window.showInputBox({ + prompt: 'Enter the name of the new function', + validateInput: (input) => input.trim() === '' ? 'Function name cannot be empty' : null, + }); + + if (!functionName) { + vscode.window.showErrorMessage('Function name is required!'); + return; + } + + // Generate the new function code + const newFunction = ` +function ${functionName}() { +${selectedText.split("\n").map(line => "\t" + line).join("\n")} +} +`; + + // Insert the new function at the end of the file + const document = editor.document; + const fullText = document.getText(); + + + const position = new vscode.Position(document.lineCount, 0); + + await editor.edit((editBuilder) => { + // Add the new function + editBuilder.insert(position, newFunction); + + // Replace the selected code with a function call + editBuilder.replace(selection, `${functionName}();`); + }); + + vscode.window.showInformationMessage(`Extracted code as a new function: ${functionName}`); +} diff --git a/syntaxes/examples/code_actions_example.gd b/syntaxes/examples/code_actions_example.gd index 64d493c46..84ffbc5e7 100644 --- a/syntaxes/examples/code_actions_example.gd +++ b/syntaxes/examples/code_actions_example.gd @@ -9,3 +9,17 @@ var ex const ex var ex var ex + + +func method(args): + if condition: + var a + var b + var c + else: + pass + pass + pass + +func method(args): + pass \ No newline at end of file From 088f414f5835e4209e07122daed696bba867c4c2 Mon Sep 17 00:00:00 2001 From: David Space Date: Tue, 22 Apr 2025 22:31:12 -0400 Subject: [PATCH 11/33] added refractor command --- src/providers/refractor_provider.ts | 44 ++++++++++++----------- syntaxes/examples/code_actions_example.gd | 6 +++- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index 5feb551ca..e69dfc752 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -1,4 +1,5 @@ import * as vscode from "vscode"; +import { integer } from "vscode-languageclient"; /** * @param $1 The variable name (including the : if it has it), @@ -176,7 +177,6 @@ export async function extractFunctionCommand(): Promise { const editor = vscode.window.activeTextEditor; if (!editor) { - vscode.window.showErrorMessage('No active editor!'); return; } @@ -184,10 +184,8 @@ export async function extractFunctionCommand(): Promise { const selectedText = editor.document.getText(selection); if (!selectedText) { - vscode.window.showErrorMessage('No code selected!'); return; } - // Prompt for function name const functionName = await vscode.window.showInputBox({ prompt: 'Enter the name of the new function', @@ -198,28 +196,32 @@ export async function extractFunctionCommand(): Promise { vscode.window.showErrorMessage('Function name is required!'); return; } - - // Generate the new function code - const newFunction = ` -function ${functionName}() { -${selectedText.split("\n").map(line => "\t" + line).join("\n")} -} -`; - - // Insert the new function at the end of the file + const newFunction = `func ${functionName}():\n${selectedText.split("\n").map(line => "\t" + line).join("\n")}\n`; const document = editor.document; - const fullText = document.getText(); + var pasteLine: number = editor.selection.end.line; + /** + * Look in each line, starting with this one and go down, + * if you find a line with a method declaration, go up one and paste the new function + * or the end of the document + */ + + for (let i = 0; i < document.lineCount; i++) { + if (i < pasteLine) continue; + const textLine = document.lineAt(i); + + if (textLine.text.includes("func")) { + break; + } else { + pasteLine++; + } + } - const position = new vscode.Position(document.lineCount, 0); - await editor.edit((editBuilder) => { - // Add the new function - editBuilder.insert(position, newFunction); + const position = new vscode.Position(Math.min(pasteLine, document.lineCount), 0); - // Replace the selected code with a function call - editBuilder.replace(selection, `${functionName}();`); + await editor.edit((doc) => { + doc.insert(position, newFunction); + doc.replace(selection, `${functionName}()`); }); - - vscode.window.showInformationMessage(`Extracted code as a new function: ${functionName}`); } diff --git a/syntaxes/examples/code_actions_example.gd b/syntaxes/examples/code_actions_example.gd index 84ffbc5e7..5a25b1134 100644 --- a/syntaxes/examples/code_actions_example.gd +++ b/syntaxes/examples/code_actions_example.gd @@ -11,7 +11,7 @@ var ex var ex -func method(args): +func export_code_as_new_function(args): if condition: var a var b @@ -20,6 +20,10 @@ func method(args): pass pass pass + if condition: + if condition: + if condition: + pass func method(args): pass \ No newline at end of file From 99fe57f70ffeb0c168012a634a9dff4b767ce7de Mon Sep 17 00:00:00 2001 From: David Space Date: Tue, 22 Apr 2025 22:34:57 -0400 Subject: [PATCH 12/33] moved the command to where all the other commands are --- src/extension.ts | 58 ++++++++++++++++++++++++++++- src/providers/refractor_provider.ts | 52 -------------------------- 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 2c1a007ff..02eaff3f5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -31,7 +31,7 @@ import { } from "./utils"; import { prompt_for_godot_executable } from "./utils/prompts"; import { killSubProcesses, subProcess } from "./utils/subspawn"; -import { extractFunctionCommand, RefactorCodeActionProvider } from "./providers/refractor_provider"; +import { RefactorCodeActionProvider } from "./providers/refractor_provider"; interface Extension { context?: vscode.ExtensionContext; @@ -77,7 +77,7 @@ export function activate(context: vscode.ExtensionContext) { register_command("listGodotClasses", list_classes), register_command("switchSceneScript", switch_scene_script), register_command("getGodotPath", get_godot_path), - register_command("extractFunction", extractFunctionCommand), + register_command("extractFunction", extract_function), vscode.languages.registerCodeActionsProvider( { language: 'gdscript' }, new RefactorCodeActionProvider(), @@ -95,6 +95,60 @@ export function activate(context: vscode.ExtensionContext) { }); } +export async function extract_function(): Promise { + const editor: vscode.TextEditor = vscode.window.activeTextEditor; + + if (!editor) { + return; + } + + const selection: vscode.Selection = editor.selection; + const selectedText: string = editor.document.getText(selection); + + if (!selectedText) { + return; + } + // Prompt for function name + const functionName = await vscode.window.showInputBox({ + prompt: 'Enter the name of the new function', + validateInput: (input) => input.trim() === '' ? 'Function name cannot be empty' : null, + }); + + if (!functionName) { + vscode.window.showErrorMessage('Function name is required!'); + return; + } + const newFunction: string = `func ${functionName}():\n${selectedText.split("\n").map(line => "\t" + line).join("\n")}\n`; + const document: vscode.TextDocument = editor.document; + + var pasteLine: number = editor.selection.end.line; + /** + * Look in each line, starting with this one and go down, + * if you find a line with a method declaration, go up one and paste the new function + * or the end of the document + */ + + for (let i = 0; i < document.lineCount; i++) { + if (i < pasteLine) continue; + const textLine = document.lineAt(i); + + if (textLine.text.includes("func")) { + break; + } else { + pasteLine++; + } + } + + + const position = new vscode.Position(Math.min(pasteLine, document.lineCount), 0); + + await editor.edit((doc) => { + doc.insert(position, newFunction); + doc.replace(selection, `${functionName}()`); + }); +} + + async function initial_setup() { const projectVersion = await get_project_version(); if (projectVersion === undefined) { diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index e69dfc752..b177594ab 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -173,55 +173,3 @@ function exportTheseVariables(document: vscode.TextDocument): vscode.CodeAction } -export async function extractFunctionCommand(): Promise { - const editor = vscode.window.activeTextEditor; - - if (!editor) { - return; - } - - const selection = editor.selection; - const selectedText = editor.document.getText(selection); - - if (!selectedText) { - return; - } - // Prompt for function name - const functionName = await vscode.window.showInputBox({ - prompt: 'Enter the name of the new function', - validateInput: (input) => input.trim() === '' ? 'Function name cannot be empty' : null, - }); - - if (!functionName) { - vscode.window.showErrorMessage('Function name is required!'); - return; - } - const newFunction = `func ${functionName}():\n${selectedText.split("\n").map(line => "\t" + line).join("\n")}\n`; - const document = editor.document; - - var pasteLine: number = editor.selection.end.line; - /** - * Look in each line, starting with this one and go down, - * if you find a line with a method declaration, go up one and paste the new function - * or the end of the document - */ - - for (let i = 0; i < document.lineCount; i++) { - if (i < pasteLine) continue; - const textLine = document.lineAt(i); - - if (textLine.text.includes("func")) { - break; - } else { - pasteLine++; - } - } - - - const position = new vscode.Position(Math.min(pasteLine, document.lineCount), 0); - - await editor.edit((doc) => { - doc.insert(position, newFunction); - doc.replace(selection, `${functionName}()`); - }); -} From 515502a7bb63ba97af0519c51c3d9e9c848c2e55 Mon Sep 17 00:00:00 2001 From: David Space Date: Tue, 22 Apr 2025 23:11:53 -0400 Subject: [PATCH 13/33] handleDifferentTypedVariableExports was messing with everything, removed --- src/extension.ts | 7 ++- src/providers/refractor_provider.ts | 75 +++++++++++++++-------------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 02eaff3f5..27b19c1ae 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -31,7 +31,7 @@ import { } from "./utils"; import { prompt_for_godot_executable } from "./utils/prompts"; import { killSubProcesses, subProcess } from "./utils/subspawn"; -import { RefactorCodeActionProvider } from "./providers/refractor_provider"; +import { ExportVariablesCodeActionProvider } from "./providers/refractor_provider"; interface Extension { context?: vscode.ExtensionContext; @@ -80,9 +80,9 @@ export function activate(context: vscode.ExtensionContext) { register_command("extractFunction", extract_function), vscode.languages.registerCodeActionsProvider( { language: 'gdscript' }, - new RefactorCodeActionProvider(), + new ExportVariablesCodeActionProvider(), { - providedCodeActionKinds: RefactorCodeActionProvider.providedCodeActionKinds, + providedCodeActionKinds: ExportVariablesCodeActionProvider.providedCodeActionKinds, } ) ); @@ -148,7 +148,6 @@ export async function extract_function(): Promise { }); } - async function initial_setup() { const projectVersion = await get_project_version(); if (projectVersion === undefined) { diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index b177594ab..9af3dc473 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -1,5 +1,4 @@ import * as vscode from "vscode"; -import { integer } from "vscode-languageclient"; /** * @param $1 The variable name (including the : if it has it), @@ -8,10 +7,13 @@ import { integer } from "vscode-languageclient"; */ const VARIABLE_REGEXP: RegExp = /var\s*(\w+:?)\s*\s*(\w*)([\s\S\w\W]*)/ -export class RefactorCodeActionProvider implements vscode.CodeActionProvider { +export class ExportVariablesCodeActionProvider implements vscode.CodeActionProvider { // Define the kind of code actions this provider offers public static readonly providedCodeActionKinds = [ vscode.CodeActionKind.Refactor, + vscode.CodeActionKind.RefactorExtract, + vscode.CodeActionKind.RefactorRewrite, + vscode.CodeActionKind.RefactorInline, ]; public provideCodeActions( @@ -22,22 +24,33 @@ export class RefactorCodeActionProvider implements vscode.CodeActionProvider { ): vscode.ProviderResult { // Specify the edits to be applied - var _exportTheseVariables = exportTheseVariables(document); - var _handleDifferentVariableExports = handleDifferentTypedVariableExports(range, document); + const _exportTheseVariables = exportTheseVariables(document); + const _extractSelected = extractSelected(document); + + const _addExportToVariable = addExportToVariable(range, document); - return [..._handleDifferentVariableExports, _exportTheseVariables]; + return [_extractSelected, _exportTheseVariables, _addExportToVariable]; } } + + function addExportToVariable(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction { + const startLine = document.lineAt(range.start.line); + const lineText = startLine.text.trim(); + + const exec: RegExpExecArray | null = VARIABLE_REGEXP.exec(lineText) + if (!exec) { + return undefined + } + const codeAction = new vscode.CodeAction( "Export this variable", vscode.CodeActionKind.RefactorInline ); - const startLine = document.lineAt(range.start.line); - const lineText = startLine.text.trim(); + codeAction.edit = new vscode.WorkspaceEdit(); @@ -53,15 +66,20 @@ function addExportToVariable(range: vscode.Range, document: vscode.TextDocument) } function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocument, - name: string, - type: string, - body: string, + ): vscode.CodeAction { const startLine = document.lineAt(range.start.line); const lineText = startLine.text.trim(); + const exec: RegExpExecArray | null = VARIABLE_REGEXP.exec(lineText) + if (!exec) { + return undefined + } + + const [_, name, type, body] = exec; + const is_number = type.trim() === "float" || type.trim() === "int"; - if (!is_number) return; + if (!is_number) return undefined; const codeAction = new vscode.CodeAction( "Export as a range", @@ -86,34 +104,21 @@ function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocu return codeAction } +function extractSelected(document: vscode.TextDocument): vscode.CodeAction { + const codeAction = new vscode.CodeAction( + "Extract function", + vscode.CodeActionKind.RefactorRewrite, + ) + const editor = vscode.window.activeTextEditor; + const selectedText = document.getText(editor.selection); + console.log(selectedText); -/** - * For Variables that only checks for the type. - * It makes a single execution and passes down the results, furthermore , - * since the functions are only in variables, we exit early if the line is NOT a variable - * @param range - * @param document - * @returns - */ -function handleDifferentTypedVariableExports(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction[] { - var actions: vscode.CodeAction[] = []; - - const startLine = document.lineAt(range.start.line); - const lineText = startLine.text.trim(); - - const exec: RegExpExecArray | null = VARIABLE_REGEXP.exec(lineText) - if (!exec) { - return undefined - } - - const [_, name, type, body] = exec; + if (selectedText === "") return undefined; - var _addExportToVariable = addExportToVariable(range, document); - var _addRageExportToVariable = addRangeExportToVariable(range, document, name, type, body); + codeAction.command = { command: "godotTools.extractFunction", title: "Extract selected as a function" }; - actions.push(_addExportToVariable, _addRageExportToVariable) + return codeAction; - return actions; } // function exportColorNoAlpha( From d2dc733e14065d1cba3d313160da5dbbe1721d0a Mon Sep 17 00:00:00 2001 From: David Space Date: Tue, 22 Apr 2025 23:22:40 -0400 Subject: [PATCH 14/33] added extract as a function --- src/providers/refractor_provider.ts | 12 ++++++++---- syntaxes/examples/code_actions_example.gd | 1 - 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index 9af3dc473..965f3257a 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -28,8 +28,9 @@ export class ExportVariablesCodeActionProvider implements vscode.CodeActionProvi const _extractSelected = extractSelected(document); const _addExportToVariable = addExportToVariable(range, document); + const _addRangeExportToVariable = addRangeExportToVariable(range, document); - return [_extractSelected, _exportTheseVariables, _addExportToVariable]; + return [_extractSelected, _exportTheseVariables, _addExportToVariable, _addRangeExportToVariable]; } @@ -107,11 +108,10 @@ function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocu function extractSelected(document: vscode.TextDocument): vscode.CodeAction { const codeAction = new vscode.CodeAction( "Extract function", - vscode.CodeActionKind.RefactorRewrite, + vscode.CodeActionKind.RefactorExtract, ) const editor = vscode.window.activeTextEditor; const selectedText = document.getText(editor.selection); - console.log(selectedText); if (selectedText === "") return undefined; @@ -153,18 +153,22 @@ function exportTheseVariables(document: vscode.TextDocument): vscode.CodeAction const individualLines = selectedText.split("\n"); var updatedText: String[] = []; + var nonVarElements: number = 0; + var varElements: number = 0; for (let i = 0; i < individualLines.length; i++) { const element = individualLines[i]; if (!element.startsWith("var ")) { updatedText = updatedText.concat(element); + nonVarElements++; continue; } updatedText = updatedText.concat(element.replace(/^var/, "@export var")); + varElements++; } - if (updatedText.length <= 0) return undefined; + if (varElements < 1) return undefined; var newText: string = updatedText.join("\n"); codeAction.edit = new vscode.WorkspaceEdit(); diff --git a/syntaxes/examples/code_actions_example.gd b/syntaxes/examples/code_actions_example.gd index 5a25b1134..6fc6feb1d 100644 --- a/syntaxes/examples/code_actions_example.gd +++ b/syntaxes/examples/code_actions_example.gd @@ -24,6 +24,5 @@ func export_code_as_new_function(args): if condition: if condition: pass - func method(args): pass \ No newline at end of file From 7d486e82be2a33afb0ae783019e89c48e7e0bcf4 Mon Sep 17 00:00:00 2001 From: David Space Date: Thu, 24 Apr 2025 19:39:21 -0400 Subject: [PATCH 15/33] added command --- package.json | 5 +++++ src/extension.ts | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/package.json b/package.json index 3c7c08a6f..0d0e1e6fc 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,11 @@ "command": "godotTools.extractFunction", "title": "Extract the selected code as a function" }, + { + "category": "Godot Tools", + "command": "godotTools.extractVariable", + "title": "Extract the selected code as a variable" + }, { "category": "Godot Tools", "command": "godotTools.openEditor", diff --git a/src/extension.ts b/src/extension.ts index 27b19c1ae..ea3055d0d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -78,6 +78,7 @@ export function activate(context: vscode.ExtensionContext) { register_command("switchSceneScript", switch_scene_script), register_command("getGodotPath", get_godot_path), register_command("extractFunction", extract_function), + register_command("extractVariable", extract_variable), vscode.languages.registerCodeActionsProvider( { language: 'gdscript' }, new ExportVariablesCodeActionProvider(), @@ -147,6 +148,59 @@ export async function extract_function(): Promise { doc.replace(selection, `${functionName}()`); }); } +export async function extract_variable(): Promise { + const editor: vscode.TextEditor = vscode.window.activeTextEditor; + + if (!editor) { + return; + } + + const selection: vscode.Selection = editor.selection; + const selectedText: string = editor.document.getText(selection); + + if (!selectedText) { + return; + } + // Prompt for function name + const functionName = await vscode.window.showInputBox({ + prompt: 'Enter the name of the new function', + validateInput: (input) => input.trim() === '' ? 'Function name cannot be empty' : null, + }); + + if (!functionName) { + vscode.window.showErrorMessage('Function name is required!'); + return; + } + const newFunction: string = `func ${functionName}():\n${selectedText.split("\n").map(line => "\t" + line).join("\n")}\n`; + const document: vscode.TextDocument = editor.document; + + var pasteLine: number = editor.selection.end.line; + /** + * Look in each line, starting with this one and go down, + * if you find a line with a method declaration, go up one and paste the new function + * or the end of the document + */ + + for (let i = 0; i < document.lineCount; i++) { + if (i < pasteLine) continue; + const textLine = document.lineAt(i); + + if (textLine.text.includes("func")) { + break; + } else { + pasteLine++; + } + } + + + const position = new vscode.Position(Math.min(pasteLine, document.lineCount), 0); + + await editor.edit((doc) => { + doc.insert(position, newFunction); + doc.replace(selection, `${functionName}()`); + }); +} + async function initial_setup() { const projectVersion = await get_project_version(); From ac0953853b159c2852e254b8f056d5244f5be314 Mon Sep 17 00:00:00 2001 From: David Space Date: Thu, 24 Apr 2025 20:30:52 -0400 Subject: [PATCH 16/33] added extract_variable command --- src/extension.ts | 65 +++++++++-------------- syntaxes/examples/code_actions_example.gd | 1 + 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index ea3055d0d..841af3660 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -102,6 +102,7 @@ export async function extract_function(): Promise { if (!editor) { return; } + const document: vscode.TextDocument = editor.document; const selection: vscode.Selection = editor.selection; const selectedText: string = editor.document.getText(selection); @@ -111,23 +112,18 @@ export async function extract_function(): Promise { } // Prompt for function name const functionName = await vscode.window.showInputBox({ - prompt: 'Enter the name of the new function', - validateInput: (input) => input.trim() === '' ? 'Function name cannot be empty' : null, + prompt: "Enter the name of the new function", + validateInput: (input) => input.trim() === "" ? "Function name cannot be empty" : null, }); - if (!functionName) { - vscode.window.showErrorMessage('Function name is required!'); - return; - } - const newFunction: string = `func ${functionName}():\n${selectedText.split("\n").map(line => "\t" + line).join("\n")}\n`; - const document: vscode.TextDocument = editor.document; + const newFunction: string = `func ${functionName}():\n${selectedText.split("\n").map(line => "\t" + line).join("\n")}`; - var pasteLine: number = editor.selection.end.line; /** * Look in each line, starting with this one and go down, * if you find a line with a method declaration, go up one and paste the new function * or the end of the document */ + var pasteLine: number = editor.selection.end.line; for (let i = 0; i < document.lineCount; i++) { if (i < pasteLine) continue; @@ -140,7 +136,7 @@ export async function extract_function(): Promise { } } - + /** Position at the line above another declaration or at the end of the document */ const position = new vscode.Position(Math.min(pasteLine, document.lineCount), 0); await editor.edit((doc) => { @@ -148,60 +144,49 @@ export async function extract_function(): Promise { doc.replace(selection, `${functionName}()`); }); } + export async function extract_variable(): Promise { const editor: vscode.TextEditor = vscode.window.activeTextEditor; + const document: vscode.TextDocument = editor.document; if (!editor) { return; } const selection: vscode.Selection = editor.selection; - const selectedText: string = editor.document.getText(selection); + const selectedText: string = document.getText(selection); if (!selectedText) { return; } // Prompt for function name - const functionName = await vscode.window.showInputBox({ - prompt: 'Enter the name of the new function', - validateInput: (input) => input.trim() === '' ? 'Function name cannot be empty' : null, + const variableName = await vscode.window.showInputBox({ + prompt: "Enter the name of the new variable", + validateInput: (input) => input.trim() === "" ? "The name of the variable cannot be empty" : null, }); - - if (!functionName) { - vscode.window.showErrorMessage('Function name is required!'); + if (!variableName) { return; } - const newFunction: string = `func ${functionName}():\n${selectedText.split("\n").map(line => "\t" + line).join("\n")}\n`; - const document: vscode.TextDocument = editor.document; - - var pasteLine: number = editor.selection.end.line; - /** - * Look in each line, starting with this one and go down, - * if you find a line with a method declaration, go up one and paste the new function - * or the end of the document - */ - - for (let i = 0; i < document.lineCount; i++) { - if (i < pasteLine) continue; - const textLine = document.lineAt(i); - - if (textLine.text.includes("func")) { - break; - } else { - pasteLine++; - } + const pasteLine: number = editor.selection.start.line; + /** Paste at the same indentation level, just above the selection */ + var indentation: string = "" + + /** Look for the whitespace above at the start of the line */ + const exec = /^\s+/.exec(document.lineAt(pasteLine).text); + if (exec) { + indentation = exec[0]; } + const newVariable: string = `${indentation}var ${variableName} := ${selectedText}\n`; - const position = new vscode.Position(Math.min(pasteLine, document.lineCount), 0); + const position = new vscode.Position(document.lineAt(pasteLine).lineNumber, 0); await editor.edit((doc) => { - doc.insert(position, newFunction); - doc.replace(selection, `${functionName}()`); + doc.insert(position, newVariable); + doc.replace(selection, variableName); }); } - async function initial_setup() { const projectVersion = await get_project_version(); if (projectVersion === undefined) { diff --git a/syntaxes/examples/code_actions_example.gd b/syntaxes/examples/code_actions_example.gd index 6fc6feb1d..1298118e6 100644 --- a/syntaxes/examples/code_actions_example.gd +++ b/syntaxes/examples/code_actions_example.gd @@ -25,4 +25,5 @@ func export_code_as_new_function(args): if condition: pass func method(args): + position.x += (15 / 2) * 5 pass \ No newline at end of file From ec1eb3aca78c89335495f3f13f4b1ec62c0b4f60 Mon Sep 17 00:00:00 2001 From: David Space Date: Fri, 25 Apr 2025 01:25:05 -0400 Subject: [PATCH 17/33] added newline to extract_function and added codeAction to extract variable --- src/extension.ts | 2 +- src/providers/refractor_provider.ts | 63 ++++++++++++++--------------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 841af3660..d4dc9c181 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -116,7 +116,7 @@ export async function extract_function(): Promise { validateInput: (input) => input.trim() === "" ? "Function name cannot be empty" : null, }); - const newFunction: string = `func ${functionName}():\n${selectedText.split("\n").map(line => "\t" + line).join("\n")}`; + const newFunction: string = `func ${functionName}():\n${selectedText.split("\n").map(line => "\t" + line).join("\n")}\n`; /** * Look in each line, starting with this one and go down, diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index 965f3257a..cebc2fbd6 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -5,7 +5,7 @@ import * as vscode from "vscode"; * @param $2 The variable type, * @param $3 Everything else after the type */ -const VARIABLE_REGEXP: RegExp = /var\s*(\w+:?)\s*\s*(\w*)([\s\S\w\W]*)/ +const VARIABLE_REGEXP: RegExp = /^var\s*(\w+:?)\s*\s*(\w*)([\s\S\w\W]*)/ export class ExportVariablesCodeActionProvider implements vscode.CodeActionProvider { // Define the kind of code actions this provider offers @@ -25,12 +25,13 @@ export class ExportVariablesCodeActionProvider implements vscode.CodeActionProvi // Specify the edits to be applied const _exportTheseVariables = exportTheseVariables(document); - const _extractSelected = extractSelected(document); + const _extractFunction = extractFunction(document); + const _extractVariable = extractVariable(document); const _addExportToVariable = addExportToVariable(range, document); const _addRangeExportToVariable = addRangeExportToVariable(range, document); - return [_extractSelected, _exportTheseVariables, _addExportToVariable, _addRangeExportToVariable]; + return [_extractVariable, _extractFunction, _exportTheseVariables, _addExportToVariable, _addRangeExportToVariable]; } @@ -40,9 +41,9 @@ export class ExportVariablesCodeActionProvider implements vscode.CodeActionProvi function addExportToVariable(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction { const startLine = document.lineAt(range.start.line); - const lineText = startLine.text.trim(); + const lineText = startLine.text; - const exec: RegExpExecArray | null = VARIABLE_REGEXP.exec(lineText) + const exec: RegExpExecArray | null = VARIABLE_REGEXP.exec(lineText); if (!exec) { return undefined } @@ -66,9 +67,7 @@ function addExportToVariable(range: vscode.Range, document: vscode.TextDocument) return codeAction; } -function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocument, - -): vscode.CodeAction { +function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction { const startLine = document.lineAt(range.start.line); const lineText = startLine.text.trim(); @@ -105,40 +104,38 @@ function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocu return codeAction } -function extractSelected(document: vscode.TextDocument): vscode.CodeAction { +function extractVariable(document: vscode.TextDocument) { + const editor = vscode.window.activeTextEditor; + if (!editor) return undefined; + const selectedText = document.getText(editor.selection); + if (selectedText === "") return undefined; const codeAction = new vscode.CodeAction( - "Extract function", + "Extract Variable", vscode.CodeActionKind.RefactorExtract, ) + codeAction.command = { + command: "godotTools.extractVariable", + title: "Extract selected as a variable" + }; + return codeAction; +} + +function extractFunction(document: vscode.TextDocument) { const editor = vscode.window.activeTextEditor; + if (!editor) return undefined; const selectedText = document.getText(editor.selection); - if (selectedText === "") return undefined; - - codeAction.command = { command: "godotTools.extractFunction", title: "Extract selected as a function" }; - + const codeAction = new vscode.CodeAction( + "Extract function", + vscode.CodeActionKind.RefactorExtract, + ) + codeAction.command = { + command: "godotTools.extractFunction", + title: "Extract selected as a function" + }; return codeAction; - } -// function exportColorNoAlpha( -// range: vscode.Range, -// document: vscode.TextDocument, -// name: string, -// type: string, -// body: string, -// ): vscode.CodeAction { -// if (type !== "Color") return; -// const codeAction = new vscode.CodeAction( -// "Export as Color No Alpha", -// vscode.CodeActionKind.RefactorRewrite, -// ); - - - -// return; -// } - // TODO: When everything is done, this should export them to their designated exports function exportTheseVariables(document: vscode.TextDocument): vscode.CodeAction { const codeAction = new vscode.CodeAction( From 9dc473f96f556811f7912c0820ba6044fda1df5e Mon Sep 17 00:00:00 2001 From: David Space Date: Fri, 25 Apr 2025 01:31:15 -0400 Subject: [PATCH 18/33] added tabString to utils --- src/utils/godot_utils.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/utils/godot_utils.ts b/src/utils/godot_utils.ts index 2083763d8..1e1ce5618 100644 --- a/src/utils/godot_utils.ts +++ b/src/utils/godot_utils.ts @@ -134,6 +134,18 @@ export type VERIFY_RESULT = { version?: string; }; +/** + * Returns either a tab or spaces depending on the user config + */ +export function tabString(): string { + const editorConfig = vscode.workspace.getConfiguration("editor"); + const insertSpaces = editorConfig.get("insertSpaces") ?? false; + const tabSize = (editorConfig.get("tabSize") as number) ?? 4; + + return insertSpaces ? " ".repeat(tabSize) : "\t"; +} + + export function verify_godot_version(godotPath: string, expectedVersion: "3" | "4" | string): VERIFY_RESULT { let target = clean_godot_path(godotPath); From e548dcd6920f71a448c04c43b1dc015e40bfabd3 Mon Sep 17 00:00:00 2001 From: David Space Date: Fri, 25 Apr 2025 01:35:36 -0400 Subject: [PATCH 19/33] preparing to test my own branch --- src/extension.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index d4dc9c181..11464bb74 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,7 +1,7 @@ import * as fs from "node:fs"; import * as path from "node:path"; import * as vscode from "vscode"; -import { attemptSettingsUpdate, get_extension_uri, clean_godot_path } from "./utils"; +import { attemptSettingsUpdate, get_extension_uri, clean_godot_path, tabString } from "./utils"; import { GDInlayHintsProvider, GDHoverProvider, @@ -110,13 +110,15 @@ export async function extract_function(): Promise { if (!selectedText) { return; } + + const tabOrSpace = tabString(); // Prompt for function name const functionName = await vscode.window.showInputBox({ prompt: "Enter the name of the new function", validateInput: (input) => input.trim() === "" ? "Function name cannot be empty" : null, }); - const newFunction: string = `func ${functionName}():\n${selectedText.split("\n").map(line => "\t" + line).join("\n")}\n`; + const newFunction: string = `func ${functionName}():\n${selectedText.split("\n").map(line => tabOrSpace + line).join("\n")}\n`; /** * Look in each line, starting with this one and go down, From 09ce9cbce9e7189e46b243fe94e8cad90ecc7c91 Mon Sep 17 00:00:00 2001 From: David Space Date: Fri, 25 Apr 2025 01:46:13 -0400 Subject: [PATCH 20/33] bumped the version --- package.json | 2 +- src/utils/godot_utils.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 0d0e1e6fc..35f7821bf 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "godot-tools", "displayName": "godot-tools", "icon": "icon.png", - "version": "2.5.1", + "version": "2.6.1", "description": "Tools for game development with Godot Engine and GDScript", "repository": { "type": "git", diff --git a/src/utils/godot_utils.ts b/src/utils/godot_utils.ts index 1e1ce5618..36b546952 100644 --- a/src/utils/godot_utils.ts +++ b/src/utils/godot_utils.ts @@ -138,9 +138,9 @@ export type VERIFY_RESULT = { * Returns either a tab or spaces depending on the user config */ export function tabString(): string { - const editorConfig = vscode.workspace.getConfiguration("editor"); - const insertSpaces = editorConfig.get("insertSpaces") ?? false; - const tabSize = (editorConfig.get("tabSize") as number) ?? 4; + const editorConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("editor"); + const insertSpaces: boolean = editorConfig.get("insertSpaces") ?? false; + const tabSize: number = (editorConfig.get("tabSize") as number) ?? 4; return insertSpaces ? " ".repeat(tabSize) : "\t"; } From 04ccfe97ca2e1b5d9d817d0b5a11685bec57fa32 Mon Sep 17 00:00:00 2001 From: David Space Date: Fri, 25 Apr 2025 13:31:48 -0400 Subject: [PATCH 21/33] updated the readme and downstreamed the version --- README.md | 17 +++++++++++++++++ package.json | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cf3fb6bdb..39eaeafda 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,22 @@ # Godot Tools +## The David´s Branch + +This branch adds much needed code actions (the lightbulb at the left that lets you refractor code. I try to add that missing functionality + +### What does it do? + +* Firstly adds a simple action to export any variable, press (ctr+.) in a variable and it will appear an option to add @export at the start. + - Bonus: it also detects where the variable is a float or int (a number) and gives you an option to export it as a range + +* Secondly, it adds an option to refractor any code as a function or a variable. + +Highlight any code and it will show an option to extract as a function or a variable. If it is a variable it will put it above the selection, and if is a function, it will put it after this function. + +Additionally you can call a command (ctr+shift+p) to do the same + +**Thats about it. For now** + Game development tools for working with [Godot Engine](http://www.godotengine.org/) in Visual Studio Code. **IMPORTANT NOTE:** Versions 1.0.0 and later of this extension only support diff --git a/package.json b/package.json index 35f7821bf..0d0e1e6fc 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "godot-tools", "displayName": "godot-tools", "icon": "icon.png", - "version": "2.6.1", + "version": "2.5.1", "description": "Tools for game development with Godot Engine and GDScript", "repository": { "type": "git", From 18d019f79eba3d1d95bd5cbb9ec5ddfbc1290a91 Mon Sep 17 00:00:00 2001 From: David Space Date: Fri, 25 Apr 2025 15:59:15 -0400 Subject: [PATCH 22/33] added missing semi colons --- src/providers/refractor_provider.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index cebc2fbd6..d8b0cf4c1 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -5,7 +5,7 @@ import * as vscode from "vscode"; * @param $2 The variable type, * @param $3 Everything else after the type */ -const VARIABLE_REGEXP: RegExp = /^var\s*(\w+:?)\s*\s*(\w*)([\s\S\w\W]*)/ +const VARIABLE_REGEXP: RegExp = /^var\s*(\w+:?)\s*\s*(\w*)([\s\S\w\W]*)/; export class ExportVariablesCodeActionProvider implements vscode.CodeActionProvider { // Define the kind of code actions this provider offers @@ -45,7 +45,7 @@ function addExportToVariable(range: vscode.Range, document: vscode.TextDocument) const exec: RegExpExecArray | null = VARIABLE_REGEXP.exec(lineText); if (!exec) { - return undefined + return undefined; } const codeAction = new vscode.CodeAction( @@ -73,7 +73,7 @@ function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocu const exec: RegExpExecArray | null = VARIABLE_REGEXP.exec(lineText) if (!exec) { - return undefined + return undefined; } const [_, name, type, body] = exec; @@ -100,8 +100,8 @@ function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocu startLine.range, updatedText ); - codeAction.isPreferred = true - return codeAction + codeAction.isPreferred = true; + return codeAction; } function extractVariable(document: vscode.TextDocument) { @@ -128,7 +128,7 @@ function extractFunction(document: vscode.TextDocument) { const codeAction = new vscode.CodeAction( "Extract function", vscode.CodeActionKind.RefactorExtract, - ) + ); codeAction.command = { command: "godotTools.extractFunction", title: "Extract selected as a function" @@ -173,9 +173,9 @@ function exportTheseVariables(document: vscode.TextDocument): vscode.CodeAction document.uri, editor.selection, newText, - ) + ); codeAction.isPreferred = true; - return codeAction + return codeAction; } From 4f073dd3e721c9e98c8099273d41fe9a464e9617 Mon Sep 17 00:00:00 2001 From: David Space Date: Fri, 25 Apr 2025 15:59:45 -0400 Subject: [PATCH 23/33] added another missing semicolon --- src/extension.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index 11464bb74..08fed01b3 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -171,7 +171,7 @@ export async function extract_variable(): Promise { } const pasteLine: number = editor.selection.start.line; /** Paste at the same indentation level, just above the selection */ - var indentation: string = "" + var indentation: string = ""; /** Look for the whitespace above at the start of the line */ const exec = /^\s+/.exec(document.lineAt(pasteLine).text); From 49be85d9ce69a72fdc71421e2bfff2e0e5ac7c81 Mon Sep 17 00:00:00 2001 From: David Space Date: Sun, 27 Apr 2025 10:22:36 -0400 Subject: [PATCH 24/33] added missing semi colons --- src/providers/refractor_provider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/providers/refractor_provider.ts b/src/providers/refractor_provider.ts index d8b0cf4c1..24df0132c 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/refractor_provider.ts @@ -71,7 +71,7 @@ function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocu const startLine = document.lineAt(range.start.line); const lineText = startLine.text.trim(); - const exec: RegExpExecArray | null = VARIABLE_REGEXP.exec(lineText) + const exec: RegExpExecArray | null = VARIABLE_REGEXP.exec(lineText); if (!exec) { return undefined; } @@ -112,7 +112,7 @@ function extractVariable(document: vscode.TextDocument) { const codeAction = new vscode.CodeAction( "Extract Variable", vscode.CodeActionKind.RefactorExtract, - ) + ); codeAction.command = { command: "godotTools.extractVariable", title: "Extract selected as a variable" From da18c0451c5ff846c6422621f3b7299790f59abd Mon Sep 17 00:00:00 2001 From: David Space Date: Mon, 12 May 2025 23:50:38 -0400 Subject: [PATCH 25/33] addressed most comments --- README.md | 17 ----------------- src/extension.ts | 17 ++++++----------- ...or_provider.ts => code_action_provider.ts} | 19 +++++++++++++++++-- src/utils/godot_utils.ts | 12 ------------ src/utils/vscode_utils.ts | 12 ++++++++++++ 5 files changed, 35 insertions(+), 42 deletions(-) rename src/providers/{refractor_provider.ts => code_action_provider.ts} (92%) diff --git a/README.md b/README.md index 39eaeafda..cf3fb6bdb 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,5 @@ # Godot Tools -## The David´s Branch - -This branch adds much needed code actions (the lightbulb at the left that lets you refractor code. I try to add that missing functionality - -### What does it do? - -* Firstly adds a simple action to export any variable, press (ctr+.) in a variable and it will appear an option to add @export at the start. - - Bonus: it also detects where the variable is a float or int (a number) and gives you an option to export it as a range - -* Secondly, it adds an option to refractor any code as a function or a variable. - -Highlight any code and it will show an option to extract as a function or a variable. If it is a variable it will put it above the selection, and if is a function, it will put it after this function. - -Additionally you can call a command (ctr+shift+p) to do the same - -**Thats about it. For now** - Game development tools for working with [Godot Engine](http://www.godotengine.org/) in Visual Studio Code. **IMPORTANT NOTE:** Versions 1.0.0 and later of this extension only support diff --git a/src/extension.ts b/src/extension.ts index 08fed01b3..1cddee57c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -31,7 +31,7 @@ import { } from "./utils"; import { prompt_for_godot_executable } from "./utils/prompts"; import { killSubProcesses, subProcess } from "./utils/subspawn"; -import { ExportVariablesCodeActionProvider } from "./providers/refractor_provider"; +import { GDCodeActionProvider } from "./providers/code_action_provider"; interface Extension { context?: vscode.ExtensionContext; @@ -48,6 +48,7 @@ interface Extension { semanticTokensProvider?: GDSemanticTokensProvider; completionProvider?: GDCompletionItemProvider; tasksProvider?: GDTaskProvider; + codeActionProvider?: GDCodeActionProvider; } export const globals: Extension = {}; @@ -66,6 +67,7 @@ export function activate(context: vscode.ExtensionContext) { globals.formattingProvider = new FormattingProvider(context); globals.docsProvider = new GDDocumentationProvider(context); globals.definitionProvider = new GDDefinitionProvider(context); + globals.codeActionProvider = new GDCodeActionProvider(context); // globals.semanticTokensProvider = new GDSemanticTokensProvider(context); // globals.completionProvider = new GDCompletionItemProvider(context); // globals.tasksProvider = new GDTaskProvider(context); @@ -79,13 +81,6 @@ export function activate(context: vscode.ExtensionContext) { register_command("getGodotPath", get_godot_path), register_command("extractFunction", extract_function), register_command("extractVariable", extract_variable), - vscode.languages.registerCodeActionsProvider( - { language: 'gdscript' }, - new ExportVariablesCodeActionProvider(), - { - providedCodeActionKinds: ExportVariablesCodeActionProvider.providedCodeActionKinds, - } - ) ); set_context("godotFiles", ["gdscript", "gdscene", "gdresource", "gdshader"]); @@ -96,7 +91,7 @@ export function activate(context: vscode.ExtensionContext) { }); } -export async function extract_function(): Promise { +async function extract_function(): Promise { const editor: vscode.TextEditor = vscode.window.activeTextEditor; if (!editor) { @@ -147,7 +142,7 @@ export async function extract_function(): Promise { }); } -export async function extract_variable(): Promise { +async function extract_variable(): Promise { const editor: vscode.TextEditor = vscode.window.activeTextEditor; const document: vscode.TextDocument = editor.document; @@ -354,7 +349,7 @@ class GodotEditorTerminal implements vscode.Pseudoterminal { private closeEmitter = new vscode.EventEmitter(); onDidClose?: vscode.Event = this.closeEmitter.event; - constructor(private command: string) { } + constructor(private command: string) {} open(initialDimensions: vscode.TerminalDimensions | undefined): void { const proc = subProcess("GodotEditor", this.command, { shell: true, detached: true }); diff --git a/src/providers/refractor_provider.ts b/src/providers/code_action_provider.ts similarity index 92% rename from src/providers/refractor_provider.ts rename to src/providers/code_action_provider.ts index 24df0132c..7ed6af2d9 100644 --- a/src/providers/refractor_provider.ts +++ b/src/providers/code_action_provider.ts @@ -1,5 +1,7 @@ import * as vscode from "vscode"; - +import { + ExtensionContext +} from "vscode"; /** * @param $1 The variable name (including the : if it has it), * @param $2 The variable type, @@ -7,7 +9,19 @@ import * as vscode from "vscode"; */ const VARIABLE_REGEXP: RegExp = /^var\s*(\w+:?)\s*\s*(\w*)([\s\S\w\W]*)/; -export class ExportVariablesCodeActionProvider implements vscode.CodeActionProvider { +export class GDCodeActionProvider implements vscode.CodeActionProvider { + constructor(private context: ExtensionContext) { + context.subscriptions.push( + vscode.languages.registerCodeActionsProvider( + { language: 'gdscript' }, + this, + { + providedCodeActionKinds: GDCodeActionProvider.providedCodeActionKinds, + } + ) + ); + + } // Define the kind of code actions this provider offers public static readonly providedCodeActionKinds = [ vscode.CodeActionKind.Refactor, @@ -133,6 +147,7 @@ function extractFunction(document: vscode.TextDocument) { command: "godotTools.extractFunction", title: "Extract selected as a function" }; + // codeAction.edit.insert return codeAction; } diff --git a/src/utils/godot_utils.ts b/src/utils/godot_utils.ts index 36b546952..2083763d8 100644 --- a/src/utils/godot_utils.ts +++ b/src/utils/godot_utils.ts @@ -134,18 +134,6 @@ export type VERIFY_RESULT = { version?: string; }; -/** - * Returns either a tab or spaces depending on the user config - */ -export function tabString(): string { - const editorConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("editor"); - const insertSpaces: boolean = editorConfig.get("insertSpaces") ?? false; - const tabSize: number = (editorConfig.get("tabSize") as number) ?? 4; - - return insertSpaces ? " ".repeat(tabSize) : "\t"; -} - - export function verify_godot_version(godotPath: string, expectedVersion: "3" | "4" | string): VERIFY_RESULT { let target = clean_godot_path(godotPath); diff --git a/src/utils/vscode_utils.ts b/src/utils/vscode_utils.ts index 92e0ee154..06fe3e1af 100644 --- a/src/utils/vscode_utils.ts +++ b/src/utils/vscode_utils.ts @@ -28,3 +28,15 @@ export function register_command(command: string, callback: (...args: any[]) => export function get_extension_uri(...paths: string[]) { return vscode.Uri.joinPath(vscode.extensions.getExtension("geequlim.godot-tools").extensionUri, ...paths ?? ""); } + + +/** + * Returns either a tab or spaces depending on the user config + */ +export function tabString(): string { + const editorConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("editor"); + const insertSpaces: boolean = editorConfig.get("insertSpaces") ?? false; + const tabSize: number = (editorConfig.get("tabSize") as number) ?? 4; + + return insertSpaces ? " ".repeat(tabSize) : "\t"; +} From d42be3b9a1919a77992c177bbcf0451ac922cc71 Mon Sep 17 00:00:00 2001 From: David Space Date: Tue, 13 May 2025 00:30:46 -0400 Subject: [PATCH 26/33] removed "unnecessary" types --- src/extension.ts | 10 +++++----- src/providers/code_action_provider.ts | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 1cddee57c..35b819739 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -143,14 +143,14 @@ async function extract_function(): Promise { } async function extract_variable(): Promise { - const editor: vscode.TextEditor = vscode.window.activeTextEditor; - const document: vscode.TextDocument = editor.document; + const editor = vscode.window.activeTextEditor; + const document = editor.document; if (!editor) { return; } - const selection: vscode.Selection = editor.selection; + const selection = editor.selection; const selectedText: string = document.getText(selection); if (!selectedText) { @@ -166,7 +166,7 @@ async function extract_variable(): Promise { } const pasteLine: number = editor.selection.start.line; /** Paste at the same indentation level, just above the selection */ - var indentation: string = ""; + var indentation = ""; /** Look for the whitespace above at the start of the line */ const exec = /^\s+/.exec(document.lineAt(pasteLine).text); @@ -174,7 +174,7 @@ async function extract_variable(): Promise { indentation = exec[0]; } - const newVariable: string = `${indentation}var ${variableName} := ${selectedText}\n`; + const newVariable = `${indentation}var ${variableName} := ${selectedText}\n`; const position = new vscode.Position(document.lineAt(pasteLine).lineNumber, 0); diff --git a/src/providers/code_action_provider.ts b/src/providers/code_action_provider.ts index 7ed6af2d9..fd5d048e6 100644 --- a/src/providers/code_action_provider.ts +++ b/src/providers/code_action_provider.ts @@ -165,8 +165,8 @@ function exportTheseVariables(document: vscode.TextDocument): vscode.CodeAction const individualLines = selectedText.split("\n"); var updatedText: String[] = []; - var nonVarElements: number = 0; - var varElements: number = 0; + var nonVarElements = 0; + var varElements = 0; for (let i = 0; i < individualLines.length; i++) { const element = individualLines[i]; From 6559405ef0c73ceeee114f5a6940ea080f8b3938 Mon Sep 17 00:00:00 2001 From: David Space Date: Sat, 7 Jun 2025 11:31:04 -0400 Subject: [PATCH 27/33] made the requested changes, and fixed a bug --- src/extension.ts | 10 ++++++++-- syntaxes/examples/code_actions_example.gd | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 35b819739..0143c6e5d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -91,6 +91,11 @@ export function activate(context: vscode.ExtensionContext) { }); } +/** + * Opens a input box asking for the new function's name, when given it will move the + * selected text to below the current function declaration and immediately call the + * newly made function at the selected position + */ async function extract_function(): Promise { const editor: vscode.TextEditor = vscode.window.activeTextEditor; @@ -126,7 +131,7 @@ async function extract_function(): Promise { if (i < pasteLine) continue; const textLine = document.lineAt(i); - if (textLine.text.includes("func")) { + if (textLine.text.trim().startsWith("func")) { break; } else { pasteLine++; @@ -137,7 +142,7 @@ async function extract_function(): Promise { const position = new vscode.Position(Math.min(pasteLine, document.lineCount), 0); await editor.edit((doc) => { - doc.insert(position, newFunction); + doc.insert(position, `\n${newFunction}`); doc.replace(selection, `${functionName}()`); }); } @@ -174,6 +179,7 @@ async function extract_variable(): Promise { indentation = exec[0]; } + //\t\t var xyz := x + y + z\n const newVariable = `${indentation}var ${variableName} := ${selectedText}\n`; const position = new vscode.Position(document.lineAt(pasteLine).lineNumber, 0); diff --git a/syntaxes/examples/code_actions_example.gd b/syntaxes/examples/code_actions_example.gd index 1298118e6..ed56aed06 100644 --- a/syntaxes/examples/code_actions_example.gd +++ b/syntaxes/examples/code_actions_example.gd @@ -26,4 +26,6 @@ func export_code_as_new_function(args): pass func method(args): position.x += (15 / 2) * 5 + var lambda = func (x): + print(x) pass \ No newline at end of file From f7d172bb73524c1b750e56d579241f16a9a23596 Mon Sep 17 00:00:00 2001 From: David Space Date: Sat, 7 Jun 2025 11:36:21 -0400 Subject: [PATCH 28/33] Registered code action provider into providers --- src/extension.ts | 2 +- src/providers/index.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index 0143c6e5d..0587b5539 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -12,6 +12,7 @@ import { GDDocumentationProvider, GDDefinitionProvider, GDTaskProvider, + GDCodeActionProvider, } from "./providers"; import { ClientConnectionManager } from "./lsp"; import { ScenePreviewProvider } from "./scene_tools"; @@ -31,7 +32,6 @@ import { } from "./utils"; import { prompt_for_godot_executable } from "./utils/prompts"; import { killSubProcesses, subProcess } from "./utils/subspawn"; -import { GDCodeActionProvider } from "./providers/code_action_provider"; interface Extension { context?: vscode.ExtensionContext; diff --git a/src/providers/index.ts b/src/providers/index.ts index 4b50d703b..a403580fb 100644 --- a/src/providers/index.ts +++ b/src/providers/index.ts @@ -7,3 +7,4 @@ export * from "./hover"; export * from "./inlay_hints"; export * from "./semantic_tokens"; export * from "./tasks"; +export * from "./code_action_provider"; From 1c69c3f55a19ce40304a7e9d5a100216b67b043e Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Sun, 8 Jun 2025 13:56:17 -0400 Subject: [PATCH 29/33] Fix linter complaints --- src/providers/code_action_provider.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/providers/code_action_provider.ts b/src/providers/code_action_provider.ts index fd5d048e6..5651ba40f 100644 --- a/src/providers/code_action_provider.ts +++ b/src/providers/code_action_provider.ts @@ -101,9 +101,9 @@ function addRangeExportToVariable(range: vscode.Range, document: vscode.TextDocu ); codeAction.edit = new vscode.WorkspaceEdit(); - var updatedText = ""; + let updatedText = ""; - if (type.trim() == "float") { + if (type.trim() === "float") { updatedText = lineText.replace(VARIABLE_REGEXP, `@export_range(0.0, 1.0) var ${name} ${type} ${body}`); } else { updatedText = lineText.replace(VARIABLE_REGEXP, `@export_range(0, 1) var ${name} ${type} ${body}`); @@ -164,9 +164,9 @@ function exportTheseVariables(document: vscode.TextDocument): vscode.CodeAction const individualLines = selectedText.split("\n"); - var updatedText: String[] = []; - var nonVarElements = 0; - var varElements = 0; + let updatedText: string[] = []; + let nonVarElements = 0; + let varElements = 0; for (let i = 0; i < individualLines.length; i++) { const element = individualLines[i]; @@ -182,7 +182,7 @@ function exportTheseVariables(document: vscode.TextDocument): vscode.CodeAction if (varElements < 1) return undefined; - var newText: string = updatedText.join("\n"); + const newText: string = updatedText.join("\n"); codeAction.edit = new vscode.WorkspaceEdit(); codeAction.edit.replace( document.uri, @@ -192,5 +192,3 @@ function exportTheseVariables(document: vscode.TextDocument): vscode.CodeAction codeAction.isPreferred = true; return codeAction; } - - From cd5083ccdbe1c6ee063521cc17586b0d26d6db31 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Sun, 8 Jun 2025 14:05:28 -0400 Subject: [PATCH 30/33] Format and fix lint errors --- src/extension.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 0587b5539..c3b2c8413 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -115,17 +115,20 @@ async function extract_function(): Promise { // Prompt for function name const functionName = await vscode.window.showInputBox({ prompt: "Enter the name of the new function", - validateInput: (input) => input.trim() === "" ? "Function name cannot be empty" : null, + validateInput: (input) => (input.trim() === "" ? "Function name cannot be empty" : null), }); - const newFunction: string = `func ${functionName}():\n${selectedText.split("\n").map(line => tabOrSpace + line).join("\n")}\n`; + const newFunction: string = `func ${functionName}():\n${selectedText + .split("\n") + .map((line) => tabOrSpace + line) + .join("\n")}\n`; /** * Look in each line, starting with this one and go down, * if you find a line with a method declaration, go up one and paste the new function * or the end of the document */ - var pasteLine: number = editor.selection.end.line; + let pasteLine: number = editor.selection.end.line; for (let i = 0; i < document.lineCount; i++) { if (i < pasteLine) continue; @@ -164,14 +167,14 @@ async function extract_variable(): Promise { // Prompt for function name const variableName = await vscode.window.showInputBox({ prompt: "Enter the name of the new variable", - validateInput: (input) => input.trim() === "" ? "The name of the variable cannot be empty" : null, + validateInput: (input) => (input.trim() === "" ? "The name of the variable cannot be empty" : null), }); if (!variableName) { return; } const pasteLine: number = editor.selection.start.line; /** Paste at the same indentation level, just above the selection */ - var indentation = ""; + let indentation = ""; /** Look for the whitespace above at the start of the line */ const exec = /^\s+/.exec(document.lineAt(pasteLine).text); @@ -332,7 +335,6 @@ async function open_godot_editor_settings() { }); } - /** * Returns the executable path for Godot based on the current project's version. * Created to allow other extensions to get the path without having to go From f4ea9fcf8931f70e342933525b10ac69643e094d Mon Sep 17 00:00:00 2001 From: David Space Date: Mon, 9 Jun 2025 17:46:11 -0400 Subject: [PATCH 31/33] reworked extract function to not require external commands --- src/providers/code_action_provider.ts | 65 ++++++++++++++++++++--- src/utils/vscode_utils.ts | 23 +++++--- syntaxes/examples/code_actions_example.gd | 5 +- 3 files changed, 78 insertions(+), 15 deletions(-) diff --git a/src/providers/code_action_provider.ts b/src/providers/code_action_provider.ts index 5651ba40f..cbe2609ec 100644 --- a/src/providers/code_action_provider.ts +++ b/src/providers/code_action_provider.ts @@ -2,6 +2,7 @@ import * as vscode from "vscode"; import { ExtensionContext } from "vscode"; +import { tabString } from "../utils"; /** * @param $1 The variable name (including the : if it has it), * @param $2 The variable type, @@ -127,10 +128,17 @@ function extractVariable(document: vscode.TextDocument) { "Extract Variable", vscode.CodeActionKind.RefactorExtract, ); - codeAction.command = { - command: "godotTools.extractVariable", - title: "Extract selected as a variable" - }; + + + // codeAction.edit.replace( + // document.uri, + // startLine.range, + // updatedText + // ); + // codeAction.command = { + // command: "godotTools.extractVariable", + // title: "Extract selected as a variable" + // }; return codeAction; } @@ -143,11 +151,54 @@ function extractFunction(document: vscode.TextDocument) { "Extract function", vscode.CodeActionKind.RefactorExtract, ); + const tabOrSpace = tabString(); + // TODO: Maybe look for another name because if by any chance the user has this exact function name, it will mess things up + const functionName = "_new_function"; + const newFunction: string = `func ${functionName}():\n${selectedText + .split("\n") + .map((line) => tabOrSpace + line) + .join("\n")}\n`; + /** + * Look in each line, starting with this one and go down, + * if you find a line with a method declaration, go up one and paste the new function + * or the end of the document + */ + let pasteLine: number = editor.selection.end.line; + + for (let i = 0; i < document.lineCount; i++) { + if (i < pasteLine) continue; + const textLine = document.lineAt(i); + + if (textLine.text.trim().startsWith("func")) { + break; + } else { + pasteLine++; + } + } + const position = new vscode.Position(Math.min(pasteLine, document.lineCount), 0); + + const edit = new vscode.WorkspaceEdit(); + + const callNewFunction = vscode.SnippetTextEdit.replace( + editor.selection, + new vscode.SnippetString(`${functionName}$0()`) + ); + const insertNewFunction = vscode.TextEdit.insert( + position, `\n${newFunction}` + ); + + edit.set(document.uri, [ + callNewFunction, + insertNewFunction + ]); + + codeAction.edit = edit; + codeAction.command = { - command: "godotTools.extractFunction", - title: "Extract selected as a function" + command: "editor.action.rename", + title: "Rename the new function" }; - // codeAction.edit.insert + return codeAction; } diff --git a/src/utils/vscode_utils.ts b/src/utils/vscode_utils.ts index 06fe3e1af..78380df2c 100644 --- a/src/utils/vscode_utils.ts +++ b/src/utils/vscode_utils.ts @@ -33,10 +33,21 @@ export function get_extension_uri(...paths: string[]) { /** * Returns either a tab or spaces depending on the user config */ -export function tabString(): string { - const editorConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("editor"); - const insertSpaces: boolean = editorConfig.get("insertSpaces") ?? false; - const tabSize: number = (editorConfig.get("tabSize") as number) ?? 4; +export function tabString(document?: vscode.TextDocument): string { + // Prefer activeTextEditor, but allow explicit document for LSP calls etc. + const editor = vscode.window.activeTextEditor; + const doc = document ?? editor?.document; + + // Fallback to global config if we can't get real options + if (!doc) { + const editorConfig = vscode.workspace.getConfiguration("editor"); + const insertSpaces = editorConfig.get("insertSpaces", true); + const tabSize = editorConfig.get("tabSize", 4); + return insertSpaces ? " ".repeat(tabSize) : "\t"; + } - return insertSpaces ? " ".repeat(tabSize) : "\t"; -} + // Get options for this document + const { insertSpaces, tabSize } = vscode.window.activeTextEditor?.options ?? {}; + const size = typeof tabSize === "number" ? tabSize : 4; + return insertSpaces ? " ".repeat(size) : "\t"; +} \ No newline at end of file diff --git a/syntaxes/examples/code_actions_example.gd b/syntaxes/examples/code_actions_example.gd index ed56aed06..fc715f17d 100644 --- a/syntaxes/examples/code_actions_example.gd +++ b/syntaxes/examples/code_actions_example.gd @@ -27,5 +27,6 @@ func export_code_as_new_function(args): func method(args): position.x += (15 / 2) * 5 var lambda = func (x): - print(x) - pass \ No newline at end of file + print(x) + pass + From 721158c2fd741f4275885edf2832d7afc95eef2b Mon Sep 17 00:00:00 2001 From: David Space Date: Mon, 9 Jun 2025 21:10:45 -0400 Subject: [PATCH 32/33] reworked extractVariable --- src/providers/code_action_provider.ts | 47 ++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/providers/code_action_provider.ts b/src/providers/code_action_provider.ts index cbe2609ec..5bd0350bb 100644 --- a/src/providers/code_action_provider.ts +++ b/src/providers/code_action_provider.ts @@ -129,16 +129,45 @@ function extractVariable(document: vscode.TextDocument) { vscode.CodeActionKind.RefactorExtract, ); + const variableName = "_new_variable_name"; + + const pasteLine: number = editor.selection.start.line; + + /** Paste at the same indentation level, just above the selection */ + let indentation = ""; + + /** Look for the whitespace above at the start of the line */ + const exec = /^\s+/.exec(document.lineAt(pasteLine).text); + if (exec) { + indentation = exec[0]; + } + + //\t\t var xyz := x + y + z\n + const newVariable = new vscode.SnippetString(`${indentation}var ${variableName}$0 := ${selectedText}\n`); + + const position = new vscode.Position(document.lineAt(pasteLine).lineNumber, 0); + + const referenceNewVariable = vscode.TextEdit.replace( + editor.selection, + variableName + ); + const insertNewFunction = vscode.SnippetTextEdit.insert( + position, + newVariable + ); + + const edit = new vscode.WorkspaceEdit(); + + edit.set(document.uri, [ + referenceNewVariable, + insertNewFunction + ]); + codeAction.edit = edit; + codeAction.command = { + command: "editor.action.rename", + title: "Rename the new variable" + }; - // codeAction.edit.replace( - // document.uri, - // startLine.range, - // updatedText - // ); - // codeAction.command = { - // command: "godotTools.extractVariable", - // title: "Extract selected as a variable" - // }; return codeAction; } From ca0f061a2724b4f82e435a3f2de498e754d9b863 Mon Sep 17 00:00:00 2001 From: David Space Date: Mon, 9 Jun 2025 21:46:23 -0400 Subject: [PATCH 33/33] removed extractFunction and extractVariable commands --- package.json | 10 --- src/extension.ts | 106 +------------------------- src/providers/code_action_provider.ts | 2 +- src/utils/vscode_utils.ts | 3 - 4 files changed, 2 insertions(+), 119 deletions(-) diff --git a/package.json b/package.json index 0d0e1e6fc..855006495 100644 --- a/package.json +++ b/package.json @@ -56,16 +56,6 @@ } ], "commands": [ - { - "category": "Godot Tools", - "command": "godotTools.extractFunction", - "title": "Extract the selected code as a function" - }, - { - "category": "Godot Tools", - "command": "godotTools.extractVariable", - "title": "Extract the selected code as a variable" - }, { "category": "Godot Tools", "command": "godotTools.openEditor", diff --git a/src/extension.ts b/src/extension.ts index c3b2c8413..508e551e7 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,7 +1,7 @@ import * as fs from "node:fs"; import * as path from "node:path"; import * as vscode from "vscode"; -import { attemptSettingsUpdate, get_extension_uri, clean_godot_path, tabString } from "./utils"; +import { attemptSettingsUpdate, get_extension_uri, clean_godot_path } from "./utils"; import { GDInlayHintsProvider, GDHoverProvider, @@ -79,8 +79,6 @@ export function activate(context: vscode.ExtensionContext) { register_command("listGodotClasses", list_classes), register_command("switchSceneScript", switch_scene_script), register_command("getGodotPath", get_godot_path), - register_command("extractFunction", extract_function), - register_command("extractVariable", extract_variable), ); set_context("godotFiles", ["gdscript", "gdscene", "gdresource", "gdshader"]); @@ -91,108 +89,6 @@ export function activate(context: vscode.ExtensionContext) { }); } -/** - * Opens a input box asking for the new function's name, when given it will move the - * selected text to below the current function declaration and immediately call the - * newly made function at the selected position - */ -async function extract_function(): Promise { - const editor: vscode.TextEditor = vscode.window.activeTextEditor; - - if (!editor) { - return; - } - const document: vscode.TextDocument = editor.document; - - const selection: vscode.Selection = editor.selection; - const selectedText: string = editor.document.getText(selection); - - if (!selectedText) { - return; - } - - const tabOrSpace = tabString(); - // Prompt for function name - const functionName = await vscode.window.showInputBox({ - prompt: "Enter the name of the new function", - validateInput: (input) => (input.trim() === "" ? "Function name cannot be empty" : null), - }); - - const newFunction: string = `func ${functionName}():\n${selectedText - .split("\n") - .map((line) => tabOrSpace + line) - .join("\n")}\n`; - - /** - * Look in each line, starting with this one and go down, - * if you find a line with a method declaration, go up one and paste the new function - * or the end of the document - */ - let pasteLine: number = editor.selection.end.line; - - for (let i = 0; i < document.lineCount; i++) { - if (i < pasteLine) continue; - const textLine = document.lineAt(i); - - if (textLine.text.trim().startsWith("func")) { - break; - } else { - pasteLine++; - } - } - - /** Position at the line above another declaration or at the end of the document */ - const position = new vscode.Position(Math.min(pasteLine, document.lineCount), 0); - - await editor.edit((doc) => { - doc.insert(position, `\n${newFunction}`); - doc.replace(selection, `${functionName}()`); - }); -} - -async function extract_variable(): Promise { - const editor = vscode.window.activeTextEditor; - const document = editor.document; - - if (!editor) { - return; - } - - const selection = editor.selection; - const selectedText: string = document.getText(selection); - - if (!selectedText) { - return; - } - // Prompt for function name - const variableName = await vscode.window.showInputBox({ - prompt: "Enter the name of the new variable", - validateInput: (input) => (input.trim() === "" ? "The name of the variable cannot be empty" : null), - }); - if (!variableName) { - return; - } - const pasteLine: number = editor.selection.start.line; - /** Paste at the same indentation level, just above the selection */ - let indentation = ""; - - /** Look for the whitespace above at the start of the line */ - const exec = /^\s+/.exec(document.lineAt(pasteLine).text); - if (exec) { - indentation = exec[0]; - } - - //\t\t var xyz := x + y + z\n - const newVariable = `${indentation}var ${variableName} := ${selectedText}\n`; - - const position = new vscode.Position(document.lineAt(pasteLine).lineNumber, 0); - - await editor.edit((doc) => { - doc.insert(position, newVariable); - doc.replace(selection, variableName); - }); -} - async function initial_setup() { const projectVersion = await get_project_version(); if (projectVersion === undefined) { diff --git a/src/providers/code_action_provider.ts b/src/providers/code_action_provider.ts index 5bd0350bb..2037ae193 100644 --- a/src/providers/code_action_provider.ts +++ b/src/providers/code_action_provider.ts @@ -198,7 +198,7 @@ function extractFunction(document: vscode.TextDocument) { if (i < pasteLine) continue; const textLine = document.lineAt(i); - if (textLine.text.trim().startsWith("func")) { + if (textLine.text.startsWith("func")) { break; } else { pasteLine++; diff --git a/src/utils/vscode_utils.ts b/src/utils/vscode_utils.ts index 78380df2c..b23f17ae2 100644 --- a/src/utils/vscode_utils.ts +++ b/src/utils/vscode_utils.ts @@ -34,11 +34,9 @@ export function get_extension_uri(...paths: string[]) { * Returns either a tab or spaces depending on the user config */ export function tabString(document?: vscode.TextDocument): string { - // Prefer activeTextEditor, but allow explicit document for LSP calls etc. const editor = vscode.window.activeTextEditor; const doc = document ?? editor?.document; - // Fallback to global config if we can't get real options if (!doc) { const editorConfig = vscode.workspace.getConfiguration("editor"); const insertSpaces = editorConfig.get("insertSpaces", true); @@ -46,7 +44,6 @@ export function tabString(document?: vscode.TextDocument): string { return insertSpaces ? " ".repeat(tabSize) : "\t"; } - // Get options for this document const { insertSpaces, tabSize } = vscode.window.activeTextEditor?.options ?? {}; const size = typeof tabSize === "number" ? tabSize : 4; return insertSpaces ? " ".repeat(size) : "\t";