From af57119adbe4aa52a74bca9416d4721c61694265 Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Sat, 20 Feb 2016 04:47:35 +0200 Subject: [PATCH 01/11] local function completion --- src/completion_provider.ts | 75 ++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/src/completion_provider.ts b/src/completion_provider.ts index a20c72e..c0a0e7a 100644 --- a/src/completion_provider.ts +++ b/src/completion_provider.ts @@ -4,7 +4,8 @@ import {CompletionItemProvider, TextDocument, Position, CancellationToken, CompletionItem, CompletionItemKind} from 'vscode'; -let fs = require('fs'); +import fs = require('fs'); +import whatels = require('whatels'); const RE_MODULE = /(\w+):$/; @@ -17,6 +18,7 @@ export class ErlangCompletionProvider implements CompletionItemProvider { private modules:any = null; private moduleNames: string[] = null; private genericCompletionItems: CompletionItem[] = null; + private wConn: whatels.Connection = null; constructor(private completionPath: string) {} @@ -27,18 +29,47 @@ export class ErlangCompletionProvider implements CompletionItemProvider { return new Promise((resolve, reject) => { const line = doc.lineAt(pos.line); const m = RE_MODULE.exec(line.text.substring(0, pos.character)); - if (this.modules === null) { - this.readCompletionJson(this.completionPath, modules => { - this.modules = modules; - (m === null)? - this.resolveModuleNames(resolve) - : this.resolveFunNames(m[1], resolve); - }); + if (m === null) { + if (this.wConn === null) { + this.wConn = new whatels.Connection(); + this.wConn.connect((error: any) => { + if (error) { + this.wConn = null; + console.error(error); + reject(); + } + else { + console.log('Connnected to whatels service.'); + this.wConn.getSymbols(doc.fileName, (err, symbols) => { + if (err) { + console.error(err); + reject(); + } + else { + console.log('symbols: ', symbols); + this.resolveGenericItems(resolve, symbols.functions) + } + }); + } + }); + } + else { + this.wConn.getSymbols(doc.fileName, (err, symbols) => { + console.log('symbols: ', symbols); + this.resolveGenericItems(resolve, symbols.functions) + }); + } } else { - (m === null)? - this.resolveModuleNames(resolve) - : this.resolveFunNames(m[1], resolve); + if (this.modules === null) { + this.readCompletionJson(this.completionPath, modules => { + this.modules = modules; + this.resolveFunNames(m[1], resolve); + }); + } + else { + this.resolveFunNames(m[1], resolve); + } } }); } @@ -47,11 +78,12 @@ export class ErlangCompletionProvider implements CompletionItemProvider { resolve(this.makeModuleFunsCompletion(module)); } - private resolveModuleNames(resolve) { - if (!this.genericCompletionItems) { - this.genericCompletionItems = this.makeGenericCompletion(); - } - resolve(this.genericCompletionItems); + private resolveGenericItems(resolve, funs) { + resolve(this.makeGenericCompletion(funs)); + // if (!this.genericCompletionItems) { + // this.genericCompletionItems = this.makeGenericCompletion([]); + // } + // resolve(this.genericCompletionItems); } private makeFunctionCompletionItem(name: string): CompletionItem { @@ -74,16 +106,21 @@ export class ErlangCompletionProvider implements CompletionItemProvider { }); } - private makeGenericCompletion(): CompletionItem[] { + private makeGenericCompletion(funs: whatels.FunctionInfo[]): CompletionItem[] { + let comps: CompletionItem[] = funs.map(f => { + return this.makeFunctionCompletionItem(f.name); + }); const modules = this.modules || {}; const names = []; for (let k in modules) { names.push(k); } names.sort(); - return names.map(name => { - return this.makeModuleNameCompletionItem(name); + names.forEach(name => { + comps.push(this.makeModuleNameCompletionItem(name)); }); + + return comps; } private readCompletionJson(filename: string, done: Function): any { From c9274b7d51497bb514f429aa3362a53bb7abf627 Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Sun, 21 Feb 2016 06:59:16 +0200 Subject: [PATCH 02/11] added symbol provider --- package.json | 3 +++ src/completion_provider.ts | 2 +- src/extension.ts | 7 +++++- src/symbol_provider.ts | 50 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 src/symbol_provider.ts diff --git a/package.json b/package.json index b75be4c..726c9a5 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,9 @@ "typescript": "^1.7.5", "vscode": "^0.11.0" }, + "dependencies": { + "whatels": "^0.1.0" + }, "repository": { "type": "git", "url": "https://github.com/yuce/erlang-vscode" diff --git a/src/completion_provider.ts b/src/completion_provider.ts index c0a0e7a..5e03545 100644 --- a/src/completion_provider.ts +++ b/src/completion_provider.ts @@ -54,7 +54,7 @@ export class ErlangCompletionProvider implements CompletionItemProvider { }); } else { - this.wConn.getSymbols(doc.fileName, (err, symbols) => { + this.wConn.getSymbols(doc.getText(), (err, symbols) => { console.log('symbols: ', symbols); this.resolveGenericItems(resolve, symbols.functions) }); diff --git a/src/extension.ts b/src/extension.ts index 4f9c215..b63eb56 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -32,7 +32,8 @@ import {ExtensionContext, Disposable, workspace, window, languages, Hover} from 'vscode'; import {ErlangCompletionProvider} from './completion_provider'; -// import {range, debounce} from 'lodash'; +import {ErlangSymbolProvider} from './symbol_provider'; + export function activate(ctx: ExtensionContext) { languages.setLanguageConfiguration('erlang', { @@ -68,6 +69,10 @@ export function activate(ctx: ExtensionContext) { ctx.subscriptions.push(languages.registerCompletionItemProvider({ language: 'erlang' }, new ErlangCompletionProvider(completionJsonPath), ':')); + console.log('registering symbol provider'); + ctx.subscriptions.push(languages.registerDocumentSymbolProvider({ + language: 'erlang' + }, new ErlangSymbolProvider())); } } diff --git a/src/symbol_provider.ts b/src/symbol_provider.ts new file mode 100644 index 0000000..1b94c04 --- /dev/null +++ b/src/symbol_provider.ts @@ -0,0 +1,50 @@ +import {DocumentSymbolProvider, TextDocument, CancellationToken, + SymbolInformation, SymbolKind, Range} from 'vscode'; +import whatels = require('whatels'); + +export class ErlangSymbolProvider implements DocumentSymbolProvider { + private wConn: whatels.Connection = null; + + public provideDocumentSymbols(doc: TextDocument, token: CancellationToken): Thenable { + return new Promise((resolve, reject) => { + console.log('get symbol informations'); + if (this.wConn === null) { + this.wConn = new whatels.Connection(); + this.wConn.connect((error: any) => { + if (error) { + this.wConn = null; + console.error(error); + reject(); + } + else { + console.log('Connnected to whatels service.'); + this.wConn.getSymbols(doc.fileName, (err, symbols) => { + if (err) { + console.error(err); + reject(); + } + else { + console.log('symbols: ', symbols); + this.resolveGenericItems(resolve, symbols) + } + }); + } + }); + } + else { + this.wConn.getSymbols(doc.getText(), (err, symbols) => { + console.log('symbols: ', symbols); + this.resolveGenericItems(resolve, symbols) + }); + } + }); + } + + private resolveGenericItems(resolve, symbols: whatels.Symbols) { + let vsSymbols: SymbolInformation[] = symbols.functions.map(f => { + let range = new Range(f.line - 1, 0, f.line - 1, 0); + return new SymbolInformation(f.name, SymbolKind.Function, range); + }); + resolve(vsSymbols); + } +} \ No newline at end of file From cf1764d92acfba071dddc8c8f8779690a367ba70 Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Sun, 21 Feb 2016 18:25:56 +0200 Subject: [PATCH 03/11] introduced WhatelsClient to simplify Whatels interaction --- src/completion_provider.ts | 45 ++++++------------------ src/extension.ts | 9 +++-- src/symbol_provider.ts | 43 ++++++----------------- src/whatels_client.ts | 70 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 71 deletions(-) create mode 100644 src/whatels_client.ts diff --git a/src/completion_provider.ts b/src/completion_provider.ts index 5e03545..b482c38 100644 --- a/src/completion_provider.ts +++ b/src/completion_provider.ts @@ -3,9 +3,9 @@ import {CompletionItemProvider, TextDocument, Position, CancellationToken, CompletionItem, CompletionItemKind} from 'vscode'; - import fs = require('fs'); -import whatels = require('whatels'); +import {Symbols, FunctionInfo} from 'whatels'; +import {WhatelsClient} from './whatels_client'; const RE_MODULE = /(\w+):$/; @@ -18,9 +18,9 @@ export class ErlangCompletionProvider implements CompletionItemProvider { private modules:any = null; private moduleNames: string[] = null; private genericCompletionItems: CompletionItem[] = null; - private wConn: whatels.Connection = null; - constructor(private completionPath: string) {} + constructor(private whatelsClient: WhatelsClient, + private completionPath: string) {} public provideCompletionItems(doc: TextDocument, pos: Position, @@ -30,35 +30,10 @@ export class ErlangCompletionProvider implements CompletionItemProvider { const line = doc.lineAt(pos.line); const m = RE_MODULE.exec(line.text.substring(0, pos.character)); if (m === null) { - if (this.wConn === null) { - this.wConn = new whatels.Connection(); - this.wConn.connect((error: any) => { - if (error) { - this.wConn = null; - console.error(error); - reject(); - } - else { - console.log('Connnected to whatels service.'); - this.wConn.getSymbols(doc.fileName, (err, symbols) => { - if (err) { - console.error(err); - reject(); - } - else { - console.log('symbols: ', symbols); - this.resolveGenericItems(resolve, symbols.functions) - } - }); - } - }); - } - else { - this.wConn.getSymbols(doc.getText(), (err, symbols) => { - console.log('symbols: ', symbols); - this.resolveGenericItems(resolve, symbols.functions) - }); - } + this.whatelsClient.getSymbols(doc.fileName, doc.getText()).then( + symbols => this.resolveGenericItems(resolve, symbols.functions), + err => reject(err) + ); } else { if (this.modules === null) { @@ -106,7 +81,7 @@ export class ErlangCompletionProvider implements CompletionItemProvider { }); } - private makeGenericCompletion(funs: whatels.FunctionInfo[]): CompletionItem[] { + private makeGenericCompletion(funs: FunctionInfo[]): CompletionItem[] { let comps: CompletionItem[] = funs.map(f => { return this.makeFunctionCompletionItem(f.name); }); @@ -124,7 +99,7 @@ export class ErlangCompletionProvider implements CompletionItemProvider { } private readCompletionJson(filename: string, done: Function): any { - fs.readFile(filename, (err, data) => { + fs.readFile(filename, 'utf8', (err, data) => { if (err) { console.log(`Cannot read: ${filename}`); done({}); diff --git a/src/extension.ts b/src/extension.ts index b63eb56..a9a16c2 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -33,6 +33,7 @@ import {ExtensionContext, Disposable, workspace, window, languages, Hover} from 'vscode'; import {ErlangCompletionProvider} from './completion_provider'; import {ErlangSymbolProvider} from './symbol_provider'; +import {WhatelsClient} from './whatels_client'; export function activate(ctx: ExtensionContext) { @@ -65,14 +66,16 @@ export function activate(ctx: ExtensionContext) { // enable auto completion let config = workspace.getConfiguration('erlang'); if (config['enableExperimentalAutoComplete']) { + let whatelsClient = new WhatelsClient(); + ctx.subscriptions.push(whatelsClient); let completionJsonPath = ctx.asAbsolutePath("./priv/erlang-libs.json"); ctx.subscriptions.push(languages.registerCompletionItemProvider({ language: 'erlang' - }, new ErlangCompletionProvider(completionJsonPath), ':')); - console.log('registering symbol provider'); + }, new ErlangCompletionProvider(whatelsClient, completionJsonPath), ':')); ctx.subscriptions.push(languages.registerDocumentSymbolProvider({ language: 'erlang' - }, new ErlangSymbolProvider())); + }, new ErlangSymbolProvider(whatelsClient))); + } } diff --git a/src/symbol_provider.ts b/src/symbol_provider.ts index 1b94c04..6e1e0c8 100644 --- a/src/symbol_provider.ts +++ b/src/symbol_provider.ts @@ -1,49 +1,26 @@ import {DocumentSymbolProvider, TextDocument, CancellationToken, SymbolInformation, SymbolKind, Range} from 'vscode'; -import whatels = require('whatels'); +import {Symbols} from 'whatels'; +import {WhatelsClient} from './whatels_client'; + export class ErlangSymbolProvider implements DocumentSymbolProvider { - private wConn: whatels.Connection = null; + constructor(private whatelsClient: WhatelsClient) {} public provideDocumentSymbols(doc: TextDocument, token: CancellationToken): Thenable { return new Promise((resolve, reject) => { console.log('get symbol informations'); - if (this.wConn === null) { - this.wConn = new whatels.Connection(); - this.wConn.connect((error: any) => { - if (error) { - this.wConn = null; - console.error(error); - reject(); - } - else { - console.log('Connnected to whatels service.'); - this.wConn.getSymbols(doc.fileName, (err, symbols) => { - if (err) { - console.error(err); - reject(); - } - else { - console.log('symbols: ', symbols); - this.resolveGenericItems(resolve, symbols) - } - }); - } - }); - } - else { - this.wConn.getSymbols(doc.getText(), (err, symbols) => { - console.log('symbols: ', symbols); - this.resolveGenericItems(resolve, symbols) - }); - } + this.whatelsClient.getSymbols(doc.fileName, doc.getText()).then( + symbols => this.resolveGenericItems(resolve, symbols), + err => reject(err) + ) }); } - private resolveGenericItems(resolve, symbols: whatels.Symbols) { + private resolveGenericItems(resolve, symbols: Symbols) { let vsSymbols: SymbolInformation[] = symbols.functions.map(f => { let range = new Range(f.line - 1, 0, f.line - 1, 0); - return new SymbolInformation(f.name, SymbolKind.Function, range); + return new SymbolInformation(`${f.name}/${f.arity}`, SymbolKind.Function, range); }); resolve(vsSymbols); } diff --git a/src/whatels_client.ts b/src/whatels_client.ts new file mode 100644 index 0000000..bc7d592 --- /dev/null +++ b/src/whatels_client.ts @@ -0,0 +1,70 @@ + +import {Disposable} from 'vscode'; +import whatels = require('whatels'); + +interface SymbolTimeInfo { + symbols: whatels.Symbols; + updated: number; +} + +export class WhatelsClient implements Disposable { + private pathSymbols = {}; + private wConn: whatels.Connection; + private refreshTime: number; + + constructor(refreshTime?:number) { + this.refreshTime = refreshTime || 1000; + } + + public getSymbols(path: string, text: string): Thenable { + return new Promise((resolve, reject) => { + const now = (new Date()).getTime(); + const sti: SymbolTimeInfo = this.pathSymbols[path]; + if (sti && (now - sti.updated) < this.refreshTime) { + resolve(sti.symbols); + } + else { + this._getSymbols(path, text).then(symbols => resolve(symbols), + err => reject(err)); + } + }); + } + + public dispose() { + this.wConn.close(); + this.wConn = null; + this.pathSymbols = null; + } + + private _connect(): Thenable { + return new Promise((resolve, reject) => { + if (this.wConn) { + resolve(this.wConn); + } + else { + this.wConn = new whatels.Connection(); + this.wConn.connect(err => { + err? reject(err) : resolve(this.wConn); + }); + } + }) + } + + private _getSymbols(path: string, text: string): Thenable { + return new Promise((resolve, reject) => { + this._connect().then(conn => { + conn.getSymbols(text, (err, symbols) => { + if (err) { + reject(err); + } + else { + const now = (new Date()).getTime(); + this.pathSymbols[path] = {updated: now, symbols: symbols}; + resolve(symbols); + } + }); + }, + err => reject(err)); + }); + } +} \ No newline at end of file From b7a01228aff4616610c0fbe4ec43a40201ec43a2 Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Sun, 21 Feb 2016 20:27:20 +0200 Subject: [PATCH 04/11] updated whatels interface --- src/symbol_provider.ts | 1 + src/whatels_client.ts | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/symbol_provider.ts b/src/symbol_provider.ts index 6e1e0c8..7e99500 100644 --- a/src/symbol_provider.ts +++ b/src/symbol_provider.ts @@ -18,6 +18,7 @@ export class ErlangSymbolProvider implements DocumentSymbolProvider { } private resolveGenericItems(resolve, symbols: Symbols) { + // TODO: sort symbols by name let vsSymbols: SymbolInformation[] = symbols.functions.map(f => { let range = new Range(f.line - 1, 0, f.line - 1, 0); return new SymbolInformation(`${f.name}/${f.arity}`, SymbolKind.Function, range); diff --git a/src/whatels_client.ts b/src/whatels_client.ts index bc7d592..bfc27be 100644 --- a/src/whatels_client.ts +++ b/src/whatels_client.ts @@ -43,26 +43,25 @@ export class WhatelsClient implements Disposable { } else { this.wConn = new whatels.Connection(); - this.wConn.connect(err => { - err? reject(err) : resolve(this.wConn); - }); + this.wConn.connect().then( + () => resolve(this.wConn), + err => reject(err) + ); } - }) + }); } private _getSymbols(path: string, text: string): Thenable { return new Promise((resolve, reject) => { this._connect().then(conn => { - conn.getSymbols(text, (err, symbols) => { - if (err) { - reject(err); - } - else { + conn.getSymbols(text).then( + symbols => { const now = (new Date()).getTime(); this.pathSymbols[path] = {updated: now, symbols: symbols}; resolve(symbols); - } - }); + }, + err => reject(err) + ); }, err => reject(err)); }); From 3a09232917849af537216ca7a8a79f4e21e74ba4 Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Mon, 22 Feb 2016 23:42:46 +0200 Subject: [PATCH 05/11] updated protocol --- package.json | 5 +++-- src/symbol_provider.ts | 5 ++++- src/whatels_client.ts | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 726c9a5..dbeb27b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "erlang-otp", "displayName": "Erlang/OTP", "description": "Erlang/OTP support with syntax highlighting, auto-indent and snippets", - "version": "0.2.1", + "version": "0.2.2", "author": { "name": "Yuce Tekol" }, @@ -60,7 +60,8 @@ "vscode": "^0.11.0" }, "dependencies": { - "whatels": "^0.1.0" + "whatels": "^0.1.2", + "tmp": "^0.0.28" }, "repository": { "type": "git", diff --git a/src/symbol_provider.ts b/src/symbol_provider.ts index 7e99500..a9f7d12 100644 --- a/src/symbol_provider.ts +++ b/src/symbol_provider.ts @@ -21,7 +21,10 @@ export class ErlangSymbolProvider implements DocumentSymbolProvider { // TODO: sort symbols by name let vsSymbols: SymbolInformation[] = symbols.functions.map(f => { let range = new Range(f.line - 1, 0, f.line - 1, 0); - return new SymbolInformation(`${f.name}/${f.arity}`, SymbolKind.Function, range); + let name = symbols.module? + `${symbols.module}:${f.name}/${f.arity}` + : `${f.name}/${f.arity}`; + return new SymbolInformation(name, SymbolKind.Function, range); }); resolve(vsSymbols); } diff --git a/src/whatels_client.ts b/src/whatels_client.ts index bfc27be..3203723 100644 --- a/src/whatels_client.ts +++ b/src/whatels_client.ts @@ -56,6 +56,7 @@ export class WhatelsClient implements Disposable { this._connect().then(conn => { conn.getSymbols(text).then( symbols => { + console.log('SYMBOLS!!! ', symbols); const now = (new Date()).getTime(); this.pathSymbols[path] = {updated: now, symbols: symbols}; resolve(symbols); From 5d7e9a2ad4d2f1e533d6984aa032a7ef11554d8b Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Fri, 26 Feb 2016 04:17:20 +0200 Subject: [PATCH 06/11] adapted new whatels protocol; project-wide module/function completion; disabled std module/function completion --- package.json | 3 +- src/completion_provider.ts | 109 ++++++++++++++++++++++++------------- src/extension.ts | 11 +++- src/symbol_provider.ts | 7 ++- src/whatels_client.ts | 66 ++++++++++------------ 5 files changed, 114 insertions(+), 82 deletions(-) diff --git a/package.json b/package.json index dbeb27b..2650728 100644 --- a/package.json +++ b/package.json @@ -60,8 +60,7 @@ "vscode": "^0.11.0" }, "dependencies": { - "whatels": "^0.1.2", - "tmp": "^0.0.28" + "whatels": "^0.2.1" }, "repository": { "type": "git", diff --git a/src/completion_provider.ts b/src/completion_provider.ts index b482c38..e053dc0 100644 --- a/src/completion_provider.ts +++ b/src/completion_provider.ts @@ -3,19 +3,15 @@ import {CompletionItemProvider, TextDocument, Position, CancellationToken, CompletionItem, CompletionItemKind} from 'vscode'; -import fs = require('fs'); import {Symbols, FunctionInfo} from 'whatels'; import {WhatelsClient} from './whatels_client'; +import fs = require('fs'); +import path = require('path'); const RE_MODULE = /(\w+):$/; -interface FunctionCompletionData { - name: string; - // detail: string; -} - export class ErlangCompletionProvider implements CompletionItemProvider { - private modules:any = null; + private stdModules:any = null; private moduleNames: string[] = null; private genericCompletionItems: CompletionItem[] = null; @@ -30,21 +26,30 @@ export class ErlangCompletionProvider implements CompletionItemProvider { const line = doc.lineAt(pos.line); const m = RE_MODULE.exec(line.text.substring(0, pos.character)); if (m === null) { - this.whatelsClient.getSymbols(doc.fileName, doc.getText()).then( - symbols => this.resolveGenericItems(resolve, symbols.functions), + this.whatelsClient.getAllPathSymbols().then( + pathSymbols => { + pathSymbols? + this.resolveGenericItems(resolve, pathSymbols) + : reject(); + }, err => reject(err) ); } else { - if (this.modules === null) { - this.readCompletionJson(this.completionPath, modules => { - this.modules = modules; - this.resolveFunNames(m[1], resolve); - }); - } - else { - this.resolveFunNames(m[1], resolve); - } + const moduleName = m[1]; + this.whatelsClient.getAllPathSymbols().then( + pathSymbols => this.resolveModuleItems(resolve, moduleName, pathSymbols), + err => reject(err) + ); + // if (this.stdModules === null) { + // this.readCompletionJson(this.completionPath, modules => { + // this.stdModules = modules; + // this.resolveFunNames(m[1], resolve); + // }); + // } + // else { + // this.resolveFunNames(m[1], resolve); + // } } }); } @@ -53,12 +58,12 @@ export class ErlangCompletionProvider implements CompletionItemProvider { resolve(this.makeModuleFunsCompletion(module)); } - private resolveGenericItems(resolve, funs) { - resolve(this.makeGenericCompletion(funs)); - // if (!this.genericCompletionItems) { - // this.genericCompletionItems = this.makeGenericCompletion([]); - // } - // resolve(this.genericCompletionItems); + private resolveGenericItems(resolve, pathSymbols) { + resolve(this.makeGenericCompletion(pathSymbols)); + } + + private resolveModuleItems(resolve, moduleName, pathSymbols) { + resolve(this.makeModuleCompletion(moduleName, pathSymbols)); } private makeFunctionCompletionItem(name: string): CompletionItem { @@ -75,29 +80,59 @@ export class ErlangCompletionProvider implements CompletionItemProvider { } private makeModuleFunsCompletion(module: string): CompletionItem[] { - const moduleFuns = this.modules[module] || []; + const moduleFuns = this.stdModules[module] || []; return moduleFuns.map(name => { return this.makeFunctionCompletionItem(name); }); } - private makeGenericCompletion(funs: FunctionInfo[]): CompletionItem[] { - let comps: CompletionItem[] = funs.map(f => { - return this.makeFunctionCompletionItem(f.name); - }); - const modules = this.modules || {}; - const names = []; - for (let k in modules) { - names.push(k); + private makeGenericCompletion(pathSymbols: {[index: string]: Symbols}) { + let comps: CompletionItem[] = []; + for (var p in pathSymbols) { + var item = new CompletionItem(path.basename(p, '.erl')); + item.kind = CompletionItemKind.Module; + comps.push(item); + } + return comps; + } + + private makeModuleCompletion(moduleName: string, + pathSymbols: {[index: string]: Symbols}) + { + let comps: CompletionItem[] = []; + for (var p in pathSymbols) { + if (pathSymbols[p].module == moduleName) { + var module = pathSymbols[p]; + if (module.functions) { + module.functions.forEach(f => { + var item = new CompletionItem(f.name); + item.kind = CompletionItemKind.Function; + comps.push(item); + }); + } + } } - names.sort(); - names.forEach(name => { - comps.push(this.makeModuleNameCompletionItem(name)); - }); return comps; } + // private makeGenericCompletion(funs: FunctionInfo[]): CompletionItem[] { + // let comps: CompletionItem[] = funs.map(f => { + // return this.makeFunctionCompletionItem(f.name); + // }); + // const modules = this.modules || {}; + // const names = []; + // for (let k in modules) { + // names.push(k); + // } + // names.sort(); + // names.forEach(name => { + // comps.push(this.makeModuleNameCompletionItem(name)); + // }); + + // return comps; + // } + private readCompletionJson(filename: string, done: Function): any { fs.readFile(filename, 'utf8', (err, data) => { if (err) { diff --git a/src/extension.ts b/src/extension.ts index a9a16c2..4558d56 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -66,19 +66,24 @@ export function activate(ctx: ExtensionContext) { // enable auto completion let config = workspace.getConfiguration('erlang'); if (config['enableExperimentalAutoComplete']) { - let whatelsClient = new WhatelsClient(); + const whatelsClient = createWhatelsClient(workspace.rootPath); ctx.subscriptions.push(whatelsClient); - let completionJsonPath = ctx.asAbsolutePath("./priv/erlang-libs.json"); + const completionJsonPath = ctx.asAbsolutePath("./priv/erlang-libs.json"); ctx.subscriptions.push(languages.registerCompletionItemProvider({ language: 'erlang' }, new ErlangCompletionProvider(whatelsClient, completionJsonPath), ':')); ctx.subscriptions.push(languages.registerDocumentSymbolProvider({ language: 'erlang' }, new ErlangSymbolProvider(whatelsClient))); - } } export function deactivate() { } +function createWhatelsClient(rootPath: string) { + const wc = new WhatelsClient(); + wc.watch(`${rootPath}/src/*.erl`); + wc.watch(`${rootPath}/apps/**/src/*.erl`); + return wc; +} diff --git a/src/symbol_provider.ts b/src/symbol_provider.ts index a9f7d12..cfe26d9 100644 --- a/src/symbol_provider.ts +++ b/src/symbol_provider.ts @@ -10,8 +10,11 @@ export class ErlangSymbolProvider implements DocumentSymbolProvider { public provideDocumentSymbols(doc: TextDocument, token: CancellationToken): Thenable { return new Promise((resolve, reject) => { console.log('get symbol informations'); - this.whatelsClient.getSymbols(doc.fileName, doc.getText()).then( - symbols => this.resolveGenericItems(resolve, symbols), + this.whatelsClient.getPathSymbols(doc.fileName).then( + symbols => { + console.log(`path: ${doc.fileName}, symbols: ${symbols}`); + symbols? this.resolveGenericItems(resolve, symbols) : reject() + }, err => reject(err) ) }); diff --git a/src/whatels_client.ts b/src/whatels_client.ts index 3203723..8196cc8 100644 --- a/src/whatels_client.ts +++ b/src/whatels_client.ts @@ -2,38 +2,45 @@ import {Disposable} from 'vscode'; import whatels = require('whatels'); -interface SymbolTimeInfo { - symbols: whatels.Symbols; - updated: number; -} - export class WhatelsClient implements Disposable { - private pathSymbols = {}; private wConn: whatels.Connection; - private refreshTime: number; + private port: number; - constructor(refreshTime?:number) { - this.refreshTime = refreshTime || 1000; + constructor(refreshTime?: number, port?: number) { + this.port = port || 10998; } - public getSymbols(path: string, text: string): Thenable { + public getPathSymbols(path: string): Thenable { return new Promise((resolve, reject) => { - const now = (new Date()).getTime(); - const sti: SymbolTimeInfo = this.pathSymbols[path]; - if (sti && (now - sti.updated) < this.refreshTime) { - resolve(sti.symbols); - } - else { - this._getSymbols(path, text).then(symbols => resolve(symbols), - err => reject(err)); - } + this._connect().then( + conn => resolve(conn.getPathSymbols(path)), + err => reject(err) + ) }); } + public getAllPathSymbols(): Thenable<{[index: string]: whatels.Symbols}> { + return new Promise<{[index: string]: whatels.Symbols}>((resolve, reject) => { + this._connect().then( + conn => resolve(conn.getAllPathSymbols()), + err => reject(err) + ) + }); + } + + public watch(wildcard: string) { + console.log('watch: ', wildcard); + this._connect().then( + conn => { + conn.watch(wildcard); + }, + err => console.error(err) + ); + } + public dispose() { this.wConn.close(); this.wConn = null; - this.pathSymbols = null; } private _connect(): Thenable { @@ -42,7 +49,7 @@ export class WhatelsClient implements Disposable { resolve(this.wConn); } else { - this.wConn = new whatels.Connection(); + this.wConn = new whatels.Connection(this.port); this.wConn.connect().then( () => resolve(this.wConn), err => reject(err) @@ -50,21 +57,4 @@ export class WhatelsClient implements Disposable { } }); } - - private _getSymbols(path: string, text: string): Thenable { - return new Promise((resolve, reject) => { - this._connect().then(conn => { - conn.getSymbols(text).then( - symbols => { - console.log('SYMBOLS!!! ', symbols); - const now = (new Date()).getTime(); - this.pathSymbols[path] = {updated: now, symbols: symbols}; - resolve(symbols); - }, - err => reject(err) - ); - }, - err => reject(err)); - }); - } } \ No newline at end of file From 1c60d81aaa5262a1d3796808a83a584a0983ac13 Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Fri, 26 Feb 2016 17:10:11 +0200 Subject: [PATCH 07/11] added support for exported functions --- package.json | 2 +- src/completion_provider.ts | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 2650728..a02a13e 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "vscode": "^0.11.0" }, "dependencies": { - "whatels": "^0.2.1" + "whatels": "^0.2.2" }, "repository": { "type": "git", diff --git a/src/completion_provider.ts b/src/completion_provider.ts index e053dc0..5337927 100644 --- a/src/completion_provider.ts +++ b/src/completion_provider.ts @@ -105,9 +105,11 @@ export class ErlangCompletionProvider implements CompletionItemProvider { var module = pathSymbols[p]; if (module.functions) { module.functions.forEach(f => { - var item = new CompletionItem(f.name); - item.kind = CompletionItemKind.Function; - comps.push(item); + if (f.exported) { + var item = new CompletionItem(f.name); + item.kind = CompletionItemKind.Function; + comps.push(item); + } }); } } From 5be15538b25b15a9a262199bc65dc9e28537efd9 Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Sat, 27 Feb 2016 04:06:12 +0200 Subject: [PATCH 08/11] Added workspace symbols provider; cache created symbols, uncache on file changes --- src/extension.ts | 7 ++- src/symbol_provider.ts | 140 +++++++++++++++++++++++++++++++++++------ src/whatels_client.ts | 14 ++++- 3 files changed, 138 insertions(+), 23 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 4558d56..8974c49 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -32,7 +32,7 @@ import {ExtensionContext, Disposable, workspace, window, languages, Hover} from 'vscode'; import {ErlangCompletionProvider} from './completion_provider'; -import {ErlangSymbolProvider} from './symbol_provider'; +import {ErlangDocumentSymbolProvider, ErlangWorkspaceDocumentSymbolProvider} from './symbol_provider'; import {WhatelsClient} from './whatels_client'; @@ -74,7 +74,10 @@ export function activate(ctx: ExtensionContext) { }, new ErlangCompletionProvider(whatelsClient, completionJsonPath), ':')); ctx.subscriptions.push(languages.registerDocumentSymbolProvider({ language: 'erlang' - }, new ErlangSymbolProvider(whatelsClient))); + }, new ErlangDocumentSymbolProvider(whatelsClient))); + ctx.subscriptions.push(languages.registerWorkspaceSymbolProvider( + new ErlangWorkspaceDocumentSymbolProvider(whatelsClient) + )); } } diff --git a/src/symbol_provider.ts b/src/symbol_provider.ts index cfe26d9..21d8e6e 100644 --- a/src/symbol_provider.ts +++ b/src/symbol_provider.ts @@ -1,34 +1,134 @@ -import {DocumentSymbolProvider, TextDocument, CancellationToken, - SymbolInformation, SymbolKind, Range} from 'vscode'; -import {Symbols} from 'whatels'; +import {DocumentSymbolProvider, WorkspaceSymbolProvider, TextDocument, + CancellationToken, SymbolInformation, SymbolKind, + Range, Uri} from 'vscode'; +import {Symbols, CallbackAction} from 'whatels'; import {WhatelsClient} from './whatels_client'; -export class ErlangSymbolProvider implements DocumentSymbolProvider { - constructor(private whatelsClient: WhatelsClient) {} +export class ErlangDocumentSymbolProvider implements DocumentSymbolProvider { + private symbols: SymbolInformation[] = null; + private symbolsPath: string; - public provideDocumentSymbols(doc: TextDocument, token: CancellationToken): Thenable { + constructor(private whatelsClient: WhatelsClient) { + let cb = (action: CallbackAction, msg: any) => { + if (action == CallbackAction.getSymbols) { + console.log('ErlangDocumentSymbolProvider: Invalidating symbols'); + this.symbols = null; + } + } + whatelsClient.subscribe(cb); + } + + public provideDocumentSymbols(doc: TextDocument, token: CancellationToken) + :Thenable + { return new Promise((resolve, reject) => { - console.log('get symbol informations'); - this.whatelsClient.getPathSymbols(doc.fileName).then( - symbols => { - console.log(`path: ${doc.fileName}, symbols: ${symbols}`); - symbols? this.resolveGenericItems(resolve, symbols) : reject() - }, - err => reject(err) - ) + console.log('get doc symbol informations'); + if (!this.symbols || this.symbolsPath != doc.fileName) { + this.whatelsClient.getPathSymbols(doc.fileName).then( + symbols => { + this.symbolsPath = doc.fileName; + this.createSymbolInformations(symbols); + this.resolveItems(resolve); + }, + err => reject(err) + ) + } + else { + this.resolveItems(resolve); + } }); } - private resolveGenericItems(resolve, symbols: Symbols) { + private resolveItems(resolve) { + resolve(this.symbols || []); + } + + private createSymbolInformations(symbols: Symbols) { + if (!symbols) { + this.symbols = null; + return; + } // TODO: sort symbols by name - let vsSymbols: SymbolInformation[] = symbols.functions.map(f => { + this.symbols = symbols.functions.map(f => { let range = new Range(f.line - 1, 0, f.line - 1, 0); - let name = symbols.module? - `${symbols.module}:${f.name}/${f.arity}` - : `${f.name}/${f.arity}`; + let name = `${f.name}/${f.arity}`; return new SymbolInformation(name, SymbolKind.Function, range); }); - resolve(vsSymbols); } +} + +export class ErlangWorkspaceDocumentSymbolProvider implements WorkspaceSymbolProvider { + private symbols: {[index: string]: SymbolInformation[]} = null; + + constructor(private whatelsClient: WhatelsClient) { + let cb = (action: CallbackAction, msg: any) => { + if (action == CallbackAction.getSymbols) { + console.log('ErlangWorkspaceDocumentSymbolProvider: Invalidating symbols'); + if (this.symbols) { + this.symbols[msg.path] = null; + } + + } + } + whatelsClient.subscribe(cb); + } + + public provideWorkspaceSymbols(query: string, token: CancellationToken) + :SymbolInformation[] | Thenable + { + console.log('will provide workspace symbols'); + return new Promise((resolve, reject) => { + console.log('get doc symbol informations'); + if (!this.symbols) { + this.whatelsClient.getAllPathSymbols().then( + symbols => { + this.createSymbolInformations(symbols); + this.resolveItems(resolve, query); + }, + err => reject(err) + ); + } + else { + this.resolveItems(resolve, query); + } + }); + } + + private resolveItems(resolve, query) { + let sis: SymbolInformation[] = []; + if (!this.symbols) { + resolve([]); + } + for (var k in this.symbols) { + var symbols = this.symbols[k] || []; + symbols.forEach(sym => { + if (sym.name.indexOf(query) >= 0) { + sis.push(sym); + } + }); + } + resolve(sis); + } + + private createSymbolInformations(pathSymbols: {[index: string]: Symbols}) { + if (!this.symbols) { + this.symbols = {}; + } + for (var path in pathSymbols) { + this.symbols[path] = []; + var symbols = pathSymbols[path]; + pathSymbols[path].functions.forEach(f => { + const range = new Range(f.line - 1, 0, f.line - 1, 0); + const uri = Uri.file(path); + const name = `${symbols.module}:${f.name}/${f.arity}`; + var si = new SymbolInformation(name, + SymbolKind.Function, + range, + uri); + this.symbols[path].push(si); + }); + } + } + } \ No newline at end of file diff --git a/src/whatels_client.ts b/src/whatels_client.ts index 8196cc8..d580793 100644 --- a/src/whatels_client.ts +++ b/src/whatels_client.ts @@ -2,14 +2,20 @@ import {Disposable} from 'vscode'; import whatels = require('whatels'); + export class WhatelsClient implements Disposable { private wConn: whatels.Connection; private port: number; + private subscribers: whatels.CallbackFunction[] = []; constructor(refreshTime?: number, port?: number) { this.port = port || 10998; } + public subscribe(callback: whatels.CallbackFunction) { + this.subscribers.push(callback); + } + public getPathSymbols(path: string): Thenable { return new Promise((resolve, reject) => { this._connect().then( @@ -44,13 +50,19 @@ export class WhatelsClient implements Disposable { } private _connect(): Thenable { + let callback = (action: whatels.CallbackAction, msg: any) => { + this.subscribers.forEach(cb => { + cb(action, msg); + }); + } + return new Promise((resolve, reject) => { if (this.wConn) { resolve(this.wConn); } else { this.wConn = new whatels.Connection(this.port); - this.wConn.connect().then( + this.wConn.connect(callback).then( () => resolve(this.wConn), err => reject(err) ); From fa189494e69df13570d209b5a9158a8befc9673e Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Sat, 27 Feb 2016 10:44:05 +0200 Subject: [PATCH 09/11] Symbol proiders invalidation optimization --- src/symbol_provider.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/symbol_provider.ts b/src/symbol_provider.ts index 21d8e6e..afc40db 100644 --- a/src/symbol_provider.ts +++ b/src/symbol_provider.ts @@ -11,9 +11,10 @@ export class ErlangDocumentSymbolProvider implements DocumentSymbolProvider { constructor(private whatelsClient: WhatelsClient) { let cb = (action: CallbackAction, msg: any) => { - if (action == CallbackAction.getSymbols) { - console.log('ErlangDocumentSymbolProvider: Invalidating symbols'); + if (action == CallbackAction.getSymbols && msg.path == this.symbolsPath) { + console.log(`ErlangDocumentSymbolProvider: Invalidating symbols - ${this.symbolsPath}`); this.symbols = null; + this.symbolsPath = ''; } } whatelsClient.subscribe(cb); @@ -23,7 +24,7 @@ export class ErlangDocumentSymbolProvider implements DocumentSymbolProvider { :Thenable { return new Promise((resolve, reject) => { - console.log('get doc symbol informations'); + console.log('ErlangDocumentSymbolProvider: get doc symbol informations'); if (!this.symbols || this.symbolsPath != doc.fileName) { this.whatelsClient.getPathSymbols(doc.fileName).then( symbols => { @@ -64,8 +65,8 @@ export class ErlangWorkspaceDocumentSymbolProvider implements WorkspaceSymbolPro constructor(private whatelsClient: WhatelsClient) { let cb = (action: CallbackAction, msg: any) => { if (action == CallbackAction.getSymbols) { - console.log('ErlangWorkspaceDocumentSymbolProvider: Invalidating symbols'); if (this.symbols) { + console.log(`ErlangWorkspaceDocumentSymbolProvider: Invalidating symbols - ${msg.path}`); this.symbols[msg.path] = null; } @@ -77,9 +78,8 @@ export class ErlangWorkspaceDocumentSymbolProvider implements WorkspaceSymbolPro public provideWorkspaceSymbols(query: string, token: CancellationToken) :SymbolInformation[] | Thenable { - console.log('will provide workspace symbols'); return new Promise((resolve, reject) => { - console.log('get doc symbol informations'); + console.log('ErlangWorkspaceDocumentSymbolProvider: get doc symbol informations'); if (!this.symbols) { this.whatelsClient.getAllPathSymbols().then( symbols => { From c57850f0ac426f2725391f38afec45c887d3bf65 Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Sat, 27 Feb 2016 15:17:44 +0200 Subject: [PATCH 10/11] complete std lib modules --- src/completion_provider.ts | 177 ++++++++++++++++++++----------------- 1 file changed, 95 insertions(+), 82 deletions(-) diff --git a/src/completion_provider.ts b/src/completion_provider.ts index 5337927..e041f4d 100644 --- a/src/completion_provider.ts +++ b/src/completion_provider.ts @@ -3,7 +3,7 @@ import {CompletionItemProvider, TextDocument, Position, CancellationToken, CompletionItem, CompletionItemKind} from 'vscode'; -import {Symbols, FunctionInfo} from 'whatels'; +import {Symbols, FunctionInfo, CallbackAction} from 'whatels'; import {WhatelsClient} from './whatels_client'; import fs = require('fs'); import path = require('path'); @@ -11,12 +11,27 @@ import path = require('path'); const RE_MODULE = /(\w+):$/; export class ErlangCompletionProvider implements CompletionItemProvider { - private stdModules:any = null; + private stdModules: any = null; private moduleNames: string[] = null; + private docPath: string; private genericCompletionItems: CompletionItem[] = null; + private moduleCompletionItems: CompletionItem[] = null; + private stdLibCompletionItems: CompletionItem[] = null; constructor(private whatelsClient: WhatelsClient, - private completionPath: string) {} + private completionPath: string) + { + whatelsClient.subscribe((action, msg) => { + if (action == CallbackAction.getSymbols) { + this.genericCompletionItems = null; + if (msg.path == this.docPath) { + // invalidate completion items of the current doc + this.docPath = ''; + this.moduleCompletionItems = null; + } + } + }); + } public provideCompletionItems(doc: TextDocument, pos: Position, @@ -26,21 +41,15 @@ export class ErlangCompletionProvider implements CompletionItemProvider { const line = doc.lineAt(pos.line); const m = RE_MODULE.exec(line.text.substring(0, pos.character)); if (m === null) { - this.whatelsClient.getAllPathSymbols().then( - pathSymbols => { - pathSymbols? - this.resolveGenericItems(resolve, pathSymbols) - : reject(); - }, - err => reject(err) - ); + this.resolveGenericItems(resolve, reject, doc.fileName); } else { - const moduleName = m[1]; - this.whatelsClient.getAllPathSymbols().then( - pathSymbols => this.resolveModuleItems(resolve, moduleName, pathSymbols), - err => reject(err) - ); + resolve([]); + // const moduleName = m[1]; + // this.whatelsClient.getAllPathSymbols().then( + // pathSymbols => this.resolveModuleItems(resolve, moduleName, pathSymbols), + // err => reject(err) + // ); // if (this.stdModules === null) { // this.readCompletionJson(this.completionPath, modules => { // this.stdModules = modules; @@ -54,87 +63,91 @@ export class ErlangCompletionProvider implements CompletionItemProvider { }); } - private resolveFunNames(module, resolve) { - resolve(this.makeModuleFunsCompletion(module)); + private resolveGenericItems(resolve, reject, path: string) { + this.getGenericCompletionItems(path).then( + items => resolve(items), + err => reject(err) + ) } - private resolveGenericItems(resolve, pathSymbols) { - resolve(this.makeGenericCompletion(pathSymbols)); - } - - private resolveModuleItems(resolve, moduleName, pathSymbols) { - resolve(this.makeModuleCompletion(moduleName, pathSymbols)); - } - - private makeFunctionCompletionItem(name: string): CompletionItem { - const item = new CompletionItem(name); - // item.documentation = cd.detail; - item.kind = CompletionItemKind.Function; - return item; + private getGenericCompletionItems(path: string): Thenable { + return new Promise((resolve, reject) => { + if (this.genericCompletionItems) { + resolve(this.genericCompletionItems); + } + else { + let cis: CompletionItem[] = []; + Promise.all([this.getModuleCompletionItems(path), + this.getStdLibCompletionItems()]).then( + allCompletionItems => { + allCompletionItems.forEach(items => { + items.forEach(ci => cis.push(ci)); + }); + }, + err => reject(err) + ); + this.genericCompletionItems = cis; + } + }); } - private makeModuleNameCompletionItem(name: string): CompletionItem { - const item = new CompletionItem(name); - item.kind = CompletionItemKind.Module; - return item; + private getModuleCompletionItems(path: string): Thenable { + return new Promise((resolve, reject) => { + if (!this.moduleCompletionItems || path != this.docPath) { + this.whatelsClient.getPathSymbols(path).then( + symbols => { + this.docPath = path; + this.createModuleCompletionItems(path, symbols); + resolve(this.moduleCompletionItems); + }, + err => reject(err) + ); + } + else { + resolve(this.moduleCompletionItems); + } + }); } - private makeModuleFunsCompletion(module: string): CompletionItem[] { - const moduleFuns = this.stdModules[module] || []; - return moduleFuns.map(name => { - return this.makeFunctionCompletionItem(name); + private getStdLibCompletionItems(): Thenable { + return new Promise((resolve, reject) => { + if (this.stdLibCompletionItems) { + resolve(this.stdLibCompletionItems); + } + else { + this.readCompletionJson(this.completionPath, modules => { + this.stdModules = modules; + this.createStdLibCompletionItems(modules); + }); + } }); } - private makeGenericCompletion(pathSymbols: {[index: string]: Symbols}) { - let comps: CompletionItem[] = []; - for (var p in pathSymbols) { - var item = new CompletionItem(path.basename(p, '.erl')); - item.kind = CompletionItemKind.Module; - comps.push(item); + private createModuleCompletionItems(path: string, symbols: Symbols) { + let cis: CompletionItem[] = []; + if (symbols && symbols.functions) { + let funNames = new Set(symbols.functions.map(f => { + return f.name; + })); + funNames.forEach(name => { + var item = new CompletionItem(name); + item.kind = CompletionItemKind.Function; + cis.push(item); + }); } - return comps; + this.moduleCompletionItems = cis; } - private makeModuleCompletion(moduleName: string, - pathSymbols: {[index: string]: Symbols}) - { - let comps: CompletionItem[] = []; - for (var p in pathSymbols) { - if (pathSymbols[p].module == moduleName) { - var module = pathSymbols[p]; - if (module.functions) { - module.functions.forEach(f => { - if (f.exported) { - var item = new CompletionItem(f.name); - item.kind = CompletionItemKind.Function; - comps.push(item); - } - }); - } - } + private createStdLibCompletionItems(modules) { + let cis: CompletionItem[] = []; + for (var k in modules) { + var item = new CompletionItem(k); + item.kind = CompletionItemKind.Module; + cis.push(item); } - - return comps; + this.stdLibCompletionItems = cis; } - // private makeGenericCompletion(funs: FunctionInfo[]): CompletionItem[] { - // let comps: CompletionItem[] = funs.map(f => { - // return this.makeFunctionCompletionItem(f.name); - // }); - // const modules = this.modules || {}; - // const names = []; - // for (let k in modules) { - // names.push(k); - // } - // names.sort(); - // names.forEach(name => { - // comps.push(this.makeModuleNameCompletionItem(name)); - // }); - - // return comps; - // } - private readCompletionJson(filename: string, done: Function): any { fs.readFile(filename, 'utf8', (err, data) => { if (err) { From 74c9467457119bf4894ca402bf9a2200109f18d7 Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Sat, 27 Feb 2016 18:21:42 +0200 Subject: [PATCH 11/11] act on discardPath action; support workspace modules completion --- README.md | 3 +-- src/completion_provider.ts | 50 +++++++++++++++++++++++++++++++------- src/symbol_provider.ts | 2 +- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 09cd24f..059670b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Erlang/OTP Support for Visual Studio Code -This extension provides Erlang/OTP support for [Visual Studio Code](https://code.visualstudio.com/)and is available at the [Marketplace](https://marketplace.visualstudio.com/items?itemName=yuce.erlang-otp). +This experimantal extension provides Erlang/OTP support for [Visual Studio Code](https://code.visualstudio.com/). ## News @@ -19,7 +19,6 @@ setting `erlang.enableExperimentalAutoComplete` to `true` in your user settings ## Planned Features * Build support -* Erlang shell ## Work In Progress diff --git a/src/completion_provider.ts b/src/completion_provider.ts index e041f4d..781a12c 100644 --- a/src/completion_provider.ts +++ b/src/completion_provider.ts @@ -17,6 +17,7 @@ export class ErlangCompletionProvider implements CompletionItemProvider { private genericCompletionItems: CompletionItem[] = null; private moduleCompletionItems: CompletionItem[] = null; private stdLibCompletionItems: CompletionItem[] = null; + private workspaceCompletionItems: CompletionItem[] = null; constructor(private whatelsClient: WhatelsClient, private completionPath: string) @@ -30,6 +31,9 @@ export class ErlangCompletionProvider implements CompletionItemProvider { this.moduleCompletionItems = null; } } + else if (action == CallbackAction.discardPath) { + this.genericCompletionItems = null; + } }); } @@ -78,11 +82,13 @@ export class ErlangCompletionProvider implements CompletionItemProvider { else { let cis: CompletionItem[] = []; Promise.all([this.getModuleCompletionItems(path), + this.getWorkspaceCompletionItems(), this.getStdLibCompletionItems()]).then( allCompletionItems => { allCompletionItems.forEach(items => { items.forEach(ci => cis.push(ci)); }); + resolve(cis); }, err => reject(err) ); @@ -93,19 +99,18 @@ export class ErlangCompletionProvider implements CompletionItemProvider { private getModuleCompletionItems(path: string): Thenable { return new Promise((resolve, reject) => { - if (!this.moduleCompletionItems || path != this.docPath) { + if (this.moduleCompletionItems && path == this.docPath) { + resolve(this.moduleCompletionItems); + } + else { this.whatelsClient.getPathSymbols(path).then( symbols => { this.docPath = path; - this.createModuleCompletionItems(path, symbols); - resolve(this.moduleCompletionItems); + resolve(this.createModuleCompletionItems(path, symbols)); }, err => reject(err) ); } - else { - resolve(this.moduleCompletionItems); - } }); } @@ -117,12 +122,26 @@ export class ErlangCompletionProvider implements CompletionItemProvider { else { this.readCompletionJson(this.completionPath, modules => { this.stdModules = modules; - this.createStdLibCompletionItems(modules); + resolve(this.createStdLibCompletionItems(modules)); }); } }); } + private getWorkspaceCompletionItems(): Thenable { + return new Promise((resolve, reject) => { + if (this.workspaceCompletionItems) { + resolve(this.workspaceCompletionItems); + } + else { + this.whatelsClient.getAllPathSymbols().then( + pathSymbols => resolve(this.createWorkspaceCompletionItems(pathSymbols)), + err => reject(err) + ); + } + }); + } + private createModuleCompletionItems(path: string, symbols: Symbols) { let cis: CompletionItem[] = []; if (symbols && symbols.functions) { @@ -135,7 +154,7 @@ export class ErlangCompletionProvider implements CompletionItemProvider { cis.push(item); }); } - this.moduleCompletionItems = cis; + return this.moduleCompletionItems = cis; } private createStdLibCompletionItems(modules) { @@ -145,7 +164,20 @@ export class ErlangCompletionProvider implements CompletionItemProvider { item.kind = CompletionItemKind.Module; cis.push(item); } - this.stdLibCompletionItems = cis; + return this.stdLibCompletionItems = cis; + } + + private createWorkspaceCompletionItems(pathSymbols) { + let cis: CompletionItem[] = []; + for (var p in pathSymbols) { + var item = new CompletionItem(path.basename(p, '.erl')); + item.kind = CompletionItemKind.Module; + cis.push(item); + } + console.log('createWorkspaceCompletionItems'); + console.log(cis); + console.log(this); + return this.workspaceCompletionItems = cis; } private readCompletionJson(filename: string, done: Function): any { diff --git a/src/symbol_provider.ts b/src/symbol_provider.ts index afc40db..bf301bb 100644 --- a/src/symbol_provider.ts +++ b/src/symbol_provider.ts @@ -64,7 +64,7 @@ export class ErlangWorkspaceDocumentSymbolProvider implements WorkspaceSymbolPro constructor(private whatelsClient: WhatelsClient) { let cb = (action: CallbackAction, msg: any) => { - if (action == CallbackAction.getSymbols) { + if (action == CallbackAction.getSymbols || action == CallbackAction.discardPath) { if (this.symbols) { console.log(`ErlangWorkspaceDocumentSymbolProvider: Invalidating symbols - ${msg.path}`); this.symbols[msg.path] = null;