mirror of
1
0
Fork 0
ultimate-vim/sources_non_forked/coc.nvim/src/__tests__/handler/parser.ts

119 lines
3.6 KiB
TypeScript

import { DocumentSymbol, Range, SymbolKind } from 'vscode-languageserver-protocol'
import { TextDocument } from 'vscode-languageserver-textdocument'
/**
* A syntax parser that parse `class` and `method` only.
*/
export default class Parser {
private _curr = 0
private _symbols: DocumentSymbol[] = []
private currSymbol: DocumentSymbol | undefined
private len: number
private textDocument: TextDocument
constructor(private _content: string, private showDetail = false) {
this.len = _content.length
this.textDocument = TextDocument.create('test:///a', 'txt', 1, _content)
}
public parse(): DocumentSymbol[] {
while (this._curr <= this.len - 1) {
this.parseToken()
}
return this._symbols
}
/**
* Parse a symbol, reset currSymbol & _curr
*/
private parseToken(): void {
this.skipSpaces()
if (this.currSymbol) {
let endOffset = this.textDocument.offsetAt(this.currSymbol.range.end)
if (this._curr > endOffset) {
this.currSymbol = undefined
}
}
let remain = this.getLineRemain()
let ms = remain.match(/^(class)\s(\w+)\s\{\s*/)
if (ms) {
// find class
let start = this._curr + 6
let end = start + ms[2].length
let selectionRange = Range.create(this.textDocument.positionAt(start), this.textDocument.positionAt(end))
let endPosition = this.findMatchedIndex(this._curr + ms[0].length)
let range = Range.create(this.textDocument.positionAt(this._curr), this.textDocument.positionAt(endPosition))
let symbolInfo: DocumentSymbol = {
range,
selectionRange,
kind: SymbolKind.Class,
name: ms[2],
children: []
}
if (this.currSymbol && this.currSymbol.children) {
this.currSymbol.children.push(symbolInfo)
} else {
this._symbols.push(symbolInfo)
}
this.currSymbol = symbolInfo
} else if (this.currSymbol && this.currSymbol.kind == SymbolKind.Class) {
let ms = remain.match(/(\w+)\((.*)\)\s*\{/)
if (ms) {
// find method
let start = this._curr
let end = start + ms[1].length
let selectionRange = Range.create(this.textDocument.positionAt(start), this.textDocument.positionAt(end))
let endPosition = this.findMatchedIndex(this._curr + ms[0].length)
let range = Range.create(this.textDocument.positionAt(this._curr), this.textDocument.positionAt(endPosition))
let symbolInfo: DocumentSymbol = {
range,
selectionRange,
kind: SymbolKind.Method,
detail: this.showDetail ? `(${ms[2]})` : undefined,
name: ms[1]
}
if (this.currSymbol && this.currSymbol.children) {
this.currSymbol.children.push(symbolInfo)
} else {
this._symbols.push(symbolInfo)
}
}
}
this._curr = this._curr + remain.length + 1
}
private findMatchedIndex(start: number): number {
let level = 0
for (let i = start; i < this.len; i++) {
let ch = this._content[i]
if (ch == '{') {
level = level + 1
}
if (ch == '}') {
if (level == 0) return i
level = level - 1
}
}
throw new Error(`Can't find matched }`)
}
private getLineRemain(): string {
let chars = ''
for (let i = this._curr; i < this.len; i++) {
let ch = this._content[i]
if (ch == '\n') break
chars = chars + ch
}
return chars
}
private skipSpaces(): void {
for (let i = this._curr; i < this.len; i++) {
let ch = this._content[i]
if (!ch || /\S/.test(ch)) {
this._curr = i
break
}
}
}
}