1
0
Fork 0
mirror of synced 2024-06-25 18:21:11 -04:00
ultimate-vim/sources_non_forked/coc.nvim/src/cursors/index.ts
2022-07-20 13:20:15 +08:00

127 lines
4.4 KiB
TypeScript

'use strict'
import { Neovim } from '@chemzqm/neovim'
import { Disposable, Range } from 'vscode-languageserver-protocol'
import Document from '../model/document'
import { comparePosition } from '../util/position'
import window from '../window'
import workspace from '../workspace'
import CursorSession from './session'
import { getVisualRanges, splitRange } from './util'
const logger = require('../util/logger')('cursors')
export default class Cursors {
private sessionsMap: Map<number, CursorSession> = new Map()
private disposables: Disposable[] = []
constructor(private nvim: Neovim) {
workspace.onDidCloseTextDocument(e => {
let session = this.getSession(e.bufnr)
if (!session) return
this.sessionsMap.delete(e.bufnr)
session.cancel()
}, null, this.disposables)
}
public cancel(uri: number | string): void {
let doc = workspace.getDocument(uri)
if (!doc) return
let session = this.getSession(doc.bufnr)
if (session) session.cancel()
}
public getSession(bufnr: number): CursorSession | undefined {
return this.sessionsMap.get(bufnr)
}
public async isActivated(): Promise<boolean> {
let bufnr = await this.nvim.call('bufnr', ['%']) as number
return this.sessionsMap.get(bufnr) != null
}
public async select(bufnr: number, kind: string, mode: string): Promise<void> {
let doc = workspace.getAttachedDocument(bufnr)
let { nvim } = this
let session = this.createSession(doc)
let pos = await window.getCursorPosition()
let range: Range
if (kind == 'operator') {
await nvim.command(`normal! ${mode == 'line' ? `'[` : '`['}`)
let start = await window.getCursorPosition()
await nvim.command(`normal! ${mode == 'line' ? `']` : '`]'}`)
let end = await window.getCursorPosition()
await window.moveTo(pos)
let relative = comparePosition(start, end)
// do nothing for empty range
if (relative == 0) return
if (relative >= 0) [start, end] = [end, start]
// include end character
let line = doc.getline(end.line)
if (end.character < line.length) {
end.character = end.character + 1
}
let ranges = splitRange(doc, Range.create(start, end))
session.addRanges(ranges)
} else if (kind == 'word') {
range = doc.getWordRangeAtPosition(pos)
if (!range) {
let line = doc.getline(pos.line)
if (pos.character == line.length) {
range = Range.create(pos.line, Math.max(0, line.length - 1), pos.line, line.length)
} else {
range = Range.create(pos.line, pos.character, pos.line, pos.character + 1)
}
}
session.addRange(range)
await nvim.command(`silent! call repeat#set("\\<Plug>(coc-cursors-${kind})", -1)`)
} else if (kind == 'position') {
// make sure range contains character for highlight
let line = doc.getline(pos.line)
if (pos.character >= line.length) {
range = Range.create(pos.line, line.length - 1, pos.line, line.length)
} else {
range = Range.create(pos.line, pos.character, pos.line, pos.character + 1)
}
session.addRange(range)
await nvim.command(`silent! call repeat#set("\\<Plug>(coc-cursors-${kind})", -1)`)
} else if (kind == 'range') {
await nvim.call('eval', 'feedkeys("\\<esc>", "in")')
let range = await window.getSelectedRange(mode)
if (!range) return
let ranges = mode == '\x16' ? getVisualRanges(doc, range) : splitRange(doc, range)
for (let r of ranges) {
session.addRange(r)
}
} else {
throw new Error(`select kind "${kind}" not supported`)
}
}
public createSession(doc: Document): CursorSession {
let { bufnr } = doc
let session = this.getSession(bufnr)
if (session) return session
session = new CursorSession(this.nvim, doc)
this.sessionsMap.set(bufnr, session)
session.onDidCancel(() => {
session.dispose()
this.sessionsMap.delete(bufnr)
})
return session
}
// Add ranges to current document
public async addRanges(ranges: Range[]): Promise<boolean> {
let { nvim } = this
let bufnr = await nvim.call('bufnr', ['%']) as number
let doc = workspace.getAttachedDocument(bufnr)
let session = this.createSession(doc)
return session.addRanges(ranges)
}
public reset(): void {
for (let session of this.sessionsMap.values()) {
session.cancel()
}
this.sessionsMap.clear()
}
}