static validating

This commit is contained in:
Geequlim
2016-12-25 22:10:35 +08:00
parent 893ae24438
commit 425e8214ed
5 changed files with 69 additions and 74 deletions

View File

@@ -30,8 +30,8 @@ interface CompletionResult {
}
class GDScriptCompletionItemProvider implements CompletionItemProvider {
constructor() {
constructor() {
}
provideCompletionItems(document : TextDocument, position : Position, token : CancellationToken) : CompletionItem[] | Thenable < CompletionItem[] > | CompletionList | Thenable < CompletionList > {
@@ -74,16 +74,4 @@ class GDScriptCompletionItemProvider implements CompletionItemProvider {
}
class GDScriptCompleter {
private _provider: Disposable;
constructor() {
this._provider = languages.registerCompletionItemProvider('gdscript', new GDScriptCompletionItemProvider(), '.');
}
dispose() {
this._provider.dispose();
}
}
export default GDScriptCompleter;
export default GDScriptCompletionItemProvider;

View File

@@ -41,49 +41,52 @@ class GDScriptDiagnosticSeverity {
this._subscription.dispose()
}
private parseGDScript(script: GDScript, request: ParseRequest) {
// console.log("Parse GDScript ", script);
let canonicalFile = vscode.Uri.file(request.path);
this._subscription.delete(canonicalFile)
if(script.valid) { // Parse symbols
// TODO
validateScript(doc: vscode.TextDocument, script: any) {
if(doc.languageId == 'gdscript') {
if(script) {
let diagnostics = [
...(this.validateExpression(doc)),
...(this.validateUnusedSymbols(doc, script)),
];
// Update diagnostics
this._subscription.set(doc.uri, diagnostics);
}
}
// Parse errors
let diagnostics = [];
script.errors.map( error => {
let range = new vscode.Range(error.row-1, error.column, error.row-1, error.row + 10);
diagnostics.push(new vscode.Diagnostic(range, error.message, DiagnosticSeverity.Error));
});
// Unused variables
const checker = (name:string, line: number) => {
const lines = request.text.split(/\r?\n/);
const pattern = `[\\s\\+\\-\\*/%\\^\\(]${name}[^a-zA-Z_\\$]`;
var matchs = request.text.match(new RegExp(pattern, 'gi'));
if(matchs.length <= 1)
diagnostics.push(new vscode.Diagnostic(new vscode.Range(line-1, lines[line-1].indexOf(name), line-1, lines[line-1].indexOf(name) + name.length), `${name} is never used.`, DiagnosticSeverity.Warning));
};
for (let key of Object.keys(script.members.variables))
checker(key, script.members.variables[key]);
for (let key of Object.keys(script.members.constants))
checker(key, script.members.constants[key]);
// Update diagnostics
this._subscription.set(canonicalFile, diagnostics);
}
parseDocument(doc: vscode.TextDocument) {
if(doc.languageId == 'gdscript') {
// console.log('[GodotTools]:start parsing document ', doc);
const self = this;
const request: ParseRequest = {text: doc.getText(), path: config.normalizePath(doc.fileName)};
requestGodot({action: "parsescript",request}).then((data: any)=>{
const result: GDScript = data.result;
if(result && vscode.window.activeTextEditor.document == doc){
self.parseGDScript(result, request);
}
}).catch(e=>{
console.error(e);
});
}
private validateUnusedSymbols(doc: vscode.TextDocument,script) {
let diagnostics = [];
const text = doc.getText();
const check = (name:string, range: vscode.Range) => {
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));
};
// Unused variables
for (let key of Object.keys(script.variables))
check(key, script.variables[key]);
for (let key of Object.keys(script.constants))
check(key, script.variables[key]);
return diagnostics;
}
private validateExpression(doc: vscode.TextDocument) {
let diagnostics = [];
const text = doc.getText();
const lines = text.split(/\r?\n/);
lines.map((line:string, i: number) =>{
const semicolonIndex = line.indexOf(';');
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(/if|elif|else|for|while|func|class/g) && line.indexOf(":") == -1) {
diagnostics.push(new vscode.Diagnostic(new vscode.Range(i, 0, i, line.length), "':' expected at end of the line.", DiagnosticSeverity.Error));
}
});
return diagnostics;
}
}

View File

@@ -55,30 +55,34 @@ class GDScriptSymbolParser {
return sm;
}
let funcsnames = getMatches(text, /func\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*\(.*\)/gi, 1);
const determRange = (key:string, array: any): Range =>{
return new Range(array[key], lines[array[key]].indexOf(key), array[key], lines[array[key]].indexOf(key) + key.length);
};
let funcsnames = getMatches(text, /func\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*\(/g, 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);
script.functions[key] = determRange(key, funcs);
let signalnames = getMatches(text, /signal\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*\(.*\)/gi, 1);
let signalnames = getMatches(text, /signal\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*\(.*\)/g, 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);
script.signals[key] = determRange(key, signals);
let varnames = getMatches(text, /var\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*/gi, 1);
let varnames = getMatches(text, /var\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*/g, 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);
script.variables[key] = determRange(key, vars);
let constnames = getMatches(text, /const\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*/gi, 1);
let constnames = getMatches(text, /const\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*/g, 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);
script.constants[key] = determRange(key, consts);
let classnames = getMatches(text, /class\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*extends\s+/gi, 1);
let classnames = getMatches(text, /class\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*extends\s+/g, 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);
script.classes[key] = determRange(key, classes);
return script;
}

View File

@@ -2,6 +2,7 @@ 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';
var glob = require("glob")
import config from './config';
import * as path from 'path';
@@ -25,13 +26,14 @@ class ToolManager {
this.validate();
}
this.loadClasses();
// 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);
// code completion provider
vscode.languages.registerCompletionItemProvider('gdscript', new GDScriptCompletionItemProvider(), '.', '"', "'");
// Commands
this._disposable = vscode.Disposable.from(
vscode.commands.registerCommand('godot.updateWorkspaceSymbols', this.loadWorkspaceSymbols.bind(this))

View File

@@ -13,7 +13,6 @@ class WindowWatcher {
private _disposable: Disposable;
private _diagnosticSeverity: GDScriptDiagnosticSeverity;
private _lastText: DocumentFlag;
private _completer: GDScriptCompleter;
constructor() {
let subscriptions: Disposable[] = [];
@@ -23,8 +22,7 @@ class WindowWatcher {
window.onDidChangeTextEditorViewColumn(this.onDidChangeTextEditorViewColumn.bind(this), this, subscriptions);
this._diagnosticSeverity = new GDScriptDiagnosticSeverity();
this._completer = new GDScriptCompleter();
this._disposable = Disposable.from(...subscriptions, this._diagnosticSeverity, this._completer);
this._disposable = Disposable.from(...subscriptions, this._diagnosticSeverity);
this._lastText = {path: "-1", version: -1};
}
@@ -41,9 +39,9 @@ class WindowWatcher {
// console.log("[GodotTools]:onDidChangeActiveTextEditor", event);
if(window.activeTextEditor != undefined) {
const doc = window.activeTextEditor.document;
this._diagnosticSeverity.parseDocument(doc);
const script = config.loadSymbolsFromFile(doc.fileName);
this._diagnosticSeverity.validateScript(doc, script);
this._lastText = {path: doc.fileName, version: doc.version};
config.loadSymbolsFromFile(doc.fileName);
}
}
@@ -56,9 +54,9 @@ class WindowWatcher {
const curText: DocumentFlag= {path: doc.fileName, version: doc.version};
// Check content changed
if(this._lastText.path != curText.path || this._lastText.version != curText.version) {
this._diagnosticSeverity.parseDocument(doc);
const script = config.loadSymbolsFromFile(doc.fileName);
this._diagnosticSeverity.validateScript(doc, script);
this._lastText = curText;
config.loadSymbolsFromFile(doc.fileName);
}
}