@vybestack/llxprt-code 0.1.19-gamma → 0.1.20
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/README.md +9 -0
- package/dist/package.json +5 -5
- package/dist/src/auth/oauth-manager.d.ts +1 -0
- package/dist/src/auth/oauth-manager.js +24 -13
- package/dist/src/auth/oauth-manager.js.map +1 -1
- package/dist/src/auth/oauth-manager.spec.js +4 -4
- package/dist/src/auth/oauth-manager.spec.js.map +1 -1
- package/dist/src/commands/mcp/list.js +1 -1
- package/dist/src/commands/mcp/list.js.map +1 -1
- package/dist/src/config/config.d.ts +2 -1
- package/dist/src/config/config.js +183 -25
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/keyBindings.js +4 -0
- package/dist/src/config/keyBindings.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +18 -9
- package/dist/src/config/settingsSchema.js +18 -9
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/config/trustedFolders.d.ts +36 -0
- package/dist/src/config/trustedFolders.js +112 -0
- package/dist/src/config/trustedFolders.js.map +1 -0
- package/dist/src/gemini.js +28 -18
- package/dist/src/gemini.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/nonInteractiveCli.js +3 -4
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/providers/logging/git-stats.js +0 -1
- package/dist/src/providers/logging/git-stats.js.map +1 -1
- package/dist/src/providers/providerConfigUtils.js +1 -1
- package/dist/src/providers/providerConfigUtils.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +2 -0
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/todo-continuation/todoContinuationService.d.ts +0 -1
- package/dist/src/services/todo-continuation/todoContinuationService.js +2 -1
- package/dist/src/services/todo-continuation/todoContinuationService.js.map +1 -1
- package/dist/src/ui/App.js +50 -34
- package/dist/src/ui/App.js.map +1 -1
- package/dist/src/ui/IdeIntegrationNudge.d.ts +7 -4
- package/dist/src/ui/IdeIntegrationNudge.js +31 -10
- package/dist/src/ui/IdeIntegrationNudge.js.map +1 -1
- package/dist/src/ui/commands/authCommand.js +16 -8
- package/dist/src/ui/commands/authCommand.js.map +1 -1
- package/dist/src/ui/commands/directoryCommand.js +2 -4
- package/dist/src/ui/commands/directoryCommand.js.map +1 -1
- package/dist/src/ui/commands/ideCommand.js +11 -8
- package/dist/src/ui/commands/ideCommand.js.map +1 -1
- package/dist/src/ui/commands/keyCommand.js +1 -1
- package/dist/src/ui/commands/keyCommand.js.map +1 -1
- package/dist/src/ui/commands/keyfileCommand.js +1 -1
- package/dist/src/ui/commands/keyfileCommand.js.map +1 -1
- package/dist/src/ui/commands/mcpCommand.js +10 -6
- package/dist/src/ui/commands/mcpCommand.js.map +1 -1
- package/dist/src/ui/commands/setCommand.js +43 -3
- package/dist/src/ui/commands/setCommand.js.map +1 -1
- package/dist/src/ui/commands/setupGithubCommand.js +5 -16
- package/dist/src/ui/commands/setupGithubCommand.js.map +1 -1
- package/dist/src/ui/commands/terminalSetupCommand.d.ts +13 -0
- package/dist/src/ui/commands/terminalSetupCommand.js +41 -0
- package/dist/src/ui/commands/terminalSetupCommand.js.map +1 -0
- package/dist/src/ui/commands/types.d.ts +1 -0
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/components/AuthDialog.js +56 -29
- package/dist/src/ui/components/AuthDialog.js.map +1 -1
- package/dist/src/ui/components/AuthInProgress.js +5 -4
- package/dist/src/ui/components/AuthInProgress.js.map +1 -1
- package/dist/src/ui/components/DebugProfiler.js +5 -4
- package/dist/src/ui/components/DebugProfiler.js.map +1 -1
- package/dist/src/ui/components/DetailedMessagesDisplay.js +4 -4
- package/dist/src/ui/components/DetailedMessagesDisplay.js.map +1 -1
- package/dist/src/ui/components/EditorSettingsDialog.js +6 -5
- package/dist/src/ui/components/EditorSettingsDialog.js.map +1 -1
- package/dist/src/ui/components/ErrorBoundary.js +2 -2
- package/dist/src/ui/components/ErrorBoundary.js.map +1 -1
- package/dist/src/ui/components/FolderTrustDialog.js +5 -4
- package/dist/src/ui/components/FolderTrustDialog.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.js +7 -1
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/LoggingDialog.js +5 -1
- package/dist/src/ui/components/LoggingDialog.js.map +1 -1
- package/dist/src/ui/components/OAuthCodeDialog.js +12 -14
- package/dist/src/ui/components/OAuthCodeDialog.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.js +12 -10
- package/dist/src/ui/components/SettingsDialog.js.map +1 -1
- package/dist/src/ui/components/ShellConfirmationDialog.js +5 -4
- package/dist/src/ui/components/ShellConfirmationDialog.js.map +1 -1
- package/dist/src/ui/components/ThemeDialog.js +6 -5
- package/dist/src/ui/components/ThemeDialog.js.map +1 -1
- package/dist/src/ui/components/TodoPanel.js +2 -2
- package/dist/src/ui/components/TodoPanel.js.map +1 -1
- package/dist/src/ui/components/messages/InfoMessage.js +1 -1
- package/dist/src/ui/components/messages/InfoMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js +8 -7
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
- package/dist/src/ui/components/shared/RadioButtonSelect.js +11 -9
- package/dist/src/ui/components/shared/RadioButtonSelect.js.map +1 -1
- package/dist/src/ui/components/shared/text-buffer.d.ts +17 -4
- package/dist/src/ui/components/shared/text-buffer.js +256 -80
- package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
- package/dist/src/ui/components/shared/vim-buffer-actions.js +139 -152
- package/dist/src/ui/components/shared/vim-buffer-actions.js.map +1 -1
- package/dist/src/ui/containers/SessionController.js +23 -23
- package/dist/src/ui/containers/SessionController.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.js +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.js +7 -1
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useAuthCommand.js +8 -60
- package/dist/src/ui/hooks/useAuthCommand.js.map +1 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.js +5 -5
- package/dist/src/ui/hooks/useAutoAcceptIndicator.js.map +1 -1
- package/dist/src/ui/hooks/useFocus.d.ts +4 -0
- package/dist/src/ui/hooks/useFocus.js +4 -4
- package/dist/src/ui/hooks/useFocus.js.map +1 -1
- package/dist/src/ui/hooks/useFolderTrust.d.ts +3 -2
- package/dist/src/ui/hooks/useFolderTrust.js +24 -9
- package/dist/src/ui/hooks/useFolderTrust.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.d.ts +1 -0
- package/dist/src/ui/hooks/useGeminiStream.js +126 -40
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useKeypress.d.ts +9 -1
- package/dist/src/ui/hooks/useKeypress.js +191 -8
- package/dist/src/ui/hooks/useKeypress.js.map +1 -1
- package/dist/src/ui/hooks/useKittyKeyboardProtocol.d.ts +15 -0
- package/dist/src/ui/hooks/useKittyKeyboardProtocol.js +20 -0
- package/dist/src/ui/hooks/useKittyKeyboardProtocol.js.map +1 -0
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.js +5 -4
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.js.map +1 -1
- package/dist/src/ui/privacy/CloudPaidPrivacyNotice.js +5 -4
- package/dist/src/ui/privacy/CloudPaidPrivacyNotice.js.map +1 -1
- package/dist/src/ui/privacy/GeminiPrivacyNotice.js +5 -4
- package/dist/src/ui/privacy/GeminiPrivacyNotice.js.map +1 -1
- package/dist/src/ui/reducers/sessionReducer.js +1 -0
- package/dist/src/ui/reducers/sessionReducer.js.map +1 -1
- package/dist/src/ui/utils/kittyProtocolDetector.d.ts +13 -0
- package/dist/src/ui/utils/kittyProtocolDetector.js +88 -0
- package/dist/src/ui/utils/kittyProtocolDetector.js.map +1 -0
- package/dist/src/ui/utils/platformConstants.d.ts +38 -0
- package/dist/src/ui/utils/platformConstants.js +39 -0
- package/dist/src/ui/utils/platformConstants.js.map +1 -0
- package/dist/src/ui/utils/renderLoopDetector.js +3 -3
- package/dist/src/ui/utils/renderLoopDetector.js.map +1 -1
- package/dist/src/ui/utils/terminalSetup.d.ts +30 -0
- package/dist/src/ui/utils/terminalSetup.js +281 -0
- package/dist/src/ui/utils/terminalSetup.js.map +1 -0
- package/dist/src/utils/checks.d.ts +19 -0
- package/dist/src/utils/checks.js +24 -0
- package/dist/src/utils/checks.js.map +1 -0
- package/dist/src/utils/privacy/ConversationDataRedactor.d.ts +0 -2
- package/dist/src/utils/privacy/ConversationDataRedactor.js +4 -37
- package/dist/src/utils/privacy/ConversationDataRedactor.js.map +1 -1
- package/dist/src/zed-integration/acp.d.ts +63 -0
- package/dist/src/{acp → zed-integration}/acp.js +76 -44
- package/dist/src/zed-integration/acp.js.map +1 -0
- package/dist/src/zed-integration/schema.d.ts +11679 -0
- package/dist/src/zed-integration/schema.js +305 -0
- package/dist/src/zed-integration/schema.js.map +1 -0
- package/dist/src/zed-integration/zedIntegration.d.ts +10 -0
- package/dist/src/{acp/acpPeer.js → zed-integration/zedIntegration.js} +333 -188
- package/dist/src/zed-integration/zedIntegration.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/dist/src/acp/acp.d.ts +0 -208
- package/dist/src/acp/acp.js.map +0 -1
- package/dist/src/acp/acpPeer.d.ts +0 -8
- package/dist/src/acp/acpPeer.js.map +0 -1
- package/dist/src/ui/utils/errorParsing.d.ts +0 -7
- package/dist/src/ui/utils/errorParsing.js +0 -106
- package/dist/src/ui/utils/errorParsing.js.map +0 -1
@@ -4,6 +4,7 @@
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
5
5
|
*/
|
6
6
|
import stripAnsi from 'strip-ansi';
|
7
|
+
import { stripVTControlCharacters } from 'util';
|
7
8
|
import { spawnSync } from 'child_process';
|
8
9
|
import fs from 'fs';
|
9
10
|
import os from 'os';
|
@@ -20,112 +21,266 @@ function isWordChar(ch) {
|
|
20
21
|
}
|
21
22
|
return !/[\s,.;!?]/.test(ch);
|
22
23
|
}
|
23
|
-
//
|
24
|
-
export const
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
// Helper functions for line-based word navigation
|
25
|
+
export const isWordCharStrict = (char) => /[\w\p{L}\p{N}]/u.test(char); // Matches a single character that is any Unicode letter, any Unicode number, or an underscore
|
26
|
+
export const isWhitespace = (char) => /\s/.test(char);
|
27
|
+
// Check if a character is a combining mark (only diacritics for now)
|
28
|
+
export const isCombiningMark = (char) => /\p{M}/u.test(char);
|
29
|
+
// Check if a character should be considered part of a word (including combining marks)
|
30
|
+
export const isWordCharWithCombining = (char) => isWordCharStrict(char) || isCombiningMark(char);
|
31
|
+
// Get the script of a character (simplified for common scripts)
|
32
|
+
export const getCharScript = (char) => {
|
33
|
+
if (/[\p{Script=Latin}]/u.test(char))
|
34
|
+
return 'latin'; // All Latin script chars including diacritics
|
35
|
+
if (/[\p{Script=Han}]/u.test(char))
|
36
|
+
return 'han'; // Chinese
|
37
|
+
if (/[\p{Script=Arabic}]/u.test(char))
|
38
|
+
return 'arabic';
|
39
|
+
if (/[\p{Script=Hiragana}]/u.test(char))
|
40
|
+
return 'hiragana';
|
41
|
+
if (/[\p{Script=Katakana}]/u.test(char))
|
42
|
+
return 'katakana';
|
43
|
+
if (/[\p{Script=Cyrillic}]/u.test(char))
|
44
|
+
return 'cyrillic';
|
45
|
+
return 'other';
|
46
|
+
};
|
47
|
+
// Check if two characters are from different scripts (indicating word boundary)
|
48
|
+
export const isDifferentScript = (char1, char2) => {
|
49
|
+
if (!isWordCharStrict(char1) || !isWordCharStrict(char2))
|
50
|
+
return false;
|
51
|
+
return getCharScript(char1) !== getCharScript(char2);
|
52
|
+
};
|
53
|
+
// Find next word start within a line, starting from col
|
54
|
+
export const findNextWordStartInLine = (line, col) => {
|
55
|
+
const chars = toCodePoints(line);
|
56
|
+
let i = col;
|
57
|
+
if (i >= chars.length)
|
58
|
+
return null;
|
59
|
+
const currentChar = chars[i];
|
29
60
|
// Skip current word/sequence based on character type
|
30
|
-
if (
|
31
|
-
|
32
|
-
|
61
|
+
if (isWordCharStrict(currentChar)) {
|
62
|
+
while (i < chars.length && isWordCharWithCombining(chars[i])) {
|
63
|
+
// Check for script boundary - if next character is from different script, stop here
|
64
|
+
if (i + 1 < chars.length &&
|
65
|
+
isWordCharStrict(chars[i + 1]) &&
|
66
|
+
isDifferentScript(chars[i], chars[i + 1])) {
|
67
|
+
i++; // Include current character
|
68
|
+
break; // Stop at script boundary
|
69
|
+
}
|
33
70
|
i++;
|
34
71
|
}
|
35
72
|
}
|
36
|
-
else if (
|
37
|
-
|
38
|
-
|
73
|
+
else if (!isWhitespace(currentChar)) {
|
74
|
+
while (i < chars.length &&
|
75
|
+
!isWordCharStrict(chars[i]) &&
|
76
|
+
!isWhitespace(chars[i])) {
|
39
77
|
i++;
|
40
78
|
}
|
41
79
|
}
|
42
80
|
// Skip whitespace
|
43
|
-
while (i <
|
81
|
+
while (i < chars.length && isWhitespace(chars[i])) {
|
44
82
|
i++;
|
45
83
|
}
|
46
|
-
|
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;
|
84
|
+
return i < chars.length ? i : null;
|
58
85
|
};
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
// Move back one character to start searching
|
86
|
+
// Find previous word start within a line
|
87
|
+
export const findPrevWordStartInLine = (line, col) => {
|
88
|
+
const chars = toCodePoints(line);
|
89
|
+
let i = col;
|
90
|
+
if (i <= 0)
|
91
|
+
return null;
|
66
92
|
i--;
|
67
93
|
// Skip whitespace moving backwards
|
68
|
-
while (i >= 0 && (
|
94
|
+
while (i >= 0 && isWhitespace(chars[i])) {
|
69
95
|
i--;
|
70
96
|
}
|
71
|
-
if (i < 0)
|
72
|
-
return
|
73
|
-
|
74
|
-
const charAtI = text[i];
|
75
|
-
if (/\w/.test(charAtI)) {
|
97
|
+
if (i < 0)
|
98
|
+
return null;
|
99
|
+
if (isWordCharStrict(chars[i])) {
|
76
100
|
// We're in a word, move to its beginning
|
77
|
-
while (i >= 0 &&
|
101
|
+
while (i >= 0 && isWordCharStrict(chars[i])) {
|
102
|
+
// Check for script boundary - if previous character is from different script, stop here
|
103
|
+
if (i - 1 >= 0 &&
|
104
|
+
isWordCharStrict(chars[i - 1]) &&
|
105
|
+
isDifferentScript(chars[i], chars[i - 1])) {
|
106
|
+
return i; // Return current position at script boundary
|
107
|
+
}
|
78
108
|
i--;
|
79
109
|
}
|
80
|
-
return i + 1;
|
110
|
+
return i + 1;
|
81
111
|
}
|
82
112
|
else {
|
83
113
|
// 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') {
|
114
|
+
while (i >= 0 && !isWordCharStrict(chars[i]) && !isWhitespace(chars[i])) {
|
89
115
|
i--;
|
90
116
|
}
|
91
|
-
return i + 1;
|
117
|
+
return i + 1;
|
92
118
|
}
|
93
119
|
};
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
120
|
+
// Find word end within a line
|
121
|
+
export const findWordEndInLine = (line, col) => {
|
122
|
+
const chars = toCodePoints(line);
|
123
|
+
let i = col;
|
124
|
+
// If we're already at the end of a word (including punctuation sequences), advance to next word
|
125
|
+
// This includes both regular word endings and script boundaries
|
126
|
+
const atEndOfWordChar = i < chars.length &&
|
127
|
+
isWordCharWithCombining(chars[i]) &&
|
128
|
+
(i + 1 >= chars.length ||
|
129
|
+
!isWordCharWithCombining(chars[i + 1]) ||
|
130
|
+
(isWordCharStrict(chars[i]) &&
|
131
|
+
i + 1 < chars.length &&
|
132
|
+
isWordCharStrict(chars[i + 1]) &&
|
133
|
+
isDifferentScript(chars[i], chars[i + 1])));
|
134
|
+
const atEndOfPunctuation = i < chars.length &&
|
135
|
+
!isWordCharWithCombining(chars[i]) &&
|
136
|
+
!isWhitespace(chars[i]) &&
|
137
|
+
(i + 1 >= chars.length ||
|
138
|
+
isWhitespace(chars[i + 1]) ||
|
139
|
+
isWordCharWithCombining(chars[i + 1]));
|
140
|
+
if (atEndOfWordChar || atEndOfPunctuation) {
|
141
|
+
// We're at the end of a word or punctuation sequence, move forward to find next word
|
101
142
|
i++;
|
102
|
-
// Skip whitespace
|
103
|
-
while (i <
|
143
|
+
// Skip whitespace to find next word or punctuation
|
144
|
+
while (i < chars.length && isWhitespace(chars[i])) {
|
104
145
|
i++;
|
105
146
|
}
|
106
147
|
}
|
107
|
-
// If we're not on a word character, find the next word
|
108
|
-
if (i <
|
109
|
-
|
148
|
+
// If we're not on a word character, find the next word or punctuation sequence
|
149
|
+
if (i < chars.length && !isWordCharWithCombining(chars[i])) {
|
150
|
+
// Skip whitespace to find next word or punctuation
|
151
|
+
while (i < chars.length && isWhitespace(chars[i])) {
|
110
152
|
i++;
|
111
153
|
}
|
112
154
|
}
|
113
|
-
// Move to end of current word
|
114
|
-
|
115
|
-
|
155
|
+
// Move to end of current word (including combining marks, but stop at script boundaries)
|
156
|
+
let foundWord = false;
|
157
|
+
let lastBaseCharPos = -1;
|
158
|
+
if (i < chars.length && isWordCharWithCombining(chars[i])) {
|
159
|
+
// Handle word characters
|
160
|
+
while (i < chars.length && isWordCharWithCombining(chars[i])) {
|
161
|
+
foundWord = true;
|
162
|
+
// Track the position of the last base character (not combining mark)
|
163
|
+
if (isWordCharStrict(chars[i])) {
|
164
|
+
lastBaseCharPos = i;
|
165
|
+
}
|
166
|
+
// Check if next character is from a different script (word boundary)
|
167
|
+
if (i + 1 < chars.length &&
|
168
|
+
isWordCharStrict(chars[i + 1]) &&
|
169
|
+
isDifferentScript(chars[i], chars[i + 1])) {
|
170
|
+
i++; // Include current character
|
171
|
+
if (isWordCharStrict(chars[i - 1])) {
|
172
|
+
lastBaseCharPos = i - 1;
|
173
|
+
}
|
174
|
+
break; // Stop at script boundary
|
175
|
+
}
|
176
|
+
i++;
|
177
|
+
}
|
178
|
+
}
|
179
|
+
else if (i < chars.length && !isWhitespace(chars[i])) {
|
180
|
+
// Handle punctuation sequences (like ████)
|
181
|
+
while (i < chars.length &&
|
182
|
+
!isWordCharStrict(chars[i]) &&
|
183
|
+
!isWhitespace(chars[i])) {
|
184
|
+
foundWord = true;
|
185
|
+
lastBaseCharPos = i;
|
186
|
+
i++;
|
187
|
+
}
|
116
188
|
}
|
117
|
-
//
|
118
|
-
|
189
|
+
// Only return a position if we actually found a word
|
190
|
+
// Return the position of the last base character, not combining marks
|
191
|
+
if (foundWord && lastBaseCharPos >= col) {
|
192
|
+
return lastBaseCharPos;
|
193
|
+
}
|
194
|
+
return null;
|
119
195
|
};
|
120
|
-
//
|
121
|
-
export const
|
122
|
-
|
123
|
-
|
124
|
-
|
196
|
+
// Find next word across lines
|
197
|
+
export const findNextWordAcrossLines = (lines, cursorRow, cursorCol, searchForWordStart) => {
|
198
|
+
// First try current line
|
199
|
+
const currentLine = lines[cursorRow] || '';
|
200
|
+
const colInCurrentLine = searchForWordStart
|
201
|
+
? findNextWordStartInLine(currentLine, cursorCol)
|
202
|
+
: findWordEndInLine(currentLine, cursorCol);
|
203
|
+
if (colInCurrentLine !== null) {
|
204
|
+
return { row: cursorRow, col: colInCurrentLine };
|
125
205
|
}
|
126
|
-
|
127
|
-
|
206
|
+
// Search subsequent lines
|
207
|
+
for (let row = cursorRow + 1; row < lines.length; row++) {
|
208
|
+
const line = lines[row] || '';
|
209
|
+
const chars = toCodePoints(line);
|
210
|
+
// For empty lines, if we haven't found any words yet, return the empty line
|
211
|
+
if (chars.length === 0) {
|
212
|
+
// Check if there are any words in remaining lines
|
213
|
+
let hasWordsInLaterLines = false;
|
214
|
+
for (let laterRow = row + 1; laterRow < lines.length; laterRow++) {
|
215
|
+
const laterLine = lines[laterRow] || '';
|
216
|
+
const laterChars = toCodePoints(laterLine);
|
217
|
+
let firstNonWhitespace = 0;
|
218
|
+
while (firstNonWhitespace < laterChars.length &&
|
219
|
+
isWhitespace(laterChars[firstNonWhitespace])) {
|
220
|
+
firstNonWhitespace++;
|
221
|
+
}
|
222
|
+
if (firstNonWhitespace < laterChars.length) {
|
223
|
+
hasWordsInLaterLines = true;
|
224
|
+
break;
|
225
|
+
}
|
226
|
+
}
|
227
|
+
// If no words in later lines, return the empty line
|
228
|
+
if (!hasWordsInLaterLines) {
|
229
|
+
return { row, col: 0 };
|
230
|
+
}
|
231
|
+
continue;
|
232
|
+
}
|
233
|
+
// Find first non-whitespace
|
234
|
+
let firstNonWhitespace = 0;
|
235
|
+
while (firstNonWhitespace < chars.length &&
|
236
|
+
isWhitespace(chars[firstNonWhitespace])) {
|
237
|
+
firstNonWhitespace++;
|
238
|
+
}
|
239
|
+
if (firstNonWhitespace < chars.length) {
|
240
|
+
if (searchForWordStart) {
|
241
|
+
return { row, col: firstNonWhitespace };
|
242
|
+
}
|
243
|
+
else {
|
244
|
+
// For word end, find the end of the first word
|
245
|
+
const endCol = findWordEndInLine(line, firstNonWhitespace);
|
246
|
+
if (endCol !== null) {
|
247
|
+
return { row, col: endCol };
|
248
|
+
}
|
249
|
+
}
|
250
|
+
}
|
251
|
+
}
|
252
|
+
return null;
|
128
253
|
};
|
254
|
+
// Find previous word across lines
|
255
|
+
export const findPrevWordAcrossLines = (lines, cursorRow, cursorCol) => {
|
256
|
+
// First try current line
|
257
|
+
const currentLine = lines[cursorRow] || '';
|
258
|
+
const colInCurrentLine = findPrevWordStartInLine(currentLine, cursorCol);
|
259
|
+
if (colInCurrentLine !== null) {
|
260
|
+
return { row: cursorRow, col: colInCurrentLine };
|
261
|
+
}
|
262
|
+
// Search previous lines
|
263
|
+
for (let row = cursorRow - 1; row >= 0; row--) {
|
264
|
+
const line = lines[row] || '';
|
265
|
+
const chars = toCodePoints(line);
|
266
|
+
if (chars.length === 0)
|
267
|
+
continue;
|
268
|
+
// Find last word start
|
269
|
+
let lastWordStart = chars.length;
|
270
|
+
while (lastWordStart > 0 && isWhitespace(chars[lastWordStart - 1])) {
|
271
|
+
lastWordStart--;
|
272
|
+
}
|
273
|
+
if (lastWordStart > 0) {
|
274
|
+
// Find start of this word
|
275
|
+
const wordStart = findPrevWordStartInLine(line, lastWordStart);
|
276
|
+
if (wordStart !== null) {
|
277
|
+
return { row, col: wordStart };
|
278
|
+
}
|
279
|
+
}
|
280
|
+
}
|
281
|
+
return null;
|
282
|
+
};
|
283
|
+
// Helper functions for vim line operations
|
129
284
|
export const getPositionFromOffsets = (startOffset, endOffset, lines) => {
|
130
285
|
let offset = 0;
|
131
286
|
let startRow = 0;
|
@@ -221,21 +376,42 @@ export const replaceRangeInternal = (state, startRow, startCol, endRow, endCol,
|
|
221
376
|
/**
|
222
377
|
* Strip characters that can break terminal rendering.
|
223
378
|
*
|
224
|
-
*
|
225
|
-
*
|
379
|
+
* Uses Node.js built-in stripVTControlCharacters to handle VT sequences,
|
380
|
+
* then filters remaining control characters that can disrupt display.
|
381
|
+
*
|
382
|
+
* Characters stripped:
|
383
|
+
* - ANSI escape sequences (via strip-ansi)
|
384
|
+
* - VT control sequences (via Node.js util.stripVTControlCharacters)
|
385
|
+
* - C0 control chars (0x00-0x1F) except CR/LF which are handled elsewhere
|
386
|
+
* - C1 control chars (0x80-0x9F) that can cause display issues
|
387
|
+
*
|
388
|
+
* Characters preserved:
|
389
|
+
* - All printable Unicode including emojis
|
390
|
+
* - DEL (0x7F) - handled functionally by applyOperations, not a display issue
|
391
|
+
* - CR/LF (0x0D/0x0A) - needed for line breaks
|
226
392
|
*/
|
227
393
|
function stripUnsafeCharacters(str) {
|
228
|
-
const
|
229
|
-
|
394
|
+
const strippedAnsi = stripAnsi(str);
|
395
|
+
const strippedVT = stripVTControlCharacters(strippedAnsi);
|
396
|
+
return toCodePoints(strippedVT)
|
230
397
|
.filter((char) => {
|
231
|
-
if (char.length > 1)
|
232
|
-
return false;
|
233
398
|
const code = char.codePointAt(0);
|
234
|
-
if (code === undefined)
|
399
|
+
if (code === undefined)
|
235
400
|
return false;
|
236
|
-
|
237
|
-
|
238
|
-
|
401
|
+
// Preserve CR/LF for line handling
|
402
|
+
if (code === 0x0a || code === 0x0d)
|
403
|
+
return true;
|
404
|
+
// Remove C0 control chars (except CR/LF) that can break display
|
405
|
+
// Examples: BELL(0x07) makes noise, BS(0x08) moves cursor, VT(0x0B), FF(0x0C)
|
406
|
+
if (code >= 0x00 && code <= 0x1f)
|
407
|
+
return false;
|
408
|
+
// Remove C1 control chars (0x80-0x9F) - legacy 8-bit control codes
|
409
|
+
if (code >= 0x80 && code <= 0x9f)
|
410
|
+
return false;
|
411
|
+
// Preserve DEL (0x7F) - it's handled functionally by applyOperations as backspace
|
412
|
+
// and doesn't cause rendering issues when displayed
|
413
|
+
// Preserve all other characters including Unicode/emojis
|
414
|
+
return true;
|
239
415
|
})
|
240
416
|
.join('');
|
241
417
|
}
|