mirror of
https://github.com/godotengine/godot-vscode-plugin.git
synced 2025-12-31 13:48:24 +03:00
284 lines
11 KiB
TypeScript
284 lines
11 KiB
TypeScript
import * as vscode from 'vscode';
|
|
import godotRequest from './request';
|
|
import GDScriptSymbolProvider from './gdscript/symbolprovider';
|
|
import GDScriptWorkspaceSymbolProvider from './gdscript/workspace_symbol_provider';
|
|
import GDScriptCompletionItemProvider from './gdscript/completion';
|
|
import GDScriptDefinitionProivder from './gdscript/definitionprovider';
|
|
import GDScriptHoverProvider from './gdscript/hoverprovider';
|
|
import GDScriptDocumentContentProvider from './gdscript/docprovider';
|
|
import GDScriptSignatureHelpProvider from './gdscript/signature_helper';
|
|
var glob = require("glob")
|
|
import config from './config';
|
|
import * as path from 'path';
|
|
import * as fs from 'fs';
|
|
class ToolManager {
|
|
|
|
private workspaceDir: string = "";
|
|
private symbolprovider: GDScriptSymbolProvider = null;
|
|
private workspacesymbolprovider: GDScriptWorkspaceSymbolProvider = null;
|
|
private _disposable: vscode.Disposable;
|
|
private _context: vscode.ExtensionContext;
|
|
private _projectFile : string = "project.godot";
|
|
private _rootDir : string = "";
|
|
private _biuitinDocFile : string = "doc/classes-3.0.json";
|
|
|
|
constructor(context: vscode.ExtensionContext) {
|
|
this._context = context;
|
|
this.workspaceDir = vscode.workspace.rootPath;
|
|
let completionDollar = false;
|
|
|
|
if (vscode.workspace.getConfiguration("GodotTools").get("godotVersion", 3.0) < 3) {
|
|
this._projectFile = "engine.cfg";
|
|
this._biuitinDocFile = "doc/classes-2.1.json";
|
|
completionDollar = true;
|
|
}
|
|
this.loadClasses();
|
|
|
|
if (vscode.workspace && this.workspaceDir) {
|
|
vscode.workspace.registerTextDocumentContentProvider('godotdoc', new GDScriptDocumentContentProvider());
|
|
this.workspaceDir = this.workspaceDir.replace(/\\/g, "/");
|
|
this._rootDir = vscode.workspace.getConfiguration("GodotTools").get("godotProjectRoot", this.workspaceDir);
|
|
this._rootDir = this._rootDir.replace("${workspaceRoot}", this.workspaceDir);
|
|
this.loadWorkspaceSymbols();
|
|
}
|
|
|
|
// documentation symbol provider
|
|
this.symbolprovider = new GDScriptSymbolProvider();
|
|
vscode.languages.registerDocumentSymbolProvider('gdscript', this.symbolprovider);
|
|
// workspace symbol provider
|
|
this.workspacesymbolprovider = new GDScriptWorkspaceSymbolProvider();
|
|
vscode.languages.registerWorkspaceSymbolProvider(this.workspacesymbolprovider);
|
|
// definition provider
|
|
vscode.languages.registerDefinitionProvider('gdscript', new GDScriptDefinitionProivder(this._rootDir));
|
|
// hover provider
|
|
vscode.languages.registerHoverProvider('gdscript', new GDScriptHoverProvider());
|
|
// code completion provider
|
|
if (completionDollar)
|
|
vscode.languages.registerCompletionItemProvider('gdscript', new GDScriptCompletionItemProvider(), '.', '"', "'", "$");
|
|
else
|
|
vscode.languages.registerCompletionItemProvider('gdscript', new GDScriptCompletionItemProvider(), '.', '"', "'");
|
|
// signature help provider
|
|
vscode.languages.registerSignatureHelpProvider('gdscript', new GDScriptSignatureHelpProvider(), '(', ',');
|
|
// Commands
|
|
this._disposable = vscode.Disposable.from(
|
|
vscode.commands.registerCommand('godot.updateWorkspaceSymbols', this.loadWorkspaceSymbols.bind(this)),
|
|
vscode.commands.registerCommand('godot.runWorkspace', () => { this.openWorkspaceWithEditor() }),
|
|
vscode.commands.registerCommand('godot.openWithEditor', () => { this.openWorkspaceWithEditor("-e") }),
|
|
vscode.commands.registerCommand('godot.runCurrentScene', this.runCurrentScene.bind(this)),
|
|
);
|
|
}
|
|
|
|
validate() {
|
|
const self = this;
|
|
godotRequest({
|
|
action: "editor",
|
|
command: "projectdir"
|
|
}).then((res: any) => {
|
|
let path = res.path;
|
|
if (path && path.length > 0 && path.endsWith("/"))
|
|
path = path.substring(0, path.length - 1)
|
|
if (path.toLowerCase() == self.workspaceDir.toLowerCase())
|
|
vscode.window.showInformationMessage("Connected to the Godot editor server");
|
|
else {
|
|
vscode.window.showWarningMessage("The opened project is not the same within the Godot editor");
|
|
}
|
|
}).catch(e => {
|
|
vscode.window.showErrorMessage("Failed connecting to the Godot editor server");
|
|
});
|
|
}
|
|
|
|
loadAllSymbols(): Promise < any > {
|
|
const self = this;
|
|
return new Promise((resolve, reject) => {
|
|
glob(self.workspaceDir + "/**/*.gd", (err, files) => {
|
|
if (!err) {
|
|
const symbols = {};
|
|
for (let i = 0; i < files.length; i++)
|
|
symbols[config.normalizePath(files[i])] = config.loadSymbolsFromFile(files[i]);
|
|
// load autoloads from engin.cfg
|
|
const engincfg = path.join(self.workspaceDir, this._projectFile);
|
|
if (fs.existsSync(engincfg) && fs.statSync(engincfg).isFile()) {
|
|
try {
|
|
const script = {
|
|
constants: {},
|
|
functions: {},
|
|
variables: {},
|
|
signals: {},
|
|
classes: {},
|
|
base: "Object",
|
|
native: "Object",
|
|
constpathes: {},
|
|
documents: {},
|
|
constvalues: {}
|
|
};
|
|
let content: string = fs.readFileSync(engincfg, 'utf-8');
|
|
if (content && content.indexOf("[autoload]") != -1) {
|
|
content = content.substring(content.indexOf("[autoload]") + "[autoload]".length, content.length);
|
|
content = content.substring(0, content.indexOf("["));
|
|
const lines = content.split(/\r?\n/);
|
|
lines.map((l) => {
|
|
if (l.indexOf("=") != 0) {
|
|
const name = l.substring(0, l.indexOf("="));
|
|
|
|
let gdpath = l.substring(l.indexOf("res://") + "res://".length, l.indexOf(".gd") + ".gd".length);
|
|
gdpath = path.join(this._rootDir, gdpath);
|
|
let showgdpath = vscode.workspace.asRelativePath(gdpath);
|
|
|
|
let doc = "Autoloaded instance of " + `[${showgdpath}](${vscode.Uri.file(gdpath).toString()})`;
|
|
doc = doc.replace(/"/g, " ");
|
|
|
|
script.constants[name] = new vscode.Range(0, 0, 0, 0);
|
|
script.constvalues[name] = "autoload";
|
|
script.documents[name] = doc;
|
|
script.constpathes[name] = gdpath;
|
|
}
|
|
});
|
|
}
|
|
symbols["autoload"] = script;
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
resolve(symbols);
|
|
} else
|
|
reject(err);
|
|
});
|
|
});
|
|
}
|
|
|
|
private loadAllNodesInWorkspace() {
|
|
glob(this.workspaceDir + "/**/*.tscn", (err, files) => {
|
|
if (!err) {
|
|
const symbols = {};
|
|
for (let i = 0; i < files.length; i++)
|
|
config.loadScene(files[i]);
|
|
}
|
|
});
|
|
}
|
|
|
|
private loadWorkspaceSymbols() {
|
|
let handle = this.showProgress("Loading symbols");
|
|
if (vscode.workspace.getConfiguration("GodotTools").get("parseTextScene", false)) {
|
|
this.loadAllNodesInWorkspace();
|
|
}
|
|
this.loadAllSymbols().then(symbols => {
|
|
handle();
|
|
vscode.window.setStatusBarMessage("$(check) Workspace symbols", 5000);
|
|
config.setAllSymbols(symbols);
|
|
}).catch(e => {
|
|
handle();
|
|
vscode.window.setStatusBarMessage("$(x) Workspace symbols", 5000);
|
|
});
|
|
}
|
|
|
|
private openWorkspaceWithEditor(params = "") {
|
|
let workspaceValid = false
|
|
if (this.workspaceDir) {
|
|
let cfg = path.join(this._rootDir, this._projectFile);
|
|
console.log(cfg);
|
|
if (fs.existsSync(cfg) && fs.statSync(cfg).isFile())
|
|
workspaceValid = true;
|
|
}
|
|
if (workspaceValid) {
|
|
let pathFlag = "-path";
|
|
if (vscode.workspace.getConfiguration("GodotTools").get("godotVersion", 2.1) >= 3)
|
|
pathFlag = "--path";
|
|
this.runEditor(`${pathFlag} "${this._rootDir}" ${params}`);
|
|
}
|
|
else
|
|
vscode.window.showErrorMessage("Current workspace is not a Godot project");
|
|
}
|
|
|
|
private runEditor(params = "") {
|
|
let editorPath = vscode.workspace.getConfiguration("GodotTools").get("editorPath", "")
|
|
editorPath = editorPath.replace("${workspaceRoot}", this.workspaceDir);
|
|
console.log(editorPath);
|
|
if (!fs.existsSync(editorPath) || !fs.statSync(editorPath).isFile()) {
|
|
vscode.window.showErrorMessage("Invalid editor path to run the project");
|
|
} else {
|
|
let existingTerminal = vscode.window.terminals.find(t => t._name === "GodotTools")
|
|
if (existingTerminal) {
|
|
existingTerminal.dispose()
|
|
}
|
|
let terminal = vscode.window.createTerminal("GodotTools");
|
|
editorPath = this.escapeCmd(editorPath);
|
|
let cmmand = `${editorPath} ${params}`;
|
|
terminal.sendText(cmmand, true);
|
|
terminal.show();
|
|
}
|
|
}
|
|
|
|
private runCurrentScene() {
|
|
const absFilePath = vscode.window.activeTextEditor.document.uri.fsPath;
|
|
let scenePath = null
|
|
if (vscode.window.activeTextEditor) {
|
|
scenePath = path.relative(this._rootDir, absFilePath);
|
|
scenePath = scenePath.replace(/\\/g, "/");
|
|
}
|
|
// Run scripts directly which is inhired from SceneTree or MainLoop
|
|
if (scenePath.endsWith(".gd")) {
|
|
const scriptPath = scenePath;
|
|
scenePath = config.scriptSceneMap[config.normalizePath(scenePath)];
|
|
if (!scenePath) {
|
|
const script = config.loadSymbolsFromFile(absFilePath);
|
|
if (script) {
|
|
if(script.native == "SceneTree" || script.native == "MainLoop") {
|
|
this.runEditor(`-s "${absFilePath}"`);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (scenePath) {
|
|
if (scenePath.endsWith(".gd"))
|
|
scenePath = ` -s "res://${scenePath}" `;
|
|
else
|
|
scenePath = ` "res://${scenePath}" `;
|
|
this.openWorkspaceWithEditor(scenePath);
|
|
} else
|
|
vscode.window.showErrorMessage("Current document is not a scene file or MainLoop");
|
|
}
|
|
|
|
loadClasses() {
|
|
let done: boolean = false;
|
|
if (this.workspaceDir)
|
|
done = config.loadClasses(path.join(this.workspaceDir, ".vscode", "classes.json"));
|
|
if (!done)
|
|
done = config.loadClasses(path.join(this._context.extensionPath, this._biuitinDocFile));
|
|
if (!done)
|
|
vscode.window.showErrorMessage("Loading GDScript documentation failed");
|
|
}
|
|
|
|
dispose() {
|
|
this._disposable.dispose();
|
|
}
|
|
|
|
private showProgress(message: string) {
|
|
let r_resolve;
|
|
vscode.window.withProgress({ location: vscode.ProgressLocation.Window}, p => {
|
|
return new Promise((resolve, reject) => {
|
|
p.report({message});
|
|
r_resolve = resolve;
|
|
});
|
|
});
|
|
return r_resolve;
|
|
}
|
|
|
|
private escapeCmd(cmd: string) {
|
|
// Double quote command (should work in at least cmd.exe and bash)
|
|
let cmdEsc = `"${cmd}"`;
|
|
|
|
// Fetch Windows shell type
|
|
let shell = vscode.workspace.getConfiguration("terminal.integrated.shell").get("windows", "");
|
|
|
|
// For powershell we prepend an & to prevent the command being treated as a string
|
|
if (shell.endsWith("powershell.exe") && process.platform === "win32") {
|
|
cmdEsc = `&${cmdEsc}`;
|
|
}
|
|
return cmdEsc
|
|
}
|
|
|
|
};
|
|
|
|
export default ToolManager;
|