@fresh-editor/fresh-editor 0.2.23 → 0.2.24
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/CHANGELOG.md +56 -0
- package/package.json +1 -1
- package/plugins/audit_mode.i18n.json +497 -119
- package/plugins/audit_mode.ts +2568 -551
- package/plugins/config-schema.json +7 -1
- package/plugins/git_blame.ts +1 -6
- package/plugins/git_log.ts +616 -1025
- package/plugins/lib/fresh.d.ts +69 -2
- package/plugins/lib/git_history.ts +596 -0
- package/plugins/markdown_compose.ts +183 -7
- package/plugins/search_replace.i18n.json +42 -14
- package/plugins/search_replace.ts +146 -96
- package/plugins/vi_mode.ts +8 -3
|
@@ -37,6 +37,11 @@ function setGlobalComposeEnabled(value: boolean): void {
|
|
|
37
37
|
interface TableWidthInfo {
|
|
38
38
|
maxW: number[];
|
|
39
39
|
allocated: number[];
|
|
40
|
+
// True iff this row is the markdown source separator (`|---|---|---|`) — the
|
|
41
|
+
// border code uses this to avoid drawing a duplicate `├─┼─┤` next to it.
|
|
42
|
+
// Optional for backwards-compat with persisted view states from older
|
|
43
|
+
// sessions.
|
|
44
|
+
isSourceSep?: boolean;
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
// Helper: check whether the active split has compose mode for this buffer
|
|
@@ -90,6 +95,161 @@ const HTML_ENTITY_MAP: Record<string, string> = {
|
|
|
90
95
|
laquo: "\u00AB", raquo: "\u00BB", ensp: "\u2002", emsp: "\u2003", thinsp: "\u2009",
|
|
91
96
|
};
|
|
92
97
|
|
|
98
|
+
// =============================================================================
|
|
99
|
+
// Table border virtual lines (top/bottom + inter-row separators)
|
|
100
|
+
// =============================================================================
|
|
101
|
+
//
|
|
102
|
+
// Markdown tables source-encode only an underline-style separator between the
|
|
103
|
+
// header and the first data row. In compose mode we already conceal the
|
|
104
|
+
// pipe characters into Unicode box-drawing (`│`, `├`, `┼`, `┤`). This module
|
|
105
|
+
// adds the *missing* visual frame: a `┌─┬─┐` top border above the header,
|
|
106
|
+
// `├─┼─┤` separators between consecutive data rows (so each row reads as a
|
|
107
|
+
// distinct cell), and a `└─┴─┘` bottom border below the last row.
|
|
108
|
+
//
|
|
109
|
+
// Implementation:
|
|
110
|
+
//
|
|
111
|
+
// * Borders are virtual lines (no source bytes), keyed per-line via a
|
|
112
|
+
// unique namespace `md-tb-${lineNumber}`. The namespace lets us
|
|
113
|
+
// clear+rebuild borders for one row without disturbing other tables.
|
|
114
|
+
// * "First/last/source-separator" classification is derived from the
|
|
115
|
+
// cached widthMap (a row is "known" iff it has a TableWidthInfo entry).
|
|
116
|
+
// This is cheap and stable across scrolls because widthMap accumulates.
|
|
117
|
+
// * Border column widths come from the same `allocated` widths used by
|
|
118
|
+
// processLineConceals, so the borders line up exactly with the cell
|
|
119
|
+
// conceals.
|
|
120
|
+
|
|
121
|
+
/** Build a horizontal table border line of the given style for a row. */
|
|
122
|
+
function buildTableBorderLine(
|
|
123
|
+
allocated: number[],
|
|
124
|
+
left: string,
|
|
125
|
+
mid: string,
|
|
126
|
+
right: string,
|
|
127
|
+
): string {
|
|
128
|
+
// Each cell render is `│ <text padded to allocated[i] - 2> │` (2 chars of
|
|
129
|
+
// inside padding). The matching border slot must therefore be
|
|
130
|
+
// `allocated[i]` wide of `─` characters between the corner/junction marks.
|
|
131
|
+
const parts: string[] = [];
|
|
132
|
+
for (let i = 0; i < allocated.length; i++) {
|
|
133
|
+
const fill = "─".repeat(Math.max(1, allocated[i]));
|
|
134
|
+
parts.push(fill);
|
|
135
|
+
}
|
|
136
|
+
return left + parts.join(mid) + right;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** True if `lineContent` looks like a markdown table separator row. */
|
|
140
|
+
function isTableSeparatorContent(lineContent: string): boolean {
|
|
141
|
+
return /^\|[-:\s|]+\|$/.test(lineContent.trim());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Re-emit the table border virtual lines for the given table-row group.
|
|
145
|
+
*
|
|
146
|
+
* Detects the group's first/last visible rows by consulting `widthMap`
|
|
147
|
+
* (which is updated by `processTableAlignment` before this runs). A row at
|
|
148
|
+
* `lineNumber - 1` or `lineNumber + 1` that is *not* in `widthMap` is treated
|
|
149
|
+
* as the boundary of the table's visible extent.
|
|
150
|
+
*/
|
|
151
|
+
function processTableBorders(
|
|
152
|
+
bufferId: number,
|
|
153
|
+
lines: Array<{
|
|
154
|
+
line_number: number;
|
|
155
|
+
byte_start: number;
|
|
156
|
+
byte_end: number;
|
|
157
|
+
content: string;
|
|
158
|
+
}>,
|
|
159
|
+
widthMap: Map<number, TableWidthInfo>,
|
|
160
|
+
): void {
|
|
161
|
+
// Use theme keys (resolved at render time so the borders follow theme
|
|
162
|
+
// changes — same pattern as addOverlay's fg/bg options).
|
|
163
|
+
//
|
|
164
|
+
// * fg → editor.fg (the default document foreground, matching the
|
|
165
|
+
// concealed `│` / `─` glyphs inside row text so the virtual
|
|
166
|
+
// `┌─┬─┐` / `├─┼─┤` / `└─┴─┘` frame doesn't create a visible seam
|
|
167
|
+
// where it meets the in-text borders)
|
|
168
|
+
// * bg → editor.bg (matches the document background so the borders
|
|
169
|
+
// blend in rather than carving an opaque slab through the page)
|
|
170
|
+
const borderOptions = { fg: "editor.fg", bg: "editor.bg" };
|
|
171
|
+
|
|
172
|
+
for (const line of lines) {
|
|
173
|
+
const ns = `md-tb-${line.line_number}`;
|
|
174
|
+
// Always start by clearing this row's previous borders (handles
|
|
175
|
+
// edits that removed/widened the row, scrolls that change the
|
|
176
|
+
// first/last classification, etc.).
|
|
177
|
+
editor.clearVirtualTextNamespace(bufferId, ns);
|
|
178
|
+
|
|
179
|
+
const trimmed = line.content.trim();
|
|
180
|
+
const isTableRow = trimmed.startsWith("|") || trimmed.endsWith("|");
|
|
181
|
+
if (!isTableRow) continue;
|
|
182
|
+
|
|
183
|
+
const widthInfo = widthMap.get(line.line_number);
|
|
184
|
+
if (!widthInfo || widthInfo.allocated.length === 0) continue;
|
|
185
|
+
|
|
186
|
+
const allocated = widthInfo.allocated;
|
|
187
|
+
// Prefer the cached flag (set by processTableAlignment from the source
|
|
188
|
+
// text of this exact row); fall back to a regex check in case this row
|
|
189
|
+
// was loaded from a persisted view state without the flag.
|
|
190
|
+
const isSourceSep = widthInfo.isSourceSep === true
|
|
191
|
+
|| isTableSeparatorContent(line.content);
|
|
192
|
+
|
|
193
|
+
const prevIsTable = widthMap.has(line.line_number - 1);
|
|
194
|
+
const nextIsTable = widthMap.has(line.line_number + 1);
|
|
195
|
+
|
|
196
|
+
// Top border: only above the very first known row of the table.
|
|
197
|
+
// ┌─┬─┐ — opens the frame above the header.
|
|
198
|
+
if (!prevIsTable) {
|
|
199
|
+
editor.addVirtualLine(
|
|
200
|
+
bufferId,
|
|
201
|
+
line.byte_start,
|
|
202
|
+
buildTableBorderLine(allocated, "┌", "┬", "┐"),
|
|
203
|
+
borderOptions,
|
|
204
|
+
true, // above
|
|
205
|
+
ns,
|
|
206
|
+
0,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Inter-row separator: between consecutive *data* rows.
|
|
211
|
+
//
|
|
212
|
+
// Skip if either side is the source separator row (`|---|---|---|`)
|
|
213
|
+
// because the source already provides `├─┼─┤` there via conceals —
|
|
214
|
+
// adding another above/below would draw two adjacent separator lines.
|
|
215
|
+
//
|
|
216
|
+
// Drawn ABOVE the current row when its predecessor is also a (non-
|
|
217
|
+
// source-separator) table row, so each row owns the separator that
|
|
218
|
+
// appears above it.
|
|
219
|
+
const prevInfo = widthMap.get(line.line_number - 1);
|
|
220
|
+
const prevIsSourceSep = prevInfo?.isSourceSep === true;
|
|
221
|
+
if (prevIsTable && !isSourceSep && !prevIsSourceSep) {
|
|
222
|
+
editor.addVirtualLine(
|
|
223
|
+
bufferId,
|
|
224
|
+
line.byte_start,
|
|
225
|
+
buildTableBorderLine(allocated, "├", "┼", "┤"),
|
|
226
|
+
borderOptions,
|
|
227
|
+
true, // above
|
|
228
|
+
ns,
|
|
229
|
+
1,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Bottom border: only below the last known row of the table.
|
|
234
|
+
// └─┴─┘ — closes the frame. Anchor at the END of the row's bytes
|
|
235
|
+
// (one before the trailing newline) and place "below".
|
|
236
|
+
if (!nextIsTable) {
|
|
237
|
+
// byte_end points just past the newline; anchor at last byte of
|
|
238
|
+
// the row content so the virtual line renders directly under it.
|
|
239
|
+
const anchor = Math.max(line.byte_start, line.byte_end - 1);
|
|
240
|
+
editor.addVirtualLine(
|
|
241
|
+
bufferId,
|
|
242
|
+
anchor,
|
|
243
|
+
buildTableBorderLine(allocated, "└", "┴", "┘"),
|
|
244
|
+
borderOptions,
|
|
245
|
+
false, // below
|
|
246
|
+
ns,
|
|
247
|
+
0,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
93
253
|
// =============================================================================
|
|
94
254
|
// Block-based parser for hanging indent support
|
|
95
255
|
// =============================================================================
|
|
@@ -1375,18 +1535,25 @@ function processTableAlignment(
|
|
|
1375
1535
|
if (allocGrew(widthMap.get(ln)!)) { needsRefresh = true; break; }
|
|
1376
1536
|
}
|
|
1377
1537
|
|
|
1378
|
-
// Store merged widths for
|
|
1379
|
-
//
|
|
1380
|
-
//
|
|
1381
|
-
|
|
1538
|
+
// Store merged widths for each line in the group. We tag the source
|
|
1539
|
+
// separator row (`|---|---|---|`) so the border renderer can skip
|
|
1540
|
+
// drawing a duplicate `├─┼─┤` adjacent to it (the source separator is
|
|
1541
|
+
// already concealed into one). Each line gets its own info object so
|
|
1542
|
+
// the per-row `isSourceSep` flag is independent.
|
|
1382
1543
|
for (const line of group) {
|
|
1383
|
-
|
|
1544
|
+
const isSep = /^\|[-:\s|]+\|$/.test(line.content.trim());
|
|
1545
|
+
widthMap.set(line.line_number, { maxW: merged, allocated, isSourceSep: isSep });
|
|
1384
1546
|
}
|
|
1547
|
+
// Adjacent cached lines (already-processed neighbours of this group)
|
|
1548
|
+
// need their `allocated` updated but should keep their existing
|
|
1549
|
+
// `isSourceSep` flag — they were classified when they were processed.
|
|
1385
1550
|
for (let ln = firstLine - 1; widthMap.has(ln); ln--) {
|
|
1386
|
-
widthMap.
|
|
1551
|
+
const prev = widthMap.get(ln)!;
|
|
1552
|
+
widthMap.set(ln, { maxW: merged, allocated, isSourceSep: prev.isSourceSep });
|
|
1387
1553
|
}
|
|
1388
1554
|
for (let ln = lastLine + 1; widthMap.has(ln); ln++) {
|
|
1389
|
-
widthMap.
|
|
1555
|
+
const prev = widthMap.get(ln)!;
|
|
1556
|
+
widthMap.set(ln, { maxW: merged, allocated, isSourceSep: prev.isSourceSep });
|
|
1390
1557
|
}
|
|
1391
1558
|
}
|
|
1392
1559
|
|
|
@@ -1426,6 +1593,15 @@ function onMarkdownLinesChanged(data: {
|
|
|
1426
1593
|
processLineSoftBreaks(data.buffer_id, line.content, line.byte_start, line.byte_end, cursors, line.line_number);
|
|
1427
1594
|
}
|
|
1428
1595
|
|
|
1596
|
+
// Add/refresh table border virtual lines (top/bottom + inter-row separators).
|
|
1597
|
+
// Runs AFTER processTableAlignment so the widthMap reflects the latest
|
|
1598
|
+
// allocated widths, and AFTER processLineConceals so the borders we draw
|
|
1599
|
+
// line up with the cell pipes the conceals produce.
|
|
1600
|
+
const widthMapForBorders = getTableWidths(data.buffer_id);
|
|
1601
|
+
if (widthMapForBorders) {
|
|
1602
|
+
processTableBorders(data.buffer_id, data.lines, widthMapForBorders);
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1429
1605
|
if (tableWidthsGrew) {
|
|
1430
1606
|
editor.refreshLines(data.buffer_id);
|
|
1431
1607
|
}
|
|
@@ -36,7 +36,9 @@
|
|
|
36
36
|
"panel.replace_all_btn": "Replace All (Alt+Ret)",
|
|
37
37
|
"panel.type_pattern": "Type a search pattern above",
|
|
38
38
|
"panel.matches_count": "Matches (%{count} in %{files} files)",
|
|
39
|
-
"panel.matches_title": "Matches"
|
|
39
|
+
"panel.matches_title": "Matches",
|
|
40
|
+
"prompt.confirm_replace": "Replace %{count} match(es) in %{files} file(s)? Undo only covers files still open in this session. Press Enter to confirm, Esc to cancel.",
|
|
41
|
+
"status.replace_cancelled": "Replacement cancelled"
|
|
40
42
|
},
|
|
41
43
|
"cs": {
|
|
42
44
|
"cmd.search_replace": "Hledat a nahradit v projektu",
|
|
@@ -75,7 +77,9 @@
|
|
|
75
77
|
"panel.replace_all_btn": "Nahradit vše (Alt+Ret)",
|
|
76
78
|
"panel.type_pattern": "Zadejte vyhledávací vzor",
|
|
77
79
|
"panel.matches_count": "Shody (%{count} v %{files} souborech)",
|
|
78
|
-
"panel.matches_title": "Shody"
|
|
80
|
+
"panel.matches_title": "Shody",
|
|
81
|
+
"prompt.confirm_replace": "Nahradit %{count} výskyt(ů) v %{files} souboru(ech)? Vrátit zpět lze jen soubory otevřené v této relaci. Enter potvrdí, Esc zruší.",
|
|
82
|
+
"status.replace_cancelled": "Nahrazení zrušeno"
|
|
79
83
|
},
|
|
80
84
|
"de": {
|
|
81
85
|
"cmd.search_replace": "Suchen und Ersetzen im Projekt",
|
|
@@ -114,7 +118,9 @@
|
|
|
114
118
|
"panel.replace_all_btn": "Alle ersetzen (Alt+Ret)",
|
|
115
119
|
"panel.type_pattern": "Suchmuster oben eingeben",
|
|
116
120
|
"panel.matches_count": "Treffer (%{count} in %{files} Dateien)",
|
|
117
|
-
"panel.matches_title": "Treffer"
|
|
121
|
+
"panel.matches_title": "Treffer",
|
|
122
|
+
"prompt.confirm_replace": "%{count} Treffer in %{files} Datei(en) ersetzen? Rückgängig wirkt nur auf Dateien, die in dieser Sitzung geöffnet sind. Enter bestätigt, Esc bricht ab.",
|
|
123
|
+
"status.replace_cancelled": "Ersetzen abgebrochen"
|
|
118
124
|
},
|
|
119
125
|
"es": {
|
|
120
126
|
"cmd.search_replace": "Buscar y Reemplazar en Proyecto",
|
|
@@ -153,7 +159,9 @@
|
|
|
153
159
|
"panel.replace_all_btn": "Reemplazar todo (Alt+Ret)",
|
|
154
160
|
"panel.type_pattern": "Escriba un patrón de búsqueda",
|
|
155
161
|
"panel.matches_count": "Coincidencias (%{count} en %{files} archivos)",
|
|
156
|
-
"panel.matches_title": "Coincidencias"
|
|
162
|
+
"panel.matches_title": "Coincidencias",
|
|
163
|
+
"prompt.confirm_replace": "¿Reemplazar %{count} coincidencia(s) en %{files} archivo(s)? Deshacer solo cubre los archivos abiertos en esta sesión. Intro confirma, Esc cancela.",
|
|
164
|
+
"status.replace_cancelled": "Reemplazo cancelado"
|
|
157
165
|
},
|
|
158
166
|
"fr": {
|
|
159
167
|
"cmd.search_replace": "Rechercher et Remplacer dans le Projet",
|
|
@@ -192,7 +200,9 @@
|
|
|
192
200
|
"panel.replace_all_btn": "Tout remplacer (Alt+Ret)",
|
|
193
201
|
"panel.type_pattern": "Saisissez un motif de recherche",
|
|
194
202
|
"panel.matches_count": "Correspondances (%{count} dans %{files} fichiers)",
|
|
195
|
-
"panel.matches_title": "Correspondances"
|
|
203
|
+
"panel.matches_title": "Correspondances",
|
|
204
|
+
"prompt.confirm_replace": "Remplacer %{count} correspondance(s) dans %{files} fichier(s) ? L'annulation ne concerne que les fichiers ouverts dans cette session. Entrée confirme, Échap annule.",
|
|
205
|
+
"status.replace_cancelled": "Remplacement annulé"
|
|
196
206
|
},
|
|
197
207
|
"it": {
|
|
198
208
|
"cmd.search_replace": "Cerca e sostituisci nel progetto",
|
|
@@ -231,7 +241,9 @@
|
|
|
231
241
|
"panel.replace_all_btn": "Sostituisci tutto (Alt+Ret)",
|
|
232
242
|
"panel.type_pattern": "Digita un modello di ricerca",
|
|
233
243
|
"panel.matches_count": "Corrispondenze (%{count} in %{files} file)",
|
|
234
|
-
"panel.matches_title": "Corrispondenze"
|
|
244
|
+
"panel.matches_title": "Corrispondenze",
|
|
245
|
+
"prompt.confirm_replace": "Sostituire %{count} corrispondenza/e in %{files} file? L'annulla copre solo i file aperti in questa sessione. Invio conferma, Esc annulla.",
|
|
246
|
+
"status.replace_cancelled": "Sostituzione annullata"
|
|
235
247
|
},
|
|
236
248
|
"ja": {
|
|
237
249
|
"cmd.search_replace": "プロジェクト内で検索と置換",
|
|
@@ -270,7 +282,9 @@
|
|
|
270
282
|
"panel.replace_all_btn": "すべて置換 (Alt+Ret)",
|
|
271
283
|
"panel.type_pattern": "検索パターンを入力してください",
|
|
272
284
|
"panel.matches_count": "一致 (%{files}ファイル中%{count}件)",
|
|
273
|
-
"panel.matches_title": "一致"
|
|
285
|
+
"panel.matches_title": "一致",
|
|
286
|
+
"prompt.confirm_replace": "%{files} 個のファイルで %{count} 件の一致を置換しますか?元に戻せるのはこのセッションで開いているファイルのみです。Enterで確認、Escでキャンセル。",
|
|
287
|
+
"status.replace_cancelled": "置換がキャンセルされました"
|
|
274
288
|
},
|
|
275
289
|
"ko": {
|
|
276
290
|
"cmd.search_replace": "프로젝트에서 검색 및 바꾸기",
|
|
@@ -309,7 +323,9 @@
|
|
|
309
323
|
"panel.replace_all_btn": "모두 바꾸기 (Alt+Ret)",
|
|
310
324
|
"panel.type_pattern": "검색 패턴을 입력하세요",
|
|
311
325
|
"panel.matches_count": "일치 (%{files}개 파일에서 %{count}개)",
|
|
312
|
-
"panel.matches_title": "일치"
|
|
326
|
+
"panel.matches_title": "일치",
|
|
327
|
+
"prompt.confirm_replace": "%{files}개 파일에서 %{count}개 일치를 바꾸시겠습니까? 실행 취소는 이 세션에서 열려 있는 파일에만 적용됩니다. Enter 확인, Esc 취소.",
|
|
328
|
+
"status.replace_cancelled": "바꾸기가 취소됨"
|
|
313
329
|
},
|
|
314
330
|
"pt-BR": {
|
|
315
331
|
"cmd.search_replace": "Pesquisar e Substituir no Projeto",
|
|
@@ -348,7 +364,9 @@
|
|
|
348
364
|
"panel.replace_all_btn": "Substituir tudo (Alt+Ret)",
|
|
349
365
|
"panel.type_pattern": "Digite um padrão de pesquisa",
|
|
350
366
|
"panel.matches_count": "Correspondências (%{count} em %{files} arquivos)",
|
|
351
|
-
"panel.matches_title": "Correspondências"
|
|
367
|
+
"panel.matches_title": "Correspondências",
|
|
368
|
+
"prompt.confirm_replace": "Substituir %{count} ocorrência(s) em %{files} arquivo(s)? Desfazer só cobre arquivos abertos nesta sessão. Enter confirma, Esc cancela.",
|
|
369
|
+
"status.replace_cancelled": "Substituição cancelada"
|
|
352
370
|
},
|
|
353
371
|
"ru": {
|
|
354
372
|
"cmd.search_replace": "Поиск и замена в проекте",
|
|
@@ -387,7 +405,9 @@
|
|
|
387
405
|
"panel.replace_all_btn": "Заменить все (Alt+Ret)",
|
|
388
406
|
"panel.type_pattern": "Введите шаблон поиска",
|
|
389
407
|
"panel.matches_count": "Совпадения (%{count} в %{files} файлах)",
|
|
390
|
-
"panel.matches_title": "Совпадения"
|
|
408
|
+
"panel.matches_title": "Совпадения",
|
|
409
|
+
"prompt.confirm_replace": "Заменить %{count} совпадений в %{files} файлах? Отмена применяется только к файлам, открытым в этом сеансе. Enter — подтвердить, Esc — отменить.",
|
|
410
|
+
"status.replace_cancelled": "Замена отменена"
|
|
391
411
|
},
|
|
392
412
|
"th": {
|
|
393
413
|
"cmd.search_replace": "ค้นหาและแทนที่ในโปรเจกต์",
|
|
@@ -426,7 +446,9 @@
|
|
|
426
446
|
"panel.replace_all_btn": "แทนที่ทั้งหมด (Alt+Ret)",
|
|
427
447
|
"panel.type_pattern": "พิมพ์รูปแบบการค้นหา",
|
|
428
448
|
"panel.matches_count": "รายการที่ตรงกัน (%{count} ใน %{files} ไฟล์)",
|
|
429
|
-
"panel.matches_title": "รายการที่ตรงกัน"
|
|
449
|
+
"panel.matches_title": "รายการที่ตรงกัน",
|
|
450
|
+
"prompt.confirm_replace": "แทนที่ %{count} รายการใน %{files} ไฟล์? เลิกทำครอบคลุมเฉพาะไฟล์ที่เปิดอยู่ในเซสชันนี้เท่านั้น กด Enter เพื่อยืนยัน, Esc เพื่อยกเลิก",
|
|
451
|
+
"status.replace_cancelled": "ยกเลิกการแทนที่"
|
|
430
452
|
},
|
|
431
453
|
"uk": {
|
|
432
454
|
"cmd.search_replace": "Пошук та заміна в проекті",
|
|
@@ -465,7 +487,9 @@
|
|
|
465
487
|
"panel.replace_all_btn": "Замінити все (Alt+Ret)",
|
|
466
488
|
"panel.type_pattern": "Введіть шаблон пошуку",
|
|
467
489
|
"panel.matches_count": "Збіги (%{count} у %{files} файлах)",
|
|
468
|
-
"panel.matches_title": "Збіги"
|
|
490
|
+
"panel.matches_title": "Збіги",
|
|
491
|
+
"prompt.confirm_replace": "Замінити %{count} збіг(ів) у %{files} файл(ах)? Скасування охоплює лише файли, відкриті в цьому сеансі. Enter — підтвердити, Esc — скасувати.",
|
|
492
|
+
"status.replace_cancelled": "Заміну скасовано"
|
|
469
493
|
},
|
|
470
494
|
"vi": {
|
|
471
495
|
"cmd.search_replace": "Tìm và Thay thế trong Dự án",
|
|
@@ -504,7 +528,9 @@
|
|
|
504
528
|
"panel.replace_all_btn": "Thay thế tất cả (Alt+Ret)",
|
|
505
529
|
"panel.type_pattern": "Nhập mẫu tìm kiếm",
|
|
506
530
|
"panel.matches_count": "Kết quả (%{count} trong %{files} tệp)",
|
|
507
|
-
"panel.matches_title": "Kết quả"
|
|
531
|
+
"panel.matches_title": "Kết quả",
|
|
532
|
+
"prompt.confirm_replace": "Thay thế %{count} kết quả trong %{files} tệp? Hoàn tác chỉ áp dụng cho các tệp đang mở trong phiên này. Enter để xác nhận, Esc để hủy.",
|
|
533
|
+
"status.replace_cancelled": "Đã hủy thay thế"
|
|
508
534
|
},
|
|
509
535
|
"zh-CN": {
|
|
510
536
|
"cmd.search_replace": "在项目中搜索和替换",
|
|
@@ -543,6 +569,8 @@
|
|
|
543
569
|
"panel.replace_all_btn": "全部替换 (Alt+Ret)",
|
|
544
570
|
"panel.type_pattern": "请输入搜索模式",
|
|
545
571
|
"panel.matches_count": "匹配 (%{files} 个文件中 %{count} 个)",
|
|
546
|
-
"panel.matches_title": "匹配"
|
|
572
|
+
"panel.matches_title": "匹配",
|
|
573
|
+
"prompt.confirm_replace": "在 %{files} 个文件中替换 %{count} 处匹配?撤销仅适用于本次会话中打开的文件。按 Enter 确认,Esc 取消。",
|
|
574
|
+
"status.replace_cancelled": "替换已取消"
|
|
547
575
|
}
|
|
548
576
|
}
|