@adecore/editor-core 0.41.1 → 0.43.0
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/code-blocks.d.ts +8 -0
- package/dist/code-blocks.js +59 -0
- package/dist/complete-statement.d.ts +8 -0
- package/dist/complete-statement.js +157 -0
- package/dist/document.d.ts +16 -0
- package/dist/document.js +122 -0
- package/dist/hippie.d.ts +34 -0
- package/dist/hippie.js +183 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/lexical.d.ts +1 -0
- package/dist/lexical.js +6 -0
- package/dist/line-order.d.ts +10 -0
- package/dist/line-order.js +156 -0
- package/dist/statement-move.d.ts +21 -0
- package/dist/statement-move.js +361 -0
- package/dist/statements.d.ts +23 -0
- package/dist/statements.js +221 -0
- package/dist/todo.d.ts +21 -0
- package/dist/todo.js +72 -0
- package/dist/types.d.ts +5 -1
- package/dist/whitespace-cleanup.d.ts +26 -0
- package/dist/whitespace-cleanup.js +100 -0
- package/package.json +1 -1
- package/src/code-blocks.ts +93 -0
- package/src/complete-statement.ts +231 -0
- package/src/document.ts +161 -0
- package/src/hippie.ts +290 -0
- package/src/index.ts +3 -0
- package/src/lexical.ts +9 -0
- package/src/line-order.ts +215 -0
- package/src/statement-move.ts +533 -0
- package/src/statements.ts +307 -0
- package/src/todo.ts +118 -0
- package/src/types.ts +23 -1
- package/src/whitespace-cleanup.ts +162 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import type { EditPlan, EditSource } from './edit-source.ts';
|
|
2
|
+
import { indentText, rangeOf, whitespaceOf } from './line-text.ts';
|
|
3
|
+
import { indentationColumn } from './structure.ts';
|
|
4
|
+
import type { Selection, TextEdit } from './types.ts';
|
|
5
|
+
|
|
6
|
+
export type LineOrder = 'sort' | 'reverse' | 'unique';
|
|
7
|
+
|
|
8
|
+
interface LineRun {
|
|
9
|
+
first: number;
|
|
10
|
+
last: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* The runs of lines the selections touch, or every line when nothing is selected. A selection that ends at the start of a line does not touch it. */
|
|
14
|
+
function runsOf(source: EditSource, selections: readonly Selection[]): LineRun[] {
|
|
15
|
+
const ranges = selections.map(rangeOf).filter(({ from, to }) => from !== to);
|
|
16
|
+
const wanted = ranges.length > 0 ? ranges : [{ from: 0, to: source.line(source.lineCount - 1).end }];
|
|
17
|
+
const runs: LineRun[] = [];
|
|
18
|
+
|
|
19
|
+
for (const { from, to } of [...wanted].sort((left, right) => left.from - right.from)) {
|
|
20
|
+
const first = source.lineAt(from);
|
|
21
|
+
let last = source.lineAt(to);
|
|
22
|
+
|
|
23
|
+
if (last > first && source.line(last).start === to) {
|
|
24
|
+
last--;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const previous = runs.at(-1);
|
|
28
|
+
|
|
29
|
+
if (previous !== undefined && first <= previous.last) {
|
|
30
|
+
previous.last = Math.max(previous.last, last);
|
|
31
|
+
} else {
|
|
32
|
+
runs.push({ first, last });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return runs;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function reorder(lines: readonly string[], order: LineOrder): string[] {
|
|
40
|
+
if (order === 'reverse') {
|
|
41
|
+
return [...lines].reverse();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (order === 'unique') {
|
|
45
|
+
return [...new Set(lines)];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return [...lines].sort((left, right) => (left < right ? -1 : left > right ? 1 : 0));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/*
|
|
52
|
+
* Sorts, reverses or dedupes the lines the selections touch, or the whole text with nothing selected,
|
|
53
|
+
* each run of lines on its own. Every line break stays where it was, so a last line without one stays
|
|
54
|
+
* without one. Sorting compares by code unit, so capitals come first, and keeps equal lines in order;
|
|
55
|
+
* dedupe keeps the first of equal lines. A selection keeps its offsets, as far as the text still reaches.
|
|
56
|
+
*/
|
|
57
|
+
export function planLineOrder(source: EditSource, selections: readonly Selection[], order: LineOrder): EditPlan | null {
|
|
58
|
+
const edits: TextEdit[] = [];
|
|
59
|
+
const runs: { from: number; to: number; length: number }[] = [];
|
|
60
|
+
|
|
61
|
+
for (const { first, last } of runsOf(source, selections)) {
|
|
62
|
+
if (last <= first) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const lines: string[] = [];
|
|
67
|
+
const breaks: string[] = [];
|
|
68
|
+
|
|
69
|
+
for (let index = first; index <= last; index++) {
|
|
70
|
+
lines.push(source.line(index).text);
|
|
71
|
+
|
|
72
|
+
if (index < last) {
|
|
73
|
+
breaks.push(source.newline(index));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const result = reorder(lines, order);
|
|
78
|
+
const text = result.map((line, index) => line + (index < result.length - 1 ? breaks[index]! : '')).join('');
|
|
79
|
+
const from = source.line(first).start;
|
|
80
|
+
const to = source.line(last).end;
|
|
81
|
+
|
|
82
|
+
if (text !== source.slice(from, to)) {
|
|
83
|
+
edits.push({ from, to, text });
|
|
84
|
+
runs.push({ from, to, length: text.length });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (edits.length === 0) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const place = (offset: number): number => {
|
|
93
|
+
let shift = 0;
|
|
94
|
+
|
|
95
|
+
for (const run of runs) {
|
|
96
|
+
if (offset < run.from) {
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (offset < run.to) {
|
|
101
|
+
return Math.min(offset, run.from + run.length) + shift;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
shift += run.length - (run.to - run.from);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return offset + shift;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
return { edits, selections: selections.map((selection) => ({ anchor: place(selection.anchor), head: place(selection.head) })) };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/*
|
|
114
|
+
* Rewrites the indentation of the lines the selections touch, or of every line with nothing selected:
|
|
115
|
+
* to spaces with each tab taken to its stop, or to as many tabs as fit and the spaces left over. Only the
|
|
116
|
+
* whitespace a line starts with changes.
|
|
117
|
+
*/
|
|
118
|
+
export function planConvertIndents(source: EditSource, selections: readonly Selection[], tabSize: number, insertSpaces: boolean): TextEdit[] {
|
|
119
|
+
const edits: TextEdit[] = [];
|
|
120
|
+
|
|
121
|
+
for (const { first, last } of runsOf(source, selections)) {
|
|
122
|
+
for (let index = first; index <= last; index++) {
|
|
123
|
+
const line = source.line(index);
|
|
124
|
+
const current = whitespaceOf(line.text);
|
|
125
|
+
const target = indentText(indentationColumn(current, tabSize), { tabSize, insertSpaces });
|
|
126
|
+
|
|
127
|
+
if (current !== target) {
|
|
128
|
+
edits.push({ from: line.start, to: line.start + current.length, text: target });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return edits;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* The indentation a text uses: spaces with the width of one level, or tabs, whose width a text cannot say. */
|
|
137
|
+
export interface DetectedIndentation {
|
|
138
|
+
insertSpaces: boolean;
|
|
139
|
+
tabSize?: number;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Past this many lines the rest says nothing new. */
|
|
143
|
+
const DETECT_LINES = 10_000;
|
|
144
|
+
const MAX_INDENT_SIZE = 8;
|
|
145
|
+
|
|
146
|
+
/*
|
|
147
|
+
* Reads the indentation a text already uses, to edit it the same way. Tabs win when more lines start with
|
|
148
|
+
* a tab than with spaces; otherwise the width of one level is the step between a line and a deeper one
|
|
149
|
+
* after it that occurs most often. On a tie the step more indents are a multiple of wins, so a line
|
|
150
|
+
* aligned under a parenthesis does not pass for a level, and then the smaller. The lines inside a block
|
|
151
|
+
* comment that start with a star are left out, since their single space is no level. Null when no line says.
|
|
152
|
+
*/
|
|
153
|
+
export function detectIndentation(text: string): DetectedIndentation | null {
|
|
154
|
+
const steps = new Map<number, number>();
|
|
155
|
+
const widths: number[] = [];
|
|
156
|
+
let tabs = 0;
|
|
157
|
+
let spaces = 0;
|
|
158
|
+
let previous = 0;
|
|
159
|
+
let count = 0;
|
|
160
|
+
|
|
161
|
+
for (const line of text.split(/\r\n|\r|\n/)) {
|
|
162
|
+
if (++count > DETECT_LINES) {
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const indent = whitespaceOf(line);
|
|
167
|
+
const content = line.slice(indent.length);
|
|
168
|
+
|
|
169
|
+
if (content === '' || content.startsWith('*')) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (indent.startsWith('\t')) {
|
|
174
|
+
tabs++;
|
|
175
|
+
previous = -1;
|
|
176
|
+
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (indent.includes('\t')) {
|
|
181
|
+
previous = -1;
|
|
182
|
+
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (indent.length > 0) {
|
|
187
|
+
spaces++;
|
|
188
|
+
widths.push(indent.length);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const step = indent.length - previous;
|
|
192
|
+
|
|
193
|
+
if (previous >= 0 && step > 0 && step <= MAX_INDENT_SIZE) {
|
|
194
|
+
steps.set(step, (steps.get(step) ?? 0) + 1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
previous = indent.length;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (tabs > spaces) {
|
|
201
|
+
return { insertSpaces: false };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let best: { size: number; uses: number; fits: number } | null = null;
|
|
205
|
+
|
|
206
|
+
for (const [size, uses] of steps) {
|
|
207
|
+
const fits = widths.filter((width) => width % size === 0).length;
|
|
208
|
+
|
|
209
|
+
if (best === null || uses > best.uses || (uses === best.uses && (fits > best.fits || (fits === best.fits && size < best.size)))) {
|
|
210
|
+
best = { size, uses, fits };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return best === null ? null : { insertSpaces: true, tabSize: best.size };
|
|
215
|
+
}
|