@adecore/editor-core 0.39.0 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/block-folds.js +5 -2
- package/dist/bracket-tracker.js +4 -3
- package/dist/brackets.d.ts +2 -12
- package/dist/brackets.js +3 -15
- package/dist/closer-tracker.js +0 -1
- package/dist/doc-comments.js +0 -1
- package/dist/document.d.ts +2 -0
- package/dist/document.js +33 -35
- package/dist/edit-regions.js +3 -4
- package/dist/enter.d.ts +2 -0
- package/dist/enter.js +11 -9
- package/dist/fold-roles.js +2 -2
- package/dist/lexical-folds.d.ts +0 -1
- package/dist/lexical-folds.js +1 -4
- package/dist/lexical.d.ts +11 -5
- package/dist/lexical.js +13 -17
- package/dist/line-commands.js +7 -15
- package/dist/line-text.d.ts +2 -0
- package/dist/line-text.js +4 -0
- package/dist/markup-regions.js +2 -3
- package/dist/paste.js +2 -7
- package/dist/structure.js +2 -1
- package/dist/typing-context.js +5 -15
- package/package.json +2 -2
- package/src/block-folds.ts +39 -2
- package/src/bracket-tracker.ts +55 -12
- package/src/brackets.ts +35 -42
- package/src/closer-tracker.ts +6 -1
- package/src/comments.ts +103 -0
- package/src/doc-comments.ts +19 -1
- package/src/document.ts +505 -36
- package/src/edit-regions.ts +26 -4
- package/src/enter.ts +104 -9
- package/src/fold-roles.ts +34 -2
- package/src/index.ts +2 -0
- package/src/languages.ts +11 -0
- package/src/lexical-folds.ts +21 -5
- package/src/lexical.ts +31 -23
- package/src/line-commands.ts +81 -17
- package/src/line-text.ts +7 -0
- package/src/markup-regions.ts +101 -3
- package/src/offsets.ts +10 -0
- package/src/paste.ts +20 -7
- package/src/php-markup.ts +23 -0
- package/src/preserve-case.ts +12 -0
- package/src/rope.ts +70 -0
- package/src/script-source.ts +10 -0
- package/src/search.ts +38 -0
- package/src/structure.ts +29 -1
- package/src/text-span.ts +53 -0
- package/src/typing-context.ts +65 -41
- package/src/typing-handlers.ts +28 -0
- package/src/words.ts +10 -0
package/dist/block-folds.js
CHANGED
|
@@ -46,8 +46,11 @@ function markdownFolds(lineCount, getLine) {
|
|
|
46
46
|
}
|
|
47
47
|
flushTable(lineCount - 1);
|
|
48
48
|
for (const [index, heading] of headings.entries()) {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
let next = index + 1;
|
|
50
|
+
while (next < headings.length && headings[next].level > heading.level) {
|
|
51
|
+
next++;
|
|
52
|
+
}
|
|
53
|
+
let end = (headings[next]?.line ?? lineCount) - 1;
|
|
51
54
|
while (end > heading.line && getLine(end).text.trim() === '') {
|
|
52
55
|
end--;
|
|
53
56
|
}
|
package/dist/bracket-tracker.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { initialBracketState, lexBrackets, sameBracketState } from './brackets.js';
|
|
2
|
+
import { lexerRules } from './lexical.js';
|
|
2
3
|
/* How much text one stretch between checkpoints holds, which is what an edit makes the tracker lex again. */
|
|
3
4
|
const STRETCH_LENGTH = 4096;
|
|
4
5
|
const IGNORE = {
|
|
@@ -16,7 +17,7 @@ export class BracketTracker {
|
|
|
16
17
|
text;
|
|
17
18
|
stretchLength;
|
|
18
19
|
language = '';
|
|
19
|
-
rules =
|
|
20
|
+
rules = lexerRules('');
|
|
20
21
|
checkpoints = [];
|
|
21
22
|
/* The first stale checkpoint: what comes before it is proven, and so is its state. */
|
|
22
23
|
proven = 0;
|
|
@@ -107,7 +108,7 @@ export class BracketTracker {
|
|
|
107
108
|
return;
|
|
108
109
|
}
|
|
109
110
|
this.language = language;
|
|
110
|
-
this.rules =
|
|
111
|
+
this.rules = lexerRules(language);
|
|
111
112
|
this.checkpoints = [{ line: 0, state: initialBracketState(), low: 0, stale: true }];
|
|
112
113
|
this.proven = 0;
|
|
113
114
|
}
|
package/dist/brackets.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LexerRules } from './lexical.ts';
|
|
1
2
|
export interface BracketIndex {
|
|
2
3
|
pairs: ReadonlyMap<number, number>;
|
|
3
4
|
unmatched: ReadonlySet<number>;
|
|
@@ -18,23 +19,12 @@ export interface BracketState {
|
|
|
18
19
|
expression: boolean;
|
|
19
20
|
stack: OpenBracket | undefined;
|
|
20
21
|
}
|
|
21
|
-
export interface BracketRules {
|
|
22
|
-
script: boolean;
|
|
23
|
-
hash: boolean;
|
|
24
|
-
hashAtWordStart: boolean;
|
|
25
|
-
dash: boolean;
|
|
26
|
-
phpAttributes: boolean;
|
|
27
|
-
slash: boolean;
|
|
28
|
-
block: boolean;
|
|
29
|
-
markup: boolean;
|
|
30
|
-
}
|
|
31
22
|
export interface BracketVisitor {
|
|
32
23
|
open(at: number, depth: number): void;
|
|
33
24
|
close(at: number, depth: number): void;
|
|
34
25
|
mismatch(at: number): void;
|
|
35
26
|
}
|
|
36
27
|
export declare function initialBracketState(): BracketState;
|
|
37
|
-
export declare function bracketRules(language: string): BracketRules;
|
|
38
28
|
export declare function sameBracketState(left: BracketState, right: BracketState): boolean;
|
|
39
|
-
export declare function lexBrackets(state: BracketState, rules:
|
|
29
|
+
export declare function lexBrackets(state: BracketState, rules: LexerRules, text: string, from: number, to: number, offset: number, visitor: BracketVisitor): void;
|
|
40
30
|
export declare function scanBrackets(text: string, language?: string): BracketIndex;
|
package/dist/brackets.js
CHANGED
|
@@ -1,19 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isWordCharacter, lexerRules, openers, startsExpressionAfter, startsExpressionAt, startsWord } from './lexical.js';
|
|
2
2
|
export function initialBracketState() {
|
|
3
3
|
return { mode: 'code', quote: '', inClass: false, expression: true, stack: undefined };
|
|
4
4
|
}
|
|
5
|
-
export function bracketRules(language) {
|
|
6
|
-
return {
|
|
7
|
-
script: isScript(language),
|
|
8
|
-
hash: hasHashComments(language),
|
|
9
|
-
hashAtWordStart: hashNeedsWordStart(language),
|
|
10
|
-
dash: hasDashComments(language),
|
|
11
|
-
phpAttributes: isPhp(language),
|
|
12
|
-
slash: hasSlashComments(language),
|
|
13
|
-
block: hasBlockComments(language),
|
|
14
|
-
markup: hasMarkupComments(language)
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
5
|
export function sameBracketState(left, right) {
|
|
18
6
|
if (left.mode !== right.mode || left.quote !== right.quote || left.inClass !== right.inClass || left.expression !== right.expression) {
|
|
19
7
|
return false;
|
|
@@ -121,7 +109,7 @@ export function lexBrackets(state, rules, text, from, to, offset, visitor) {
|
|
|
121
109
|
}
|
|
122
110
|
else if ((rules.slash && char === '/' && next === '/') ||
|
|
123
111
|
(rules.dash && char === '-' && next === '-') ||
|
|
124
|
-
(rules.hash && char === '#' && !(rules.
|
|
112
|
+
(rules.hash && char === '#' && !(rules.php && next === '[') && (!rules.hashAtWordStart || startsWord(text, at)))) {
|
|
125
113
|
state.mode = 'line-comment';
|
|
126
114
|
}
|
|
127
115
|
else if (rules.block && char === '/' && next === '*') {
|
|
@@ -179,7 +167,7 @@ export function scanBrackets(text, language = 'typescript') {
|
|
|
179
167
|
const pairs = new Map();
|
|
180
168
|
const unmatched = new Set();
|
|
181
169
|
let open = [];
|
|
182
|
-
lexBrackets(initialBracketState(),
|
|
170
|
+
lexBrackets(initialBracketState(), lexerRules(language), text, 0, text.length, 0, {
|
|
183
171
|
open(at) {
|
|
184
172
|
open.push(at);
|
|
185
173
|
},
|
package/dist/closer-tracker.js
CHANGED
package/dist/doc-comments.js
CHANGED
|
@@ -53,7 +53,6 @@ export function findDocComments(text, language, limit = MAX_DOC_COMMENTS) {
|
|
|
53
53
|
if (range.kind !== 'comment' || commentRole((from, to) => text.slice(from, to), range.from, range.to) === undefined) {
|
|
54
54
|
continue;
|
|
55
55
|
}
|
|
56
|
-
// Three stars or more is a banner, not a comment about the code under it.
|
|
57
56
|
if (text[range.from + 3] === '*' || bodies.some(([from, to]) => range.from >= from && range.from < to)) {
|
|
58
57
|
continue;
|
|
59
58
|
}
|
package/dist/document.d.ts
CHANGED
package/dist/document.js
CHANGED
|
@@ -38,7 +38,7 @@ function sameSelections(left, right) {
|
|
|
38
38
|
return left.length === right.length && left.every((selection, index) => selection.anchor === right[index].anchor && selection.head === right[index].head);
|
|
39
39
|
}
|
|
40
40
|
function trailingBlankLength(text) {
|
|
41
|
-
return
|
|
41
|
+
return /[\t ]*$/.exec(text)[0].length;
|
|
42
42
|
}
|
|
43
43
|
function changesText(rope, edit) {
|
|
44
44
|
return edit.text.length !== edit.to - edit.from || rope.slice(edit.from, edit.to) !== edit.text;
|
|
@@ -789,10 +789,10 @@ export class DocumentModel {
|
|
|
789
789
|
return null;
|
|
790
790
|
}
|
|
791
791
|
const wrapped = surround(selected, text);
|
|
792
|
-
const reversed = selection.anchor > selection.head;
|
|
793
|
-
const start = 1;
|
|
794
792
|
const end = 1 + wrapped.inner.length;
|
|
795
|
-
return
|
|
793
|
+
return selection.anchor > selection.head
|
|
794
|
+
? { from, to, text: wrapped.text, anchor: end, head: 1 }
|
|
795
|
+
: { from, to, text: wrapped.text, anchor: 1, head: end };
|
|
796
796
|
}
|
|
797
797
|
/* A closer alone on its line goes back to the indentation of the line its opener is on. */
|
|
798
798
|
dedentCloser(offset, closer, opener) {
|
|
@@ -802,7 +802,7 @@ export class DocumentModel {
|
|
|
802
802
|
if (!/^[\t ]*$/.test(before)) {
|
|
803
803
|
return null;
|
|
804
804
|
}
|
|
805
|
-
const indent = whitespaceOf(this.
|
|
805
|
+
const indent = whitespaceOf(this.lineOf(opener).text);
|
|
806
806
|
if (indent === before) {
|
|
807
807
|
return null;
|
|
808
808
|
}
|
|
@@ -848,16 +848,14 @@ export class DocumentModel {
|
|
|
848
848
|
return false;
|
|
849
849
|
}
|
|
850
850
|
const text = this.getText();
|
|
851
|
-
|
|
852
|
-
return scanned.pairs.has(offset);
|
|
851
|
+
return scanBrackets(text.slice(0, offset) + opener + text.slice(offset), language).pairs.has(offset);
|
|
853
852
|
}
|
|
854
853
|
/*
|
|
855
854
|
* Whether the text before `offset` ends in something a member is read from: a variable, the name of a
|
|
856
855
|
* property or method after `->` or `?->`, or a `)` or `]`.
|
|
857
856
|
*/
|
|
858
857
|
endsOperand(offset) {
|
|
859
|
-
const
|
|
860
|
-
const before = this.slice(line.start, offset);
|
|
858
|
+
const before = this.slice(this.boundsOf(offset).start, offset);
|
|
861
859
|
if (/[)\]]$/.test(before)) {
|
|
862
860
|
return true;
|
|
863
861
|
}
|
|
@@ -919,9 +917,8 @@ export class DocumentModel {
|
|
|
919
917
|
return { from: offset, to: offset, text, anchor: text.length, head: text.length };
|
|
920
918
|
}), { source: 'command' });
|
|
921
919
|
}
|
|
922
|
-
/* The visual column of an offset, with tabs going to the next tab stop. */
|
|
923
920
|
columnOf(offset, tabSize) {
|
|
924
|
-
return indentationColumn(this.slice(this.
|
|
921
|
+
return indentationColumn(this.slice(this.boundsOf(offset).start, offset), tabSize);
|
|
925
922
|
}
|
|
926
923
|
moveToLineEdge(command, tabSize, visualLine) {
|
|
927
924
|
const home = command.endsWith('Home');
|
|
@@ -943,12 +940,12 @@ export class DocumentModel {
|
|
|
943
940
|
* text to the first character, and from the first character or inside the indentation to the start.
|
|
944
941
|
*/
|
|
945
942
|
homeOffset(offset, tabSize, visualLine) {
|
|
946
|
-
const line = this.
|
|
943
|
+
const line = this.lineOf(offset);
|
|
947
944
|
const row = visualLine?.(offset) ?? line;
|
|
945
|
+
const indent = this.firstNonSpace(line);
|
|
948
946
|
if (row.start > line.start) {
|
|
949
|
-
return offset > row.start ? row.start : Math.max(
|
|
947
|
+
return offset > row.start ? row.start : Math.max(indent, line.start);
|
|
950
948
|
}
|
|
951
|
-
const indent = this.firstNonSpace(line);
|
|
952
949
|
if (offset === line.start) {
|
|
953
950
|
return this.offsetAtColumn(line, this.smartIndentColumn(line.start, tabSize), tabSize);
|
|
954
951
|
}
|
|
@@ -985,12 +982,9 @@ export class DocumentModel {
|
|
|
985
982
|
* the way, and a stop at the end of the text sends the next one past them.
|
|
986
983
|
*/
|
|
987
984
|
endOffset(offset, visualLine) {
|
|
988
|
-
const line = this.
|
|
985
|
+
const line = this.lineOf(offset);
|
|
989
986
|
const row = visualLine?.(offset) ?? line;
|
|
990
|
-
|
|
991
|
-
if (offset === row.end && offset < this.rope.length) {
|
|
992
|
-
end = line.end;
|
|
993
|
-
}
|
|
987
|
+
const end = offset === row.end && offset < this.rope.length ? line.end : row.end;
|
|
994
988
|
let target = end;
|
|
995
989
|
for (let at = end - 1; at >= line.start; at--) {
|
|
996
990
|
if (at > line.start && visualLine !== undefined && visualLine(at).start === at) {
|
|
@@ -1168,7 +1162,7 @@ export class DocumentModel {
|
|
|
1168
1162
|
if (command === 'indent') {
|
|
1169
1163
|
return { from: line.start, to: line.start, text: indentation };
|
|
1170
1164
|
}
|
|
1171
|
-
const length = line.text.startsWith('\t') ? 1 : Math.min(line.text
|
|
1165
|
+
const length = line.text.startsWith('\t') ? 1 : Math.min(/^ */.exec(line.text)[0].length, tabSize);
|
|
1172
1166
|
return { from: line.start, to: line.start + length, text: '' };
|
|
1173
1167
|
});
|
|
1174
1168
|
return this.applyEdits(edits, { source: 'command' });
|
|
@@ -1346,10 +1340,10 @@ export class DocumentModel {
|
|
|
1346
1340
|
const plan = (reach) => this.selections.map((selection) => {
|
|
1347
1341
|
const { from, to } = rangeOf(selection);
|
|
1348
1342
|
if (mode === 'below') {
|
|
1349
|
-
const end = this.
|
|
1343
|
+
const end = this.boundsOf(selection.head).end;
|
|
1350
1344
|
return planEnter(source, end, end, { ...options, reach });
|
|
1351
1345
|
}
|
|
1352
|
-
const before = this.slice(this.
|
|
1346
|
+
const before = this.slice(this.boundsOf(from).start, from);
|
|
1353
1347
|
if (mode === 'split' && /^[\t ]*$/.test(before)) {
|
|
1354
1348
|
const start = from - before.length;
|
|
1355
1349
|
return { from: start, to: start, text: before + this.newlineAt(this.rope.lineAt(from)), caret: before.length };
|
|
@@ -1423,7 +1417,7 @@ export class DocumentModel {
|
|
|
1423
1417
|
return true;
|
|
1424
1418
|
}
|
|
1425
1419
|
const closer = index.pairs.get(offset);
|
|
1426
|
-
const indentAt = (at) => whitespaceOf(this.
|
|
1420
|
+
const indentAt = (at) => whitespaceOf(this.lineOf(at).text).length;
|
|
1427
1421
|
if (closer === undefined || indentAt(closer) >= indentAt(offset)) {
|
|
1428
1422
|
return false;
|
|
1429
1423
|
}
|
|
@@ -1545,10 +1539,11 @@ export class DocumentModel {
|
|
|
1545
1539
|
}
|
|
1546
1540
|
/* Backspace inside the indentation of a line goes back to the previous tab stop, and at the start of a line joins it with the one above. */
|
|
1547
1541
|
backspaceStart(offset, tabSize) {
|
|
1548
|
-
const
|
|
1542
|
+
const index = this.rope.lineAt(offset);
|
|
1543
|
+
const line = this.rope.lineBounds(index);
|
|
1549
1544
|
if (offset === line.start && line.start > 0) {
|
|
1550
1545
|
// Joining with the line above also takes the whitespace that trails it.
|
|
1551
|
-
const previous = this.getLine(
|
|
1546
|
+
const previous = this.getLine(index - 1);
|
|
1552
1547
|
return previous.end - trailingBlankLength(previous.text);
|
|
1553
1548
|
}
|
|
1554
1549
|
const prefix = this.slice(line.start, offset);
|
|
@@ -1585,7 +1580,13 @@ export class DocumentModel {
|
|
|
1585
1580
|
const first = this.rope.lineBounds(0);
|
|
1586
1581
|
return first.next > first.end ? this.slice(first.end, first.next) : '\n';
|
|
1587
1582
|
}
|
|
1588
|
-
|
|
1583
|
+
lineOf(offset) {
|
|
1584
|
+
return this.rope.getLine(this.rope.lineAt(offset));
|
|
1585
|
+
}
|
|
1586
|
+
boundsOf(offset) {
|
|
1587
|
+
return this.rope.lineBounds(this.rope.lineAt(offset));
|
|
1588
|
+
}
|
|
1589
|
+
/* Preceded by an odd number of backslashes. */
|
|
1589
1590
|
escaped(offset) {
|
|
1590
1591
|
let count = 0;
|
|
1591
1592
|
while (offset > 0 && this.rope.charAt(--offset) === '\\') {
|
|
@@ -1613,7 +1614,7 @@ export class DocumentModel {
|
|
|
1613
1614
|
if (this.rope.charAt(from) === ';') {
|
|
1614
1615
|
return from;
|
|
1615
1616
|
}
|
|
1616
|
-
const line = this.
|
|
1617
|
+
const line = this.lineOf(from);
|
|
1617
1618
|
const prefix = this.slice(line.start, from).trimEnd();
|
|
1618
1619
|
if (!prefix || /[([,=:+\-*/%&|!?]$/.test(prefix)) {
|
|
1619
1620
|
return undefined;
|
|
@@ -1662,7 +1663,7 @@ export class DocumentModel {
|
|
|
1662
1663
|
if (word !== null) {
|
|
1663
1664
|
return word;
|
|
1664
1665
|
}
|
|
1665
|
-
const line = this.
|
|
1666
|
+
const line = this.lineOf(at);
|
|
1666
1667
|
return line.start === line.end ? { from: 0, to: length } : { from: line.start, to: line.end };
|
|
1667
1668
|
}
|
|
1668
1669
|
/* The identifier, or its camel hump, that an offset is in or at the end of; null when it is on something else. */
|
|
@@ -1778,13 +1779,12 @@ export class DocumentModel {
|
|
|
1778
1779
|
expandSelectionTo(ranges) {
|
|
1779
1780
|
return this.expandSelection(ranges);
|
|
1780
1781
|
}
|
|
1781
|
-
/* The range when it holds the stretch and is larger than it, else null. */
|
|
1782
1782
|
largerRange(range, from, to) {
|
|
1783
1783
|
return range !== null && range.from <= from && range.to >= to && (range.from < from || range.to > to) ? range : null;
|
|
1784
1784
|
}
|
|
1785
1785
|
lexicalExpansion(selection, from, to) {
|
|
1786
|
-
const first = this.
|
|
1787
|
-
const last = this.
|
|
1786
|
+
const first = this.lineOf(from);
|
|
1787
|
+
const last = this.lineOf(to);
|
|
1788
1788
|
const candidates = [
|
|
1789
1789
|
this.wordRange(selection.head),
|
|
1790
1790
|
{ from: first.start, to: last.end },
|
|
@@ -1823,7 +1823,6 @@ export class DocumentModel {
|
|
|
1823
1823
|
this.occurrenceSession = { wholeWord, notFound, signature: this.occurrenceSignature() };
|
|
1824
1824
|
this.occurrencesExhausted = notFound;
|
|
1825
1825
|
}
|
|
1826
|
-
/* Whether a range touches a selection that is already there. */
|
|
1827
1826
|
overlapsSelection(from, to) {
|
|
1828
1827
|
return this.selections.some((selection) => {
|
|
1829
1828
|
const range = rangeOf(selection);
|
|
@@ -1867,9 +1866,8 @@ export class DocumentModel {
|
|
|
1867
1866
|
}
|
|
1868
1867
|
/* Takes the newest caret away, or the selection when there is one caret. */
|
|
1869
1868
|
unselectOccurrence() {
|
|
1870
|
-
const
|
|
1871
|
-
|
|
1872
|
-
: this.changeSelections([{ anchor: this.selections[0].head, head: this.selections[0].head }]);
|
|
1869
|
+
const head = this.selections[0].head;
|
|
1870
|
+
const changed = this.changeSelections(this.selections.length > 1 ? this.selections.slice(0, -1) : [{ anchor: head, head }]);
|
|
1873
1871
|
this.occurrenceSession = undefined;
|
|
1874
1872
|
this.occurrencesExhausted = false;
|
|
1875
1873
|
return changed;
|
package/dist/edit-regions.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { vueRegionAt } from './languages.js';
|
|
2
|
+
import { textStart } from './line-text.js';
|
|
2
3
|
import { hasJsx, jsxChildren } from './markup-regions.js';
|
|
3
4
|
const SCAN_LIMIT = 2_000_000;
|
|
4
5
|
export function phpLiteralAt(source, offset) {
|
|
@@ -10,8 +11,7 @@ export function phpTagAt(source, offset) {
|
|
|
10
11
|
return context.mode === 'html' && (context.markup === 'tag' || context.markup === 'equals' || context.markup === 'value');
|
|
11
12
|
}
|
|
12
13
|
function firstContent(source, line) {
|
|
13
|
-
|
|
14
|
-
return bounds.start + bounds.text.length - bounds.text.trimStart().length;
|
|
14
|
+
return textStart(source.line(line));
|
|
15
15
|
}
|
|
16
16
|
function mixedPhpLine(source, line) {
|
|
17
17
|
const bounds = source.line(line);
|
|
@@ -22,8 +22,7 @@ function mixedPhpLine(source, line) {
|
|
|
22
22
|
return true;
|
|
23
23
|
}
|
|
24
24
|
const at = bounds.start + match.index;
|
|
25
|
-
const
|
|
26
|
-
const { mode, embedded } = context;
|
|
25
|
+
const { mode, embedded } = source.context(at);
|
|
27
26
|
if (match[0] === '?>') {
|
|
28
27
|
if (mode !== 'html' && embedded === undefined) {
|
|
29
28
|
return true;
|
package/dist/enter.d.ts
CHANGED
|
@@ -13,5 +13,7 @@ export interface EnterPlan {
|
|
|
13
13
|
text: string;
|
|
14
14
|
caret: number;
|
|
15
15
|
}
|
|
16
|
+
export declare const caseLabel: RegExp;
|
|
16
17
|
export declare function continues(text: string, context: TypingContext, language: string): boolean;
|
|
18
|
+
export declare function lineContinues(source: EditSource, language: string, index: number): boolean;
|
|
17
19
|
export declare function planEnter(source: EditSource, from: number, to: number, options: EnterOptions): EnterPlan;
|
package/dist/enter.js
CHANGED
|
@@ -13,6 +13,7 @@ const markupLanguages = /^(html|xml|svg|vue|svelte|astro|mdx|jsx|tsx|javascriptr
|
|
|
13
13
|
const voidElements = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/i;
|
|
14
14
|
const trailingOperator = /(?:=>|&&|\|\||\?\?|[=*%?|&]|(?<!\+)\+|(?<!-)-)$/;
|
|
15
15
|
const lookBack = 50;
|
|
16
|
+
export const caseLabel = /^(?:case\b[^]*|default)\s*:$/;
|
|
16
17
|
/* The longest text a JSX attribute is looked for in on an Enter. */
|
|
17
18
|
const jsxScanLimit = 2_000_000;
|
|
18
19
|
/* How far past the caret a comment is searched for its end. */
|
|
@@ -24,7 +25,7 @@ function escapeForPattern(text) {
|
|
|
24
25
|
return text.replace(/[.*+?^${}()|[\]\\/-]/g, '\\$&');
|
|
25
26
|
}
|
|
26
27
|
/*
|
|
27
|
-
* Whether the code
|
|
28
|
+
* Whether the code in `text` leaves its statement open: after the parenthesis of a braceless
|
|
28
29
|
* `if` or `for`, after `else`, `=>` or a binary operator. Languages with braces only.
|
|
29
30
|
*/
|
|
30
31
|
export function continues(text, context, language) {
|
|
@@ -40,6 +41,11 @@ export function continues(text, context, language) {
|
|
|
40
41
|
}
|
|
41
42
|
return trailingOperator.test(text);
|
|
42
43
|
}
|
|
44
|
+
export function lineContinues(source, language, index) {
|
|
45
|
+
const line = source.line(index);
|
|
46
|
+
const text = line.text.trimEnd();
|
|
47
|
+
return text.length > 0 && continues(text, source.context(line.start + text.length), language);
|
|
48
|
+
}
|
|
43
49
|
class Enter {
|
|
44
50
|
source;
|
|
45
51
|
options;
|
|
@@ -50,11 +56,9 @@ class Enter {
|
|
|
50
56
|
plan(from, to) {
|
|
51
57
|
const { source, options } = this;
|
|
52
58
|
const index = source.lineAt(from);
|
|
53
|
-
const line = source.line(index);
|
|
54
|
-
const before = source.slice(line.start, from);
|
|
55
|
-
const leading = whitespaceOf(before);
|
|
56
|
-
const newline = source.newline(index);
|
|
57
59
|
if (!options.smart) {
|
|
60
|
+
const leading = whitespaceOf(source.slice(source.line(index).start, from));
|
|
61
|
+
const newline = source.newline(index);
|
|
58
62
|
return { from, to, text: newline + leading, caret: newline.length + leading.length };
|
|
59
63
|
}
|
|
60
64
|
const context = source.context(from);
|
|
@@ -92,9 +96,7 @@ class Enter {
|
|
|
92
96
|
return null;
|
|
93
97
|
}
|
|
94
98
|
lineContinues(index) {
|
|
95
|
-
|
|
96
|
-
const text = line.text.trimEnd();
|
|
97
|
-
return text.length > 0 && continues(text, this.source.context(line.start + text.length), this.options.language);
|
|
99
|
+
return lineContinues(this.source, this.options.language, index);
|
|
98
100
|
}
|
|
99
101
|
inCode(from, to, lineContext, index) {
|
|
100
102
|
const { source, options } = this;
|
|
@@ -193,7 +195,7 @@ class Enter {
|
|
|
193
195
|
return /(?::|[|>][+-]?)$/.test(trimmed) && !trimmed.trimStart().startsWith('#');
|
|
194
196
|
}
|
|
195
197
|
if (isBraced(language)) {
|
|
196
|
-
return (context.bracket === undefined || context.bracket.close === '}') &&
|
|
198
|
+
return (context.bracket === undefined || context.bracket.close === '}') && caseLabel.test(trimmed.trimStart());
|
|
197
199
|
}
|
|
198
200
|
return false;
|
|
199
201
|
}
|
package/dist/fold-roles.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isImportLine, rangeOf
|
|
2
|
-
import { isBlank } from './line-text.js';
|
|
1
|
+
import { isImportLine, rangeOf } from './lexical-folds.js';
|
|
2
|
+
import { isBlank, textStart } from './line-text.js';
|
|
3
3
|
const SCRIPT_LANGUAGES = /^(typescript|javascript|typescriptreact|javascriptreact|tsx|jsx|ts|js|mjs|cjs|mts|cts)$/i;
|
|
4
4
|
/* How far back from a bracket the text before it is read. */
|
|
5
5
|
const LOOK_BEHIND = 200;
|
package/dist/lexical-folds.d.ts
CHANGED
|
@@ -6,6 +6,5 @@ export interface LexicalFoldOptions {
|
|
|
6
6
|
regions?: boolean;
|
|
7
7
|
lineComments?: boolean;
|
|
8
8
|
}
|
|
9
|
-
export declare function textStart(line: DocumentLine): number;
|
|
10
9
|
export declare function rangeOf(startLine: number, endLine: number, getLine: (line: number) => DocumentLine, kind: FoldingRange['kind'], role?: FoldRole): FoldingRange;
|
|
11
10
|
export declare function lexicalFolds(lineCount: number, getLine: (line: number) => DocumentLine, language: string, options?: LexicalFoldOptions): FoldingRange[];
|
package/dist/lexical-folds.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { commentSyntax } from './languages.js';
|
|
2
|
+
import { textStart } from './line-text.js';
|
|
2
3
|
/* The words a line starts with when it brings another file in, by language. A language not listed here has no import list to fold. */
|
|
3
4
|
const IMPORT_WORDS = new Map();
|
|
4
5
|
function register(languages, words) {
|
|
@@ -30,10 +31,6 @@ function bracketDelta(text) {
|
|
|
30
31
|
}
|
|
31
32
|
return depth;
|
|
32
33
|
}
|
|
33
|
-
/* The offset of the first character of a line that is not indentation. */
|
|
34
|
-
export function textStart(line) {
|
|
35
|
-
return line.start + (line.text.length - line.text.trimStart().length);
|
|
36
|
-
}
|
|
37
34
|
/* A fold of whole lines, from the end of the first to the end of the last. */
|
|
38
35
|
export function rangeOf(startLine, endLine, getLine, kind, role) {
|
|
39
36
|
return {
|
package/dist/lexical.d.ts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
export declare const openers: Readonly<Record<string, string>>;
|
|
2
2
|
export declare function startsWord(text: string, at: number): boolean;
|
|
3
|
+
export interface LexerRules {
|
|
4
|
+
script: boolean;
|
|
5
|
+
php: boolean;
|
|
6
|
+
hash: boolean;
|
|
7
|
+
hashAtWordStart: boolean;
|
|
8
|
+
dash: boolean;
|
|
9
|
+
slash: boolean;
|
|
10
|
+
block: boolean;
|
|
11
|
+
markup: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function lexerRules(language: string): LexerRules;
|
|
3
14
|
export declare function isScript(language: string): boolean;
|
|
4
15
|
export declare function isPhp(language: string): boolean;
|
|
5
16
|
export declare function hasHashComments(language: string): boolean;
|
|
6
|
-
export declare function hashNeedsWordStart(language: string): boolean;
|
|
7
|
-
export declare function hasDashComments(language: string): boolean;
|
|
8
|
-
export declare function hasBlockComments(language: string): boolean;
|
|
9
|
-
export declare function hasSlashComments(language: string): boolean;
|
|
10
|
-
export declare function hasMarkupComments(language: string): boolean;
|
|
11
17
|
export declare function isBraced(language: string): boolean;
|
|
12
18
|
export declare function indentsByBrackets(language: string): boolean;
|
|
13
19
|
export declare function hasSmartSemicolon(language: string): boolean;
|
package/dist/lexical.js
CHANGED
|
@@ -28,6 +28,19 @@ register('lua haskell hs', { dash: true });
|
|
|
28
28
|
function rulesOf(language) {
|
|
29
29
|
return languageRules.get(language.toLowerCase()) ?? NO_RULES;
|
|
30
30
|
}
|
|
31
|
+
export function lexerRules(language) {
|
|
32
|
+
const rules = rulesOf(language);
|
|
33
|
+
return {
|
|
34
|
+
script: isScript(language),
|
|
35
|
+
php: isPhp(language),
|
|
36
|
+
hash: hasHashComments(language),
|
|
37
|
+
hashAtWordStart: rules.hash === 'boundary',
|
|
38
|
+
dash: rules.dash,
|
|
39
|
+
slash: rules.slash,
|
|
40
|
+
block: rules.block,
|
|
41
|
+
markup: rules.markup
|
|
42
|
+
};
|
|
43
|
+
}
|
|
31
44
|
export function isScript(language) {
|
|
32
45
|
return /^(typescript|javascript|typescriptreact|javascriptreact|tsx|jsx|ts|js|vue)$/i.test(language);
|
|
33
46
|
}
|
|
@@ -37,23 +50,6 @@ export function isPhp(language) {
|
|
|
37
50
|
export function hasHashComments(language) {
|
|
38
51
|
return rulesOf(language).hash !== false;
|
|
39
52
|
}
|
|
40
|
-
/* Whether a `#` only starts a comment at the start of a word, which keeps `$#` and `${#list}` in shell code. */
|
|
41
|
-
export function hashNeedsWordStart(language) {
|
|
42
|
-
return rulesOf(language).hash === 'boundary';
|
|
43
|
-
}
|
|
44
|
-
export function hasDashComments(language) {
|
|
45
|
-
return rulesOf(language).dash;
|
|
46
|
-
}
|
|
47
|
-
export function hasBlockComments(language) {
|
|
48
|
-
return rulesOf(language).block;
|
|
49
|
-
}
|
|
50
|
-
export function hasSlashComments(language) {
|
|
51
|
-
return rulesOf(language).slash;
|
|
52
|
-
}
|
|
53
|
-
export function hasMarkupComments(language) {
|
|
54
|
-
return rulesOf(language).markup;
|
|
55
|
-
}
|
|
56
|
-
/* Languages whose blocks are in braces: where a statement can be left open and a `case` label opens a block. */
|
|
57
53
|
export function isBraced(language) {
|
|
58
54
|
return rulesOf(language).braced;
|
|
59
55
|
}
|
package/dist/line-commands.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { commentSyntax } from './languages.js';
|
|
2
|
-
import {
|
|
2
|
+
import { caseLabel, lineContinues } from './enter.js';
|
|
3
3
|
import { indentsByBrackets } from './lexical.js';
|
|
4
4
|
import { isBlank, rangeOf, whitespaceOf } from './line-text.js';
|
|
5
5
|
import { markupGuard } from './markup-regions.js';
|
|
@@ -172,17 +172,15 @@ export function planToggleCase(source, selections) {
|
|
|
172
172
|
const result = [];
|
|
173
173
|
let delta = 0;
|
|
174
174
|
for (const [index, target] of targets.entries()) {
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
const converted = convertCase(texts[index], lower, inString);
|
|
175
|
+
const { mode } = source.context(target.from);
|
|
176
|
+
const converted = convertCase(texts[index], lower, mode === 'quote' || mode === 'template');
|
|
178
177
|
if (converted !== texts[index]) {
|
|
179
178
|
edits.push({ from: target.from, to: target.to, text: converted });
|
|
180
179
|
}
|
|
181
180
|
const selection = selections[index];
|
|
182
|
-
const reversed = selection.anchor > selection.head;
|
|
183
181
|
const start = target.from + delta;
|
|
184
|
-
const end =
|
|
185
|
-
result.push(target.word ||
|
|
182
|
+
const end = start + converted.length;
|
|
183
|
+
result.push(target.word || selection.anchor <= selection.head ? { anchor: start, head: end } : { anchor: end, head: start });
|
|
186
184
|
delta += converted.length - texts[index].length;
|
|
187
185
|
}
|
|
188
186
|
if (targets.every((target) => target.from === target.to)) {
|
|
@@ -198,7 +196,6 @@ function frames(context) {
|
|
|
198
196
|
}
|
|
199
197
|
return result;
|
|
200
198
|
}
|
|
201
|
-
const caseLabel = /^(?:case\b[^]*|default)\s*:$/;
|
|
202
199
|
/*
|
|
203
200
|
* Sets the indentation of the lines to what their brackets say: a line is one level in from the line
|
|
204
201
|
* its innermost bracket opened on, a closing bracket lines up with it, the lines of a case follow
|
|
@@ -251,22 +248,17 @@ function targetIndent(source, options, line, context, indentOf) {
|
|
|
251
248
|
}
|
|
252
249
|
}
|
|
253
250
|
const previous = previousCodeLine(source, line);
|
|
254
|
-
if (previous !== null && source.context(source.line(previous).start).bracket === context.bracket && lineContinues(source, options, previous)) {
|
|
251
|
+
if (previous !== null && source.context(source.line(previous).start).bracket === context.bracket && lineContinues(source, options.language, previous)) {
|
|
255
252
|
return isContinuation(source, options, previous) ? indentOf(previous) : indentOf(previous) + options.unit;
|
|
256
253
|
}
|
|
257
254
|
return base;
|
|
258
255
|
}
|
|
259
|
-
function lineContinues(source, options, line) {
|
|
260
|
-
const bounds = source.line(line);
|
|
261
|
-
const text = bounds.text.trimEnd();
|
|
262
|
-
return text.length > 0 && continues(text, source.context(bounds.start + text.length), options.language);
|
|
263
|
-
}
|
|
264
256
|
/* Whether the line goes on with a statement the line before it left open. */
|
|
265
257
|
function isContinuation(source, options, line) {
|
|
266
258
|
const previous = previousCodeLine(source, line);
|
|
267
259
|
return (previous !== null &&
|
|
268
260
|
source.context(source.line(previous).start).bracket === source.context(source.line(line).start).bracket &&
|
|
269
|
-
lineContinues(source, options, previous));
|
|
261
|
+
lineContinues(source, options.language, previous));
|
|
270
262
|
}
|
|
271
263
|
function previousCodeLine(source, line) {
|
|
272
264
|
for (let candidate = line - 1; candidate >= 0; candidate--) {
|
package/dist/line-text.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { DocumentLine } from './rope.ts';
|
|
1
2
|
import type { Selection } from './types.ts';
|
|
2
3
|
export declare function whitespaceOf(text: string): string;
|
|
4
|
+
export declare function textStart(line: DocumentLine): number;
|
|
3
5
|
export declare function isBlank(text: string): boolean;
|
|
4
6
|
export declare function indentText(width: number, options: {
|
|
5
7
|
tabSize: number;
|
package/dist/line-text.js
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
export function whitespaceOf(text) {
|
|
3
3
|
return /^[\t ]*/.exec(text)[0];
|
|
4
4
|
}
|
|
5
|
+
/* The offset of the first character of a line that is not indentation. */
|
|
6
|
+
export function textStart(line) {
|
|
7
|
+
return line.start + (line.text.length - line.text.trimStart().length);
|
|
8
|
+
}
|
|
5
9
|
export function isBlank(text) {
|
|
6
10
|
return text.trim() === '';
|
|
7
11
|
}
|
package/dist/markup-regions.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { whitespaceOf } from './line-text.js';
|
|
1
2
|
const JSX_LANGUAGES = /^(typescriptreact|javascriptreact|tsx|jsx|javascript|js|mjs|cjs)$/i;
|
|
2
3
|
const NAME = /[\w$.:-]/;
|
|
3
4
|
const START_OF_NAME = /[A-Za-z_$>]/;
|
|
@@ -352,7 +353,6 @@ export function jsxChildren(text) {
|
|
|
352
353
|
scanner.scan();
|
|
353
354
|
return (offset) => insideAny(scanner.children, offset);
|
|
354
355
|
}
|
|
355
|
-
/* Whether a language is one whose scripts can hold JSX. */
|
|
356
356
|
export function hasJsx(language) {
|
|
357
357
|
return JSX_LANGUAGES.test(language);
|
|
358
358
|
}
|
|
@@ -379,8 +379,7 @@ export function markupGuard(source, language) {
|
|
|
379
379
|
const ranges = jsxRanges(source.slice(0, source.line(source.lineCount - 1).end));
|
|
380
380
|
return (line) => {
|
|
381
381
|
const bounds = source.line(line);
|
|
382
|
-
|
|
383
|
-
return insideAny(ranges, start);
|
|
382
|
+
return insideAny(ranges, bounds.start + whitespaceOf(bounds.text).length);
|
|
384
383
|
};
|
|
385
384
|
}
|
|
386
385
|
return () => false;
|