Comparing version 1.1.5 to 1.1.6
{ | ||
"spellright.language": "en", | ||
"spellright.documentTypes": [ | ||
"markdown", | ||
"latex", | ||
"plaintext", | ||
"antlr", | ||
"typescript", | ||
"javascript" | ||
] | ||
} |
@@ -53,12 +53,16 @@ import { ParseTree } from 'antlr4ts/tree/ParseTree'; | ||
context: ParseTree | undefined; | ||
protected parent: Symbol | undefined; | ||
constructor(name?: string); | ||
setParent(parent: Symbol | undefined): void; | ||
getParent(): Symbol | undefined; | ||
readonly parent: Symbol | undefined; | ||
readonly first: Symbol; | ||
readonly previous: Symbol; | ||
readonly next: Symbol; | ||
readonly last: Symbol; | ||
removeFromParent(): void; | ||
getRoot(): Symbol | undefined; | ||
getSymbolTable(): SymbolTable | undefined; | ||
readonly root: Symbol | undefined; | ||
readonly symbolTable: SymbolTable | undefined; | ||
getParentOfType<T extends Symbol>(t: new (...args: any[]) => T): T | undefined; | ||
getSymbolPath(): Symbol[]; | ||
readonly symbolPath: Symbol[]; | ||
qualifiedName(separator?: string, full?: boolean, includeAnonymous?: boolean): string; | ||
protected _parent: Symbol | undefined; | ||
} | ||
@@ -78,2 +82,4 @@ export declare class TypedSymbol extends Symbol { | ||
constructor(name?: string); | ||
readonly children: Symbol[]; | ||
clear(): void; | ||
addSymbol(symbol: Symbol): void; | ||
@@ -88,8 +94,15 @@ removeSymbol(symbol: Symbol): void; | ||
getTypedSymbolNames(localOnly?: boolean): string[]; | ||
getDirectScopes(): ScopedSymbol[]; | ||
readonly directScopes: ScopedSymbol[]; | ||
symbolFromPath(path: string, separator?: string): Symbol | undefined; | ||
protected children: Symbol[]; | ||
indexOfChild(child: Symbol): number; | ||
nextSiblingOf(child: Symbol): Symbol | undefined; | ||
previousSiblingOf(child: Symbol): Symbol | undefined; | ||
readonly firstChild: Symbol | undefined; | ||
readonly lastChild: Symbol | undefined; | ||
private _children; | ||
} | ||
export declare class NamespaceSymbol extends ScopedSymbol { | ||
} | ||
export declare class BlockSymbol extends ScopedSymbol { | ||
} | ||
export declare class VariableSymbol extends TypedSymbol { | ||
@@ -150,40 +163,2 @@ constructor(name: string, value: any, type?: Type); | ||
} | ||
export declare class CatalogSymbol extends ScopedSymbol { | ||
} | ||
export declare class SchemaSymbol extends ScopedSymbol { | ||
} | ||
export declare class TableSymbol extends ScopedSymbol { | ||
} | ||
export declare class ViewSymbol extends ScopedSymbol { | ||
} | ||
export declare class EventSymbol extends ScopedSymbol { | ||
} | ||
export declare class ColumnSymbol extends TypedSymbol { | ||
} | ||
export declare class IndexSymbol extends Symbol { | ||
} | ||
export declare class PrimaryKeySymbol extends Symbol { | ||
} | ||
export declare class ForeignKeySymbol extends Symbol { | ||
} | ||
export declare class StoredRoutineSymbol extends RoutineSymbol { | ||
} | ||
export declare class TriggerSymbol extends ScopedSymbol { | ||
} | ||
export declare class UdfSymbol extends Symbol { | ||
} | ||
export declare class EngineSymbol extends Symbol { | ||
} | ||
export declare class TableSpaceSymbol extends Symbol { | ||
} | ||
export declare class LogfileGroupSymbol extends Symbol { | ||
} | ||
export declare class CharsetSymbol extends Symbol { | ||
} | ||
export declare class CollationSymbol extends Symbol { | ||
} | ||
export declare class UserVariableSymbol extends VariableSymbol { | ||
} | ||
export declare class SystemVariableSymbol extends Symbol { | ||
} | ||
export declare class SymbolTable extends ScopedSymbol { | ||
@@ -195,3 +170,3 @@ readonly options: SymbolTableOptions; | ||
removeDependency(table: SymbolTable): void; | ||
getInfo(): { | ||
readonly info: { | ||
dependencyCount: number; | ||
@@ -198,0 +173,0 @@ symbolCount: number; |
@@ -58,31 +58,71 @@ 'use strict'; | ||
setParent(parent) { | ||
this.parent = parent; | ||
this._parent = parent; | ||
} | ||
getParent() { | ||
return this.parent; | ||
get parent() { | ||
return this._parent; | ||
} | ||
get first() { | ||
if (!(this._parent instanceof ScopedSymbol)) { | ||
return this; | ||
} | ||
let result = this._parent.firstChild; | ||
if (result) { | ||
return result; | ||
} | ||
return this; | ||
} | ||
get previous() { | ||
if (!(this._parent instanceof ScopedSymbol)) { | ||
return this; | ||
} | ||
let result = this._parent.previousSiblingOf(this); | ||
if (result) { | ||
return result; | ||
} | ||
return this; | ||
} | ||
get next() { | ||
if (!(this._parent instanceof ScopedSymbol)) { | ||
return this; | ||
} | ||
let result = this._parent.nextSiblingOf(this); | ||
if (result) { | ||
return result; | ||
} | ||
return this; | ||
} | ||
get last() { | ||
if (!(this._parent instanceof ScopedSymbol)) { | ||
return this; | ||
} | ||
let result = this._parent.lastChild; | ||
if (result) { | ||
return result; | ||
} | ||
return this; | ||
} | ||
removeFromParent() { | ||
if (this.parent instanceof ScopedSymbol) { | ||
this.parent.removeSymbol(this); | ||
this.parent = undefined; | ||
if (this._parent instanceof ScopedSymbol) { | ||
this._parent.removeSymbol(this); | ||
this._parent = undefined; | ||
} | ||
} | ||
getRoot() { | ||
let run = this.parent; | ||
get root() { | ||
let run = this._parent; | ||
while (run) { | ||
if (!run.parent || (run.parent instanceof SymbolTable)) | ||
if (!run._parent || (run._parent instanceof SymbolTable)) | ||
return run; | ||
run = run.parent; | ||
run = run._parent; | ||
} | ||
return run; | ||
} | ||
getSymbolTable() { | ||
get symbolTable() { | ||
if (this instanceof SymbolTable) { | ||
return this; | ||
} | ||
let run = this.parent; | ||
let run = this._parent; | ||
while (run) { | ||
if (run instanceof SymbolTable) | ||
return run; | ||
run = run.parent; | ||
run = run._parent; | ||
} | ||
@@ -92,11 +132,11 @@ return undefined; | ||
getParentOfType(t) { | ||
let run = this.parent; | ||
let run = this._parent; | ||
while (run) { | ||
if (run instanceof t) | ||
return run; | ||
run = run.parent; | ||
run = run._parent; | ||
} | ||
return undefined; | ||
} | ||
getSymbolPath() { | ||
get symbolPath() { | ||
let result = []; | ||
@@ -106,5 +146,5 @@ let run = this; | ||
result.push(run); | ||
if (!run.parent) | ||
if (!run._parent) | ||
break; | ||
run = run.parent; | ||
run = run._parent; | ||
} | ||
@@ -117,10 +157,10 @@ return result; | ||
let result = this.name.length == 0 ? "<anonymous>" : this.name; | ||
let run = this.parent; | ||
let run = this._parent; | ||
while (run) { | ||
if (includeAnonymous || run.name.length > 0) { | ||
result = (run.name.length == 0 ? "<anonymous>" : run.name) + separator + result; | ||
if (!full || !run.parent) | ||
if (!full || !run._parent) | ||
break; | ||
} | ||
run = run.parent; | ||
run = run._parent; | ||
} | ||
@@ -154,9 +194,15 @@ return result; | ||
super(name); | ||
this.children = []; | ||
this._children = []; | ||
} | ||
get children() { | ||
return this._children; | ||
} | ||
clear() { | ||
this._children = []; | ||
} | ||
addSymbol(symbol) { | ||
symbol.removeFromParent(); | ||
let symbolTable = this.getSymbolTable(); | ||
let symbolTable = this.symbolTable; | ||
if (!symbolTable || !symbolTable.options.allowDuplicateSymbols) { | ||
for (let child of this.children) { | ||
for (let child of this._children) { | ||
if (child == symbol || (symbol.name.length > 0 && child.name == symbol.name)) { | ||
@@ -170,9 +216,9 @@ let name = symbol.name; | ||
} | ||
this.children.push(symbol); | ||
this._children.push(symbol); | ||
symbol.setParent(this); | ||
} | ||
removeSymbol(symbol) { | ||
let index = this.children.indexOf(symbol); | ||
let index = this._children.indexOf(symbol); | ||
if (index > -1) { | ||
this.children.splice(index, 1); | ||
this._children.splice(index, 1); | ||
symbol.setParent(undefined); | ||
@@ -183,3 +229,3 @@ } | ||
let result = []; | ||
for (let child of this.children) { | ||
for (let child of this._children) { | ||
if (child instanceof t) | ||
@@ -192,3 +238,3 @@ result.push(child); | ||
let result = []; | ||
for (let child of this.children) { | ||
for (let child of this._children) { | ||
if (child instanceof t) | ||
@@ -203,3 +249,3 @@ result.push(child); | ||
let result = []; | ||
for (let child of this.children) { | ||
for (let child of this._children) { | ||
result.push(child); | ||
@@ -213,3 +259,3 @@ if (child instanceof ScopedSymbol) | ||
let result = []; | ||
for (let child of this.children) { | ||
for (let child of this._children) { | ||
if (child instanceof t) { | ||
@@ -222,4 +268,4 @@ result.push(child); | ||
if (!localOnly) { | ||
if (this.parent && this.parent instanceof ScopedSymbol) | ||
result.push(...this.parent.getAllSymbols(t)); | ||
if (this._parent && this._parent instanceof ScopedSymbol) | ||
result.push(...this._parent.getAllSymbols(t)); | ||
} | ||
@@ -229,3 +275,3 @@ return result; | ||
resolve(name, localOnly = false) { | ||
for (let child of this.children) { | ||
for (let child of this._children) { | ||
if (child.name == name) | ||
@@ -235,4 +281,4 @@ return child; | ||
if (!localOnly) { | ||
if (this.parent && this.parent instanceof ScopedSymbol) | ||
return this.parent.resolve(name, false); | ||
if (this._parent && this._parent instanceof ScopedSymbol) | ||
return this._parent.resolve(name, false); | ||
} | ||
@@ -243,3 +289,3 @@ return undefined; | ||
let result = []; | ||
for (let child of this.children) { | ||
for (let child of this._children) { | ||
if (child instanceof TypedSymbol) { | ||
@@ -250,4 +296,4 @@ result.push(child); | ||
if (!localOnly) { | ||
if (this.parent instanceof ScopedSymbol) { | ||
let localList = this.parent.getTypedSymbols(true); | ||
if (this._parent instanceof ScopedSymbol) { | ||
let localList = this._parent.getTypedSymbols(true); | ||
result.push(...localList); | ||
@@ -260,3 +306,3 @@ } | ||
let result = []; | ||
for (let child of this.children) { | ||
for (let child of this._children) { | ||
if (child instanceof TypedSymbol) { | ||
@@ -267,4 +313,4 @@ result.push(child.name); | ||
if (!localOnly) { | ||
if (this.parent instanceof ScopedSymbol) { | ||
let localList = this.parent.getTypedSymbolNames(true); | ||
if (this._parent instanceof ScopedSymbol) { | ||
let localList = this._parent.getTypedSymbolNames(true); | ||
result.push(...localList); | ||
@@ -275,3 +321,3 @@ } | ||
} | ||
getDirectScopes() { | ||
get directScopes() { | ||
return this.getSymbolsOfType(ScopedSymbol); | ||
@@ -288,3 +334,3 @@ } | ||
return undefined; | ||
let child = result.children.find(child => child.name == elements[index]); | ||
let child = result._children.find(child => child.name == elements[index]); | ||
if (!child) | ||
@@ -297,2 +343,31 @@ return undefined; | ||
} | ||
indexOfChild(child) { | ||
return this._children.findIndex((value, index) => { | ||
return value == child; | ||
}); | ||
} | ||
nextSiblingOf(child) { | ||
let index = this.indexOfChild(child); | ||
if (index == -1 || index >= this._children.length - 1) { | ||
return; | ||
} | ||
return this._children[index + 1]; | ||
} | ||
previousSiblingOf(child) { | ||
let index = this.indexOfChild(child); | ||
if (index < 1) { | ||
return; | ||
} | ||
return this._children[index - 1]; | ||
} | ||
get firstChild() { | ||
if (this._children.length > 0) { | ||
return this._children[0]; | ||
} | ||
} | ||
get lastChild() { | ||
if (this._children.length > 0) { | ||
return this._children[this._children.length - 1]; | ||
} | ||
} | ||
} | ||
@@ -304,2 +379,5 @@ exports.ScopedSymbol = ScopedSymbol; | ||
exports.NamespaceSymbol = NamespaceSymbol; | ||
class BlockSymbol extends ScopedSymbol { | ||
} | ||
exports.BlockSymbol = BlockSymbol; | ||
class VariableSymbol extends TypedSymbol { | ||
@@ -401,78 +479,2 @@ constructor(name, value, type) { | ||
; | ||
class CatalogSymbol extends ScopedSymbol { | ||
} | ||
exports.CatalogSymbol = CatalogSymbol; | ||
; | ||
class SchemaSymbol extends ScopedSymbol { | ||
} | ||
exports.SchemaSymbol = SchemaSymbol; | ||
; | ||
class TableSymbol extends ScopedSymbol { | ||
} | ||
exports.TableSymbol = TableSymbol; | ||
; | ||
class ViewSymbol extends ScopedSymbol { | ||
} | ||
exports.ViewSymbol = ViewSymbol; | ||
; | ||
class EventSymbol extends ScopedSymbol { | ||
} | ||
exports.EventSymbol = EventSymbol; | ||
; | ||
class ColumnSymbol extends TypedSymbol { | ||
} | ||
exports.ColumnSymbol = ColumnSymbol; | ||
; | ||
class IndexSymbol extends Symbol { | ||
} | ||
exports.IndexSymbol = IndexSymbol; | ||
; | ||
class PrimaryKeySymbol extends Symbol { | ||
} | ||
exports.PrimaryKeySymbol = PrimaryKeySymbol; | ||
; | ||
class ForeignKeySymbol extends Symbol { | ||
} | ||
exports.ForeignKeySymbol = ForeignKeySymbol; | ||
; | ||
class StoredRoutineSymbol extends RoutineSymbol { | ||
} | ||
exports.StoredRoutineSymbol = StoredRoutineSymbol; | ||
; | ||
class TriggerSymbol extends ScopedSymbol { | ||
} | ||
exports.TriggerSymbol = TriggerSymbol; | ||
; | ||
class UdfSymbol extends Symbol { | ||
} | ||
exports.UdfSymbol = UdfSymbol; | ||
; | ||
class EngineSymbol extends Symbol { | ||
} | ||
exports.EngineSymbol = EngineSymbol; | ||
; | ||
class TableSpaceSymbol extends Symbol { | ||
} | ||
exports.TableSpaceSymbol = TableSpaceSymbol; | ||
; | ||
class LogfileGroupSymbol extends Symbol { | ||
} | ||
exports.LogfileGroupSymbol = LogfileGroupSymbol; | ||
; | ||
class CharsetSymbol extends Symbol { | ||
} | ||
exports.CharsetSymbol = CharsetSymbol; | ||
; | ||
class CollationSymbol extends Symbol { | ||
} | ||
exports.CollationSymbol = CollationSymbol; | ||
; | ||
class UserVariableSymbol extends VariableSymbol { | ||
} | ||
exports.UserVariableSymbol = UserVariableSymbol; | ||
; | ||
class SystemVariableSymbol extends Symbol { | ||
} | ||
exports.SystemVariableSymbol = SystemVariableSymbol; | ||
; | ||
class SymbolTable extends ScopedSymbol { | ||
@@ -485,4 +487,4 @@ constructor(name, options) { | ||
clear() { | ||
super.clear(); | ||
this.dependencies.clear(); | ||
this.children = []; | ||
} | ||
@@ -499,3 +501,3 @@ addDependencies(...tables) { | ||
} | ||
getInfo() { | ||
get info() { | ||
return { | ||
@@ -502,0 +504,0 @@ dependencyCount: this.dependencies.size, |
{ | ||
"name": "antlr4-c3", | ||
"version": "1.1.5", | ||
"version": "1.1.6", | ||
"description": "A code completion core implmentation for ANTLR4 based parsers", | ||
@@ -5,0 +5,0 @@ "main": "out/index.js", |
@@ -9,7 +9,7 @@ [![NPM](https://nodei.co/npm/antlr4-c3.png?downloads=true&downloadRank=true)](https://nodei.co/npm/antlr4-c3/) | ||
The original implementation is provided as a node module, and is written in TypeScript. A port to Java is available under ports/java. | ||
The original implementation is provided as a node module, and is written in TypeScript. A port to Java is available under `ports/java`. Implementations under the `ports` folder might not be up to date compared to the Typescript version. | ||
# Abstract | ||
There have been quite a number of requests over the past years for getting support from ANTLR to create a code completion implementation, but so far that turned out as an isolated task with only custom solutions. This library aims to provide a common infrastructure for code completion implementions in a more general way, so that people can share their solutions and provide others with ideas to solve specific problems related to that. | ||
There have been quite a number of requests over the past years for getting support from ANTLR to create a code completion implementation, but so far that turned out as an isolated task with only custom solutions. This library aims to provide a common infrastructure for code completion implementations in a more general way, so that people can share their solutions and provide others with ideas to solve specific problems related to that. | ||
@@ -183,3 +183,3 @@ The c3 engine implementation is based on an idea presented a while ago under [Universal Code Completion using ANTLR3](http://www.soft-gems.net/index.php/tools/47-universal-code-completion-using-antlr3). There a grammar was loaded into a memory structure so that it can be walked through with the current input to find a specific location (usually the caret position) and then collect all possible tokens and special rules, which then describe the possible set of code completion candidates for that position. With ANTLR4 we no longer need to load a grammar, because the grammar structure is now available as part of a parser (via the ATN - [Augmented Transition Network](https://en.wikipedia.org/wiki/Augmented_transition_network)). The ANTLR4 runtime even provides the [LL1Analyzer](https://github.com/antlr/antlr4/blob/master/runtime/Java/src/org/antlr/v4/runtime/atn/LL1Analyzer.java) class, which helps with retrieving follow sets for a given ATN state, but has a few shortcomings and is in general not easy to use. | ||
## Preferred Rules | ||
As mentioned already the `preferredRules` field is an essential part for getting more than just keywords. It lets you specify the parser rules that are interesting for you and should include the rule indexes for the entitites we talked about in the code completion breakdown paragraph above. Whenever the c3 engine hits a lexer token when collecting candidates from a specific ATN state it will check the call stack for it and, if that contains any of the preferred rules, will select that instead of the lexer token. This transformation ensures that the engine returns contextual informations which can actually be used to look up symbols. | ||
As mentioned already the `preferredRules` field is an essential part for getting more than just keywords. It lets you specify the parser rules that are interesting for you and should include the rule indexes for the entities we talked about in the code completion breakdown paragraph above. Whenever the c3 engine hits a lexer token when collecting candidates from a specific ATN state it will check the call stack for it and, if that contains any of the preferred rules, will select that instead of the lexer token. This transformation ensures that the engine returns contextual informations which can actually be used to look up symbols. | ||
@@ -198,3 +198,3 @@ ## Constraining the Search Space | ||
Each vertical line corresponds to a possible caret position. The first 3 lines clearly belong to token index 0, but the next line is no longer that clear. At that position we already are on token index 1 while visually the caret still belongs to index 0, because it could be that we are just at the end of a word and want to add more letters to it and hence have to provide candidates for that word. However, for token position 5 the situation is different. After the equal sign there are no possible further characters that could belong to it, so in this case position 5 really means 5. Similarly, token position 7 visually belongs to 6, while 8 is really 8. That means in order to find the correct candidates you have to change the token index based on the type of the token that immediately preceeds the caret token. | ||
Each vertical line corresponds to a possible caret position. The first 3 lines clearly belong to token index 0, but the next line is no longer that clear. At that position we already are on token index 1 while visually the caret still belongs to index 0, because it could be that we are just at the end of a word and want to add more letters to it and hence have to provide candidates for that word. However, for token position 5 the situation is different. After the equal sign there are no possible further characters that could belong to it, so in this case position 5 really means 5. Similarly, token position 7 visually belongs to 6, while 8 is really 8. That means in order to find the correct candidates you have to change the token index based on the type of the token that immediately precedes the caret token. | ||
@@ -207,6 +207,24 @@ Things get really tricky however, when your grammar never stores whitespaces (i.e. when using the `skip` lexer action). In that case you won't get token indexes for whitespaces, as demonstrated in the second index line in the image. In such a scenario you cannot even tell (e.g. for token position 1) whether you still have to complete the `var` keyword or want candidates for the `a`. Also the position between the two whitespaces is unclear, since you have no token index for that and have to use other indicators to decide if that position should go to index 3 (`b`) or 4 (`+`). Given these problems it is probably better not to use the `skip` action for your whitespace rule, but simply put whitespaces on a hidden channel instead. | ||
* `showResult`: Set this field to true to print a summary of what has been processed and collected. It will print the number of visited ATN states as well as all collected tokens and rules (along with their additional info). | ||
* `showDebugOutput`: This setting enables output of states and symbols (labels) seen for transations as they are processed by the engine. There will also be lines showing when input is consumed and candidates are added to the result. | ||
* `debugOutputWithTransitions`: This setting only has an effect if `showDebugOutput` is enabled. It adds all transitiions to the output which the engine encountered (not all of them are actually followed, however). | ||
* `showDebugOutput`: This setting enables output of states and symbols (labels) seen for transitions as they are processed by the engine. There will also be lines showing when input is consumed and candidates are added to the result. | ||
* `debugOutputWithTransitions`: This setting only has an effect if `showDebugOutput` is enabled. It adds all transitions to the output which the engine encountered (not all of them are actually followed, however). | ||
* `showRuleStack`: Also this setting only has an effect if `showDebugOutput` is enabled. It will make the engine print the current rule stack whenever it enters a new rule during the walk. | ||
The last two options potentially create a lot of output which can significantly slow down the collection process. | ||
## Release Notes | ||
### 1.1.6 | ||
- Added Java port from Nick Stephen. | ||
- Added contributors.txt file. | ||
- A symbol can now store a `ParseTree` reference (which allows for terminal symbols in addition to parser rules). | ||
- Added navigation functions to `Symbol` and `ScopedSymbol` (first/last child, next/previous sibling) and added tests for that. | ||
- Fixed formatting and spelling in tests + `SymbolTable`. | ||
- Updated readme. | ||
### 1.1.1 | ||
- Travis-CI integration | ||
- Implemented completion optimizations | ||
### 1.0.4 | ||
- First public release | ||
- Added initial tests, `SymbolTable` and `CodeCompletionCore` classes |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
171744
227
1229