Skip to content

Commit

Permalink
Automatically closing tags in multi-cursor mode only closes one tag
Browse files Browse the repository at this point in the history
Fixes #225

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Jun 4, 2021
1 parent 2eb21e2 commit 29a1cf7
Showing 1 changed file with 82 additions and 80 deletions.
162 changes: 82 additions & 80 deletions src/client/tagClosing.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*
*
* Retrieved from: https://github.com/Microsoft/vscode/blob/f707828426bd87e88c17d2da34f2ceed0019d8bd/extensions/html-language-features/client/src/tagClosing.ts
*--------------------------------------------------------------------------------------------*/
'use strict';
Expand All @@ -14,87 +14,89 @@ export interface AutoCloseResult {
}

export function activateTagClosing(tagProvider: (document: TextDocument, position: Position) => Thenable<AutoCloseResult>, supportedLanguages: { [id: string]: boolean }, configName: string): Disposable {
const TRIGGER_CHARACTERS = ['>', '/'];
let disposables: Disposable[] = [];
workspace.onDidChangeTextDocument(event => onDidChangeTextDocument(event.document, event.contentChanges), null, disposables);

let isEnabled = false;
updateEnabledState();
window.onDidChangeActiveTextEditor(updateEnabledState, null, disposables);
let disposables: Disposable[] = [];
workspace.onDidChangeTextDocument(event => onDidChangeTextDocument(event.document, event.contentChanges), null, disposables);

let timeout: NodeJS.Timer | undefined = void 0;
let isEnabled = false;
updateEnabledState();
window.onDidChangeActiveTextEditor(updateEnabledState, null, disposables);

function updateEnabledState() {
isEnabled = false;
let editor = window.activeTextEditor;
if (!editor) {
return;
}
let document = editor.document;
if (!supportedLanguages[document.languageId]) {
return;
}
if (!workspace.getConfiguration(void 0, document.uri).get<boolean>(configName)) {
return;
}
isEnabled = true;
}
let timeout: NodeJS.Timer | undefined = undefined;

function onDidChangeTextDocument(document: TextDocument, changes: ReadonlyArray<TextDocumentContentChangeEvent>) {
if (!isEnabled) {
return;
}
let activeDocument = window.activeTextEditor && window.activeTextEditor.document;
if (document !== activeDocument || changes.length === 0) {
return;
}
if (typeof timeout !== 'undefined') {
clearTimeout(timeout);
}
let lastChange = changes[changes.length - 1];
let lastCharacter = lastChange.text[lastChange.text.length - 1];
if (lastChange.rangeLength > 0 || lastChange.text.length > 1 || TRIGGER_CHARACTERS.indexOf(lastCharacter) < 0) {
return;
}
let rangeStart = lastChange.range.start;
let version = document.version;
timeout = setTimeout(() => {
let position = new Position(rangeStart.line, rangeStart.character + lastChange.text.length);
tagProvider(document, position).then(result => {
if (!result) {
return;
}
let text = result.snippet;
let replaceLocation : Position | Range;
let range : Range = result.range;
if(range != null) {
// re-create Range
let line = range.start.line;
let character = range.start.character;
let startPosition = new Position(line, character);
line = range.end.line;
character = range.end.character;
let endPosition = new Position(line, character);
replaceLocation = new Range(startPosition, endPosition);
}
else {
replaceLocation = position;
}
if (text && isEnabled) {
let activeEditor = window.activeTextEditor;
if (activeEditor) {
let activeDocument = activeEditor.document;
if (document === activeDocument && activeDocument.version === version) {
activeEditor.insertSnippet(new SnippetString(text), replaceLocation);

}
}
}
}, (reason: any) => {
console.log('xml/closeTag request has been cancelled');
});
timeout = void 0;
}, 100);
}
return Disposable.from(...disposables);
function updateEnabledState() {
isEnabled = false;
let editor = window.activeTextEditor;
if (!editor) {
return;
}
let document = editor.document;
if (!supportedLanguages[document.languageId]) {
return;
}
if (!workspace.getConfiguration(undefined, document.uri).get<boolean>(configName)) {
return;
}
isEnabled = true;
}

function onDidChangeTextDocument(document: TextDocument, changes: ReadonlyArray<TextDocumentContentChangeEvent>) {
if (!isEnabled) {
return;
}
let activeDocument = window.activeTextEditor && window.activeTextEditor.document;
if (document !== activeDocument || changes.length === 0) {
return;
}
if (typeof timeout !== 'undefined') {
clearTimeout(timeout);
}
let lastChange = changes[changes.length - 1];
let lastCharacter = lastChange.text[lastChange.text.length - 1];
if (lastChange.rangeLength > 0 || lastCharacter !== '>' && lastCharacter !== '/') {
return;
}
let rangeStart = lastChange.range.start;
let version = document.version;
timeout = setTimeout(() => {
let position = new Position(rangeStart.line, rangeStart.character + lastChange.text.length);
tagProvider(document, position).then(result => {
const text = result?.snippet;
if (text && isEnabled) {
let activeEditor = window.activeTextEditor;
if (activeEditor) {
let activeDocument = activeEditor.document;
if (document === activeDocument && activeDocument.version === version) {
let selections = activeEditor.selections;
if (selections.length && selections.length > 1 && selections.some(s => s.active.isEqual(position))) {
// multiple cusror case
activeEditor.insertSnippet(new SnippetString(text), selections.map(s => s.active));
} else {
// no multiple cursor we use the range coming from le LSP closeTag request to support 'xml.completion.autoCloseRemovesContent' setting
activeEditor.insertSnippet(new SnippetString(text), getReplaceLocation(result.range, position));
}
}
}
}
}, (reason: any) => {
console.log('xml/closeTag request has been cancelled');
});
timeout = void 0;
}, 100);
}
return Disposable.from(...disposables);
}

function getReplaceLocation(range: Range, position: Position): Range | Position {
if (range != null) {
// re-create Range
let line = range.start.line;
let character = range.start.character;
let startPosition = new Position(line, character);
line = range.end.line;
character = range.end.character;
let endPosition = new Position(line, character);
return new Range(startPosition, endPosition);
}
return position;
}

0 comments on commit 29a1cf7

Please sign in to comment.