浏览代码

static validating

Geequlim 8 年之前
父节点
当前提交
425e8214ed
共有 5 个文件被更改,包括 66 次插入71 次删除
  1. 2 14
      src/gdscript/completion.ts
  2. 40 37
      src/gdscript/diagnostic.ts
  3. 14 10
      src/gdscript/symbolparser.ts
  4. 5 3
      src/tool_manager.ts
  5. 5 7
      src/window_watcher.ts

+ 2 - 14
src/gdscript/completion.ts

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

+ 40 - 37
src/gdscript/diagnostic.ts

@@ -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
+  }
+
+  private validateUnusedSymbols(doc: vscode.TextDocument,script) {
     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'));
+    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(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));
+        diagnostics.push(new vscode.Diagnostic(range, `${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);    
+    // 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;    
   }
 
-  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 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;
   }
   
 }

+ 14 - 10
src/gdscript/symbolparser.ts

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

+ 5 - 3
src/tool_manager.ts

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

+ 5 - 7
src/window_watcher.ts

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