Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/emmet/src/balance.ts
4772 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as vscode from 'vscode';
7
import { getHtmlFlatNode, offsetRangeToSelection, validate } from './util';
8
import { getRootNode } from './parseDocument';
9
import { HtmlNode as HtmlFlatNode } from 'EmmetFlatNode';
10
11
let balanceOutStack: Array<readonly vscode.Selection[]> = [];
12
let lastBalancedSelections: readonly vscode.Selection[] = [];
13
14
export function balanceOut() {
15
balance(true);
16
}
17
18
export function balanceIn() {
19
balance(false);
20
}
21
22
function balance(out: boolean) {
23
if (!validate(false) || !vscode.window.activeTextEditor) {
24
return;
25
}
26
const editor = vscode.window.activeTextEditor;
27
const document = editor.document;
28
const rootNode = <HtmlFlatNode>getRootNode(document, true);
29
if (!rootNode) {
30
return;
31
}
32
33
const rangeFn = out ? getRangeToBalanceOut : getRangeToBalanceIn;
34
let newSelections: readonly vscode.Selection[] = editor.selections.map(selection => {
35
return rangeFn(document, rootNode, selection);
36
});
37
38
// check whether we are starting a balance elsewhere
39
if (areSameSelections(lastBalancedSelections, editor.selections)) {
40
// we are not starting elsewhere, so use the stack as-is
41
if (out) {
42
// make sure we are able to expand outwards
43
if (!areSameSelections(editor.selections, newSelections)) {
44
balanceOutStack.push(editor.selections);
45
}
46
} else if (balanceOutStack.length) {
47
newSelections = balanceOutStack.pop()!;
48
}
49
} else {
50
// we are starting elsewhere, so reset the stack
51
balanceOutStack = out ? [editor.selections] : [];
52
}
53
54
editor.selections = newSelections;
55
lastBalancedSelections = editor.selections;
56
}
57
58
function getRangeToBalanceOut(document: vscode.TextDocument, rootNode: HtmlFlatNode, selection: vscode.Selection): vscode.Selection {
59
const offset = document.offsetAt(selection.start);
60
const nodeToBalance = getHtmlFlatNode(document.getText(), rootNode, offset, false);
61
if (!nodeToBalance) {
62
return selection;
63
}
64
if (!nodeToBalance.open || !nodeToBalance.close) {
65
return offsetRangeToSelection(document, nodeToBalance.start, nodeToBalance.end);
66
}
67
68
// Set reverse direction if we were in the end tag
69
let innerSelection: vscode.Selection;
70
let outerSelection: vscode.Selection;
71
if (nodeToBalance.close.start <= offset && nodeToBalance.close.end > offset) {
72
innerSelection = offsetRangeToSelection(document, nodeToBalance.close.start, nodeToBalance.open.end);
73
outerSelection = offsetRangeToSelection(document, nodeToBalance.close.end, nodeToBalance.open.start);
74
}
75
else {
76
innerSelection = offsetRangeToSelection(document, nodeToBalance.open.end, nodeToBalance.close.start);
77
outerSelection = offsetRangeToSelection(document, nodeToBalance.open.start, nodeToBalance.close.end);
78
}
79
80
if (innerSelection.contains(selection) && !innerSelection.isEqual(selection)) {
81
return innerSelection;
82
}
83
if (outerSelection.contains(selection) && !outerSelection.isEqual(selection)) {
84
return outerSelection;
85
}
86
return selection;
87
}
88
89
function getRangeToBalanceIn(document: vscode.TextDocument, rootNode: HtmlFlatNode, selection: vscode.Selection): vscode.Selection {
90
const offset = document.offsetAt(selection.start);
91
const nodeToBalance = getHtmlFlatNode(document.getText(), rootNode, offset, true);
92
if (!nodeToBalance) {
93
return selection;
94
}
95
96
const selectionStart = document.offsetAt(selection.start);
97
const selectionEnd = document.offsetAt(selection.end);
98
if (nodeToBalance.open && nodeToBalance.close) {
99
const entireNodeSelected = selectionStart === nodeToBalance.start && selectionEnd === nodeToBalance.end;
100
const startInOpenTag = selectionStart > nodeToBalance.open.start && selectionStart < nodeToBalance.open.end;
101
const startInCloseTag = selectionStart > nodeToBalance.close.start && selectionStart < nodeToBalance.close.end;
102
103
if (entireNodeSelected || startInOpenTag || startInCloseTag) {
104
return offsetRangeToSelection(document, nodeToBalance.open.end, nodeToBalance.close.start);
105
}
106
}
107
108
if (!nodeToBalance.firstChild) {
109
return selection;
110
}
111
112
const firstChild = nodeToBalance.firstChild;
113
if (selectionStart === firstChild.start
114
&& selectionEnd === firstChild.end
115
&& firstChild.open
116
&& firstChild.close) {
117
return offsetRangeToSelection(document, firstChild.open.end, firstChild.close.start);
118
}
119
120
return offsetRangeToSelection(document, firstChild.start, firstChild.end);
121
}
122
123
function areSameSelections(a: readonly vscode.Selection[], b: readonly vscode.Selection[]): boolean {
124
if (a.length !== b.length) {
125
return false;
126
}
127
for (let i = 0; i < a.length; i++) {
128
if (!a[i].isEqual(b[i])) {
129
return false;
130
}
131
}
132
return true;
133
}
134
135