Implements workspace symbol provider

This commit is contained in:
Geequlim
2016-12-19 23:06:00 +08:00
parent 71bf862172
commit 6ba675ad21
9 changed files with 246 additions and 63 deletions

35
src/config.ts Normal file
View File

@@ -0,0 +1,35 @@
import GDScriptSymbolParser from './gdscript/symbolparser';
class Config {
private symbols;
public parser: GDScriptSymbolParser;
constructor() {
this.symbols = {};
this.parser = new GDScriptSymbolParser();
}
loadSymbolsFromFile(path) {
const script = this.parser.parseFile(path);
this.setSymbols(path, script);
return script;
}
setSymbols(path, s) {
this.symbols[path] = s;
}
getSymbols(path) {
return this.symbols[path];
}
setAllSymbols(s) {
this.symbols = s;
}
getAllSymbols() {
return this.symbols;
}
};
export default new Config();

View File

@@ -7,7 +7,7 @@ let tool: ToolManager = null;
export function activate(context: ExtensionContext) {
tool = new ToolManager(context);
context.subscriptions.push(tool);
// context.subscriptions.push(tool);
context.subscriptions.push(new WindowWatch());
console.log("[GodotTools]: Extension Activated");
}

View File

@@ -28,7 +28,7 @@ interface ParseRequest {
class GDParser {
class GDScriptDiagnosticSeverity {
private _subscription: DiagnosticCollection;
constructor() {
@@ -86,4 +86,4 @@ class GDParser {
}
export default GDParser;
export default GDScriptDiagnosticSeverity;

View File

@@ -0,0 +1,97 @@
import {Range} from 'vscode';
import * as fs from 'fs';
interface GDScript {
constants: {},
functions: {},
variables: {},
signals: {},
classes: {},
base: string,
native: string
}
class GDScriptSymbolParser {
constructor() {
}
parseContent(content: string): GDScript {
const script: GDScript = {
constants: {},
functions: {},
variables: {},
signals: {},
classes: {},
base: "Object",
native: "Object"
}
const text = content;
const lines = text.split(/\r?\n/);
const getMatches = (string, regex, index=1) => {
var matches = [];
var match;
while (match = regex.exec(string)) {
matches.push(match[index]);
}
return matches;
};
const findLineRanges = (symbols, reg)=>{
const sm = {};
symbols.map((name:string)=>{
let line = 0;
let curline = 0;
lines.map(l=>{
const nreg = reg.replace("$X$", name);
if(l.match(nreg) != null) {
line = curline;
return;
}
curline += 1;
});
sm[name] = line;
});
return sm;
}
let funcsnames = getMatches(text, /func\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*\(.*\)/gi, 1);
const funcs = findLineRanges(funcsnames, "func\\s+$X$\\s*\\(.*\\)");
for (let key of Object.keys(funcs))
script.functions[key] = new Range(funcs[key], 0, funcs[key],lines[funcs[key]].length);
let signalnames = getMatches(text, /signal\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*\(.*\)/gi, 1);
const signals = findLineRanges(signalnames, "signal\\s+$X$\\s*\\(.*\\)");
for (let key of Object.keys(signals))
script.signals[key] = new Range(signals[key], 0, signals[key],lines[signals[key]].length);
let varnames = getMatches(text, /var\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*/gi, 1);
const vars = findLineRanges(varnames, "var\\s+$X$\\s*");
for (let key of Object.keys(vars))
script.variables[key] = new Range(vars[key], 0, vars[key],lines[vars[key]].length);
let constnames = getMatches(text, /const\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*/gi, 1);
const consts = findLineRanges(constnames, "const\\s+$X$\\s*");
for (let key of Object.keys(consts))
script.constants[key] = new Range(consts[key], 0, consts[key],lines[consts[key]].length);
let classnames = getMatches(text, /class\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*extends\s+/gi, 1);
const classes = findLineRanges(classnames, "class\\s+$X$\\s*extends\\s+");
for (let key of Object.keys(classes))
script.classes[key] = new Range(classes[key], 0, classes[key],lines[classes[key]].length);
return script;
}
parseFile(path:string): GDScript {
const self = this;
if(fs.existsSync(path) && fs.statSync(path).isFile()){
const content = fs.readFileSync(path, 'utf-8');
return this.parseContent(content);
}
return null;
}
}
export default GDScriptSymbolParser;

View File

@@ -7,61 +7,41 @@ import {
Range
} from 'vscode';
import GDScriptSymbolParser from '../gdscript/symbolparser';
import config from '../config';
class GDScriptSymbolProvider implements DocumentSymbolProvider {
constructor() {}
private parser: GDScriptSymbolParser = null;
constructor() {
this.parser = new GDScriptSymbolParser();
}
provideDocumentSymbols(document: TextDocument, token: CancellationToken): SymbolInformation[] | Thenable<SymbolInformation[]> {
const symbols: SymbolInformation[] = [];
const text =document.getText();
const lines = text.split(/\r?\n/);
const script = this.parser.parseContent(document.getText());
config.setSymbols(document.fileName, script);
const getMatches = (string, regex, index=1) => {
var matches = [];
var match;
while (match = regex.exec(string)) {
matches.push(match[index]);
}
return matches;
};
const findLineRanges = (symbols, reg)=>{
const sm = {};
symbols.map((name:string)=>{
let line = 0;
let curline = 0;
lines.map(l=>{
const nreg = reg.replace("$X$", name);
if(l.match(nreg) != null) {
line = curline;
return;
}
curline += 1;
});
sm[name] = line;
});
return sm;
}
let funcsnames = getMatches(text, /func\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*\(.*\)/gi, 1);
const funcs = findLineRanges(funcsnames, "func\\s+$X$\\s*\\(.*\\)");
const funcs = script.functions;
for (let key of Object.keys(funcs))
symbols.push(new SymbolInformation(key, SymbolKind.Function, new Range(funcs[key], 0, funcs[key],lines[funcs[key]].length)));
symbols.push(new SymbolInformation(key, SymbolKind.Function, funcs[key]));
let signalnames = getMatches(text, /signal\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*\(.*\)/gi, 1);
const signals = findLineRanges(signalnames, "signal\\s+$X$\\s*\\(.*\\)");
const signals = script.signals;
for (let key of Object.keys(signals))
symbols.push(new SymbolInformation(key, SymbolKind.Interface, new Range(signals[key], 0, signals[key],lines[signals[key]].length)));
let varnames = getMatches(text, /var\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*/gi, 1);
const vars = findLineRanges(varnames, "var\\s+$X$\\s*");
symbols.push(new SymbolInformation(key, SymbolKind.Interface, signals[key]));
const vars = script.variables;
for (let key of Object.keys(vars))
symbols.push(new SymbolInformation(key, SymbolKind.Variable, new Range(vars[key], 0, vars[key],lines[vars[key]].length)));
symbols.push(new SymbolInformation(key, SymbolKind.Variable, vars[key]));
let constnames = getMatches(text, /const\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*/gi, 1);
const consts = findLineRanges(constnames, "const\\s+$X$\\s*");
const consts = script.constants;
for (let key of Object.keys(consts))
symbols.push(new SymbolInformation(key, SymbolKind.Constant, new Range(consts[key], 0, consts[key],lines[consts[key]].length)));
symbols.push(new SymbolInformation(key, SymbolKind.Constant, consts[key]));
const classes = script.classes;
for (let key of Object.keys(classes))
symbols.push(new SymbolInformation(key, SymbolKind.Class, classes[key]));
return symbols;
}

View File

@@ -0,0 +1,38 @@
import * as vscode from 'vscode';
import config from '../config';
class GDScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider {
public provideWorkspaceSymbols(query: string, token: vscode.CancellationToken): vscode.SymbolInformation[] {
const scripts = config.getAllSymbols();
const symbols: vscode.SymbolInformation[] = [];
console.log(query);
for (let path of Object.keys(scripts)) {
const queryMembers = (query, members, kind: vscode.SymbolKind, path:string)=> {
for (let name of Object.keys(members)) {
const range: vscode.Range = members[name];
if(name.toLowerCase().indexOf(query.toLowerCase()) != -1) {
const symbol: vscode.SymbolInformation = {
name,
kind,
containerName: "",
location: {
uri: vscode.Uri.file(path),
range
}
};
symbols.push(symbol);
}
}
}
const scrip = scripts[path];
queryMembers(query, scrip.functions, vscode.SymbolKind.Function, path);
queryMembers(query, scrip.signals, vscode.SymbolKind.Interface, path);
queryMembers(query, scrip.variables, vscode.SymbolKind.Variable, path);
queryMembers(query, scrip.constants, vscode.SymbolKind.Constant, path);
queryMembers(query, scrip.classes, vscode.SymbolKind.Class, path);
}
return symbols;
}
}
export default GDScriptWorkspaceSymbolProvider;

View File

@@ -2,19 +2,28 @@ import * as vscode from 'vscode';
import DocDataManager from './docdata';
import godotRequest from './request';
import GDScriptSymbolProvider from './gdscript/symbolprovider';
import GDScriptWorkspaceSymbolProvider from './gdscript/workspace_symbol_provider';
var glob = require("glob")
import config from './config';
class ToolManager {
private workspaceDir: string = "";
private docs: DocDataManager = null;
private symbolprovider: GDScriptSymbolProvider = null;
private workspacesymbolprovider: GDScriptWorkspaceSymbolProvider = null;
constructor(context: vscode.ExtensionContext) {
this.workspaceDir = vscode.workspace.rootPath;
this.validate();
this.loadWorkspaceSymbols();
this.docs = new DocDataManager(context.extensionPath);
this.symbolprovider = new GDScriptSymbolProvider();
vscode.languages.registerDocumentSymbolProvider('gdscript', this.symbolprovider);
this.workspacesymbolprovider = new GDScriptWorkspaceSymbolProvider();
vscode.languages.registerWorkspaceSymbolProvider(this.workspacesymbolprovider);
}
validate() {
@@ -33,6 +42,31 @@ class ToolManager {
});
}
loadAllSymbols(): Promise<any> {
const self = this;
return new Promise((resolve, reject) => {
glob( this.workspaceDir +"/**/*.gd", (err, files)=>{
if(!err) {
const symbols = {};
for(let i=0; i< files.length; i++)
symbols[files[i]] = config.loadSymbolsFromFile(files[i]);
resolve(symbols);
}
else
reject(err);
});
});
}
loadWorkspaceSymbols() {
this.loadAllSymbols().then(symbols=>{
// vscode.window.showInformationMessage("GDScript symbols done");
config.setAllSymbols(symbols);
}).catch(e=>{
// vscode.window.showWarningMessage("GDScript symbols parse failed");
});
}
dispose() {
}

View File

@@ -1,8 +1,7 @@
import {Disposable, window} from 'vscode';
import parse from "./gdscript/parser";
import GDParser from './gdscript/parser';
import GDScriptDiagnosticSeverity from './gdscript/diagnostic';
import GDScriptCompleter from './gdscript/completion';
import config from './config';
interface DocumentFlag {
path: string,
@@ -12,7 +11,7 @@ interface DocumentFlag {
class WindowWatcher {
private _disposable: Disposable;
private _parser: GDParser;
private _diagnosticSeverity: GDScriptDiagnosticSeverity;
private _lastText: DocumentFlag;
private _completer: GDScriptCompleter;
@@ -23,9 +22,9 @@ class WindowWatcher {
window.onDidChangeTextEditorOptions(this.onDidChangeTextEditorOptions.bind(this), this, subscriptions);
window.onDidChangeTextEditorViewColumn(this.onDidChangeTextEditorViewColumn.bind(this), this, subscriptions);
this._parser = new GDParser();
this._diagnosticSeverity = new GDScriptDiagnosticSeverity();
this._completer = new GDScriptCompleter();
this._disposable = Disposable.from(...subscriptions, this._parser, this._completer);
this._disposable = Disposable.from(...subscriptions, this._diagnosticSeverity, this._completer);
this._lastText = {path: "-1", version: -1};
}
@@ -39,11 +38,12 @@ class WindowWatcher {
* to `undefined`.
*/
private onDidChangeActiveTextEditor(event: any) {
console.log("[GodotTools]:onDidChangeActiveTextEditor", event);
if(window.activeTextEditor != undefined) {
// console.log("[GodotTools]:onDidChangeActiveTextEditor", event);
if(window.activeTextEditor != undefined) {
const doc = window.activeTextEditor.document;
this._parser.parseDocument(doc);
this._diagnosticSeverity.parseDocument(doc);
this._lastText = {path: doc.fileName, version: doc.version};
config.loadSymbolsFromFile(doc.fileName);
}
}
@@ -54,9 +54,11 @@ class WindowWatcher {
console.log("[GodotTools]:onDidChangeTextEditorSelection");
const doc = window.activeTextEditor.document;
const curText: DocumentFlag= {path: doc.fileName, version: doc.version};
// Check content changed
if(this._lastText.path != curText.path || this._lastText.version != curText.version) {
this._parser.parseDocument(doc);
this._diagnosticSeverity.parseDocument(doc);
this._lastText = curText;
config.loadSymbolsFromFile(doc.fileName);
}
}