Add signature helper for builtin functions

Add signature infomations for workspace functions in hover message
This commit is contained in:
Geequlim
2017-01-10 23:43:23 +08:00
parent 75d4bee8aa
commit f890468f98
7 changed files with 132 additions and 9 deletions

View File

@@ -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")];

View File

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

View File

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

View File

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

View File

@@ -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*\\(");

View File

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

View File

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