Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/preferences/common/smartSnippetInserter.ts
3296 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 { JSONScanner, createScanner as createJSONScanner, SyntaxKind as JSONSyntaxKind } from '../../../../base/common/json.js';
7
import { Position } from '../../../../editor/common/core/position.js';
8
import { Range } from '../../../../editor/common/core/range.js';
9
import { ITextModel } from '../../../../editor/common/model.js';
10
11
export interface InsertSnippetResult {
12
position: Position;
13
prepend: string;
14
append: string;
15
}
16
17
export class SmartSnippetInserter {
18
19
private static hasOpenBrace(scanner: JSONScanner): boolean {
20
21
while (scanner.scan() !== JSONSyntaxKind.EOF) {
22
const kind = scanner.getToken();
23
24
if (kind === JSONSyntaxKind.OpenBraceToken) {
25
return true;
26
}
27
}
28
29
return false;
30
}
31
32
private static offsetToPosition(model: ITextModel, offset: number): Position {
33
let offsetBeforeLine = 0;
34
const eolLength = model.getEOL().length;
35
const lineCount = model.getLineCount();
36
for (let lineNumber = 1; lineNumber <= lineCount; lineNumber++) {
37
const lineTotalLength = model.getLineLength(lineNumber) + eolLength;
38
const offsetAfterLine = offsetBeforeLine + lineTotalLength;
39
40
if (offsetAfterLine > offset) {
41
return new Position(
42
lineNumber,
43
offset - offsetBeforeLine + 1
44
);
45
}
46
offsetBeforeLine = offsetAfterLine;
47
}
48
return new Position(
49
lineCount,
50
model.getLineMaxColumn(lineCount)
51
);
52
}
53
54
static insertSnippet(model: ITextModel, _position: Position): InsertSnippetResult {
55
56
const desiredPosition = model.getValueLengthInRange(new Range(1, 1, _position.lineNumber, _position.column));
57
58
// <INVALID> [ <BEFORE_OBJECT> { <INVALID> } <AFTER_OBJECT>, <BEFORE_OBJECT> { <INVALID> } <AFTER_OBJECT> ] <INVALID>
59
enum State {
60
INVALID = 0,
61
AFTER_OBJECT = 1,
62
BEFORE_OBJECT = 2,
63
}
64
let currentState = State.INVALID;
65
let lastValidPos = -1;
66
let lastValidState = State.INVALID;
67
68
const scanner = createJSONScanner(model.getValue());
69
let arrayLevel = 0;
70
let objLevel = 0;
71
72
const checkRangeStatus = (pos: number, state: State) => {
73
if (state !== State.INVALID && arrayLevel === 1 && objLevel === 0) {
74
currentState = state;
75
lastValidPos = pos;
76
lastValidState = state;
77
} else {
78
if (currentState !== State.INVALID) {
79
currentState = State.INVALID;
80
lastValidPos = scanner.getTokenOffset();
81
}
82
}
83
};
84
85
while (scanner.scan() !== JSONSyntaxKind.EOF) {
86
const currentPos = scanner.getPosition();
87
const kind = scanner.getToken();
88
89
let goodKind = false;
90
switch (kind) {
91
case JSONSyntaxKind.OpenBracketToken:
92
goodKind = true;
93
arrayLevel++;
94
checkRangeStatus(currentPos, State.BEFORE_OBJECT);
95
break;
96
case JSONSyntaxKind.CloseBracketToken:
97
goodKind = true;
98
arrayLevel--;
99
checkRangeStatus(currentPos, State.INVALID);
100
break;
101
case JSONSyntaxKind.CommaToken:
102
goodKind = true;
103
checkRangeStatus(currentPos, State.BEFORE_OBJECT);
104
break;
105
case JSONSyntaxKind.OpenBraceToken:
106
goodKind = true;
107
objLevel++;
108
checkRangeStatus(currentPos, State.INVALID);
109
break;
110
case JSONSyntaxKind.CloseBraceToken:
111
goodKind = true;
112
objLevel--;
113
checkRangeStatus(currentPos, State.AFTER_OBJECT);
114
break;
115
case JSONSyntaxKind.Trivia:
116
case JSONSyntaxKind.LineBreakTrivia:
117
goodKind = true;
118
}
119
120
if (currentPos >= desiredPosition && (currentState !== State.INVALID || lastValidPos !== -1)) {
121
let acceptPosition: number;
122
let acceptState: State;
123
124
if (currentState !== State.INVALID) {
125
acceptPosition = (goodKind ? currentPos : scanner.getTokenOffset());
126
acceptState = currentState;
127
} else {
128
acceptPosition = lastValidPos;
129
acceptState = lastValidState;
130
}
131
132
if (acceptState as State === State.AFTER_OBJECT) {
133
return {
134
position: this.offsetToPosition(model, acceptPosition),
135
prepend: ',',
136
append: ''
137
};
138
} else {
139
scanner.setPosition(acceptPosition);
140
return {
141
position: this.offsetToPosition(model, acceptPosition),
142
prepend: '',
143
append: this.hasOpenBrace(scanner) ? ',' : ''
144
};
145
}
146
}
147
}
148
149
// no valid position found!
150
const modelLineCount = model.getLineCount();
151
return {
152
position: new Position(modelLineCount, model.getLineMaxColumn(modelLineCount)),
153
prepend: '\n[',
154
append: ']'
155
};
156
}
157
}
158
159