@adminforth/markdown 1.8.0 → 1.9.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/build.log
CHANGED
|
@@ -9,6 +9,8 @@ custom/MarkdownRenderer.vue
|
|
|
9
9
|
custom/package-lock.json
|
|
10
10
|
custom/package.json
|
|
11
11
|
custom/tsconfig.json
|
|
12
|
+
custom/utils/
|
|
13
|
+
custom/utils/monacoMarkdownToggle.ts
|
|
12
14
|
|
|
13
|
-
sent
|
|
14
|
-
total size is
|
|
15
|
+
sent 37,202 bytes received 146 bytes 74,696.00 bytes/sec
|
|
16
|
+
total size is 36,649 speedup is 0.98
|
|
@@ -18,6 +18,7 @@ import { callAdminForthApi } from '@/utils';
|
|
|
18
18
|
import * as monaco from 'monaco-editor';
|
|
19
19
|
import TurndownService from 'turndown';
|
|
20
20
|
import { gfm, tables } from 'turndown-plugin-gfm';
|
|
21
|
+
import { toggleWrapSmart } from './utils/monacoMarkdownToggle';
|
|
21
22
|
|
|
22
23
|
const props = defineProps<{
|
|
23
24
|
column: any,
|
|
@@ -229,10 +230,11 @@ function getTurndownService(): TurndownService {
|
|
|
229
230
|
return ``;
|
|
230
231
|
},
|
|
231
232
|
});
|
|
232
|
-
|
|
233
|
+
turndownService.escape = (s: string) => s;
|
|
233
234
|
return turndownService;
|
|
234
235
|
}
|
|
235
236
|
|
|
237
|
+
|
|
236
238
|
let editor: monaco.editor.IStandaloneCodeEditor | null = null;
|
|
237
239
|
let model: monaco.editor.ITextModel | null = null;
|
|
238
240
|
const disposables: monaco.IDisposable[] = [];
|
|
@@ -310,13 +312,14 @@ function updateImagePreviews() {
|
|
|
310
312
|
preview.style.display = 'inline-block';
|
|
311
313
|
preview.style.borderRadius = '6px';
|
|
312
314
|
preview.style.overflow = 'hidden';
|
|
313
|
-
preview.style.maxWidth = '
|
|
314
|
-
preview.style.maxHeight = '
|
|
315
|
+
preview.style.maxWidth = '440px';
|
|
316
|
+
preview.style.maxHeight = '280px';
|
|
317
|
+
|
|
315
318
|
|
|
316
319
|
const imageEl = document.createElement('img');
|
|
317
320
|
imageEl.src = img.src;
|
|
318
|
-
imageEl.style.maxWidth = '
|
|
319
|
-
imageEl.style.maxHeight = '
|
|
321
|
+
imageEl.style.maxWidth = '440px';
|
|
322
|
+
imageEl.style.maxHeight = '280px';
|
|
320
323
|
imageEl.style.display = 'block';
|
|
321
324
|
imageEl.style.opacity = '0.95';
|
|
322
325
|
|
|
@@ -325,7 +328,7 @@ function updateImagePreviews() {
|
|
|
325
328
|
|
|
326
329
|
const zone: monaco.editor.IViewZone = {
|
|
327
330
|
afterLineNumber: img.lineNumber,
|
|
328
|
-
heightInPx:
|
|
331
|
+
heightInPx: 320,
|
|
329
332
|
domNode: wrapper,
|
|
330
333
|
};
|
|
331
334
|
|
|
@@ -336,7 +339,7 @@ function updateImagePreviews() {
|
|
|
336
339
|
imageEl.onload = () => {
|
|
337
340
|
if (!editor) return;
|
|
338
341
|
const measured = wrapper.offsetHeight;
|
|
339
|
-
const nextHeight = Math.max(
|
|
342
|
+
const nextHeight = Math.max(60, Math.min(520, measured || 320));
|
|
340
343
|
if (zone.heightInPx !== nextHeight) {
|
|
341
344
|
zone.heightInPx = nextHeight;
|
|
342
345
|
editor.changeViewZones((a) => a.layoutZone(zoneId));
|
|
@@ -455,6 +458,41 @@ onMounted(async () => {
|
|
|
455
458
|
model,
|
|
456
459
|
language: 'markdown',
|
|
457
460
|
automaticLayout: true,
|
|
461
|
+
wordWrap: 'on',
|
|
462
|
+
wrappingStrategy: 'advanced',
|
|
463
|
+
wrappingIndent: 'same',
|
|
464
|
+
scrollbar: {
|
|
465
|
+
horizontal: 'hidden',
|
|
466
|
+
},
|
|
467
|
+
scrollBeyondLastColumn: 0,
|
|
468
|
+
});
|
|
469
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyB, () => {
|
|
470
|
+
toggleWrapSmart(editor!, '**');
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyI, () => {
|
|
474
|
+
toggleWrapSmart(editor!, '*');
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyE, () => {
|
|
478
|
+
toggleWrapSmart(editor!, '`');
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyX, () => {
|
|
482
|
+
toggleWrapSmart(editor!, '~~');
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyU, () => {
|
|
486
|
+
toggleWrapSmart(editor!, '<u>', '</u>');
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK, () => {
|
|
490
|
+
const selection = editor!.getSelection();
|
|
491
|
+
if (!selection) return;
|
|
492
|
+
const text = model?.getValueInRange(selection) || '';
|
|
493
|
+
const escaped = escapeMarkdownLinkText(text);
|
|
494
|
+
const markdownLink = `[${escaped}](url)`;
|
|
495
|
+
editor!.executeEdits('insert-link', [{ range: selection, text: markdownLink, forceMoveMarkers: true }]);
|
|
458
496
|
});
|
|
459
497
|
|
|
460
498
|
debug('Monaco editor created', {
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import * as monaco from 'monaco-editor';
|
|
2
|
+
|
|
3
|
+
function trimTrailingEolSelection(
|
|
4
|
+
m: monaco.editor.ITextModel,
|
|
5
|
+
sel: monaco.Selection,
|
|
6
|
+
): monaco.Selection {
|
|
7
|
+
if (!sel.isEmpty() && sel.endColumn === 1 && sel.endLineNumber > sel.startLineNumber) {
|
|
8
|
+
const prevLine = sel.endLineNumber - 1;
|
|
9
|
+
const endCol = m.getLineMaxColumn(prevLine); // after last char
|
|
10
|
+
return new monaco.Selection(sel.startLineNumber, sel.startColumn, prevLine, endCol);
|
|
11
|
+
}
|
|
12
|
+
return sel;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function rangeFromOffsets(m: monaco.editor.ITextModel, startOffset: number, endOffset: number): monaco.Range {
|
|
16
|
+
const s = m.getPositionAt(startOffset);
|
|
17
|
+
const e = m.getPositionAt(endOffset);
|
|
18
|
+
return new monaco.Range(s.lineNumber, s.column, e.lineNumber, e.column);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function toggleWrapSmart(
|
|
22
|
+
ed: monaco.editor.IStandaloneCodeEditor,
|
|
23
|
+
left: string,
|
|
24
|
+
right: string = left,
|
|
25
|
+
) {
|
|
26
|
+
let m = ed.getModel();
|
|
27
|
+
if (!m) return;
|
|
28
|
+
|
|
29
|
+
const selections = ed.getSelections() || [];
|
|
30
|
+
if (!selections.length) return;
|
|
31
|
+
|
|
32
|
+
const leftLen = left.length;
|
|
33
|
+
const rightLen = right.length;
|
|
34
|
+
|
|
35
|
+
const indexed = selections.map((s, idx) => ({ s, idx }));
|
|
36
|
+
|
|
37
|
+
indexed.sort((a, b) => {
|
|
38
|
+
const ma = ed.getModel();
|
|
39
|
+
if (!ma) return 0;
|
|
40
|
+
const ao = ma.getOffsetAt(a.s.getStartPosition());
|
|
41
|
+
const bo = ma.getOffsetAt(b.s.getStartPosition());
|
|
42
|
+
if (ao !== bo) return bo - ao;
|
|
43
|
+
const ae = ma.getOffsetAt(a.s.getEndPosition());
|
|
44
|
+
const be = ma.getOffsetAt(b.s.getEndPosition());
|
|
45
|
+
return be - ae;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const nextSelections: Array<monaco.Selection | null> = new Array(selections.length).fill(null);
|
|
49
|
+
|
|
50
|
+
const getTextByOffsets = (startOffset: number, endOffset: number) => {
|
|
51
|
+
const mm = ed.getModel();
|
|
52
|
+
if (!mm) return '';
|
|
53
|
+
return mm.getValueInRange(rangeFromOffsets(mm, startOffset, endOffset));
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
ed.pushUndoStop();
|
|
57
|
+
|
|
58
|
+
for (const item of indexed) {
|
|
59
|
+
m = ed.getModel();
|
|
60
|
+
if (!m) break;
|
|
61
|
+
|
|
62
|
+
let sel = item.s;
|
|
63
|
+
sel = trimTrailingEolSelection(m, sel);
|
|
64
|
+
|
|
65
|
+
const startPos = sel.getStartPosition();
|
|
66
|
+
const endPos = sel.getEndPosition();
|
|
67
|
+
const startOffset = m.getOffsetAt(startPos);
|
|
68
|
+
const endOffset = m.getOffsetAt(endPos);
|
|
69
|
+
|
|
70
|
+
const isEmpty = sel.isEmpty();
|
|
71
|
+
const modelLen = m.getValueLength();
|
|
72
|
+
|
|
73
|
+
const hasAdjacentWrap = () => {
|
|
74
|
+
if (startOffset < leftLen) return false;
|
|
75
|
+
if (endOffset + rightLen > modelLen) return false;
|
|
76
|
+
|
|
77
|
+
const l = getTextByOffsets(startOffset - leftLen, startOffset);
|
|
78
|
+
const r = getTextByOffsets(endOffset, endOffset + rightLen);
|
|
79
|
+
return l === left && r === right;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const removeAdjacentWrap = () => {
|
|
83
|
+
const leftRange = rangeFromOffsets(m!, startOffset - leftLen, startOffset);
|
|
84
|
+
const rightRange = rangeFromOffsets(m!, endOffset, endOffset + rightLen);
|
|
85
|
+
|
|
86
|
+
ed.executeEdits('md-toggle-wrap', [
|
|
87
|
+
{ range: rightRange, text: '', forceMoveMarkers: true },
|
|
88
|
+
{ range: leftRange, text: '', forceMoveMarkers: true },
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
const mm = ed.getModel()!;
|
|
92
|
+
const ns = startOffset - leftLen;
|
|
93
|
+
const ne = endOffset - leftLen;
|
|
94
|
+
|
|
95
|
+
const s = mm.getPositionAt(ns);
|
|
96
|
+
const e = mm.getPositionAt(ne);
|
|
97
|
+
nextSelections[item.idx] = new monaco.Selection(s.lineNumber, s.column, e.lineNumber, e.column);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (isEmpty) {
|
|
101
|
+
if (startOffset >= leftLen && startOffset + rightLen <= modelLen) {
|
|
102
|
+
const l = getTextByOffsets(startOffset - leftLen, startOffset);
|
|
103
|
+
const r = getTextByOffsets(startOffset, startOffset + rightLen);
|
|
104
|
+
if (l === left && r === right) {
|
|
105
|
+
const leftRange = rangeFromOffsets(m, startOffset - leftLen, startOffset);
|
|
106
|
+
const rightRange = rangeFromOffsets(m, startOffset, startOffset + rightLen);
|
|
107
|
+
|
|
108
|
+
ed.executeEdits('md-toggle-wrap', [
|
|
109
|
+
{ range: rightRange, text: '', forceMoveMarkers: true },
|
|
110
|
+
{ range: leftRange, text: '', forceMoveMarkers: true },
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
const mm = ed.getModel()!;
|
|
114
|
+
const p = mm.getPositionAt(startOffset - leftLen);
|
|
115
|
+
nextSelections[item.idx] = new monaco.Selection(p.lineNumber, p.column, p.lineNumber, p.column);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const insertRange = rangeFromOffsets(m, startOffset, startOffset);
|
|
121
|
+
ed.executeEdits('md-toggle-wrap', [
|
|
122
|
+
{ range: insertRange, text: `${left}${right}`, forceMoveMarkers: true },
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
const mm = ed.getModel()!;
|
|
126
|
+
const p = mm.getPositionAt(startOffset + leftLen);
|
|
127
|
+
nextSelections[item.idx] = new monaco.Selection(p.lineNumber, p.column, p.lineNumber, p.column);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const selectedText = m.getValueInRange(sel);
|
|
132
|
+
|
|
133
|
+
const isExplicitWrapped =
|
|
134
|
+
selectedText.length >= leftLen + rightLen &&
|
|
135
|
+
selectedText.startsWith(left) &&
|
|
136
|
+
selectedText.endsWith(right);
|
|
137
|
+
|
|
138
|
+
if (isExplicitWrapped) {
|
|
139
|
+
const unwrapped = selectedText.slice(leftLen, selectedText.length - rightLen);
|
|
140
|
+
|
|
141
|
+
ed.executeEdits('md-toggle-wrap', [
|
|
142
|
+
{ range: sel, text: unwrapped, forceMoveMarkers: true },
|
|
143
|
+
]);
|
|
144
|
+
|
|
145
|
+
const mm = ed.getModel()!;
|
|
146
|
+
const s = mm.getPositionAt(startOffset);
|
|
147
|
+
const e = mm.getPositionAt(startOffset + unwrapped.length);
|
|
148
|
+
nextSelections[item.idx] = new monaco.Selection(s.lineNumber, s.column, e.lineNumber, e.column);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (hasAdjacentWrap()) {
|
|
153
|
+
removeAdjacentWrap();
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
ed.executeEdits('md-toggle-wrap', [
|
|
158
|
+
{ range: sel, text: `${left}${selectedText}${right}`, forceMoveMarkers: true },
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
const mm = ed.getModel()!;
|
|
162
|
+
const ns = startOffset + leftLen;
|
|
163
|
+
const ne = endOffset + leftLen;
|
|
164
|
+
const s = mm.getPositionAt(ns);
|
|
165
|
+
const e = mm.getPositionAt(ne);
|
|
166
|
+
nextSelections[item.idx] = new monaco.Selection(s.lineNumber, s.column, e.lineNumber, e.column);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
ed.pushUndoStop();
|
|
170
|
+
|
|
171
|
+
const finalSelections = nextSelections.map((s, i) => s ?? selections[i]);
|
|
172
|
+
ed.setSelections(finalSelections);
|
|
173
|
+
}
|
|
@@ -18,6 +18,7 @@ import { callAdminForthApi } from '@/utils';
|
|
|
18
18
|
import * as monaco from 'monaco-editor';
|
|
19
19
|
import TurndownService from 'turndown';
|
|
20
20
|
import { gfm, tables } from 'turndown-plugin-gfm';
|
|
21
|
+
import { toggleWrapSmart } from './utils/monacoMarkdownToggle';
|
|
21
22
|
|
|
22
23
|
const props = defineProps<{
|
|
23
24
|
column: any,
|
|
@@ -229,10 +230,11 @@ function getTurndownService(): TurndownService {
|
|
|
229
230
|
return ``;
|
|
230
231
|
},
|
|
231
232
|
});
|
|
232
|
-
|
|
233
|
+
turndownService.escape = (s: string) => s;
|
|
233
234
|
return turndownService;
|
|
234
235
|
}
|
|
235
236
|
|
|
237
|
+
|
|
236
238
|
let editor: monaco.editor.IStandaloneCodeEditor | null = null;
|
|
237
239
|
let model: monaco.editor.ITextModel | null = null;
|
|
238
240
|
const disposables: monaco.IDisposable[] = [];
|
|
@@ -310,13 +312,14 @@ function updateImagePreviews() {
|
|
|
310
312
|
preview.style.display = 'inline-block';
|
|
311
313
|
preview.style.borderRadius = '6px';
|
|
312
314
|
preview.style.overflow = 'hidden';
|
|
313
|
-
preview.style.maxWidth = '
|
|
314
|
-
preview.style.maxHeight = '
|
|
315
|
+
preview.style.maxWidth = '440px';
|
|
316
|
+
preview.style.maxHeight = '280px';
|
|
317
|
+
|
|
315
318
|
|
|
316
319
|
const imageEl = document.createElement('img');
|
|
317
320
|
imageEl.src = img.src;
|
|
318
|
-
imageEl.style.maxWidth = '
|
|
319
|
-
imageEl.style.maxHeight = '
|
|
321
|
+
imageEl.style.maxWidth = '440px';
|
|
322
|
+
imageEl.style.maxHeight = '280px';
|
|
320
323
|
imageEl.style.display = 'block';
|
|
321
324
|
imageEl.style.opacity = '0.95';
|
|
322
325
|
|
|
@@ -325,7 +328,7 @@ function updateImagePreviews() {
|
|
|
325
328
|
|
|
326
329
|
const zone: monaco.editor.IViewZone = {
|
|
327
330
|
afterLineNumber: img.lineNumber,
|
|
328
|
-
heightInPx:
|
|
331
|
+
heightInPx: 320,
|
|
329
332
|
domNode: wrapper,
|
|
330
333
|
};
|
|
331
334
|
|
|
@@ -336,7 +339,7 @@ function updateImagePreviews() {
|
|
|
336
339
|
imageEl.onload = () => {
|
|
337
340
|
if (!editor) return;
|
|
338
341
|
const measured = wrapper.offsetHeight;
|
|
339
|
-
const nextHeight = Math.max(
|
|
342
|
+
const nextHeight = Math.max(60, Math.min(520, measured || 320));
|
|
340
343
|
if (zone.heightInPx !== nextHeight) {
|
|
341
344
|
zone.heightInPx = nextHeight;
|
|
342
345
|
editor.changeViewZones((a) => a.layoutZone(zoneId));
|
|
@@ -455,6 +458,41 @@ onMounted(async () => {
|
|
|
455
458
|
model,
|
|
456
459
|
language: 'markdown',
|
|
457
460
|
automaticLayout: true,
|
|
461
|
+
wordWrap: 'on',
|
|
462
|
+
wrappingStrategy: 'advanced',
|
|
463
|
+
wrappingIndent: 'same',
|
|
464
|
+
scrollbar: {
|
|
465
|
+
horizontal: 'hidden',
|
|
466
|
+
},
|
|
467
|
+
scrollBeyondLastColumn: 0,
|
|
468
|
+
});
|
|
469
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyB, () => {
|
|
470
|
+
toggleWrapSmart(editor!, '**');
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyI, () => {
|
|
474
|
+
toggleWrapSmart(editor!, '*');
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyE, () => {
|
|
478
|
+
toggleWrapSmart(editor!, '`');
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyX, () => {
|
|
482
|
+
toggleWrapSmart(editor!, '~~');
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyU, () => {
|
|
486
|
+
toggleWrapSmart(editor!, '<u>', '</u>');
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK, () => {
|
|
490
|
+
const selection = editor!.getSelection();
|
|
491
|
+
if (!selection) return;
|
|
492
|
+
const text = model?.getValueInRange(selection) || '';
|
|
493
|
+
const escaped = escapeMarkdownLinkText(text);
|
|
494
|
+
const markdownLink = `[${escaped}](url)`;
|
|
495
|
+
editor!.executeEdits('insert-link', [{ range: selection, text: markdownLink, forceMoveMarkers: true }]);
|
|
458
496
|
});
|
|
459
497
|
|
|
460
498
|
debug('Monaco editor created', {
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import * as monaco from 'monaco-editor';
|
|
2
|
+
|
|
3
|
+
function trimTrailingEolSelection(
|
|
4
|
+
m: monaco.editor.ITextModel,
|
|
5
|
+
sel: monaco.Selection,
|
|
6
|
+
): monaco.Selection {
|
|
7
|
+
if (!sel.isEmpty() && sel.endColumn === 1 && sel.endLineNumber > sel.startLineNumber) {
|
|
8
|
+
const prevLine = sel.endLineNumber - 1;
|
|
9
|
+
const endCol = m.getLineMaxColumn(prevLine); // after last char
|
|
10
|
+
return new monaco.Selection(sel.startLineNumber, sel.startColumn, prevLine, endCol);
|
|
11
|
+
}
|
|
12
|
+
return sel;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function rangeFromOffsets(m: monaco.editor.ITextModel, startOffset: number, endOffset: number): monaco.Range {
|
|
16
|
+
const s = m.getPositionAt(startOffset);
|
|
17
|
+
const e = m.getPositionAt(endOffset);
|
|
18
|
+
return new monaco.Range(s.lineNumber, s.column, e.lineNumber, e.column);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function toggleWrapSmart(
|
|
22
|
+
ed: monaco.editor.IStandaloneCodeEditor,
|
|
23
|
+
left: string,
|
|
24
|
+
right: string = left,
|
|
25
|
+
) {
|
|
26
|
+
let m = ed.getModel();
|
|
27
|
+
if (!m) return;
|
|
28
|
+
|
|
29
|
+
const selections = ed.getSelections() || [];
|
|
30
|
+
if (!selections.length) return;
|
|
31
|
+
|
|
32
|
+
const leftLen = left.length;
|
|
33
|
+
const rightLen = right.length;
|
|
34
|
+
|
|
35
|
+
const indexed = selections.map((s, idx) => ({ s, idx }));
|
|
36
|
+
|
|
37
|
+
indexed.sort((a, b) => {
|
|
38
|
+
const ma = ed.getModel();
|
|
39
|
+
if (!ma) return 0;
|
|
40
|
+
const ao = ma.getOffsetAt(a.s.getStartPosition());
|
|
41
|
+
const bo = ma.getOffsetAt(b.s.getStartPosition());
|
|
42
|
+
if (ao !== bo) return bo - ao;
|
|
43
|
+
const ae = ma.getOffsetAt(a.s.getEndPosition());
|
|
44
|
+
const be = ma.getOffsetAt(b.s.getEndPosition());
|
|
45
|
+
return be - ae;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const nextSelections: Array<monaco.Selection | null> = new Array(selections.length).fill(null);
|
|
49
|
+
|
|
50
|
+
const getTextByOffsets = (startOffset: number, endOffset: number) => {
|
|
51
|
+
const mm = ed.getModel();
|
|
52
|
+
if (!mm) return '';
|
|
53
|
+
return mm.getValueInRange(rangeFromOffsets(mm, startOffset, endOffset));
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
ed.pushUndoStop();
|
|
57
|
+
|
|
58
|
+
for (const item of indexed) {
|
|
59
|
+
m = ed.getModel();
|
|
60
|
+
if (!m) break;
|
|
61
|
+
|
|
62
|
+
let sel = item.s;
|
|
63
|
+
sel = trimTrailingEolSelection(m, sel);
|
|
64
|
+
|
|
65
|
+
const startPos = sel.getStartPosition();
|
|
66
|
+
const endPos = sel.getEndPosition();
|
|
67
|
+
const startOffset = m.getOffsetAt(startPos);
|
|
68
|
+
const endOffset = m.getOffsetAt(endPos);
|
|
69
|
+
|
|
70
|
+
const isEmpty = sel.isEmpty();
|
|
71
|
+
const modelLen = m.getValueLength();
|
|
72
|
+
|
|
73
|
+
const hasAdjacentWrap = () => {
|
|
74
|
+
if (startOffset < leftLen) return false;
|
|
75
|
+
if (endOffset + rightLen > modelLen) return false;
|
|
76
|
+
|
|
77
|
+
const l = getTextByOffsets(startOffset - leftLen, startOffset);
|
|
78
|
+
const r = getTextByOffsets(endOffset, endOffset + rightLen);
|
|
79
|
+
return l === left && r === right;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const removeAdjacentWrap = () => {
|
|
83
|
+
const leftRange = rangeFromOffsets(m!, startOffset - leftLen, startOffset);
|
|
84
|
+
const rightRange = rangeFromOffsets(m!, endOffset, endOffset + rightLen);
|
|
85
|
+
|
|
86
|
+
ed.executeEdits('md-toggle-wrap', [
|
|
87
|
+
{ range: rightRange, text: '', forceMoveMarkers: true },
|
|
88
|
+
{ range: leftRange, text: '', forceMoveMarkers: true },
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
const mm = ed.getModel()!;
|
|
92
|
+
const ns = startOffset - leftLen;
|
|
93
|
+
const ne = endOffset - leftLen;
|
|
94
|
+
|
|
95
|
+
const s = mm.getPositionAt(ns);
|
|
96
|
+
const e = mm.getPositionAt(ne);
|
|
97
|
+
nextSelections[item.idx] = new monaco.Selection(s.lineNumber, s.column, e.lineNumber, e.column);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (isEmpty) {
|
|
101
|
+
if (startOffset >= leftLen && startOffset + rightLen <= modelLen) {
|
|
102
|
+
const l = getTextByOffsets(startOffset - leftLen, startOffset);
|
|
103
|
+
const r = getTextByOffsets(startOffset, startOffset + rightLen);
|
|
104
|
+
if (l === left && r === right) {
|
|
105
|
+
const leftRange = rangeFromOffsets(m, startOffset - leftLen, startOffset);
|
|
106
|
+
const rightRange = rangeFromOffsets(m, startOffset, startOffset + rightLen);
|
|
107
|
+
|
|
108
|
+
ed.executeEdits('md-toggle-wrap', [
|
|
109
|
+
{ range: rightRange, text: '', forceMoveMarkers: true },
|
|
110
|
+
{ range: leftRange, text: '', forceMoveMarkers: true },
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
const mm = ed.getModel()!;
|
|
114
|
+
const p = mm.getPositionAt(startOffset - leftLen);
|
|
115
|
+
nextSelections[item.idx] = new monaco.Selection(p.lineNumber, p.column, p.lineNumber, p.column);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const insertRange = rangeFromOffsets(m, startOffset, startOffset);
|
|
121
|
+
ed.executeEdits('md-toggle-wrap', [
|
|
122
|
+
{ range: insertRange, text: `${left}${right}`, forceMoveMarkers: true },
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
const mm = ed.getModel()!;
|
|
126
|
+
const p = mm.getPositionAt(startOffset + leftLen);
|
|
127
|
+
nextSelections[item.idx] = new monaco.Selection(p.lineNumber, p.column, p.lineNumber, p.column);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const selectedText = m.getValueInRange(sel);
|
|
132
|
+
|
|
133
|
+
const isExplicitWrapped =
|
|
134
|
+
selectedText.length >= leftLen + rightLen &&
|
|
135
|
+
selectedText.startsWith(left) &&
|
|
136
|
+
selectedText.endsWith(right);
|
|
137
|
+
|
|
138
|
+
if (isExplicitWrapped) {
|
|
139
|
+
const unwrapped = selectedText.slice(leftLen, selectedText.length - rightLen);
|
|
140
|
+
|
|
141
|
+
ed.executeEdits('md-toggle-wrap', [
|
|
142
|
+
{ range: sel, text: unwrapped, forceMoveMarkers: true },
|
|
143
|
+
]);
|
|
144
|
+
|
|
145
|
+
const mm = ed.getModel()!;
|
|
146
|
+
const s = mm.getPositionAt(startOffset);
|
|
147
|
+
const e = mm.getPositionAt(startOffset + unwrapped.length);
|
|
148
|
+
nextSelections[item.idx] = new monaco.Selection(s.lineNumber, s.column, e.lineNumber, e.column);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (hasAdjacentWrap()) {
|
|
153
|
+
removeAdjacentWrap();
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
ed.executeEdits('md-toggle-wrap', [
|
|
158
|
+
{ range: sel, text: `${left}${selectedText}${right}`, forceMoveMarkers: true },
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
const mm = ed.getModel()!;
|
|
162
|
+
const ns = startOffset + leftLen;
|
|
163
|
+
const ne = endOffset + leftLen;
|
|
164
|
+
const s = mm.getPositionAt(ns);
|
|
165
|
+
const e = mm.getPositionAt(ne);
|
|
166
|
+
nextSelections[item.idx] = new monaco.Selection(s.lineNumber, s.column, e.lineNumber, e.column);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
ed.pushUndoStop();
|
|
170
|
+
|
|
171
|
+
const finalSelections = nextSelections.map((s, i) => s ?? selections[i]);
|
|
172
|
+
ed.setSelections(finalSelections);
|
|
173
|
+
}
|