浏览代码

Add signature helper for builtin functions
Add signature infomations for workspace functions in hover message

Geequlim 8 年之前
父节点
当前提交
f890468f98

+ 4 - 2
src/config.ts

@@ -20,6 +20,8 @@ class Config {
   public nodeInfoMap: Object;
   // symbolname: {completionItem: CompletionItem, rowDoc: docdata}
   public builtinSymbolInfoMap: Object;
+  // path.function: signature
+  public workspaceMethodSignatureMap: Object;
 
   constructor() {
     this.symbols = {};
@@ -90,7 +92,7 @@ class Config {
         builtinSymbolInfoMap[classdoc.name] = {completionItem: item, rowDoc: classdoc};
         // methods
         const methods = classdoc.methods
-        const parsMethod = (m, kind: CompletionItemKind, insertAction=(name)=>name+"()")=>{
+        const parsMethod = (m, kind: CompletionItemKind, insertAction=(name)=>name)=>{
           const mi = new CompletionItem(m.name, kind);
           mi.insertText = insertAction(m.name)
           mi.filterText = m.name
@@ -154,7 +156,7 @@ class Config {
           return _items;
         }
         items = [...items, ...addScriptItems(script.classes, CompletionItemKind.Class, "Class")];
-        items = [...items, ...addScriptItems(script.functions, CompletionItemKind.Method, "Method", (t)=>`${t}()`)];
+        items = [...items, ...addScriptItems(script.functions, CompletionItemKind.Method, "Method", (f)=>f+`${script.signatures[f]}`)];
         items = [...items, ...addScriptItems(script.variables, CompletionItemKind.Variable, "Variable")];
         items = [...items, ...addScriptItems(script.signals, CompletionItemKind.Interface, "Signal")];
         items = [...items, ...addScriptItems(script.constants, CompletionItemKind.Enum, "Constant")];

+ 1 - 1
src/gdscript/diagnostic.ts

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

+ 4 - 1
src/gdscript/hoverprovider.ts

@@ -44,7 +44,10 @@ class GDScriptHoverProvider implements HoverProvider {
                         let dfile = path;
                         if (workspace && workspace.asRelativePath(dfile))
                             dfile = workspace.asRelativePath(dfile);
-                        _items.push({language:'gdscript', value:`${type} ${name}`});
+                        let extra = "";
+                        if(type == "func"|| type == "signal" && script.signatures[name])
+                            extra = script.signatures[name];
+                        _items.push({language:'gdscript', value:`${type} ${name}${extra}`});
                         _items.push(`Defined in *[${dfile}](${Uri.file(path).toString()})*`)
                         break;
                     }

+ 94 - 0
src/gdscript/signature_helper.ts

@@ -0,0 +1,94 @@
+import {
+  SignatureHelpProvider,
+  TextDocument,
+  Position,
+  CancellationToken,
+  SignatureInformation,
+  SignatureHelp,
+  CompletionItemKind,
+  ParameterInformation
+} from 'vscode';
+import config from '../config';
+import { countSubStr } from './utils';
+class GDScriptSignatureHelpProvider implements SignatureHelpProvider {
+  constructor() {}
+
+  /**
+   * Provide help for the signature at the given position and document.
+   *
+   * @param document The document in which the command was invoked.
+   * @param position The position at which the command was invoked.
+   * @param token A cancellation token.
+   * @return Signature help or a thenable that resolves to such. The lack of a result can be
+   * signaled by returning `undefined` or `null`.
+   */
+  provideSignatureHelp(document : TextDocument, position : Position, token : CancellationToken) : SignatureHelp | Thenable < SignatureHelp > {
+    const range = document.getWordRangeAtPosition(position);
+    let funcname = "";
+    let curparam = 0;
+    const checkPosition = () => {
+      const line = document.lineAt(position);
+      const startPos = line.firstNonWhitespaceCharacterIndex;
+      const endPos = position.character;
+      const queryStr = line.text.substring(startPos, endPos);
+      
+      var reg = /([A-z_]+[A-z0-9_]*)\(/g;
+      let match = reg.exec(queryStr);
+      while (match != null) {
+        funcname = match[1];
+        match = reg.exec(queryStr);
+      }
+      if(funcname != "") {
+        const funcrangestr = line.text.substring(line.text.indexOf(queryStr)+queryStr.indexOf(funcname)+funcname.length, endPos);
+        curparam = countSubStr(funcrangestr, ",");
+      }
+
+    };
+
+    checkPosition();
+
+    let help: SignatureHelp = {
+      signatures: [],
+      activeSignature: 0,
+      activeParameter: curparam
+    };
+
+    if (funcname.length > 0) {
+      // Builtin functions
+      for (let key of Object.keys(config.builtinSymbolInfoMap)) {
+        if (key.endsWith(`\.${funcname}`)) {
+          if (config.builtinSymbolInfoMap[key].completionItem.kind == CompletionItemKind.Method || config.builtinSymbolInfoMap[key].completionItem.kind == CompletionItemKind.Function) {
+            const rawDoc = config.builtinSymbolInfoMap[key].rowDoc;
+            const item = config.builtinSymbolInfoMap[key].completionItem;
+            let signatureInfor: SignatureInformation = new SignatureInformation(item.documentation.split('\n')[0], rawDoc.description);
+            for(let arg of rawDoc.arguments){
+              let param: ParameterInformation = new ParameterInformation(`${arg.type} ${arg.name}${arg.default_value.length>0?'='+arg.default_value:''}`, "");
+              signatureInfor.parameters.push(param);
+            }
+            help.signatures.push(signatureInfor);
+          }
+        }
+      }
+      // workspace functions
+    //   for (let path of Object.keys(config.getAllSymbols())) {
+    //       const script = config.getSymbols(path);
+    //       for(let f of Object.keys(script.signatures)) {
+    //         if(f == funcname) {
+    //           let signatureInfor: SignatureInformation = new SignatureInformation(`func ${f}${script.signatures[f]}`, `Method defined in ${path}`);
+    //           let param: ParameterInformation = new ParameterInformation(script.signatures[f], "");
+    //           signatureInfor.parameters.push(param);
+    //           help.signatures.push(signatureInfor);
+    //         }
+    //       }
+    //   }
+    
+    }
+    if(help.signatures.length>0)
+      return help;
+
+    return null
+}
+
+}
+
+export default GDScriptSignatureHelpProvider;

+ 16 - 4
src/gdscript/symbolparser.ts

@@ -8,7 +8,8 @@ interface GDScript {
   signals: {},
   classes: {},
   base: string,
-  native: string
+  native: string,
+  signatures: {}
 }
 
 class GDScriptSymbolParser {
@@ -23,7 +24,8 @@ class GDScriptSymbolParser {
         signals: {},
         classes: {},
         base: "Object",
-        native: "Object"
+        native: "Object",
+        signatures: {}
     }
     const text  = content;
     const lines = text.split(/\r?\n/);
@@ -66,8 +68,18 @@ class GDScriptSymbolParser {
     
     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] = determRange(key, funcs);
+    for (let key of Object.keys(funcs)) {
+      let r: Range = determRange(key, funcs);
+      script.functions[key] = r;
+      const line = lines[r.start.line];
+      if(line.indexOf("(")!= -1 && line.indexOf(")")!=-1) {
+        const signature = line.substring(line.indexOf("("), line.indexOf(")")+1);
+        if(signature && signature.length >0) {
+          script.signatures[key] = signature;
+          // console.log(key, signature);
+        }
+      }
+    }
     
     let signalnames = getMatches(text, /signal\s+([_A-Za-z]+[_A-Za-z0-9]*)\s*\(/g, 1);
     const signals = findLineRanges(signalnames, "signal\\s+$X$\\s*\\(");

+ 10 - 0
src/gdscript/utils.ts

@@ -28,4 +28,14 @@ export function getStrContent(rawstr: string):string {
         ss = ss.replace(/"|'|@"|"""/g,"")
     }
     return ss;
+}
+
+export function countSubStr(str:string, sub:string): number {
+    let count = 0;
+    let pos = str.indexOf(sub);
+    while (pos !== -1) {
+        count++;
+        pos = str.indexOf(sub, pos + sub.length);
+    }
+    return count;
 }

+ 3 - 1
src/tool_manager.ts

@@ -6,7 +6,7 @@ import GDScriptCompletionItemProvider from './gdscript/completion';
 import GDScriptDefinitionProivder from './gdscript/definitionprovider';
 import GDScriptHoverProvider from './gdscript/hoverprovider';
 import GDScriptDocumentContentProvider from './gdscript/docprovider';
-
+import GDScriptSignatureHelpProvider from './gdscript/signature_helper';
 var glob = require("glob")
 import config from './config';
 import * as path from 'path';
@@ -45,6 +45,8 @@ class ToolManager {
     vscode.languages.registerHoverProvider('gdscript', new GDScriptHoverProvider());
     // code completion provider
     vscode.languages.registerCompletionItemProvider('gdscript', new GDScriptCompletionItemProvider(), '.', '"', "'");
+    // signature help provider
+    vscode.languages.registerSignatureHelpProvider('gdscript', new GDScriptSignatureHelpProvider(), '(', ',');
     // Commands
     this._disposable = vscode.Disposable.from(
       vscode.commands.registerCommand('godot.updateWorkspaceSymbols', this.loadWorkspaceSymbols.bind(this)),