Add actions to run project and scenes

Fix errors with validating
Add node completion
This commit is contained in:
Geequlim
2016-12-27 21:31:00 +08:00
parent 81a3aeef57
commit 815a80dfed
5 changed files with 229 additions and 8 deletions

View File

@@ -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)"
]
}
}

View File

@@ -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"
}
}
},

View File

@@ -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];
}

View File

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

View File

@@ -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)