@pierre/diffs 1.3.0-beta.1 → 1.3.0-beta.3
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/components/CodeView.d.ts +4 -0
- package/dist/components/CodeView.d.ts.map +1 -1
- package/dist/components/CodeView.js +38 -0
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/File.d.ts.map +1 -1
- package/dist/components/File.js +9 -9
- package/dist/components/File.js.map +1 -1
- package/dist/components/FileDiff.d.ts.map +1 -1
- package/dist/components/FileDiff.js +3 -2
- package/dist/components/FileDiff.js.map +1 -1
- package/dist/components/VirtualizedFile.js +6 -1
- package/dist/components/VirtualizedFile.js.map +1 -1
- package/dist/components/VirtualizedFileDiff.js +22 -42
- package/dist/components/VirtualizedFileDiff.js.map +1 -1
- package/dist/components/Virtualizer.js +5 -3
- package/dist/components/Virtualizer.js.map +1 -1
- package/dist/editor/editor.d.ts +7 -1
- package/dist/editor/editor.d.ts.map +1 -1
- package/dist/editor/editor.js +550 -405
- package/dist/editor/editor.js.map +1 -1
- package/dist/editor/editor2.js +6 -0
- package/dist/editor/editor2.js.map +1 -0
- package/dist/editor/pieceTable.d.ts +1 -1
- package/dist/editor/pieceTable.d.ts.map +1 -1
- package/dist/editor/pieceTable.js +2 -22
- package/dist/editor/pieceTable.js.map +1 -1
- package/dist/editor/quickEdit.js +2 -4
- package/dist/editor/quickEdit.js.map +1 -1
- package/dist/editor/searchPanel.d.ts +6 -7
- package/dist/editor/searchPanel.d.ts.map +1 -1
- package/dist/editor/searchPanel.js +102 -137
- package/dist/editor/searchPanel.js.map +1 -1
- package/dist/editor/selection.js +8 -2
- package/dist/editor/selection.js.map +1 -1
- package/dist/editor/sprite.d.ts +7 -0
- package/dist/editor/sprite.d.ts.map +1 -0
- package/dist/editor/sprite.js +38 -0
- package/dist/editor/sprite.js.map +1 -0
- package/dist/editor/textDocument.d.ts +1 -1
- package/dist/editor/textDocument.d.ts.map +1 -1
- package/dist/editor/textDocument.js +2 -2
- package/dist/editor/textDocument.js.map +1 -1
- package/dist/editor/textMeasure.js +3 -3
- package/dist/editor/textMeasure.js.map +1 -1
- package/dist/editor/tokenzier.d.ts +6 -2
- package/dist/editor/tokenzier.d.ts.map +1 -1
- package/dist/editor/tokenzier.js +127 -85
- package/dist/editor/tokenzier.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/jsx.d.ts +1 -0
- package/dist/react/jsx.d.ts.map +1 -1
- package/dist/react/types.js +1 -0
- package/dist/renderers/DiffHunksRenderer.js +5 -9
- package/dist/renderers/DiffHunksRenderer.js.map +1 -1
- package/dist/ssr/index.d.ts +2 -2
- package/dist/style.js +1 -1
- package/dist/style.js.map +1 -1
- package/dist/types.d.ts +13 -12
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/computeEstimatedDiffHeights.js +9 -20
- package/dist/utils/computeEstimatedDiffHeights.js.map +1 -1
- package/dist/utils/iterateOverDiff.js +147 -182
- package/dist/utils/iterateOverDiff.js.map +1 -1
- package/dist/utils/virtualDiffLayout.d.ts +23 -2
- package/dist/utils/virtualDiffLayout.d.ts.map +1 -1
- package/dist/utils/virtualDiffLayout.js +41 -1
- package/dist/utils/virtualDiffLayout.js.map +1 -1
- package/dist/worker/WorkerPoolManager.js +1 -1
- package/dist/worker/WorkerPoolManager.js.map +1 -1
- package/dist/worker/{wasm-D4DU5jgR.js → wasm-BaDzIkIn.js} +2 -2
- package/dist/worker/wasm-BaDzIkIn.js.map +1 -0
- package/dist/worker/worker-portable.js +294 -292
- package/dist/worker/worker-portable.js.map +1 -1
- package/dist/worker/worker.js +179 -181
- package/dist/worker/worker.js.map +1 -1
- package/package.json +22 -21
- package/dist/editor/css.d.ts +0 -6
- package/dist/editor/css.d.ts.map +0 -1
- package/dist/editor/css.js +0 -218
- package/dist/editor/css.js.map +0 -1
- package/dist/worker/wasm-D4DU5jgR.js.map +0 -1
|
@@ -1,116 +1,111 @@
|
|
|
1
1
|
import { isPrimaryModifier } from "./platform.js";
|
|
2
|
+
import { getEditorIconSvg } from "./sprite.js";
|
|
2
3
|
import { h } from "./utils.js";
|
|
3
4
|
|
|
4
5
|
//#region src/editor/searchPanel.ts
|
|
5
6
|
var SearchPanelWidget = class {
|
|
6
|
-
#textDocument;
|
|
7
7
|
#container;
|
|
8
8
|
#inputElement;
|
|
9
|
-
#matchesElement;
|
|
10
|
-
#searchParams = {
|
|
11
|
-
text: "",
|
|
12
|
-
replaceText: "",
|
|
13
|
-
caseSensitive: false,
|
|
14
|
-
wholeWord: false,
|
|
15
|
-
regex: false
|
|
16
|
-
};
|
|
17
|
-
#allMatches = [];
|
|
18
9
|
constructor(options) {
|
|
19
|
-
const { textDocument, containerElement, defaultQuery, initialMatch,
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
const { textDocument, containerElement, defaultQuery, initialMatch, scrollToMatch, onUpdate, onClose } = options;
|
|
11
|
+
const searchParams = {
|
|
12
|
+
text: defaultQuery,
|
|
13
|
+
replaceText: "",
|
|
14
|
+
caseSensitive: false,
|
|
15
|
+
wholeWord: false,
|
|
16
|
+
regex: false
|
|
23
17
|
};
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.updateMatches();
|
|
18
|
+
const matches = {
|
|
19
|
+
all: [],
|
|
20
|
+
current: void 0
|
|
28
21
|
};
|
|
29
|
-
const
|
|
30
|
-
|
|
22
|
+
const matchResultElement = h("div", { dataset: "matches" });
|
|
23
|
+
const updateMatches = () => {
|
|
24
|
+
matches.all = searchParams.text !== "" ? textDocument.search(searchParams) : [];
|
|
31
25
|
this.#container.querySelectorAll("[data-disabled]").forEach((element) => {
|
|
32
|
-
element.dataset.disabled = String(
|
|
26
|
+
element.dataset.disabled = String(matches.all.length === 0);
|
|
33
27
|
});
|
|
28
|
+
if (searchParams.text === "") {
|
|
29
|
+
matchResultElement.textContent = "";
|
|
30
|
+
delete matchResultElement.dataset.noMatches;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (matches.all.length === 0) {
|
|
34
|
+
matchResultElement.textContent = "No results";
|
|
35
|
+
matchResultElement.dataset.noMatches = "";
|
|
36
|
+
} else {
|
|
37
|
+
delete matchResultElement.dataset.noMatches;
|
|
38
|
+
updateCurrentMatch(onUpdate(matches.all));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
matches.current = void 0;
|
|
42
|
+
onUpdate([]);
|
|
34
43
|
};
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
const updateCurrentMatch = (currentMatch) => {
|
|
45
|
+
if (currentMatch === void 0) matchResultElement.textContent = `${matches.all.length} results`;
|
|
46
|
+
else {
|
|
47
|
+
const [start, end] = currentMatch;
|
|
48
|
+
matchResultElement.textContent = `${matches.all.findIndex((m) => m[0] === start && m[1] === end) + 1} of ${matches.all.length}`;
|
|
49
|
+
}
|
|
50
|
+
matches.current = currentMatch;
|
|
51
|
+
};
|
|
52
|
+
const updateSearchParam = (key, value) => {
|
|
53
|
+
searchParams[key] = value;
|
|
54
|
+
updateMatches();
|
|
41
55
|
};
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
const findNextMatch = (findPrevious = false, retainFocus = false) => {
|
|
57
|
+
const allMatches = matches.all;
|
|
58
|
+
let nextMatch = allMatches[0];
|
|
59
|
+
if (allMatches.length > 0) if (findPrevious) {
|
|
60
|
+
const searchOffset = matches.current?.[0] ?? 0;
|
|
61
|
+
nextMatch = allMatches.at(-1);
|
|
62
|
+
for (const m of allMatches) if (m[1] <= searchOffset) nextMatch = m;
|
|
63
|
+
else break;
|
|
64
|
+
} else {
|
|
65
|
+
const searchOffset = matches.current?.[1] ?? 0;
|
|
66
|
+
for (const m of allMatches) if (m[0] >= searchOffset) {
|
|
67
|
+
nextMatch = m;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
56
70
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
children: [
|
|
61
|
-
h("label", {
|
|
62
|
-
dataset: "checkbox",
|
|
63
|
-
children: [h("input", {
|
|
64
|
-
type: "checkbox",
|
|
65
|
-
checked: this.#searchParams.caseSensitive,
|
|
66
|
-
onchange: (e) => {
|
|
67
|
-
updateSearchParam("caseSensitive", e.target.checked);
|
|
68
|
-
}
|
|
69
|
-
}), "Match Case"]
|
|
70
|
-
}),
|
|
71
|
-
h("label", {
|
|
72
|
-
dataset: "checkbox",
|
|
73
|
-
children: [h("input", {
|
|
74
|
-
type: "checkbox",
|
|
75
|
-
checked: this.#searchParams.wholeWord,
|
|
76
|
-
onchange: (e) => {
|
|
77
|
-
updateSearchParam("wholeWord", e.target.checked);
|
|
78
|
-
}
|
|
79
|
-
}), "Whole Word"]
|
|
80
|
-
}),
|
|
81
|
-
h("label", {
|
|
82
|
-
dataset: "checkbox",
|
|
83
|
-
children: [h("input", {
|
|
84
|
-
type: "checkbox",
|
|
85
|
-
checked: this.#searchParams.regex,
|
|
86
|
-
onchange: (e) => {
|
|
87
|
-
updateSearchParam("regex", e.target.checked);
|
|
88
|
-
}
|
|
89
|
-
}), "Regexp"]
|
|
90
|
-
})
|
|
91
|
-
],
|
|
92
|
-
onmouseleave: () => {
|
|
93
|
-
closeSettingsPanelTimeout = setTimeout(() => {
|
|
94
|
-
settingsPanel.replaceWith(settingsSwitch);
|
|
95
|
-
}, 500);
|
|
96
|
-
},
|
|
97
|
-
onmouseenter: () => {
|
|
98
|
-
clearTimeout(closeSettingsPanelTimeout);
|
|
99
|
-
closeSettingsPanelTimeout = void 0;
|
|
71
|
+
if (nextMatch !== void 0) {
|
|
72
|
+
updateCurrentMatch(nextMatch);
|
|
73
|
+
scrollToMatch(nextMatch, retainFocus);
|
|
100
74
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
75
|
+
matches.current = nextMatch;
|
|
76
|
+
};
|
|
77
|
+
const close = () => {
|
|
78
|
+
this.cleanup();
|
|
79
|
+
onClose();
|
|
80
|
+
};
|
|
81
|
+
const makeToggle = (icon, title, key) => {
|
|
82
|
+
const button = h("div", {
|
|
83
|
+
dataset: {
|
|
84
|
+
icon,
|
|
85
|
+
active: String(searchParams[key])
|
|
86
|
+
},
|
|
87
|
+
title,
|
|
88
|
+
innerHTML: getEditorIconSvg(icon),
|
|
89
|
+
onclick: () => {
|
|
90
|
+
const next = !searchParams[key];
|
|
91
|
+
button.dataset.active = String(next);
|
|
92
|
+
updateSearchParam(key, next);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
return button;
|
|
96
|
+
};
|
|
97
|
+
const caseSensitiveToggle = makeToggle("case", "Match Case", "caseSensitive");
|
|
98
|
+
const wholeWordToggle = makeToggle("whole-word", "Whole Word", "wholeWord");
|
|
99
|
+
const regexToggle = makeToggle("regex", "Regexp", "regex");
|
|
105
100
|
this.#inputElement = h("input", {
|
|
106
101
|
type: "text",
|
|
107
102
|
placeholder: "Search",
|
|
108
103
|
dataset: "search",
|
|
109
104
|
value: defaultQuery,
|
|
110
105
|
oninput: (e) => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
searchParams.text = e.target.value;
|
|
107
|
+
matches.current = void 0;
|
|
108
|
+
updateMatches();
|
|
114
109
|
},
|
|
115
110
|
onkeydown: (e) => {
|
|
116
111
|
if (e.key === "Escape") {
|
|
@@ -118,11 +113,10 @@ var SearchPanelWidget = class {
|
|
|
118
113
|
close();
|
|
119
114
|
} else if (e.key === "Enter") {
|
|
120
115
|
e.preventDefault();
|
|
121
|
-
|
|
116
|
+
findNextMatch(false, true);
|
|
122
117
|
} else if (e.key === "f" && isPrimaryModifier(e)) e.preventDefault();
|
|
123
118
|
}
|
|
124
119
|
});
|
|
125
|
-
this.#matchesElement = h("div", { dataset: "matches" });
|
|
126
120
|
this.#container = h("div", {
|
|
127
121
|
dataset: "searchPanel",
|
|
128
122
|
children: [h("div", {
|
|
@@ -130,26 +124,23 @@ var SearchPanelWidget = class {
|
|
|
130
124
|
children: [
|
|
131
125
|
h("div", {
|
|
132
126
|
dataset: { icon: "search" },
|
|
133
|
-
innerHTML:
|
|
134
|
-
<line x1="16.5" y1="16.5" x2="12.0355" y2="12.0355" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></line>
|
|
135
|
-
<circle cx="8.5" cy="8.5" r="5" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle>
|
|
136
|
-
</svg>
|
|
137
|
-
`
|
|
127
|
+
innerHTML: getEditorIconSvg("search")
|
|
138
128
|
}),
|
|
139
129
|
this.#inputElement,
|
|
140
|
-
|
|
130
|
+
matchResultElement,
|
|
131
|
+
caseSensitiveToggle,
|
|
132
|
+
wholeWordToggle,
|
|
133
|
+
regexToggle,
|
|
134
|
+
h("div", { dataset: "divider" }),
|
|
141
135
|
h("div", {
|
|
142
136
|
dataset: {
|
|
143
137
|
icon: "arrow-up",
|
|
144
138
|
disabled: "true"
|
|
145
139
|
},
|
|
146
140
|
title: "Previous",
|
|
147
|
-
innerHTML:
|
|
148
|
-
<polyline points="12.5 3.5 6 10 12.5 16.5" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></polyline>
|
|
149
|
-
</svg>
|
|
150
|
-
`,
|
|
141
|
+
innerHTML: getEditorIconSvg("arrow-up"),
|
|
151
142
|
onclick: () => {
|
|
152
|
-
|
|
143
|
+
findNextMatch(true);
|
|
153
144
|
}
|
|
154
145
|
}),
|
|
155
146
|
h("div", {
|
|
@@ -158,56 +149,30 @@ var SearchPanelWidget = class {
|
|
|
158
149
|
disabled: "true"
|
|
159
150
|
},
|
|
160
151
|
title: "Next",
|
|
161
|
-
innerHTML:
|
|
162
|
-
<polyline points="7.5 16.5 14 10 7.5 3.5" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></polyline>
|
|
163
|
-
</svg>
|
|
164
|
-
`,
|
|
152
|
+
innerHTML: getEditorIconSvg("arrow-down"),
|
|
165
153
|
onclick: () => {
|
|
166
|
-
|
|
154
|
+
findNextMatch();
|
|
167
155
|
}
|
|
168
156
|
}),
|
|
169
|
-
h("div", { dataset: "spacer" }),
|
|
170
|
-
settingsSwitch,
|
|
171
157
|
h("div", {
|
|
172
158
|
dataset: { icon: "close" },
|
|
173
159
|
title: "Close",
|
|
174
|
-
innerHTML:
|
|
175
|
-
<line x1="5" y1="5" x2="15" y2="15" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></line>
|
|
176
|
-
<line x1="5" y1="15" x2="15" y2="5" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></line>
|
|
177
|
-
</svg>
|
|
178
|
-
`,
|
|
160
|
+
innerHTML: getEditorIconSvg("close"),
|
|
179
161
|
onclick: close
|
|
180
162
|
})
|
|
181
163
|
]
|
|
182
164
|
})]
|
|
183
165
|
});
|
|
166
|
+
matches.current = initialMatch;
|
|
184
167
|
containerElement.before(this.#container);
|
|
185
168
|
requestAnimationFrame(() => {
|
|
186
|
-
if (initialMatch !== void 0)
|
|
187
|
-
|
|
188
|
-
this.updateMatches(initialMatch);
|
|
189
|
-
}
|
|
169
|
+
if (initialMatch !== void 0) updateMatches();
|
|
170
|
+
else onUpdate([]);
|
|
190
171
|
this.#inputElement.select();
|
|
191
172
|
});
|
|
192
173
|
}
|
|
193
|
-
updateMatches(currentMatch = this.#allMatches[0]) {
|
|
194
|
-
const allMatches = this.#allMatches;
|
|
195
|
-
if (this.#searchParams.text === "") {
|
|
196
|
-
this.#matchesElement.textContent = "";
|
|
197
|
-
delete this.#matchesElement.dataset.noMatches;
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
if (allMatches.length === 0) {
|
|
201
|
-
this.#matchesElement.textContent = "No results";
|
|
202
|
-
this.#matchesElement.dataset.noMatches = "";
|
|
203
|
-
} else {
|
|
204
|
-
delete this.#matchesElement.dataset.noMatches;
|
|
205
|
-
const index = allMatches.findIndex((m) => m[0] === currentMatch[0] && m[1] === currentMatch[1]);
|
|
206
|
-
this.#matchesElement.textContent = index !== -1 ? `${index + 1} of ${allMatches.length}` : "No results";
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
174
|
focus() {
|
|
210
|
-
this.#inputElement.
|
|
175
|
+
this.#inputElement.focus();
|
|
211
176
|
}
|
|
212
177
|
cleanup() {
|
|
213
178
|
this.#container.remove();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"searchPanel.js","names":["#searchParams","#allMatches","#textDocument","#container","closeSettingsPanelTimeout: ReturnType<typeof setTimeout> | undefined","#inputElement","#matchesElement"],"sources":["../../src/editor/searchPanel.ts"],"sourcesContent":["import { isPrimaryModifier } from './platform';\nimport type { Range, TextDocument } from './textDocument';\nimport { h } from './utils';\n\nexport type SearchKind =\n | 'findNext'\n | 'findPrevious'\n | 'findAll'\n | 'replace'\n | 'replaceAll';\n\nexport interface SearchParams {\n text: string;\n replaceText: string;\n caseSensitive: boolean;\n wholeWord: boolean;\n regex: boolean;\n}\n\nexport interface SearchPanelOptions {\n textDocument: TextDocument<unknown>;\n containerElement: HTMLElement;\n defaultQuery: string;\n initialMatch?: [number, number];\n postSearch: (\n kind: SearchKind,\n match: [number, number],\n retainFocus?: boolean\n ) => void;\n getCurrentSearchRange: () => Range | undefined;\n onClose: () => void;\n}\n\nexport class SearchPanelWidget {\n #textDocument: TextDocument<unknown>;\n #container: HTMLDivElement;\n #inputElement: HTMLInputElement;\n #matchesElement: HTMLDivElement;\n #searchParams: SearchParams = {\n text: '',\n replaceText: '',\n caseSensitive: false,\n wholeWord: false,\n regex: false,\n };\n #allMatches: [number, number][] = [];\n\n constructor(options: SearchPanelOptions) {\n const {\n textDocument,\n containerElement,\n defaultQuery,\n initialMatch,\n postSearch,\n getCurrentSearchRange,\n onClose,\n } = options;\n\n const close = () => {\n this.cleanup();\n onClose();\n };\n\n const updateSearchParam = <K extends keyof SearchParams>(\n key: K,\n value: SearchParams[K]\n ) => {\n this.#searchParams[key] = value;\n updateAllMatches();\n this.updateMatches();\n };\n\n const updateAllMatches = () => {\n this.#allMatches =\n this.#searchParams.text !== ''\n ? this.#textDocument.search('findAll', this.#searchParams)\n : [];\n this.#container\n .querySelectorAll<HTMLElement>('[data-disabled]')\n .forEach((element) => {\n element.dataset.disabled = String(this.#allMatches.length === 0);\n });\n };\n\n const search = (kind: SearchKind, retainFocus?: boolean) => {\n const matches = this.#textDocument.search(\n kind,\n this.#searchParams,\n getCurrentSearchRange()\n );\n if (matches.length === 0) {\n return;\n }\n const firstMatch = matches[0];\n this.updateMatches(firstMatch);\n postSearch(kind, firstMatch, retainFocus);\n };\n\n const settingsSwitch = h('div', {\n dataset: { icon: 'settings' },\n title: 'Settings',\n innerHTML: `<svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <line x1=\"3\" y1=\"6\" x2=\"10\" y2=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"></line>\n <circle cx=\"12.5\" cy=\"6\" r=\"2.5\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"></circle>\n <line x1=\"15\" y1=\"6\" x2=\"17\" y2=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"></line>\n <line x1=\"17\" y1=\"14\" x2=\"10\" y2=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"></line>\n <circle cx=\"7.5\" cy=\"14\" r=\"2.5\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"></circle>\n <line x1=\"5\" y1=\"14\" x2=\"3\" y2=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"></line>\n </svg>\n `,\n onclick: () => {\n settingsSwitch.replaceWith(settingsPanel);\n },\n });\n const settingsPanel = h('div', {\n dataset: 'settings',\n children: [\n h('label', {\n dataset: 'checkbox',\n children: [\n h('input', {\n type: 'checkbox',\n checked: this.#searchParams.caseSensitive,\n onchange: (e: Event) => {\n updateSearchParam(\n 'caseSensitive',\n (e.target as HTMLInputElement).checked\n );\n },\n }),\n 'Match Case',\n ],\n }),\n h('label', {\n dataset: 'checkbox',\n children: [\n h('input', {\n type: 'checkbox',\n checked: this.#searchParams.wholeWord,\n onchange: (e: Event) => {\n updateSearchParam(\n 'wholeWord',\n (e.target as HTMLInputElement).checked\n );\n },\n }),\n 'Whole Word',\n ],\n }),\n h('label', {\n dataset: 'checkbox',\n children: [\n h('input', {\n type: 'checkbox',\n checked: this.#searchParams.regex,\n onchange: (e: Event) => {\n updateSearchParam(\n 'regex',\n (e.target as HTMLInputElement).checked\n );\n },\n }),\n 'Regexp',\n ],\n }),\n ],\n onmouseleave: () => {\n closeSettingsPanelTimeout = setTimeout(() => {\n settingsPanel.replaceWith(settingsSwitch);\n }, 500);\n },\n onmouseenter: () => {\n clearTimeout(closeSettingsPanelTimeout);\n closeSettingsPanelTimeout = undefined;\n },\n });\n\n let closeSettingsPanelTimeout: ReturnType<typeof setTimeout> | undefined;\n\n this.#textDocument = textDocument;\n this.#searchParams.text = defaultQuery;\n\n this.#inputElement = h('input', {\n type: 'text',\n placeholder: 'Search',\n dataset: 'search',\n value: defaultQuery,\n oninput: (e: Event) => {\n this.#searchParams.text = (e.target as HTMLInputElement).value;\n updateAllMatches();\n this.updateMatches();\n },\n onkeydown: (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n close();\n } else if (e.key === 'Enter') {\n e.preventDefault();\n search('findNext', true);\n } else if (e.key === 'f' && isPrimaryModifier(e)) {\n // prevent the default browser search panel open behavior\n e.preventDefault();\n }\n },\n });\n this.#matchesElement = h('div', { dataset: 'matches' });\n this.#container = h('div', {\n dataset: 'searchPanel',\n children: [\n h('div', {\n dataset: 'searchPanelRow',\n children: [\n h('div', {\n dataset: { icon: 'search' },\n innerHTML: `<svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <line x1=\"16.5\" y1=\"16.5\" x2=\"12.0355\" y2=\"12.0355\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"></line>\n <circle cx=\"8.5\" cy=\"8.5\" r=\"5\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"></circle>\n </svg>\n `,\n }),\n this.#inputElement,\n this.#matchesElement,\n h('div', {\n dataset: { icon: 'arrow-up', disabled: 'true' },\n title: 'Previous',\n innerHTML: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\">\n <polyline points=\"12.5 3.5 6 10 12.5 16.5\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"></polyline>\n </svg>\n `,\n onclick: () => {\n search('findPrevious');\n },\n }),\n h('div', {\n dataset: { icon: 'arrow-down', disabled: 'true' },\n title: 'Next',\n innerHTML: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\">\n <polyline points=\"7.5 16.5 14 10 7.5 3.5\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"></polyline>\n </svg>\n `,\n onclick: () => {\n search('findNext');\n },\n }),\n h('div', { dataset: 'spacer' }),\n settingsSwitch,\n h('div', {\n dataset: { icon: 'close' },\n title: 'Close',\n innerHTML: `<svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\">\n <line x1=\"5\" y1=\"5\" x2=\"15\" y2=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"></line>\n <line x1=\"5\" y1=\"15\" x2=\"15\" y2=\"5\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"></line>\n </svg>\n `,\n onclick: close,\n }),\n ],\n }),\n ],\n });\n containerElement.before(this.#container);\n\n requestAnimationFrame(() => {\n if (initialMatch !== undefined) {\n updateAllMatches();\n this.updateMatches(initialMatch);\n }\n this.#inputElement.select();\n });\n }\n\n updateMatches(currentMatch: [number, number] = this.#allMatches[0]): void {\n const allMatches = this.#allMatches;\n const searchText = this.#searchParams.text;\n\n if (searchText === '') {\n this.#matchesElement.textContent = '';\n delete this.#matchesElement.dataset.noMatches;\n return;\n }\n\n if (allMatches.length === 0) {\n this.#matchesElement.textContent = 'No results';\n this.#matchesElement.dataset.noMatches = '';\n } else {\n delete this.#matchesElement.dataset.noMatches;\n const index = allMatches.findIndex(\n (m) => m[0] === currentMatch[0] && m[1] === currentMatch[1]\n );\n this.#matchesElement.textContent =\n index !== -1 ? `${index + 1} of ${allMatches.length}` : 'No results';\n }\n }\n\n focus(): void {\n this.#inputElement.select();\n }\n\n cleanup(): void {\n this.#container.remove();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"searchPanel.js","names":["searchParams: SearchParams","#container","nextMatch: MatchRange | undefined","#inputElement"],"sources":["../../src/editor/searchPanel.ts"],"sourcesContent":["import { isPrimaryModifier } from './platform';\nimport { getEditorIconSvg, type SVGSpriteNames } from './sprite';\nimport type { TextDocument } from './textDocument';\nimport { h } from './utils';\n\nexport type MatchRange = [startOffset: number, endOffset: number];\n\nexport interface SearchParams {\n text: string;\n replaceText: string;\n caseSensitive: boolean;\n wholeWord: boolean;\n regex: boolean;\n}\n\nexport interface SearchPanelOptions {\n textDocument: TextDocument<unknown>;\n containerElement: HTMLElement;\n defaultQuery: string;\n initialMatch?: MatchRange;\n scrollToMatch: (nextMatch: MatchRange, retainFocus: boolean) => void;\n onUpdate: (matches: MatchRange[]) => MatchRange | undefined;\n onClose: () => void;\n}\n\nexport class SearchPanelWidget {\n #container: HTMLDivElement;\n #inputElement: HTMLInputElement;\n\n constructor(options: SearchPanelOptions) {\n const {\n textDocument,\n containerElement,\n defaultQuery,\n initialMatch,\n scrollToMatch,\n onUpdate,\n onClose,\n } = options;\n\n const searchParams: SearchParams = {\n text: defaultQuery,\n replaceText: '',\n caseSensitive: false,\n wholeWord: false,\n regex: false,\n };\n\n const matches = {\n all: [] as MatchRange[],\n current: undefined as MatchRange | undefined,\n };\n\n const matchResultElement = h('div', { dataset: 'matches' });\n const updateMatches = () => {\n matches.all =\n searchParams.text !== '' ? textDocument.search(searchParams) : [];\n this.#container\n .querySelectorAll<HTMLElement>('[data-disabled]')\n .forEach((element) => {\n element.dataset.disabled = String(matches.all.length === 0);\n });\n\n if (searchParams.text === '') {\n matchResultElement.textContent = '';\n delete matchResultElement.dataset.noMatches;\n return;\n }\n\n if (matches.all.length === 0) {\n matchResultElement.textContent = 'No results';\n matchResultElement.dataset.noMatches = '';\n } else {\n delete matchResultElement.dataset.noMatches;\n updateCurrentMatch(onUpdate(matches.all));\n return;\n }\n\n matches.current = undefined;\n onUpdate([]);\n };\n\n const updateCurrentMatch = (currentMatch: MatchRange | undefined) => {\n if (currentMatch === undefined) {\n matchResultElement.textContent = `${matches.all.length} results`;\n } else {\n const [start, end] = currentMatch;\n const index = matches.all.findIndex(\n (m) => m[0] === start && m[1] === end\n );\n matchResultElement.textContent = `${index + 1} of ${matches.all.length}`;\n }\n matches.current = currentMatch;\n };\n\n const updateSearchParam = <K extends keyof SearchParams>(\n key: K,\n value: SearchParams[K]\n ) => {\n searchParams[key] = value;\n updateMatches();\n };\n\n const findNextMatch = (\n findPrevious: boolean = false,\n retainFocus: boolean = false\n ) => {\n const allMatches = matches.all;\n let nextMatch: MatchRange | undefined = allMatches[0];\n if (allMatches.length > 0) {\n if (findPrevious) {\n const searchOffset = matches.current?.[0] ?? 0;\n nextMatch = allMatches.at(-1);\n for (const m of allMatches) {\n if (m[1] <= searchOffset) {\n nextMatch = m;\n } else {\n break;\n }\n }\n } else {\n const searchOffset = matches.current?.[1] ?? 0;\n for (const m of allMatches) {\n if (m[0] >= searchOffset) {\n nextMatch = m;\n break;\n }\n }\n }\n }\n if (nextMatch !== undefined) {\n updateCurrentMatch(nextMatch);\n scrollToMatch(nextMatch, retainFocus);\n }\n matches.current = nextMatch;\n };\n\n const close = () => {\n this.cleanup();\n onClose();\n };\n\n // Builds an always-visible icon button that toggles one boolean search\n // option (case/whole-word/regex). The button reflects its on/off state via\n // the `data-active` attribute so the stylesheet can highlight it.\n const makeToggle = (\n icon: SVGSpriteNames,\n title: string,\n key: 'caseSensitive' | 'wholeWord' | 'regex'\n ) => {\n const button = h('div', {\n dataset: { icon, active: String(searchParams[key]) },\n title,\n innerHTML: getEditorIconSvg(icon),\n onclick: () => {\n const next = !searchParams[key];\n button.dataset.active = String(next);\n updateSearchParam(key, next);\n },\n });\n return button;\n };\n\n const caseSensitiveToggle = makeToggle(\n 'case',\n 'Match Case',\n 'caseSensitive'\n );\n const wholeWordToggle = makeToggle('whole-word', 'Whole Word', 'wholeWord');\n const regexToggle = makeToggle('regex', 'Regexp', 'regex');\n\n this.#inputElement = h('input', {\n type: 'text',\n placeholder: 'Search',\n dataset: 'search',\n value: defaultQuery,\n oninput: (e: Event) => {\n searchParams.text = (e.target as HTMLInputElement).value;\n matches.current = undefined;\n updateMatches();\n },\n onkeydown: (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n close();\n } else if (e.key === 'Enter') {\n e.preventDefault();\n findNextMatch(false, true);\n } else if (e.key === 'f' && isPrimaryModifier(e)) {\n // prevent the default browser search panel open behavior\n e.preventDefault();\n }\n },\n });\n\n this.#container = h('div', {\n dataset: 'searchPanel',\n children: [\n h('div', {\n dataset: 'searchPanelRow',\n children: [\n h('div', {\n dataset: { icon: 'search' },\n innerHTML: getEditorIconSvg('search'),\n }),\n this.#inputElement,\n matchResultElement,\n caseSensitiveToggle,\n wholeWordToggle,\n regexToggle,\n h('div', { dataset: 'divider' }),\n h('div', {\n dataset: { icon: 'arrow-up', disabled: 'true' },\n title: 'Previous',\n innerHTML: getEditorIconSvg('arrow-up'),\n onclick: () => {\n findNextMatch(true);\n },\n }),\n h('div', {\n dataset: { icon: 'arrow-down', disabled: 'true' },\n title: 'Next',\n innerHTML: getEditorIconSvg('arrow-down'),\n onclick: () => {\n findNextMatch();\n },\n }),\n h('div', {\n dataset: { icon: 'close' },\n title: 'Close',\n innerHTML: getEditorIconSvg('close'),\n onclick: close,\n }),\n ],\n }),\n ],\n });\n\n matches.current = initialMatch;\n containerElement.before(this.#container);\n\n requestAnimationFrame(() => {\n if (initialMatch !== undefined) {\n updateMatches();\n } else {\n onUpdate([]);\n }\n this.#inputElement.select();\n });\n }\n\n focus(): void {\n this.#inputElement.focus();\n }\n\n cleanup(): void {\n this.#container.remove();\n }\n}\n"],"mappings":"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"}
|
package/dist/editor/selection.js
CHANGED
|
@@ -883,11 +883,17 @@ function getLineChildEnd(child, textOffsetInChild) {
|
|
|
883
883
|
}
|
|
884
884
|
function getLineIndex(el) {
|
|
885
885
|
const { line } = el.dataset;
|
|
886
|
-
if (line !== void 0)
|
|
886
|
+
if (line !== void 0) {
|
|
887
|
+
const lineNumber = parseInt(line, 10);
|
|
888
|
+
if (!Number.isNaN(lineNumber)) return lineNumber - 1;
|
|
889
|
+
}
|
|
887
890
|
}
|
|
888
891
|
function getCharacterIndex(el) {
|
|
889
892
|
const { char } = el.dataset;
|
|
890
|
-
|
|
893
|
+
if (char !== void 0) {
|
|
894
|
+
const charIndex = parseInt(char, 10);
|
|
895
|
+
if (!Number.isNaN(charIndex)) return charIndex;
|
|
896
|
+
}
|
|
891
897
|
}
|
|
892
898
|
function getTextOffset(text, offset) {
|
|
893
899
|
const value = text ?? "";
|