Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
74ec33c
registered a test refractor
DaviD4Chirino Apr 20, 2025
b6e74da
added addRangeExportToVariable
DaviD4Chirino Apr 20, 2025
4601163
tried and failed to make a snippet
DaviD4Chirino Apr 21, 2025
c524864
added export these variables
DaviD4Chirino Apr 22, 2025
49dca75
addRangeExportToVariable now discerns between a float and a int
DaviD4Chirino Apr 22, 2025
4da75dd
added todo
DaviD4Chirino Apr 22, 2025
108b6a0
refractored a bit
DaviD4Chirino Apr 22, 2025
faa2517
added documentation to one
DaviD4Chirino Apr 22, 2025
6103431
tempted to make exportColorNoAlpha
DaviD4Chirino Apr 22, 2025
6f0f710
Merge pull request #1 from DaviD4Chirino/refractoring-variables-exports
DaviD4Chirino Apr 22, 2025
33ee201
added mockup refractor
DaviD4Chirino Apr 22, 2025
088f414
added refractor command
DaviD4Chirino Apr 23, 2025
99fe57f
moved the command to where all the other commands are
DaviD4Chirino Apr 23, 2025
515502a
handleDifferentTypedVariableExports was messing with everything, removed
DaviD4Chirino Apr 23, 2025
d2dc733
added extract as a function
DaviD4Chirino Apr 23, 2025
21a46ed
Merge pull request #2 from DaviD4Chirino/extract-code-to-its-own-func…
DaviD4Chirino Apr 23, 2025
7d486e8
added command
DaviD4Chirino Apr 24, 2025
ac09538
added extract_variable command
DaviD4Chirino Apr 25, 2025
ec1eb3a
added newline to extract_function and added codeAction to extract var…
DaviD4Chirino Apr 25, 2025
9dc473f
added tabString to utils
DaviD4Chirino Apr 25, 2025
e548dcd
preparing to test my own branch
DaviD4Chirino Apr 25, 2025
09ce9cb
bumped the version
DaviD4Chirino Apr 25, 2025
04ccfe9
updated the readme and downstreamed the version
DaviD4Chirino Apr 25, 2025
18d019f
added missing semi colons
DaviD4Chirino Apr 25, 2025
4f073dd
added another missing semicolon
DaviD4Chirino Apr 25, 2025
49be85d
added missing semi colons
DaviD4Chirino Apr 27, 2025
da18c04
addressed most comments
DaviD4Chirino May 13, 2025
d42be3b
removed "unnecessary" types
DaviD4Chirino May 13, 2025
6559405
made the requested changes, and fixed a bug
DaviD4Chirino Jun 7, 2025
f7d172b
Registered code action provider into providers
DaviD4Chirino Jun 7, 2025
1c69c3f
Fix linter complaints
DaelonSuzuka Jun 8, 2025
cd5083c
Format and fix lint errors
DaelonSuzuka Jun 8, 2025
f4ea9fc
reworked extract function to not require external commands
DaviD4Chirino Jun 9, 2025
721158c
reworked extractVariable
DaviD4Chirino Jun 10, 2025
ca0f061
removed extractFunction and extractVariable commands
DaviD4Chirino Jun 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -920,4 +920,4 @@
"ws": "^8.17.1",
"ya-bbcode": "^4.0.0"
}
}
}
Comment thread
DaelonSuzuka marked this conversation as resolved.
3 changes: 3 additions & 0 deletions src/extension.ts
Comment thread
DaelonSuzuka marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
GDDocumentationProvider,
GDDefinitionProvider,
GDTaskProvider,
GDCodeActionProvider,
} from "./providers";
import { ClientConnectionManager } from "./lsp";
import { ScenePreviewProvider } from "./scene_tools";
Expand Down Expand Up @@ -47,6 +48,7 @@ interface Extension {
semanticTokensProvider?: GDSemanticTokensProvider;
completionProvider?: GDCompletionItemProvider;
tasksProvider?: GDTaskProvider;
codeActionProvider?: GDCodeActionProvider;
}

export const globals: Extension = {};
Expand All @@ -65,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);
Expand Down
274 changes: 274 additions & 0 deletions src/providers/code_action_provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
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,
* @param $3 Everything else after the type
*/
const VARIABLE_REGEXP: RegExp = /^var\s*(\w+:?)\s*\s*(\w*)([\s\S\w\W]*)/;

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,
vscode.CodeActionKind.RefactorExtract,
vscode.CodeActionKind.RefactorRewrite,
vscode.CodeActionKind.RefactorInline,
];

public provideCodeActions(
document: vscode.TextDocument,
range: vscode.Range,
context: vscode.CodeActionContext,
token: vscode.CancellationToken
): vscode.ProviderResult<vscode.CodeAction[]> {

// Specify the edits to be applied
const _exportTheseVariables = exportTheseVariables(document);
const _extractFunction = extractFunction(document);
const _extractVariable = extractVariable(document);

const _addExportToVariable = addExportToVariable(range, document);
const _addRangeExportToVariable = addRangeExportToVariable(range, document);

return [_extractVariable, _extractFunction, _exportTheseVariables, _addExportToVariable, _addRangeExportToVariable];
}


}



function addExportToVariable(range: vscode.Range, document: vscode.TextDocument): vscode.CodeAction {
const startLine = document.lineAt(range.start.line);
const lineText = startLine.text;

const exec: RegExpExecArray | null = VARIABLE_REGEXP.exec(lineText);
if (!exec) {
return undefined;
}

const codeAction = new vscode.CodeAction(
"Export this variable",
vscode.CodeActionKind.RefactorInline
);


codeAction.edit = new vscode.WorkspaceEdit();

const updatedText = lineText.replace(VARIABLE_REGEXP, "@export var $1 $2 $3");

codeAction.edit.replace(
document.uri,
startLine.range,
updatedText
);

return codeAction;
}

function addRangeExportToVariable(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 [_, name, type, body] = exec;

const is_number = type.trim() === "float" || type.trim() === "int";
if (!is_number) return undefined;

const codeAction = new vscode.CodeAction(
"Export as a range",
vscode.CodeActionKind.RefactorInline
);

codeAction.edit = new vscode.WorkspaceEdit();
let updatedText = "";

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}`);
}

codeAction.edit.replace(
document.uri,
startLine.range,
updatedText
);
codeAction.isPreferred = true;
return 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 Variable",
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"
};

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;
const codeAction = new vscode.CodeAction(
"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.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: "editor.action.rename",
title: "Rename the new function"
};

return codeAction;
}

// 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(
"Export these variables",
vscode.CodeActionKind.RefactorRewrite,
);
const editor = vscode.window.activeTextEditor;
const selectedText = document.getText(editor.selection);

if (selectedText === "") return undefined;

const individualLines = selectedText.split("\n");

let updatedText: string[] = [];
let nonVarElements = 0;
let varElements = 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 (varElements < 1) return undefined;

const newText: string = updatedText.join("\n");
codeAction.edit = new vscode.WorkspaceEdit();
codeAction.edit.replace(
document.uri,
editor.selection,
newText,
);
codeAction.isPreferred = true;
return codeAction;
}
1 change: 1 addition & 0 deletions src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from "./hover";
export * from "./inlay_hints";
export * from "./semantic_tokens";
export * from "./tasks";
export * from "./code_action_provider";
20 changes: 20 additions & 0 deletions src/utils/vscode_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,23 @@ 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(document?: vscode.TextDocument): string {
const editor = vscode.window.activeTextEditor;
const doc = document ?? editor?.document;

if (!doc) {
const editorConfig = vscode.workspace.getConfiguration("editor");
const insertSpaces = editorConfig.get<boolean>("insertSpaces", true);
const tabSize = editorConfig.get<number>("tabSize", 4);
return insertSpaces ? " ".repeat(tabSize) : "\t";
}

const { insertSpaces, tabSize } = vscode.window.activeTextEditor?.options ?? {};
const size = typeof tabSize === "number" ? tabSize : 4;
return insertSpaces ? " ".repeat(size) : "\t";
}
32 changes: 32 additions & 0 deletions syntaxes/examples/code_actions_example.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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
const ex
var ex
var ex


func export_code_as_new_function(args):
if condition:
var a
var b
var c
else:
pass
pass
pass
if condition:
if condition:
if condition:
pass
func method(args):
position.x += (15 / 2) * 5
var lambda = func (x):
print(x)
pass