mirror of
https://github.com/godotengine/godot-vscode-plugin.git
synced 2026-01-04 10:09:58 +03:00
Implements workspace symbol provider
This commit is contained in:
35
src/config.ts
Normal file
35
src/config.ts
Normal 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();
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
97
src/gdscript/symbolparser.ts
Normal file
97
src/gdscript/symbolparser.ts
Normal 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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
38
src/gdscript/workspace_symbol_provider.ts
Normal file
38
src/gdscript/workspace_symbol_provider.ts
Normal 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;
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user