Implement mouse hover provider

This commit is contained in:
Geequlim
2016-12-30 14:17:55 +00:00
parent 33840a5410
commit ffbf1ed783
5 changed files with 173 additions and 21 deletions

View File

@@ -17,11 +17,14 @@ class Config {
// scriptpath : scenepath
public scriptSceneMap: Object;
// scenepath : NodeInfo[]
private nodeInfoMap: Object;
public nodeInfoMap: Object;
// symbolname: CompletionItem
public builtinSymbolInfoMap: Object;
constructor() {
this.symbols = {};
this.bintinSybmolInfoList = [];
this.builtinSymbolInfoMap = {};
this.nodeInfoMap = {};
this.scriptSceneMap = {};
this.parser = new GDScriptSymbolParser();
@@ -78,11 +81,13 @@ class Config {
for (let key of Object.keys(this.classes)) {
const classdoc = this.classes[key];
const bintinSybmolInfoList = this.bintinSybmolInfoList;
const builtinSymbolInfoMap = this.builtinSymbolInfoMap;
// class
const item: CompletionItem = new CompletionItem(classdoc.name, CompletionItemKind.Class);
item.detail = 'Native Class';
item.documentation = classdoc.brief_description + " \n\n" +classdoc.description;
bintinSybmolInfoList.push(item);
builtinSymbolInfoMap[classdoc.name] = item;
// methods
const methods = classdoc.methods
const parsMethod = (m, kind: CompletionItemKind, insertAction=(name)=>name+"()")=>{
@@ -100,6 +105,7 @@ class Config {
mdoc += m.description;
mi.documentation = mdoc;
bintinSybmolInfoList.push(mi);
builtinSymbolInfoMap[`${classdoc.name}.${m.name}`] = mi;
};
methods.map(m=>parsMethod(m, CompletionItemKind.Method));
// signals
@@ -112,6 +118,7 @@ class Config {
ci.detail = c.value;
ci.documentation = `${classdoc.name}.${c.name} = ${c.value}`;
bintinSybmolInfoList.push(ci);
builtinSymbolInfoMap[`${classdoc.name}.${c.name}`] = ci;
});
// properties
const properties = classdoc.properties;
@@ -120,6 +127,7 @@ class Config {
pi.detail = `${p.type} of ${classdoc.name}`;
pi.documentation = p.description;
bintinSybmolInfoList.push(pi);
builtinSymbolInfoMap[`${classdoc.name}.${p.name}`] = pi;
};
properties.map(p=>parseProp(p));
// theme_properties

View File

@@ -12,6 +12,7 @@ import {
import * as path from 'path';
import * as fs from 'fs';
import config from '../config';
import {isStr, getSelectedContent, getStrContent} from './utils';
class GDScriptDefinitionProivder implements DefinitionProvider {
constructor() {
@@ -19,22 +20,6 @@ class GDScriptDefinitionProivder implements DefinitionProvider {
}
provideDefinition(document: TextDocument, position: Position, token: CancellationToken): Definition | Thenable < Definition > {
const isStr = (content:string) => (content.startsWith("'") || content.startsWith('"') || content.startsWith('@"') ) && (content.endsWith("'") || content.endsWith('"'));
const getSelectedContent = ():string =>{
const line = document.lineAt(position);
const wordRange = document.getWordRangeAtPosition(position) ;
const machs = line.text.match(/[A-z_]+[A-z_0-9]*|".*"|'.*'|@".*"/g)
let res = line.text.substring(wordRange.start.character, wordRange.end.character);
machs.map(m=>{
if(m) {
if(isStr(m)){
res = m;
return;
}
}
});
return res;
};
const getDefinitions = (content: string):Location[]| Location => {
if(content.startsWith("res://")) {
content = content.replace("res://", "");
@@ -57,6 +42,7 @@ class GDScriptDefinitionProivder implements DefinitionProvider {
for (let name of Object.keys(items)) {
if(name == content) {
_items.push(new Location(Uri.file(path), items[name]));
break;
}
}
return _items;
@@ -72,13 +58,12 @@ class GDScriptDefinitionProivder implements DefinitionProvider {
return locations;
}
};
let selStr = getSelectedContent();
let selStr = getSelectedContent(document, position);
if(selStr) {
// For strings
if(isStr(selStr)) {
selStr = selStr.replace(/"|'|@"/g,"");
selStr = getStrContent(selStr);
let fpath = path.join(path.dirname(document.uri.fsPath), selStr)
console.log(fpath);
if(fs.existsSync(fpath) && fs.statSync(fpath).isFile())

View File

@@ -0,0 +1,124 @@
import {
HoverProvider,
TextDocument,
Position,
CancellationToken,
Hover,
MarkedString,
workspace,
Uri,
CompletionItem,
CompletionItemKind
} from 'vscode';
import {
isStr,
getSelectedContent,
getStrContent
} from './utils';
import config from '../config';
import * as path from 'path';
class GDScriptHoverProvider implements HoverProvider {
constructor() {}
provideHover(document: TextDocument, position: Position, token: CancellationToken): Hover | Thenable < Hover > {
let hoverText = getSelectedContent(document, position);
if (isStr(hoverText))
hoverText = getStrContent(hoverText);
const workspaceSymbols = config.getAllSymbols();
let tips: MarkedString[] = [];
// check from workspace
for (let path of Object.keys(workspaceSymbols)) {
const script = workspaceSymbols[path];
let scriptips: MarkedString[] = [];
const getHoverText = (items, type, path): MarkedString[] => {
const _items: MarkedString[] = [];
for (let name of Object.keys(items)) {
if (name == hoverText) {
let dfile = path;
if (workspace && workspace.asRelativePath(dfile))
dfile = workspace.asRelativePath(dfile);
_items.push({language:'gdscript', value:`${type} ${name}`});
_items.push(`Defined in *[${dfile}](${Uri.file(path).toString()})*`)
break;
}
}
return _items;
}
scriptips = [...scriptips, ...getHoverText(script.variables, 'var', path)];
scriptips = [...scriptips, ...getHoverText(script.constants, 'const', path)];
scriptips = [...scriptips, ...getHoverText(script.functions, 'func', path)];
scriptips = [...scriptips, ...getHoverText(script.signals, 'signal', path)];
scriptips = [...scriptips, ...getHoverText(script.classes, 'class', path)];
tips = [...tips, ...scriptips];
}
// check from scnes
for (let scnenepath of Object.keys(config.nodeInfoMap)) {
const nodes: any[] = config.nodeInfoMap[scnenepath];
for (let index = 0; index < nodes.length; index++) {
const node:any = nodes[index];
const fullpath = node.parent + "/" + node.name;
if(fullpath == hoverText || fullpath.endsWith(hoverText)) {
let filepath = scnenepath;
if(workspace && workspace.rootPath)
filepath = path.join(workspace.rootPath, filepath);
let instance = "";
if(node.instance && node.instance.length > 1) {
let instancepath = node.instance;
if(workspace && workspace.rootPath)
instancepath = path.join(workspace.rootPath, instancepath);
instance = ` which is an instance of *[${node.instance}](${Uri.file(instancepath).toString()})*`;
}
tips = [...tips,
{language: 'gdscript', value: `${node.type} ${fullpath}`},
`${node.type} defined in *[${scnenepath}](${Uri.file(filepath).toString()})*${instance}`
];
break;
}
}
}
// check from builtin
const item2MarkdStrings = (name: string,item: CompletionItem):MarkedString[] => {
let value = "";
let doc = item.documentation;
switch (item.kind) {
case CompletionItemKind.Class:
value += name;
break;
case CompletionItemKind.Method:
value += item.documentation.substring(0, item.documentation.indexOf("\n"));
doc = item.documentation.substring(item.documentation.indexOf("\n")+1, item.documentation.length);
break;
case CompletionItemKind.Interface:
value += "signal " + item.documentation.substring(0, item.documentation.indexOf("\n"));
doc = item.documentation.substring(item.documentation.indexOf("\n")+1, item.documentation.length);
break;
case CompletionItemKind.Variable:
case CompletionItemKind.Property:
value += "var " + name;
break;
case CompletionItemKind.Enum:
value += "const " + name;
break;
default:
break;
}
return [{language: 'gdscript', value}, doc];
};
for (let name of Object.keys(config.builtinSymbolInfoMap)) {
const pattern = `[A-z@_]+[A-z0-9_]*\\.${hoverText}\\b`;
if(name == hoverText || name.match(new RegExp(pattern))) {
const item: CompletionItem = config.builtinSymbolInfoMap[name];
tips = [...tips, ...(item2MarkdStrings(name, item))];
}
}
if (tips.length > 0)
return new Hover(tips);
else
return null;
}
}
export default GDScriptHoverProvider;

31
src/gdscript/utils.ts Normal file
View File

@@ -0,0 +1,31 @@
import {TextDocument, Position} from 'vscode';
export function isStr(content:string) {
return (content.startsWith("'") || content.startsWith('"') || content.startsWith('@"') ) && (content.endsWith("'") || content.endsWith('"'));
}
export function getSelectedContent(document: TextDocument, position: Position):string {
const line = document.lineAt(position);
const wordRange = document.getWordRangeAtPosition(position) ;
const machs = line.text.match(/[A-z_]+[A-z_0-9]*|".*?"|'.*?'|@".*?"/g)
let res = line.text.substring(wordRange.start.character, wordRange.end.character);
machs.map(m=>{
if(m) {
const startPos = line.text.indexOf(m);
const endPos = startPos + m.length;
if(isStr(m) && startPos != -1 && wordRange.start.character >= startPos && wordRange.end.character <= endPos){
res = m;
return;
}
}
});
return res;
};
export function getStrContent(rawstr: string):string {
let ss = rawstr;
if(isStr(ss)) {
ss = ss.replace(/"|'|@"|"""/g,"")
}
return ss;
}

View File

@@ -4,6 +4,8 @@ import GDScriptSymbolProvider from './gdscript/symbolprovider';
import GDScriptWorkspaceSymbolProvider from './gdscript/workspace_symbol_provider';
import GDScriptCompletionItemProvider from './gdscript/completion';
import GDScriptDefinitionProivder from './gdscript/definitionprovider';
import GDScriptHoverProvider from './gdscript/hoverprovider';
var glob = require("glob")
import config from './config';
import * as path from 'path';
@@ -37,6 +39,8 @@ class ToolManager {
vscode.languages.registerWorkspaceSymbolProvider(this.workspacesymbolprovider);
// definition provider
vscode.languages.registerDefinitionProvider('gdscript', new GDScriptDefinitionProivder());
// hover provider
vscode.languages.registerHoverProvider('gdscript', new GDScriptHoverProvider());
// code completion provider
vscode.languages.registerCompletionItemProvider('gdscript', new GDScriptCompletionItemProvider(), '.', '"', "'");
// Commands