mirror of
https://github.com/godotengine/godot-vscode-plugin.git
synced 2025-12-31 13:48:24 +03:00
Add actions to run project and scenes
Fix errors with validating Add node completion
This commit is contained in:
@@ -114,7 +114,7 @@
|
||||
},
|
||||
|
||||
"function define": {
|
||||
"prefix": "while",
|
||||
"prefix": "func",
|
||||
"body": [
|
||||
"func ${1:method}(${2:args}):",
|
||||
"\t${3:pass}"
|
||||
@@ -161,5 +161,36 @@
|
||||
"body": [
|
||||
"${1:element} in ${$2:array}"
|
||||
]
|
||||
},
|
||||
|
||||
"GDScript template": {
|
||||
"prefix": "gdscript",
|
||||
"body": [
|
||||
"extends ${1:BaseClass}",
|
||||
"",
|
||||
"# class member variables go here, for example:",
|
||||
"# var a = 2",
|
||||
"# var b = \"textvar\"",
|
||||
"",
|
||||
"func _ready():",
|
||||
"\t# Called every time the node is added to the scene.",
|
||||
"\t# Initialization here",
|
||||
"\tpass",
|
||||
""
|
||||
]
|
||||
},
|
||||
|
||||
"Enable process function": {
|
||||
"prefix": "process",
|
||||
"body": [
|
||||
"set_process(true)"
|
||||
]
|
||||
},
|
||||
|
||||
"Enable process input function": {
|
||||
"prefix": "processin",
|
||||
"body": [
|
||||
"set_process_input(true)"
|
||||
]
|
||||
}
|
||||
}
|
||||
17
package.json
17
package.json
@@ -20,6 +20,18 @@
|
||||
{
|
||||
"command": "godot.updateWorkspaceSymbols",
|
||||
"title": "GodotTools: Update Workspace Symbols"
|
||||
},
|
||||
{
|
||||
"command": "godot.runWorkspace",
|
||||
"title": "GodotTools: Run workspace as godot project"
|
||||
},
|
||||
{
|
||||
"command": "godot.openWithEditor",
|
||||
"title": "GodotTools: Open workspace with godot editor"
|
||||
},
|
||||
{
|
||||
"command": "godot.runCurrentScene",
|
||||
"title": "GodotTools: Run current scene"
|
||||
}
|
||||
],
|
||||
"configuration": {
|
||||
@@ -35,6 +47,11 @@
|
||||
"type": "number",
|
||||
"default": 100,
|
||||
"description": "Controls the maximum number of problems produced by the server."
|
||||
},
|
||||
"GodotTools.editorPath": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The absolute path of your godot editor"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,15 +2,28 @@ import GDScriptSymbolParser from './gdscript/symbolparser';
|
||||
import * as fs from 'fs';
|
||||
import {CompletionItem, CompletionItemKind, TextEdit, Range, workspace} from 'vscode';
|
||||
|
||||
interface NodeInfo {
|
||||
name: string,
|
||||
type: string,
|
||||
parent: string,
|
||||
instance: string
|
||||
};
|
||||
|
||||
class Config {
|
||||
private symbols;
|
||||
private classes;
|
||||
public bintinSybmolInfoList: CompletionItem[];
|
||||
public parser: GDScriptSymbolParser;
|
||||
// scriptpath : scenepath
|
||||
public scriptSceneMap: Object;
|
||||
// scenepath : NodeInfo[]
|
||||
private nodeInfoMap: Object;
|
||||
|
||||
constructor() {
|
||||
this.symbols = {};
|
||||
this.bintinSybmolInfoList = [];
|
||||
this.nodeInfoMap = {};
|
||||
this.scriptSceneMap = {};
|
||||
this.parser = new GDScriptSymbolParser();
|
||||
}
|
||||
|
||||
@@ -37,12 +50,12 @@ class Config {
|
||||
}
|
||||
|
||||
normalizePath(path) {
|
||||
let newpath = path;
|
||||
let newpath = path.replace(/\\/g, "/");
|
||||
if( path.indexOf(":") != -1){
|
||||
let parts = path.split(":");
|
||||
newpath = parts[0].toUpperCase()
|
||||
for(let i=1; i<parts.length; i++)
|
||||
newpath += parts[i].replace(/\\/g, "/");
|
||||
newpath += parts[i];
|
||||
}
|
||||
return newpath;
|
||||
}
|
||||
@@ -138,9 +151,91 @@ class Config {
|
||||
items = [...items, ...addScriptItems(script.signals, CompletionItemKind.Interface, "Signal")];
|
||||
items = [...items, ...addScriptItems(script.constants, CompletionItemKind.Enum, "Constant")];
|
||||
}
|
||||
|
||||
const addSceneNodes = ()=>{
|
||||
const _items: CompletionItem[] = [];
|
||||
for (let scnenepath of Object.keys(this.nodeInfoMap)) {
|
||||
const nodes: NodeInfo[] = this.nodeInfoMap[scnenepath];
|
||||
nodes.map((n=>{
|
||||
const item = new CompletionItem(n.name, CompletionItemKind.Reference);
|
||||
item.detail = n.type;
|
||||
item.documentation = `${n.parent}/${n.name} in ${scnenepath}`;
|
||||
_items.push(item);
|
||||
|
||||
const fullitem = new CompletionItem(`${n.parent}/${n.name}`, CompletionItemKind.Reference);
|
||||
fullitem.detail = n.type;
|
||||
fullitem.filterText = n.name;
|
||||
fullitem.sortText = n.name;
|
||||
fullitem.documentation = `${n.parent}/${n.name} in ${scnenepath}`;
|
||||
_items.push(fullitem);
|
||||
}));
|
||||
}
|
||||
return _items;
|
||||
};
|
||||
items = [...items, ...addSceneNodes()];
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
loadScene(scenePath: string) {
|
||||
console.log(scenePath);
|
||||
if(fs.existsSync(scenePath) && fs.statSync(scenePath).isFile()) {
|
||||
try {
|
||||
const content: string = fs.readFileSync(scenePath, 'utf-8');
|
||||
if(content) {
|
||||
// extern resources
|
||||
const exteres = {};
|
||||
let reg = /ext_resource path="res:\/\/(.*)" type="(.*)" id=(\d+)/g;
|
||||
let match = reg.exec(content);
|
||||
while (match != null) {
|
||||
const path = match[1];
|
||||
const type = match[2];
|
||||
const id = match[3];
|
||||
exteres[id] = {path, type};
|
||||
if (type == "Script") {
|
||||
let workspacescenepath = scenePath;
|
||||
if(workspace)
|
||||
workspacescenepath = workspace.asRelativePath(scenePath);
|
||||
this.scriptSceneMap[path] = workspacescenepath;
|
||||
}
|
||||
match = reg.exec(content);
|
||||
}
|
||||
// nodes
|
||||
const nodes: NodeInfo[] = [];
|
||||
reg = /node\s+name="(.*)"\s+type="(.*)"\s+parent="(.*)"/g;
|
||||
match = reg.exec(content);
|
||||
while (match != null) {
|
||||
nodes.push({
|
||||
name : match[1],
|
||||
type : match[2],
|
||||
parent : match[3],
|
||||
instance: ""
|
||||
});
|
||||
match = reg.exec(content);
|
||||
}
|
||||
// packed scenes
|
||||
reg = /node name="(.*)" parent="(.*)" instance=ExtResource\(\s*(\d+)\s*\)/g;
|
||||
match = reg.exec(content);
|
||||
while (match != null) {
|
||||
const id = match[3];
|
||||
nodes.push({
|
||||
name : match[1],
|
||||
type : exteres[id].type,
|
||||
parent : match[2],
|
||||
instance: exteres[id].path
|
||||
});
|
||||
match = reg.exec(content);
|
||||
}
|
||||
if(workspace)
|
||||
scenePath = workspace.asRelativePath(scenePath);
|
||||
this.nodeInfoMap[scenePath] = nodes;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getClass(name: string) {
|
||||
return this.classes[name];
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class GDScriptDiagnosticSeverity {
|
||||
const text = doc.getText();
|
||||
|
||||
const check = (name:string, range: vscode.Range) => {
|
||||
const pattern = `[\\s\\+\\-\\*/%\\^\\(\\[\\{]${name}[^0-9A-Za-z_]\\s*`;
|
||||
const pattern = `[\\s\\+\\-\\*/%\\^\\(\\[\\{\.]${name}[^0-9A-Za-z_]\\s*`;
|
||||
var matchs = text.match(new RegExp(pattern, 'g'));
|
||||
if(matchs.length <= 1)
|
||||
diagnostics.push(new vscode.Diagnostic(range, `${name} is never used.`, DiagnosticSeverity.Warning));
|
||||
@@ -81,7 +81,7 @@ class GDScriptDiagnosticSeverity {
|
||||
if(semicolonIndex != -1) {
|
||||
diagnostics.push(new vscode.Diagnostic(new vscode.Range(i, semicolonIndex, i, semicolonIndex+1), "Statement ends with a semicolon.", DiagnosticSeverity.Warning));
|
||||
}
|
||||
if(line.match(/\s+if|elif|else|for|while|func|class\s+/g) && line.indexOf(":") == -1) {
|
||||
if(line.match(/\s*(if|elif|else|for|while|func|class)\s/g) && line.indexOf(":") == -1) {
|
||||
if(line.indexOf("#") == -1)
|
||||
diagnostics.push(new vscode.Diagnostic(new vscode.Range(i, 0, i, line.length), "':' expected at end of the line.", DiagnosticSeverity.Error));
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import GDScriptCompletionItemProvider from './gdscript/completion';
|
||||
var glob = require("glob")
|
||||
import config from './config';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
const cmd = require('node-cmd');
|
||||
|
||||
class ToolManager {
|
||||
|
||||
@@ -36,7 +38,10 @@ class ToolManager {
|
||||
vscode.languages.registerCompletionItemProvider('gdscript', new GDScriptCompletionItemProvider(), '.', '"', "'");
|
||||
// Commands
|
||||
this._disposable = vscode.Disposable.from(
|
||||
vscode.commands.registerCommand('godot.updateWorkspaceSymbols', this.loadWorkspaceSymbols.bind(this))
|
||||
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.runCurrentScenr.bind(this))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,11 +64,33 @@ class ToolManager {
|
||||
loadAllSymbols(): Promise<any> {
|
||||
const self = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
glob( this.workspaceDir +"/**/*.gd", (err, files)=>{
|
||||
glob( self.workspaceDir +"/**/*.gd", (err, files)=>{
|
||||
if(!err) {
|
||||
const symbols = {};
|
||||
for(let i=0; i< files.length; i++)
|
||||
symbols[files[i]] = config.loadSymbolsFromFile(files[i]);
|
||||
// load autoloads from engin.cfg
|
||||
const engincfg = path.join(self.workspaceDir, "engine.cfg");
|
||||
if(fs.existsSync(engincfg) && fs.statSync(engincfg).isFile()) {
|
||||
try {
|
||||
const script = { constants: {}, functions: {}, variables: {}, signals: {}, classes: {}, base: "Object", native: "Object"};
|
||||
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("="));
|
||||
script.constants[name] = new vscode.Range(0, 0, 0,0);
|
||||
}
|
||||
});
|
||||
}
|
||||
symbols["autoload"] = script;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
resolve(symbols);
|
||||
}
|
||||
else
|
||||
@@ -72,7 +99,18 @@ class ToolManager {
|
||||
});
|
||||
}
|
||||
|
||||
loadWorkspaceSymbols() {
|
||||
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() {
|
||||
this.loadAllNodesInWorkspace();
|
||||
this.loadAllSymbols().then(symbols=>{
|
||||
vscode.window.showInformationMessage("Update GDScript symbols done");
|
||||
config.setAllSymbols(symbols);
|
||||
@@ -81,6 +119,46 @@ class ToolManager {
|
||||
});
|
||||
}
|
||||
|
||||
private openWorkspaceWithEditor(params="") {
|
||||
let workspaceValid = false
|
||||
if(this.workspaceDir) {
|
||||
let cfg = path.join(this.workspaceDir, "engine.cfg");
|
||||
if( fs.existsSync(cfg) && fs.statSync(cfg).isFile())
|
||||
workspaceValid = true;
|
||||
}
|
||||
if(workspaceValid)
|
||||
this.runEditor(`-path ${this.workspaceDir} ${params}`);
|
||||
else
|
||||
vscode.window.showErrorMessage("Current workspace is not a godot project");
|
||||
}
|
||||
|
||||
private runEditor(params="") {
|
||||
const editorPath = vscode.workspace.getConfiguration("GodotTools").get("editorPath", "")
|
||||
if(!fs.existsSync(editorPath) || !fs.statSync(editorPath).isFile()) {
|
||||
vscode.window.showErrorMessage("Invalid editor path to run the project");
|
||||
}
|
||||
else {
|
||||
cmd.run(`${editorPath} ${params}`);
|
||||
}
|
||||
}
|
||||
|
||||
private runCurrentScenr() {
|
||||
let scenePath = null
|
||||
if(vscode.window.activeTextEditor)
|
||||
scenePath = vscode.workspace.asRelativePath(vscode.window.activeTextEditor.document.uri);
|
||||
console.log("======================", scenePath);
|
||||
console.log(Object.keys(config.scriptSceneMap).toString());
|
||||
if(scenePath.endsWith(".gd"))
|
||||
scenePath = config.scriptSceneMap[config.normalizePath(scenePath)];
|
||||
console.log("======================", scenePath);
|
||||
if(scenePath && (scenePath.endsWith(".tscn") || scenePath.endsWith(".scn"))) {
|
||||
scenePath = ` res://${scenePath} `;
|
||||
this.openWorkspaceWithEditor(scenePath);
|
||||
}
|
||||
else
|
||||
vscode.window.showErrorMessage("Current document is not a scene file");
|
||||
}
|
||||
|
||||
loadClasses() {
|
||||
let done :boolean = false;
|
||||
if(this.workspaceDir)
|
||||
|
||||
Reference in New Issue
Block a user