refactor tagClosing feature
Avoid workspace.registerAutocmd
This commit is contained in:
parent
66ae279b1a
commit
12c3a08776
2 changed files with 68 additions and 146 deletions
|
@ -2,8 +2,7 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { CancellationTokenSource, Disposable, disposeAll, Position, Range, snippetManager, window, workspace } from 'coc.nvim'
|
||||
import { TextDocumentContentChangeEvent } from 'vscode-languageserver-protocol'
|
||||
import { CancellationTokenSource, Disposable, disposeAll, Position, Range, snippetManager, events, workspace, InsertChange } from 'coc.nvim'
|
||||
import * as Proto from '../protocol'
|
||||
import { ITypeScriptServiceClient } from '../typescriptService'
|
||||
import API from '../utils/api'
|
||||
|
@ -19,49 +18,82 @@ export default class TagClosing implements Disposable {
|
|||
}
|
||||
|
||||
private _disposables: Disposable[] = []
|
||||
private _enabled: boolean = false
|
||||
private _disposed = false
|
||||
private _timeout: NodeJS.Timer | undefined = undefined
|
||||
private _cancel: CancellationTokenSource | undefined = undefined
|
||||
private lastInsert: string
|
||||
|
||||
constructor(
|
||||
private readonly client: ITypeScriptServiceClient,
|
||||
private readonly descriptionLanguageId: string
|
||||
) {
|
||||
workspace.onDidChangeTextDocument(
|
||||
(event) =>
|
||||
this.onDidChangeTextDocument(
|
||||
event.textDocument,
|
||||
event.contentChanges
|
||||
),
|
||||
null,
|
||||
this._disposables
|
||||
)
|
||||
|
||||
this.updateEnabledState()
|
||||
|
||||
workspace.registerAutocmd({
|
||||
event: ['BufEnter'],
|
||||
request: false,
|
||||
callback: () => this.updateEnabledState(),
|
||||
})
|
||||
events.on('InsertCharPre', character => {
|
||||
this.lastInsert = character
|
||||
}, null, this._disposables)
|
||||
events.on('TextChangedI', this.onChange, this, this._disposables)
|
||||
events.on('TextChangedP', this.onChange, this, this._disposables)
|
||||
}
|
||||
|
||||
async updateEnabledState(): Promise<void> {
|
||||
this._enabled = false
|
||||
const doc = await workspace.document
|
||||
if (!doc) {
|
||||
return
|
||||
private async onChange(bufnr: number, change: InsertChange): Promise<void> {
|
||||
let doc = workspace.getDocument((bufnr))
|
||||
if (!doc || !doc.attached) return
|
||||
let enabled = this.isEnabled(doc.filetype, doc.uri)
|
||||
if (!enabled) return
|
||||
let { pre, changedtick, lnum } = change
|
||||
if (!pre.endsWith('/') && !pre.endsWith('>')) return
|
||||
if (!pre.endsWith(this.lastInsert)) return
|
||||
if (pre.length > 1 && pre[pre.length - 2] == '>') return
|
||||
const filepath = this.client.toOpenedFilePath(doc.uri)
|
||||
if (!filepath) return
|
||||
if (this._timeout) {
|
||||
clearTimeout(this._timeout)
|
||||
}
|
||||
const document = doc.textDocument
|
||||
const configLang = TagClosing._configurationLanguages[document.languageId]
|
||||
if (this._cancel) {
|
||||
this._cancel.cancel()
|
||||
this._cancel.dispose()
|
||||
this._cancel = undefined
|
||||
}
|
||||
await (doc as any).patchChange()
|
||||
this._timeout = setTimeout(async () => {
|
||||
this._timeout = undefined
|
||||
if (this._disposed) return
|
||||
if (doc.changedtick > changedtick) return
|
||||
const position = Position.create(lnum - 1, pre.length)
|
||||
const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(
|
||||
filepath,
|
||||
position
|
||||
)
|
||||
this._cancel = new CancellationTokenSource()
|
||||
const response = await this.client.execute(
|
||||
'jsxClosingTag',
|
||||
args,
|
||||
this._cancel.token
|
||||
)
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return
|
||||
}
|
||||
if (this._disposed) return
|
||||
|
||||
const insertion = response.body
|
||||
if (doc.changedtick === changedtick) {
|
||||
snippetManager.insertSnippet(
|
||||
this.getTagSnippet(insertion).value,
|
||||
false,
|
||||
Range.create(position, position)
|
||||
)
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
|
||||
private isEnabled(languageId: string, uri: string): boolean {
|
||||
const configLang = TagClosing._configurationLanguages[languageId]
|
||||
if (!configLang || configLang !== this.descriptionLanguageId) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
if (!workspace.getConfiguration(undefined, document.uri).get<boolean>(`${configLang}.autoClosingTags`)) {
|
||||
return
|
||||
if (!workspace.getConfiguration(undefined, uri).get<boolean>(`${configLang}.autoClosingTags`)) {
|
||||
return false
|
||||
}
|
||||
this._enabled = true
|
||||
return true
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
|
@ -82,120 +114,10 @@ export default class TagClosing implements Disposable {
|
|||
this._disposables = []
|
||||
}
|
||||
|
||||
private async onDidChangeTextDocument(
|
||||
documentEvent: {
|
||||
uri: string,
|
||||
version: number,
|
||||
},
|
||||
changes: readonly TextDocumentContentChangeEvent[]
|
||||
) {
|
||||
if (!this._enabled) {
|
||||
return
|
||||
}
|
||||
const document = await workspace.document
|
||||
if (!document) {
|
||||
return
|
||||
}
|
||||
const activeDocument = document.textDocument
|
||||
if (activeDocument.uri !== documentEvent.uri || changes.length === 0) {
|
||||
return
|
||||
}
|
||||
const filepath = this.client.toOpenedFilePath(documentEvent.uri)
|
||||
if (!filepath) {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof this._timeout !== 'undefined') {
|
||||
clearTimeout(this._timeout)
|
||||
}
|
||||
|
||||
if (this._cancel) {
|
||||
this._cancel.cancel()
|
||||
this._cancel.dispose()
|
||||
this._cancel = undefined
|
||||
}
|
||||
|
||||
const lastChange = changes[changes.length - 1]
|
||||
if (!Range.is(lastChange['range']) || !lastChange.text) {
|
||||
return
|
||||
}
|
||||
|
||||
const lastCharacter = lastChange.text[lastChange.text.length - 1]
|
||||
if (lastCharacter !== '>' && lastCharacter !== '/') {
|
||||
return
|
||||
}
|
||||
|
||||
const version = documentEvent.version
|
||||
|
||||
const rangeStart = lastChange['range'].start
|
||||
const priorCharacter =
|
||||
lastChange['range'].start.character > 0
|
||||
? activeDocument.getText(
|
||||
Range.create(
|
||||
Position.create(rangeStart.line, rangeStart.character - 1),
|
||||
rangeStart
|
||||
)
|
||||
)
|
||||
: ''
|
||||
if (priorCharacter === '>') {
|
||||
return
|
||||
}
|
||||
|
||||
this._timeout = setTimeout(async () => {
|
||||
this._timeout = undefined
|
||||
|
||||
if (this._disposed) {
|
||||
return
|
||||
}
|
||||
|
||||
const addedLines = lastChange.text.split(/\r\n|\n/g)
|
||||
const position =
|
||||
addedLines.length <= 1
|
||||
? Position.create(
|
||||
rangeStart.line,
|
||||
rangeStart.character + lastChange.text.length
|
||||
)
|
||||
: Position.create(
|
||||
rangeStart.line + addedLines.length - 1,
|
||||
addedLines[addedLines.length - 1].length
|
||||
)
|
||||
|
||||
const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(
|
||||
filepath,
|
||||
position
|
||||
)
|
||||
this._cancel = new CancellationTokenSource()
|
||||
const response = await this.client.execute(
|
||||
'jsxClosingTag',
|
||||
args,
|
||||
this._cancel.token
|
||||
)
|
||||
if (response.type !== 'response' || !response.body) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._disposed) {
|
||||
return
|
||||
}
|
||||
|
||||
const insertion = response.body;
|
||||
if (
|
||||
documentEvent.uri === activeDocument.uri &&
|
||||
activeDocument.version === version
|
||||
) {
|
||||
snippetManager.insertSnippet(
|
||||
this.getTagSnippet(insertion).value,
|
||||
false,
|
||||
Range.create(position, position)
|
||||
)
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
private getTagSnippet(closingTag: Proto.TextInsertion): SnippetString {
|
||||
const snippet = new SnippetString();
|
||||
snippet.appendPlaceholder('', 0);
|
||||
snippet.appendText(closingTag.newText);
|
||||
return snippet;
|
||||
const snippet = new SnippetString()
|
||||
snippet.appendPlaceholder('', 0)
|
||||
snippet.appendText(closingTag.newText)
|
||||
return snippet
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ export default class LanguageProvider {
|
|||
public client: TypeScriptServiceClient,
|
||||
private readonly fileConfigurationManager: FileConfigurationManager,
|
||||
private description: LanguageDescription,
|
||||
private typingsStatus: TypingsStatus
|
||||
typingsStatus: TypingsStatus
|
||||
) {
|
||||
workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables)
|
||||
this.configurationChanged()
|
||||
|
|
Loading…
Reference in a new issue