1
0
Fork 0
mirror of synced 2024-06-26 02:31:09 -04:00
ultimate-vim/sources_non_forked/coc.nvim/src/model/chars.ts
2022-07-20 13:20:15 +08:00

190 lines
5.5 KiB
TypeScript

'use strict'
import { Position, CancellationToken } from 'vscode-languageserver-protocol'
import { waitImmediate } from '../util'
import { TextDocument } from 'vscode-languageserver-textdocument'
const logger = require('../util/logger')('model-chars')
export class Range {
public start: number
public end: number
constructor(start: number, end?: number) {
this.start = start
this.end = end ? end : start
}
public static fromKeywordOption(keywordOption: string): Range[] {
let parts = keywordOption.split(',')
let ranges: Range[] = []
ranges.push(new Range(65, 90))
ranges.push(new Range(97, 122))
for (let part of parts) {
if (part == '@') {
ranges.push(new Range(256, 65535))
} else if (part == '@-@') {
ranges.push(new Range(64))
} else if (/^\d+-\d+$/.test(part)) {
let ms = part.match(/^(\d+)-(\d+)$/)
ranges.push(new Range(Number(ms[1]), Number(ms[2])))
} else if (/^\d+$/.test(part)) {
ranges.push(new Range(Number(part)))
} else {
let c = part.charCodeAt(0)
if (!ranges.some(o => o.contains(c))) {
ranges.push(new Range(c))
}
}
}
return ranges
}
public contains(c: number): boolean {
return c >= this.start && c <= this.end
}
}
export class Chars {
public ranges: Range[] = []
constructor(keywordOption?: string) {
if (keywordOption) this.ranges = Range.fromKeywordOption(keywordOption)
}
public addKeyword(ch: string): void {
let c = ch.charCodeAt(0)
let { ranges } = this
if (!ranges.some(o => o.contains(c))) {
ranges.push(new Range(c))
}
}
public clone(): Chars {
let chars = new Chars()
chars.ranges = this.ranges.slice()
return chars
}
public setKeywordOption(keywordOption: string): void {
this.ranges = Range.fromKeywordOption(keywordOption)
}
public async matchLines(lines: ReadonlyArray<string>, min = 2, token?: CancellationToken): Promise<Set<string> | undefined> {
let res: Set<string> = new Set()
let ts = Date.now()
for (let line of lines) {
if (line.length === 0) continue
let str = ''
if (Date.now() - ts > 15) {
await waitImmediate()
ts = Date.now()
}
for (let codePoint of line) {
if (token && token.isCancellationRequested) return undefined
let code = codePoint.codePointAt(0)
let isKeyword = this.isKeywordCode(code)
if (isKeyword) {
str = str + codePoint
} else {
if (str.length > 0) {
if (str.length >= min && str.length < 48) res.add(str)
str = ''
}
}
}
if (str.length >= min && str.length < 48) res.add(str)
}
return res
}
public isKeywordCode(code: number): boolean {
if (code > 255) return true
if (code < 33) return false
return this.ranges.some(r => r.contains(code))
}
public isKeywordChar(ch: string): boolean {
let { ranges } = this
if (/\s/.test(ch)) return false
let c = ch.charCodeAt(0)
if (c < 33) return false
return ranges.some(r => r.contains(c))
}
public isKeyword(word: string): boolean {
for (let i = 0, l = word.length; i < l; i++) {
if (!this.isKeywordChar(word[i])) return false
}
return true
}
public getLocalifyBonus(sp: Position, ep: Position, lines: ReadonlyArray<string>, max = 10 * 1024): Map<string, number> {
let res: Map<string, number> = new Map()
let startLine = Math.max(0, sp.line - 50)
let endLine = Math.min(lines.length, sp.line + 50)
let content = lines.slice(startLine, endLine).join('\n')
// limit content to parse
if (content.length > max) {
let len = content.length
let finished = false
while (endLine > sp.line + 1) {
let length = lines[endLine - 1].length
if (len - length < max) {
finished = true
break
}
endLine = endLine - 1
len -= length
}
if (!finished) {
while (startLine <= sp.line) {
let length = lines[startLine].length
if (len - length < max) {
break
}
len -= length
startLine += 1
}
}
content = lines.slice(startLine, endLine).join('\n')
}
sp = Position.create(sp.line - startLine, sp.character)
ep = Position.create(ep.line - startLine, ep.character)
let doc = TextDocument.create('', '', 1, content)
let headCount = doc.offsetAt(sp)
let len = content.length
let tailCount = len - doc.offsetAt(ep)
let start = 0
let preKeyword = false
for (let i = 0; i < headCount; i++) {
let iskeyword = this.isKeyword(content[i])
if (!preKeyword && iskeyword) {
start = i
} else if (preKeyword && !iskeyword) {
if (i - start > 1) {
let str = content.substring(start, i)
res.set(str, i / headCount)
}
}
preKeyword = iskeyword
}
start = len - tailCount
preKeyword = false
for (let i = start; i < content.length; i++) {
let iskeyword = this.isKeyword(content[i])
if (!preKeyword && iskeyword) {
start = i
} else if (preKeyword && (!iskeyword || i == len - 1)) {
if (i - start > 1) {
let end = i == len - 1 ? i + 1 : i
let str = content.substring(start, end)
let score = res.get(str) || 0
let n = len - i + (end - start)
if (n !== tailCount) {
res.set(str, Math.max(score, n / tailCount))
}
}
}
preKeyword = iskeyword
}
return res
}
}