@google/gemini-cli 0.1.13-nightly.250726.fb751c54 → 0.1.14
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/google-gemini-cli-0.1.13.tgz +0 -0
- package/dist/package.json +2 -2
- package/dist/src/config/settings.d.ts +0 -1
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +1 -1
- package/dist/src/generated/git-commit.js +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +1 -3
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/ui/App.js +31 -42
- package/dist/src/ui/App.js.map +1 -1
- package/dist/src/ui/commands/mcpCommand.js +8 -53
- package/dist/src/ui/commands/mcpCommand.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +1 -3
- package/dist/src/ui/commands/types.js +0 -1
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/components/Footer.d.ts +0 -1
- package/dist/src/ui/components/Footer.js +2 -2
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.d.ts +0 -2
- package/dist/src/ui/components/InputPrompt.js +1 -5
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/shared/text-buffer.d.ts +2 -270
- package/dist/src/ui/components/shared/text-buffer.js +70 -415
- package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.d.ts +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.js +62 -75
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useCompletion.js +2 -7
- package/dist/src/ui/hooks/useCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useKeypress.js +2 -5
- package/dist/src/ui/hooks/useKeypress.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/dist/src/services/McpPromptLoader.d.ts +0 -25
- package/dist/src/services/McpPromptLoader.js +0 -192
- package/dist/src/services/McpPromptLoader.js.map +0 -1
- package/dist/src/ui/commands/vimCommand.d.ts +0 -7
- package/dist/src/ui/commands/vimCommand.js +0 -23
- package/dist/src/ui/commands/vimCommand.js.map +0 -1
- package/dist/src/ui/components/shared/vim-buffer-actions.d.ts +0 -72
- package/dist/src/ui/components/shared/vim-buffer-actions.js +0 -565
- package/dist/src/ui/components/shared/vim-buffer-actions.js.map +0 -1
- package/dist/src/ui/contexts/VimModeContext.d.ts +0 -19
- package/dist/src/ui/contexts/VimModeContext.js +0 -48
- package/dist/src/ui/contexts/VimModeContext.js.map +0 -1
- package/dist/src/ui/hooks/vim.d.ts +0 -28
- package/dist/src/ui/hooks/vim.js +0 -630
- package/dist/src/ui/hooks/vim.js.map +0 -1
|
@@ -12,7 +12,6 @@ import { useState, useCallback, useEffect, useMemo, useReducer } from 'react';
|
|
|
12
12
|
import stringWidth from 'string-width';
|
|
13
13
|
import { unescapePath } from '@google/gemini-cli-core';
|
|
14
14
|
import { toCodePoints, cpLen, cpSlice } from '../../utils/textUtils.js';
|
|
15
|
-
import { handleVimAction } from './vim-buffer-actions.js';
|
|
16
15
|
// Simple helper for word‑wise ops.
|
|
17
16
|
function isWordChar(ch) {
|
|
18
17
|
if (ch === undefined) {
|
|
@@ -20,209 +19,6 @@ function isWordChar(ch) {
|
|
|
20
19
|
}
|
|
21
20
|
return !/[\s,.;!?]/.test(ch);
|
|
22
21
|
}
|
|
23
|
-
// Vim-specific word boundary functions
|
|
24
|
-
export const findNextWordStart = (text, currentOffset) => {
|
|
25
|
-
let i = currentOffset;
|
|
26
|
-
if (i >= text.length)
|
|
27
|
-
return i;
|
|
28
|
-
const currentChar = text[i];
|
|
29
|
-
// Skip current word/sequence based on character type
|
|
30
|
-
if (/\w/.test(currentChar)) {
|
|
31
|
-
// Skip current word characters
|
|
32
|
-
while (i < text.length && /\w/.test(text[i])) {
|
|
33
|
-
i++;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
else if (!/\s/.test(currentChar)) {
|
|
37
|
-
// Skip current non-word, non-whitespace characters (like "/", ".", etc.)
|
|
38
|
-
while (i < text.length && !/\w/.test(text[i]) && !/\s/.test(text[i])) {
|
|
39
|
-
i++;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
// Skip whitespace
|
|
43
|
-
while (i < text.length && /\s/.test(text[i])) {
|
|
44
|
-
i++;
|
|
45
|
-
}
|
|
46
|
-
// If we reached the end of text and there's no next word,
|
|
47
|
-
// vim behavior for dw is to delete to the end of the current word
|
|
48
|
-
if (i >= text.length) {
|
|
49
|
-
// Go back to find the end of the last word
|
|
50
|
-
let endOfLastWord = text.length - 1;
|
|
51
|
-
while (endOfLastWord >= 0 && /\s/.test(text[endOfLastWord])) {
|
|
52
|
-
endOfLastWord--;
|
|
53
|
-
}
|
|
54
|
-
// For dw on last word, return position AFTER the last character to delete entire word
|
|
55
|
-
return Math.max(currentOffset + 1, endOfLastWord + 1);
|
|
56
|
-
}
|
|
57
|
-
return i;
|
|
58
|
-
};
|
|
59
|
-
export const findPrevWordStart = (text, currentOffset) => {
|
|
60
|
-
let i = currentOffset;
|
|
61
|
-
// If at beginning of text, return current position
|
|
62
|
-
if (i <= 0) {
|
|
63
|
-
return currentOffset;
|
|
64
|
-
}
|
|
65
|
-
// Move back one character to start searching
|
|
66
|
-
i--;
|
|
67
|
-
// Skip whitespace moving backwards
|
|
68
|
-
while (i >= 0 && (text[i] === ' ' || text[i] === '\t' || text[i] === '\n')) {
|
|
69
|
-
i--;
|
|
70
|
-
}
|
|
71
|
-
if (i < 0) {
|
|
72
|
-
return 0; // Reached beginning of text
|
|
73
|
-
}
|
|
74
|
-
const charAtI = text[i];
|
|
75
|
-
if (/\w/.test(charAtI)) {
|
|
76
|
-
// We're in a word, move to its beginning
|
|
77
|
-
while (i >= 0 && /\w/.test(text[i])) {
|
|
78
|
-
i--;
|
|
79
|
-
}
|
|
80
|
-
return i + 1; // Return first character of word
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
// We're in punctuation, move to its beginning
|
|
84
|
-
while (i >= 0 &&
|
|
85
|
-
!/\w/.test(text[i]) &&
|
|
86
|
-
text[i] !== ' ' &&
|
|
87
|
-
text[i] !== '\t' &&
|
|
88
|
-
text[i] !== '\n') {
|
|
89
|
-
i--;
|
|
90
|
-
}
|
|
91
|
-
return i + 1; // Return first character of punctuation sequence
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
export const findWordEnd = (text, currentOffset) => {
|
|
95
|
-
let i = currentOffset;
|
|
96
|
-
// If we're already at the end of a word, advance to next word
|
|
97
|
-
if (i < text.length &&
|
|
98
|
-
/\w/.test(text[i]) &&
|
|
99
|
-
(i + 1 >= text.length || !/\w/.test(text[i + 1]))) {
|
|
100
|
-
// We're at the end of a word, move forward to find next word
|
|
101
|
-
i++;
|
|
102
|
-
// Skip whitespace/punctuation to find next word
|
|
103
|
-
while (i < text.length && !/\w/.test(text[i])) {
|
|
104
|
-
i++;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// If we're not on a word character, find the next word
|
|
108
|
-
if (i < text.length && !/\w/.test(text[i])) {
|
|
109
|
-
while (i < text.length && !/\w/.test(text[i])) {
|
|
110
|
-
i++;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// Move to end of current word
|
|
114
|
-
while (i < text.length && /\w/.test(text[i])) {
|
|
115
|
-
i++;
|
|
116
|
-
}
|
|
117
|
-
// Move back one to be on the last character of the word
|
|
118
|
-
return Math.max(currentOffset, i - 1);
|
|
119
|
-
};
|
|
120
|
-
// Helper functions for vim operations
|
|
121
|
-
export const getOffsetFromPosition = (row, col, lines) => {
|
|
122
|
-
let offset = 0;
|
|
123
|
-
for (let i = 0; i < row; i++) {
|
|
124
|
-
offset += lines[i].length + 1; // +1 for newline
|
|
125
|
-
}
|
|
126
|
-
offset += col;
|
|
127
|
-
return offset;
|
|
128
|
-
};
|
|
129
|
-
export const getPositionFromOffsets = (startOffset, endOffset, lines) => {
|
|
130
|
-
let offset = 0;
|
|
131
|
-
let startRow = 0;
|
|
132
|
-
let startCol = 0;
|
|
133
|
-
let endRow = 0;
|
|
134
|
-
let endCol = 0;
|
|
135
|
-
// Find start position
|
|
136
|
-
for (let i = 0; i < lines.length; i++) {
|
|
137
|
-
const lineLength = lines[i].length + 1; // +1 for newline
|
|
138
|
-
if (offset + lineLength > startOffset) {
|
|
139
|
-
startRow = i;
|
|
140
|
-
startCol = startOffset - offset;
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
offset += lineLength;
|
|
144
|
-
}
|
|
145
|
-
// Find end position
|
|
146
|
-
offset = 0;
|
|
147
|
-
for (let i = 0; i < lines.length; i++) {
|
|
148
|
-
const lineLength = lines[i].length + (i < lines.length - 1 ? 1 : 0); // +1 for newline except last line
|
|
149
|
-
if (offset + lineLength >= endOffset) {
|
|
150
|
-
endRow = i;
|
|
151
|
-
endCol = endOffset - offset;
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
offset += lineLength;
|
|
155
|
-
}
|
|
156
|
-
return { startRow, startCol, endRow, endCol };
|
|
157
|
-
};
|
|
158
|
-
export const getLineRangeOffsets = (startRow, lineCount, lines) => {
|
|
159
|
-
let startOffset = 0;
|
|
160
|
-
// Calculate start offset
|
|
161
|
-
for (let i = 0; i < startRow; i++) {
|
|
162
|
-
startOffset += lines[i].length + 1; // +1 for newline
|
|
163
|
-
}
|
|
164
|
-
// Calculate end offset
|
|
165
|
-
let endOffset = startOffset;
|
|
166
|
-
for (let i = 0; i < lineCount; i++) {
|
|
167
|
-
const lineIndex = startRow + i;
|
|
168
|
-
if (lineIndex < lines.length) {
|
|
169
|
-
endOffset += lines[lineIndex].length;
|
|
170
|
-
if (lineIndex < lines.length - 1) {
|
|
171
|
-
endOffset += 1; // +1 for newline
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return { startOffset, endOffset };
|
|
176
|
-
};
|
|
177
|
-
export const replaceRangeInternal = (state, startRow, startCol, endRow, endCol, text) => {
|
|
178
|
-
const currentLine = (row) => state.lines[row] || '';
|
|
179
|
-
const currentLineLen = (row) => cpLen(currentLine(row));
|
|
180
|
-
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
181
|
-
if (startRow > endRow ||
|
|
182
|
-
(startRow === endRow && startCol > endCol) ||
|
|
183
|
-
startRow < 0 ||
|
|
184
|
-
startCol < 0 ||
|
|
185
|
-
endRow >= state.lines.length ||
|
|
186
|
-
(endRow < state.lines.length && endCol > currentLineLen(endRow))) {
|
|
187
|
-
return state; // Invalid range
|
|
188
|
-
}
|
|
189
|
-
const newLines = [...state.lines];
|
|
190
|
-
const sCol = clamp(startCol, 0, currentLineLen(startRow));
|
|
191
|
-
const eCol = clamp(endCol, 0, currentLineLen(endRow));
|
|
192
|
-
const prefix = cpSlice(currentLine(startRow), 0, sCol);
|
|
193
|
-
const suffix = cpSlice(currentLine(endRow), eCol);
|
|
194
|
-
const normalisedReplacement = text
|
|
195
|
-
.replace(/\r\n/g, '\n')
|
|
196
|
-
.replace(/\r/g, '\n');
|
|
197
|
-
const replacementParts = normalisedReplacement.split('\n');
|
|
198
|
-
// Replace the content
|
|
199
|
-
if (startRow === endRow) {
|
|
200
|
-
newLines[startRow] = prefix + normalisedReplacement + suffix;
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
const firstLine = prefix + replacementParts[0];
|
|
204
|
-
if (replacementParts.length === 1) {
|
|
205
|
-
// Single line of replacement text, but spanning multiple original lines
|
|
206
|
-
newLines.splice(startRow, endRow - startRow + 1, firstLine + suffix);
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
// Multi-line replacement text
|
|
210
|
-
const lastLine = replacementParts[replacementParts.length - 1] + suffix;
|
|
211
|
-
const middleLines = replacementParts.slice(1, -1);
|
|
212
|
-
newLines.splice(startRow, endRow - startRow + 1, firstLine, ...middleLines, lastLine);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
const finalCursorRow = startRow + replacementParts.length - 1;
|
|
216
|
-
const finalCursorCol = (replacementParts.length > 1 ? 0 : sCol) +
|
|
217
|
-
cpLen(replacementParts[replacementParts.length - 1]);
|
|
218
|
-
return {
|
|
219
|
-
...state,
|
|
220
|
-
lines: newLines,
|
|
221
|
-
cursorRow: Math.min(Math.max(finalCursorRow, 0), newLines.length - 1),
|
|
222
|
-
cursorCol: Math.max(0, Math.min(finalCursorCol, cpLen(newLines[finalCursorRow] || ''))),
|
|
223
|
-
preferredCol: null,
|
|
224
|
-
};
|
|
225
|
-
};
|
|
226
22
|
/**
|
|
227
23
|
* Strip characters that can break terminal rendering.
|
|
228
24
|
*
|
|
@@ -310,24 +106,6 @@ export function offsetToLogicalPos(text, offset) {
|
|
|
310
106
|
}
|
|
311
107
|
return [row, col];
|
|
312
108
|
}
|
|
313
|
-
/**
|
|
314
|
-
* Converts logical row/col position to absolute text offset
|
|
315
|
-
* Inverse operation of offsetToLogicalPos
|
|
316
|
-
*/
|
|
317
|
-
export function logicalPosToOffset(lines, row, col) {
|
|
318
|
-
let offset = 0;
|
|
319
|
-
// Clamp row to valid range
|
|
320
|
-
const actualRow = Math.min(row, lines.length - 1);
|
|
321
|
-
// Add lengths of all lines before the target row
|
|
322
|
-
for (let i = 0; i < actualRow; i++) {
|
|
323
|
-
offset += cpLen(lines[i]) + 1; // +1 for newline
|
|
324
|
-
}
|
|
325
|
-
// Add column offset within the target row
|
|
326
|
-
if (actualRow >= 0 && actualRow < lines.length) {
|
|
327
|
-
offset += Math.min(col, cpLen(lines[actualRow]));
|
|
328
|
-
}
|
|
329
|
-
return offset;
|
|
330
|
-
}
|
|
331
109
|
// Helper to calculate visual lines and map cursor positions
|
|
332
110
|
function calculateVisualLayout(logicalLines, logicalCursor, viewportWidth) {
|
|
333
111
|
const visualLines = [];
|
|
@@ -501,27 +279,26 @@ function calculateVisualLayout(logicalLines, logicalCursor, viewportWidth) {
|
|
|
501
279
|
};
|
|
502
280
|
}
|
|
503
281
|
const historyLimit = 100;
|
|
504
|
-
export const pushUndo = (currentState) => {
|
|
505
|
-
const snapshot = {
|
|
506
|
-
lines: [...currentState.lines],
|
|
507
|
-
cursorRow: currentState.cursorRow,
|
|
508
|
-
cursorCol: currentState.cursorCol,
|
|
509
|
-
};
|
|
510
|
-
const newStack = [...currentState.undoStack, snapshot];
|
|
511
|
-
if (newStack.length > historyLimit) {
|
|
512
|
-
newStack.shift();
|
|
513
|
-
}
|
|
514
|
-
return { ...currentState, undoStack: newStack, redoStack: [] };
|
|
515
|
-
};
|
|
516
282
|
export function textBufferReducer(state, action) {
|
|
517
|
-
const
|
|
283
|
+
const pushUndo = (currentState) => {
|
|
284
|
+
const snapshot = {
|
|
285
|
+
lines: [...currentState.lines],
|
|
286
|
+
cursorRow: currentState.cursorRow,
|
|
287
|
+
cursorCol: currentState.cursorCol,
|
|
288
|
+
};
|
|
289
|
+
const newStack = [...currentState.undoStack, snapshot];
|
|
290
|
+
if (newStack.length > historyLimit) {
|
|
291
|
+
newStack.shift();
|
|
292
|
+
}
|
|
293
|
+
return { ...currentState, undoStack: newStack, redoStack: [] };
|
|
294
|
+
};
|
|
518
295
|
const currentLine = (r) => state.lines[r] ?? '';
|
|
519
296
|
const currentLineLen = (r) => cpLen(currentLine(r));
|
|
520
297
|
switch (action.type) {
|
|
521
298
|
case 'set_text': {
|
|
522
299
|
let nextState = state;
|
|
523
300
|
if (action.pushToUndo !== false) {
|
|
524
|
-
nextState =
|
|
301
|
+
nextState = pushUndo(state);
|
|
525
302
|
}
|
|
526
303
|
const newContentLines = action.payload
|
|
527
304
|
.replace(/\r\n?/g, '\n')
|
|
@@ -537,7 +314,7 @@ export function textBufferReducer(state, action) {
|
|
|
537
314
|
};
|
|
538
315
|
}
|
|
539
316
|
case 'insert': {
|
|
540
|
-
const nextState =
|
|
317
|
+
const nextState = pushUndo(state);
|
|
541
318
|
const newLines = [...nextState.lines];
|
|
542
319
|
let newCursorRow = nextState.cursorRow;
|
|
543
320
|
let newCursorCol = nextState.cursorCol;
|
|
@@ -569,7 +346,7 @@ export function textBufferReducer(state, action) {
|
|
|
569
346
|
};
|
|
570
347
|
}
|
|
571
348
|
case 'backspace': {
|
|
572
|
-
const nextState =
|
|
349
|
+
const nextState = pushUndo(state);
|
|
573
350
|
const newLines = [...nextState.lines];
|
|
574
351
|
let newCursorRow = nextState.cursorRow;
|
|
575
352
|
let newCursorCol = nextState.cursorCol;
|
|
@@ -746,7 +523,7 @@ export function textBufferReducer(state, action) {
|
|
|
746
523
|
const { cursorRow, cursorCol, lines } = state;
|
|
747
524
|
const lineContent = currentLine(cursorRow);
|
|
748
525
|
if (cursorCol < currentLineLen(cursorRow)) {
|
|
749
|
-
const nextState =
|
|
526
|
+
const nextState = pushUndo(state);
|
|
750
527
|
const newLines = [...nextState.lines];
|
|
751
528
|
newLines[cursorRow] =
|
|
752
529
|
cpSlice(lineContent, 0, cursorCol) +
|
|
@@ -754,7 +531,7 @@ export function textBufferReducer(state, action) {
|
|
|
754
531
|
return { ...nextState, lines: newLines, preferredCol: null };
|
|
755
532
|
}
|
|
756
533
|
else if (cursorRow < lines.length - 1) {
|
|
757
|
-
const nextState =
|
|
534
|
+
const nextState = pushUndo(state);
|
|
758
535
|
const nextLineContent = currentLine(cursorRow + 1);
|
|
759
536
|
const newLines = [...nextState.lines];
|
|
760
537
|
newLines[cursorRow] = lineContent + nextLineContent;
|
|
@@ -769,7 +546,7 @@ export function textBufferReducer(state, action) {
|
|
|
769
546
|
return state;
|
|
770
547
|
if (cursorCol === 0) {
|
|
771
548
|
// Act as a backspace
|
|
772
|
-
const nextState =
|
|
549
|
+
const nextState = pushUndo(state);
|
|
773
550
|
const prevLineContent = currentLine(cursorRow - 1);
|
|
774
551
|
const currentLineContentVal = currentLine(cursorRow);
|
|
775
552
|
const newCol = cpLen(prevLineContent);
|
|
@@ -784,7 +561,7 @@ export function textBufferReducer(state, action) {
|
|
|
784
561
|
preferredCol: null,
|
|
785
562
|
};
|
|
786
563
|
}
|
|
787
|
-
const nextState =
|
|
564
|
+
const nextState = pushUndo(state);
|
|
788
565
|
const lineContent = currentLine(cursorRow);
|
|
789
566
|
const arr = toCodePoints(lineContent);
|
|
790
567
|
let start = cursorCol;
|
|
@@ -822,14 +599,14 @@ export function textBufferReducer(state, action) {
|
|
|
822
599
|
return state;
|
|
823
600
|
if (cursorCol >= arr.length) {
|
|
824
601
|
// Act as a delete
|
|
825
|
-
const nextState =
|
|
602
|
+
const nextState = pushUndo(state);
|
|
826
603
|
const nextLineContent = currentLine(cursorRow + 1);
|
|
827
604
|
const newLines = [...nextState.lines];
|
|
828
605
|
newLines[cursorRow] = lineContent + nextLineContent;
|
|
829
606
|
newLines.splice(cursorRow + 1, 1);
|
|
830
607
|
return { ...nextState, lines: newLines, preferredCol: null };
|
|
831
608
|
}
|
|
832
|
-
const nextState =
|
|
609
|
+
const nextState = pushUndo(state);
|
|
833
610
|
let end = cursorCol;
|
|
834
611
|
while (end < arr.length && !isWordChar(arr[end]))
|
|
835
612
|
end++;
|
|
@@ -844,14 +621,14 @@ export function textBufferReducer(state, action) {
|
|
|
844
621
|
const { cursorRow, cursorCol, lines } = state;
|
|
845
622
|
const lineContent = currentLine(cursorRow);
|
|
846
623
|
if (cursorCol < currentLineLen(cursorRow)) {
|
|
847
|
-
const nextState =
|
|
624
|
+
const nextState = pushUndo(state);
|
|
848
625
|
const newLines = [...nextState.lines];
|
|
849
626
|
newLines[cursorRow] = cpSlice(lineContent, 0, cursorCol);
|
|
850
627
|
return { ...nextState, lines: newLines };
|
|
851
628
|
}
|
|
852
629
|
else if (cursorRow < lines.length - 1) {
|
|
853
630
|
// Act as a delete
|
|
854
|
-
const nextState =
|
|
631
|
+
const nextState = pushUndo(state);
|
|
855
632
|
const nextLineContent = currentLine(cursorRow + 1);
|
|
856
633
|
const newLines = [...nextState.lines];
|
|
857
634
|
newLines[cursorRow] = lineContent + nextLineContent;
|
|
@@ -863,7 +640,7 @@ export function textBufferReducer(state, action) {
|
|
|
863
640
|
case 'kill_line_left': {
|
|
864
641
|
const { cursorRow, cursorCol } = state;
|
|
865
642
|
if (cursorCol > 0) {
|
|
866
|
-
const nextState =
|
|
643
|
+
const nextState = pushUndo(state);
|
|
867
644
|
const lineContent = currentLine(cursorRow);
|
|
868
645
|
const newLines = [...nextState.lines];
|
|
869
646
|
newLines[cursorRow] = cpSlice(lineContent, cursorCol);
|
|
@@ -910,8 +687,51 @@ export function textBufferReducer(state, action) {
|
|
|
910
687
|
}
|
|
911
688
|
case 'replace_range': {
|
|
912
689
|
const { startRow, startCol, endRow, endCol, text } = action.payload;
|
|
913
|
-
|
|
914
|
-
|
|
690
|
+
if (startRow > endRow ||
|
|
691
|
+
(startRow === endRow && startCol > endCol) ||
|
|
692
|
+
startRow < 0 ||
|
|
693
|
+
startCol < 0 ||
|
|
694
|
+
endRow >= state.lines.length ||
|
|
695
|
+
(endRow < state.lines.length && endCol > currentLineLen(endRow))) {
|
|
696
|
+
return state; // Invalid range
|
|
697
|
+
}
|
|
698
|
+
const nextState = pushUndo(state);
|
|
699
|
+
const newLines = [...nextState.lines];
|
|
700
|
+
const sCol = clamp(startCol, 0, currentLineLen(startRow));
|
|
701
|
+
const eCol = clamp(endCol, 0, currentLineLen(endRow));
|
|
702
|
+
const prefix = cpSlice(currentLine(startRow), 0, sCol);
|
|
703
|
+
const suffix = cpSlice(currentLine(endRow), eCol);
|
|
704
|
+
const normalisedReplacement = text
|
|
705
|
+
.replace(/\r\n/g, '\n')
|
|
706
|
+
.replace(/\r/g, '\n');
|
|
707
|
+
const replacementParts = normalisedReplacement.split('\n');
|
|
708
|
+
// Replace the content
|
|
709
|
+
if (startRow === endRow) {
|
|
710
|
+
newLines[startRow] = prefix + normalisedReplacement + suffix;
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
const firstLine = prefix + replacementParts[0];
|
|
714
|
+
if (replacementParts.length === 1) {
|
|
715
|
+
// Single line of replacement text, but spanning multiple original lines
|
|
716
|
+
newLines.splice(startRow, endRow - startRow + 1, firstLine + suffix);
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
// Multi-line replacement text
|
|
720
|
+
const lastLine = replacementParts[replacementParts.length - 1] + suffix;
|
|
721
|
+
const middleLines = replacementParts.slice(1, -1);
|
|
722
|
+
newLines.splice(startRow, endRow - startRow + 1, firstLine, ...middleLines, lastLine);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
const finalCursorRow = startRow + replacementParts.length - 1;
|
|
726
|
+
const finalCursorCol = (replacementParts.length > 1 ? 0 : sCol) +
|
|
727
|
+
cpLen(replacementParts[replacementParts.length - 1]);
|
|
728
|
+
return {
|
|
729
|
+
...nextState,
|
|
730
|
+
lines: newLines,
|
|
731
|
+
cursorRow: finalCursorRow,
|
|
732
|
+
cursorCol: finalCursorCol,
|
|
733
|
+
preferredCol: null,
|
|
734
|
+
};
|
|
915
735
|
}
|
|
916
736
|
case 'move_to_offset': {
|
|
917
737
|
const { offset } = action.payload;
|
|
@@ -924,42 +744,8 @@ export function textBufferReducer(state, action) {
|
|
|
924
744
|
};
|
|
925
745
|
}
|
|
926
746
|
case 'create_undo_snapshot': {
|
|
927
|
-
return
|
|
747
|
+
return pushUndo(state);
|
|
928
748
|
}
|
|
929
|
-
// Vim-specific operations
|
|
930
|
-
case 'vim_delete_word_forward':
|
|
931
|
-
case 'vim_delete_word_backward':
|
|
932
|
-
case 'vim_delete_word_end':
|
|
933
|
-
case 'vim_change_word_forward':
|
|
934
|
-
case 'vim_change_word_backward':
|
|
935
|
-
case 'vim_change_word_end':
|
|
936
|
-
case 'vim_delete_line':
|
|
937
|
-
case 'vim_change_line':
|
|
938
|
-
case 'vim_delete_to_end_of_line':
|
|
939
|
-
case 'vim_change_to_end_of_line':
|
|
940
|
-
case 'vim_change_movement':
|
|
941
|
-
case 'vim_move_left':
|
|
942
|
-
case 'vim_move_right':
|
|
943
|
-
case 'vim_move_up':
|
|
944
|
-
case 'vim_move_down':
|
|
945
|
-
case 'vim_move_word_forward':
|
|
946
|
-
case 'vim_move_word_backward':
|
|
947
|
-
case 'vim_move_word_end':
|
|
948
|
-
case 'vim_delete_char':
|
|
949
|
-
case 'vim_insert_at_cursor':
|
|
950
|
-
case 'vim_append_at_cursor':
|
|
951
|
-
case 'vim_open_line_below':
|
|
952
|
-
case 'vim_open_line_above':
|
|
953
|
-
case 'vim_append_at_line_end':
|
|
954
|
-
case 'vim_insert_at_line_start':
|
|
955
|
-
case 'vim_move_to_line_start':
|
|
956
|
-
case 'vim_move_to_line_end':
|
|
957
|
-
case 'vim_move_to_first_nonwhitespace':
|
|
958
|
-
case 'vim_move_to_first_line':
|
|
959
|
-
case 'vim_move_to_last_line':
|
|
960
|
-
case 'vim_move_to_line':
|
|
961
|
-
case 'vim_escape_insert_mode':
|
|
962
|
-
return handleVimAction(state, action);
|
|
963
749
|
default: {
|
|
964
750
|
const exhaustiveCheck = action;
|
|
965
751
|
console.error(`Unknown action encountered: ${exhaustiveCheck}`);
|
|
@@ -1081,104 +867,6 @@ export function useTextBuffer({ initialText = '', initialCursorOffset = 0, viewp
|
|
|
1081
867
|
const killLineLeft = useCallback(() => {
|
|
1082
868
|
dispatch({ type: 'kill_line_left' });
|
|
1083
869
|
}, []);
|
|
1084
|
-
// Vim-specific operations
|
|
1085
|
-
const vimDeleteWordForward = useCallback((count) => {
|
|
1086
|
-
dispatch({ type: 'vim_delete_word_forward', payload: { count } });
|
|
1087
|
-
}, []);
|
|
1088
|
-
const vimDeleteWordBackward = useCallback((count) => {
|
|
1089
|
-
dispatch({ type: 'vim_delete_word_backward', payload: { count } });
|
|
1090
|
-
}, []);
|
|
1091
|
-
const vimDeleteWordEnd = useCallback((count) => {
|
|
1092
|
-
dispatch({ type: 'vim_delete_word_end', payload: { count } });
|
|
1093
|
-
}, []);
|
|
1094
|
-
const vimChangeWordForward = useCallback((count) => {
|
|
1095
|
-
dispatch({ type: 'vim_change_word_forward', payload: { count } });
|
|
1096
|
-
}, []);
|
|
1097
|
-
const vimChangeWordBackward = useCallback((count) => {
|
|
1098
|
-
dispatch({ type: 'vim_change_word_backward', payload: { count } });
|
|
1099
|
-
}, []);
|
|
1100
|
-
const vimChangeWordEnd = useCallback((count) => {
|
|
1101
|
-
dispatch({ type: 'vim_change_word_end', payload: { count } });
|
|
1102
|
-
}, []);
|
|
1103
|
-
const vimDeleteLine = useCallback((count) => {
|
|
1104
|
-
dispatch({ type: 'vim_delete_line', payload: { count } });
|
|
1105
|
-
}, []);
|
|
1106
|
-
const vimChangeLine = useCallback((count) => {
|
|
1107
|
-
dispatch({ type: 'vim_change_line', payload: { count } });
|
|
1108
|
-
}, []);
|
|
1109
|
-
const vimDeleteToEndOfLine = useCallback(() => {
|
|
1110
|
-
dispatch({ type: 'vim_delete_to_end_of_line' });
|
|
1111
|
-
}, []);
|
|
1112
|
-
const vimChangeToEndOfLine = useCallback(() => {
|
|
1113
|
-
dispatch({ type: 'vim_change_to_end_of_line' });
|
|
1114
|
-
}, []);
|
|
1115
|
-
const vimChangeMovement = useCallback((movement, count) => {
|
|
1116
|
-
dispatch({ type: 'vim_change_movement', payload: { movement, count } });
|
|
1117
|
-
}, []);
|
|
1118
|
-
// New vim navigation and operation methods
|
|
1119
|
-
const vimMoveLeft = useCallback((count) => {
|
|
1120
|
-
dispatch({ type: 'vim_move_left', payload: { count } });
|
|
1121
|
-
}, []);
|
|
1122
|
-
const vimMoveRight = useCallback((count) => {
|
|
1123
|
-
dispatch({ type: 'vim_move_right', payload: { count } });
|
|
1124
|
-
}, []);
|
|
1125
|
-
const vimMoveUp = useCallback((count) => {
|
|
1126
|
-
dispatch({ type: 'vim_move_up', payload: { count } });
|
|
1127
|
-
}, []);
|
|
1128
|
-
const vimMoveDown = useCallback((count) => {
|
|
1129
|
-
dispatch({ type: 'vim_move_down', payload: { count } });
|
|
1130
|
-
}, []);
|
|
1131
|
-
const vimMoveWordForward = useCallback((count) => {
|
|
1132
|
-
dispatch({ type: 'vim_move_word_forward', payload: { count } });
|
|
1133
|
-
}, []);
|
|
1134
|
-
const vimMoveWordBackward = useCallback((count) => {
|
|
1135
|
-
dispatch({ type: 'vim_move_word_backward', payload: { count } });
|
|
1136
|
-
}, []);
|
|
1137
|
-
const vimMoveWordEnd = useCallback((count) => {
|
|
1138
|
-
dispatch({ type: 'vim_move_word_end', payload: { count } });
|
|
1139
|
-
}, []);
|
|
1140
|
-
const vimDeleteChar = useCallback((count) => {
|
|
1141
|
-
dispatch({ type: 'vim_delete_char', payload: { count } });
|
|
1142
|
-
}, []);
|
|
1143
|
-
const vimInsertAtCursor = useCallback(() => {
|
|
1144
|
-
dispatch({ type: 'vim_insert_at_cursor' });
|
|
1145
|
-
}, []);
|
|
1146
|
-
const vimAppendAtCursor = useCallback(() => {
|
|
1147
|
-
dispatch({ type: 'vim_append_at_cursor' });
|
|
1148
|
-
}, []);
|
|
1149
|
-
const vimOpenLineBelow = useCallback(() => {
|
|
1150
|
-
dispatch({ type: 'vim_open_line_below' });
|
|
1151
|
-
}, []);
|
|
1152
|
-
const vimOpenLineAbove = useCallback(() => {
|
|
1153
|
-
dispatch({ type: 'vim_open_line_above' });
|
|
1154
|
-
}, []);
|
|
1155
|
-
const vimAppendAtLineEnd = useCallback(() => {
|
|
1156
|
-
dispatch({ type: 'vim_append_at_line_end' });
|
|
1157
|
-
}, []);
|
|
1158
|
-
const vimInsertAtLineStart = useCallback(() => {
|
|
1159
|
-
dispatch({ type: 'vim_insert_at_line_start' });
|
|
1160
|
-
}, []);
|
|
1161
|
-
const vimMoveToLineStart = useCallback(() => {
|
|
1162
|
-
dispatch({ type: 'vim_move_to_line_start' });
|
|
1163
|
-
}, []);
|
|
1164
|
-
const vimMoveToLineEnd = useCallback(() => {
|
|
1165
|
-
dispatch({ type: 'vim_move_to_line_end' });
|
|
1166
|
-
}, []);
|
|
1167
|
-
const vimMoveToFirstNonWhitespace = useCallback(() => {
|
|
1168
|
-
dispatch({ type: 'vim_move_to_first_nonwhitespace' });
|
|
1169
|
-
}, []);
|
|
1170
|
-
const vimMoveToFirstLine = useCallback(() => {
|
|
1171
|
-
dispatch({ type: 'vim_move_to_first_line' });
|
|
1172
|
-
}, []);
|
|
1173
|
-
const vimMoveToLastLine = useCallback(() => {
|
|
1174
|
-
dispatch({ type: 'vim_move_to_last_line' });
|
|
1175
|
-
}, []);
|
|
1176
|
-
const vimMoveToLine = useCallback((lineNumber) => {
|
|
1177
|
-
dispatch({ type: 'vim_move_to_line', payload: { lineNumber } });
|
|
1178
|
-
}, []);
|
|
1179
|
-
const vimEscapeInsertMode = useCallback(() => {
|
|
1180
|
-
dispatch({ type: 'vim_escape_insert_mode' });
|
|
1181
|
-
}, []);
|
|
1182
870
|
const openInExternalEditor = useCallback(async (opts = {}) => {
|
|
1183
871
|
const editor = opts.editor ??
|
|
1184
872
|
process.env['VISUAL'] ??
|
|
@@ -1317,39 +1005,6 @@ export function useTextBuffer({ initialText = '', initialCursorOffset = 0, viewp
|
|
|
1317
1005
|
killLineLeft,
|
|
1318
1006
|
handleInput,
|
|
1319
1007
|
openInExternalEditor,
|
|
1320
|
-
// Vim-specific operations
|
|
1321
|
-
vimDeleteWordForward,
|
|
1322
|
-
vimDeleteWordBackward,
|
|
1323
|
-
vimDeleteWordEnd,
|
|
1324
|
-
vimChangeWordForward,
|
|
1325
|
-
vimChangeWordBackward,
|
|
1326
|
-
vimChangeWordEnd,
|
|
1327
|
-
vimDeleteLine,
|
|
1328
|
-
vimChangeLine,
|
|
1329
|
-
vimDeleteToEndOfLine,
|
|
1330
|
-
vimChangeToEndOfLine,
|
|
1331
|
-
vimChangeMovement,
|
|
1332
|
-
vimMoveLeft,
|
|
1333
|
-
vimMoveRight,
|
|
1334
|
-
vimMoveUp,
|
|
1335
|
-
vimMoveDown,
|
|
1336
|
-
vimMoveWordForward,
|
|
1337
|
-
vimMoveWordBackward,
|
|
1338
|
-
vimMoveWordEnd,
|
|
1339
|
-
vimDeleteChar,
|
|
1340
|
-
vimInsertAtCursor,
|
|
1341
|
-
vimAppendAtCursor,
|
|
1342
|
-
vimOpenLineBelow,
|
|
1343
|
-
vimOpenLineAbove,
|
|
1344
|
-
vimAppendAtLineEnd,
|
|
1345
|
-
vimInsertAtLineStart,
|
|
1346
|
-
vimMoveToLineStart,
|
|
1347
|
-
vimMoveToLineEnd,
|
|
1348
|
-
vimMoveToFirstNonWhitespace,
|
|
1349
|
-
vimMoveToFirstLine,
|
|
1350
|
-
vimMoveToLastLine,
|
|
1351
|
-
vimMoveToLine,
|
|
1352
|
-
vimEscapeInsertMode,
|
|
1353
1008
|
};
|
|
1354
1009
|
return returnValue;
|
|
1355
1010
|
}
|