@google/gemini-cli 0.23.0-preview.4 → 0.24.0-nightly.20251231.05049b5ab
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.24.0-nightly.20251227.37be16243.tgz +0 -0
- package/dist/package.json +2 -2
- package/dist/src/commands/extensions/install.js +7 -31
- package/dist/src/commands/extensions/install.js.map +1 -1
- package/dist/src/commands/extensions/install.test.js +21 -3
- package/dist/src/commands/extensions/install.test.js.map +1 -1
- package/dist/src/commands/extensions/validate.test.js +1 -1
- package/dist/src/commands/extensions/validate.test.js.map +1 -1
- package/dist/src/commands/mcp/list.js +7 -1
- package/dist/src/commands/mcp/list.js.map +1 -1
- package/dist/src/config/config.d.ts +10 -2
- package/dist/src/config/config.js +12 -4
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +4 -12
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/extension-manager.d.ts +6 -0
- package/dist/src/config/extension-manager.js +59 -2
- package/dist/src/config/extension-manager.js.map +1 -1
- package/dist/src/config/policy-engine.integration.test.js +13 -1
- package/dist/src/config/policy-engine.integration.test.js.map +1 -1
- package/dist/src/config/settings-validation.js +1 -1
- package/dist/src/config/settings-validation.js.map +1 -1
- package/dist/src/config/settings-validation.test.js +49 -1
- package/dist/src/config/settings-validation.test.js.map +1 -1
- package/dist/src/config/settings.d.ts +4 -1
- package/dist/src/config/settings.js +23 -11
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +86 -9
- package/dist/src/config/settingsSchema.js +86 -13
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/config/settings_repro.test.js +1 -1
- package/dist/src/config/settings_repro.test.js.map +1 -1
- package/dist/src/config/settings_validation_warning.test.d.ts +6 -0
- package/dist/src/config/settings_validation_warning.test.js +118 -0
- package/dist/src/config/settings_validation_warning.test.js.map +1 -0
- package/dist/src/config/trustedFolders.js.map +1 -1
- package/dist/src/gemini.js +48 -18
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +19 -6
- package/dist/src/gemini.test.js.map +1 -1
- package/dist/src/gemini_cleanup.test.js +3 -0
- package/dist/src/gemini_cleanup.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/nonInteractiveCli.js +29 -1
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/nonInteractiveCli.test.js +48 -10
- package/dist/src/nonInteractiveCli.test.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +3 -3
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.test.js +11 -14
- package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
- package/dist/src/services/FileCommandLoader.js +6 -6
- package/dist/src/services/FileCommandLoader.js.map +1 -1
- package/dist/src/ui/AppContainer.js +4 -13
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/IdeIntegrationNudge.test.js +18 -7
- package/dist/src/ui/IdeIntegrationNudge.test.js.map +1 -1
- package/dist/src/ui/auth/AuthInProgress.test.js +16 -6
- package/dist/src/ui/auth/AuthInProgress.test.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.js +103 -2
- package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.test.js +127 -4
- package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -1
- package/dist/src/ui/commands/skillsCommand.d.ts +7 -0
- package/dist/src/ui/commands/skillsCommand.js +148 -0
- package/dist/src/ui/commands/skillsCommand.js.map +1 -0
- package/dist/src/ui/commands/skillsCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/skillsCommand.test.js +162 -0
- package/dist/src/ui/commands/skillsCommand.test.js.map +1 -0
- package/dist/src/ui/components/EditorSettingsDialog.js +2 -1
- package/dist/src/ui/components/EditorSettingsDialog.js.map +1 -1
- package/dist/src/ui/components/EditorSettingsDialog.test.js +3 -2
- package/dist/src/ui/components/EditorSettingsDialog.test.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js +2 -1
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/IdeTrustChangeDialog.js +2 -1
- package/dist/src/ui/components/IdeTrustChangeDialog.js.map +1 -1
- package/dist/src/ui/components/IdeTrustChangeDialog.test.js +4 -3
- package/dist/src/ui/components/IdeTrustChangeDialog.test.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.js +19 -11
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.test.js +41 -5
- package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.js +1 -1
- package/dist/src/ui/components/ModelDialog.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.test.js +1 -0
- package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
- package/dist/src/ui/components/Notifications.js +2 -2
- package/dist/src/ui/components/Notifications.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.js +2 -2
- package/dist/src/ui/components/SettingsDialog.js.map +1 -1
- package/dist/src/ui/components/shared/MaxSizedBox.js +4 -3
- package/dist/src/ui/components/shared/MaxSizedBox.js.map +1 -1
- package/dist/src/ui/components/shared/text-buffer.d.ts +36 -0
- package/dist/src/ui/components/shared/text-buffer.js +200 -25
- package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
- package/dist/src/ui/components/shared/text-buffer.test.js +147 -1
- package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -1
- package/dist/src/ui/components/shared/vim-buffer-actions.test.js +3 -0
- package/dist/src/ui/components/shared/vim-buffer-actions.test.js.map +1 -1
- package/dist/src/ui/components/views/ExtensionsList.js +2 -2
- package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
- package/dist/src/ui/components/views/ExtensionsList.test.js +26 -0
- package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
- package/dist/src/ui/components/views/HooksList.js +17 -23
- package/dist/src/ui/components/views/HooksList.js.map +1 -1
- package/dist/src/ui/components/views/McpStatus.js +7 -2
- package/dist/src/ui/components/views/McpStatus.js.map +1 -1
- package/dist/src/ui/components/views/McpStatus.test.js +10 -0
- package/dist/src/ui/components/views/McpStatus.test.js.map +1 -1
- package/dist/src/ui/components/views/SkillsList.d.ts +13 -0
- package/dist/src/ui/components/views/SkillsList.js +15 -0
- package/dist/src/ui/components/views/SkillsList.js.map +1 -0
- package/dist/src/ui/components/views/SkillsList.test.d.ts +6 -0
- package/dist/src/ui/components/views/SkillsList.test.js +64 -0
- package/dist/src/ui/components/views/SkillsList.test.js.map +1 -0
- package/dist/src/ui/constants.d.ts +1 -0
- package/dist/src/ui/constants.js +2 -0
- package/dist/src/ui/constants.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 +0 -6
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.test.js +0 -2
- package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionUpdates.js +5 -7
- package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionUpdates.test.js +2 -2
- package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +13 -1
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.test.js +60 -0
- package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
- package/dist/src/ui/hooks/useIncludeDirsTrust.js +2 -2
- package/dist/src/ui/hooks/useIncludeDirsTrust.js.map +1 -1
- package/dist/src/ui/hooks/useQuotaAndFallback.js +3 -2
- package/dist/src/ui/hooks/useQuotaAndFallback.js.map +1 -1
- package/dist/src/ui/hooks/useQuotaAndFallback.test.js +9 -0
- package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -1
- package/dist/src/ui/hooks/useSelectionList.js +2 -1
- package/dist/src/ui/hooks/useSelectionList.js.map +1 -1
- package/dist/src/ui/hooks/useSessionBrowser.js +3 -3
- package/dist/src/ui/hooks/useSessionBrowser.js.map +1 -1
- package/dist/src/ui/hooks/useSessionBrowser.test.js +4 -10
- package/dist/src/ui/hooks/useSessionBrowser.test.js.map +1 -1
- package/dist/src/ui/hooks/useShellHistory.js +3 -3
- package/dist/src/ui/hooks/useShellHistory.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +0 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/hooks/vim.test.js +7 -0
- package/dist/src/ui/hooks/vim.test.js.map +1 -1
- package/dist/src/ui/types.d.ts +12 -1
- package/dist/src/ui/types.js +1 -0
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/ui/utils/ConsolePatcher.js +1 -0
- package/dist/src/ui/utils/ConsolePatcher.js.map +1 -1
- package/dist/src/ui/utils/InlineMarkdownRenderer.js +2 -1
- package/dist/src/ui/utils/InlineMarkdownRenderer.js.map +1 -1
- package/dist/src/ui/utils/highlight.d.ts +3 -2
- package/dist/src/ui/utils/highlight.js +46 -36
- package/dist/src/ui/utils/highlight.js.map +1 -1
- package/dist/src/ui/utils/highlight.test.js +75 -0
- package/dist/src/ui/utils/highlight.test.js.map +1 -1
- package/dist/src/ui/utils/terminalCapabilityManager.d.ts +7 -0
- package/dist/src/ui/utils/terminalCapabilityManager.js +49 -1
- package/dist/src/ui/utils/terminalCapabilityManager.js.map +1 -1
- package/dist/src/ui/utils/terminalCapabilityManager.test.js +71 -0
- package/dist/src/ui/utils/terminalCapabilityManager.test.js.map +1 -1
- package/dist/src/utils/errors.js +9 -10
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/errors.test.js +95 -37
- package/dist/src/utils/errors.test.js.map +1 -1
- package/dist/src/utils/readStdin.js +7 -0
- package/dist/src/utils/readStdin.js.map +1 -1
- package/dist/src/utils/readStdin.test.js +4 -0
- package/dist/src/utils/readStdin.test.js.map +1 -1
- package/dist/src/utils/readStdin_safety.test.d.ts +6 -0
- package/dist/src/utils/readStdin_safety.test.js +68 -0
- package/dist/src/utils/readStdin_safety.test.js.map +1 -0
- package/dist/src/utils/relaunch.js +3 -1
- package/dist/src/utils/relaunch.js.map +1 -1
- package/dist/src/utils/relaunch.test.js +13 -7
- package/dist/src/utils/relaunch.test.js.map +1 -1
- package/dist/src/utils/sandbox.js +10 -7
- package/dist/src/utils/sandbox.js.map +1 -1
- package/dist/src/utils/sessionCleanup.integration.test.js +2 -2
- package/dist/src/utils/sessionCleanup.integration.test.js.map +1 -1
- package/dist/src/utils/sessionCleanup.js +4 -4
- package/dist/src/utils/sessionCleanup.js.map +1 -1
- package/dist/src/utils/sessionCleanup.test.js +18 -68
- package/dist/src/utils/sessionCleanup.test.js.map +1 -1
- package/dist/src/utils/sessionUtils.d.ts +20 -0
- package/dist/src/utils/sessionUtils.js +32 -4
- package/dist/src/utils/sessionUtils.js.map +1 -1
- package/dist/src/utils/sessionUtils.test.js +3 -3
- package/dist/src/utils/sessionUtils.test.js.map +1 -1
- package/dist/src/utils/sessions.js +9 -9
- package/dist/src/utils/sessions.js.map +1 -1
- package/dist/src/utils/sessions.test.js +38 -41
- package/dist/src/utils/sessions.test.js.map +1 -1
- package/dist/src/validateNonInterActiveAuth.d.ts +1 -1
- package/dist/src/validateNonInterActiveAuth.js +1 -2
- package/dist/src/validateNonInterActiveAuth.js.map +1 -1
- package/dist/src/validateNonInterActiveAuth.test.js +40 -56
- package/dist/src/validateNonInterActiveAuth.test.js.map +1 -1
- package/dist/src/zed-integration/zedIntegration.js +3 -3
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/src/zed-integration/zedIntegration.test.js +1 -1
- package/dist/src/zed-integration/zedIntegration.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/dist/google-gemini-cli-0.23.0-preview.3.tgz +0 -0
|
@@ -7,8 +7,9 @@ import { spawnSync } from 'node:child_process';
|
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import os from 'node:os';
|
|
9
9
|
import pathMod from 'node:path';
|
|
10
|
+
import * as path from 'node:path';
|
|
10
11
|
import { useState, useCallback, useEffect, useMemo, useReducer } from 'react';
|
|
11
|
-
import { coreEvents, CoreEvent } from '@google/gemini-cli-core';
|
|
12
|
+
import { coreEvents, CoreEvent, debugLogger, unescapePath, } from '@google/gemini-cli-core';
|
|
12
13
|
import { toCodePoints, cpLen, cpSlice, stripUnsafeCharacters, getCachedStringWidth, } from '../../utils/textUtils.js';
|
|
13
14
|
import { parsePastedPaths } from '../../utils/clipboardUtils.js';
|
|
14
15
|
import { handleVimAction } from './vim-buffer-actions.js';
|
|
@@ -483,24 +484,136 @@ export function logicalPosToOffset(lines, row, col) {
|
|
|
483
484
|
}
|
|
484
485
|
return offset;
|
|
485
486
|
}
|
|
487
|
+
export const imagePathRegex = /@((?:\\.|[^\s\r\n\\])+?\.(?:png|jpg|jpeg|gif|webp|svg|bmp))\b/gi;
|
|
488
|
+
export function getTransformedImagePath(filePath) {
|
|
489
|
+
const raw = filePath;
|
|
490
|
+
// Ignore leading @ when stripping directories, but keep it for simple '@file.png'
|
|
491
|
+
const withoutAt = raw.startsWith('@') ? raw.slice(1) : raw;
|
|
492
|
+
// Unescape the path to handle escaped spaces and other characters
|
|
493
|
+
const unescaped = unescapePath(withoutAt);
|
|
494
|
+
// Find last directory separator, supporting both POSIX and Windows styles
|
|
495
|
+
const lastSepIndex = Math.max(unescaped.lastIndexOf('/'), unescaped.lastIndexOf('\\'));
|
|
496
|
+
// If we saw a separator, take the segment after it; otherwise fall back to the unescaped string
|
|
497
|
+
const fileName = lastSepIndex >= 0 ? unescaped.slice(lastSepIndex + 1) : unescaped;
|
|
498
|
+
const extension = path.extname(fileName);
|
|
499
|
+
const baseName = path.basename(fileName, extension);
|
|
500
|
+
const maxBaseLength = 10;
|
|
501
|
+
const truncatedBase = baseName.length > maxBaseLength
|
|
502
|
+
? `...${baseName.slice(-maxBaseLength)}`
|
|
503
|
+
: baseName;
|
|
504
|
+
return `[Image ${truncatedBase}${extension}]`;
|
|
505
|
+
}
|
|
506
|
+
export function calculateTransformationsForLine(line) {
|
|
507
|
+
const transformations = [];
|
|
508
|
+
let match;
|
|
509
|
+
// Reset regex state to ensure clean matching from start of line
|
|
510
|
+
imagePathRegex.lastIndex = 0;
|
|
511
|
+
while ((match = imagePathRegex.exec(line)) !== null) {
|
|
512
|
+
const logicalText = match[0];
|
|
513
|
+
const logStart = cpLen(line.substring(0, match.index));
|
|
514
|
+
const logEnd = logStart + cpLen(logicalText);
|
|
515
|
+
transformations.push({
|
|
516
|
+
logStart,
|
|
517
|
+
logEnd,
|
|
518
|
+
logicalText,
|
|
519
|
+
collapsedText: getTransformedImagePath(logicalText),
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
return transformations;
|
|
523
|
+
}
|
|
524
|
+
export function calculateTransformations(lines) {
|
|
525
|
+
return lines.map((ln) => calculateTransformationsForLine(ln));
|
|
526
|
+
}
|
|
527
|
+
export function getTransformUnderCursor(row, col, spansByLine) {
|
|
528
|
+
const spans = spansByLine[row];
|
|
529
|
+
if (!spans || spans.length === 0)
|
|
530
|
+
return null;
|
|
531
|
+
for (const span of spans) {
|
|
532
|
+
if (col >= span.logStart && col <= span.logEnd) {
|
|
533
|
+
return span;
|
|
534
|
+
}
|
|
535
|
+
if (col < span.logStart)
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
export function calculateTransformedLine(logLine, logIndex, logicalCursor, transformations) {
|
|
541
|
+
let transformedLine = '';
|
|
542
|
+
const transformedToLogMap = [];
|
|
543
|
+
let lastLogPos = 0;
|
|
544
|
+
const cursorIsOnThisLine = logIndex === logicalCursor[0];
|
|
545
|
+
const cursorCol = logicalCursor[1];
|
|
546
|
+
for (const transform of transformations) {
|
|
547
|
+
const textBeforeTransformation = cpSlice(logLine, lastLogPos, transform.logStart);
|
|
548
|
+
transformedLine += textBeforeTransformation;
|
|
549
|
+
for (let i = 0; i < cpLen(textBeforeTransformation); i++) {
|
|
550
|
+
transformedToLogMap.push(lastLogPos + i);
|
|
551
|
+
}
|
|
552
|
+
const isExpanded = cursorIsOnThisLine &&
|
|
553
|
+
cursorCol >= transform.logStart &&
|
|
554
|
+
cursorCol <= transform.logEnd;
|
|
555
|
+
const transformedText = isExpanded
|
|
556
|
+
? transform.logicalText
|
|
557
|
+
: transform.collapsedText;
|
|
558
|
+
transformedLine += transformedText;
|
|
559
|
+
// Map transformed characters back to logical characters
|
|
560
|
+
const transformedLen = cpLen(transformedText);
|
|
561
|
+
if (isExpanded) {
|
|
562
|
+
for (let i = 0; i < transformedLen; i++) {
|
|
563
|
+
transformedToLogMap.push(transform.logStart + i);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
// Collapsed: distribute transformed positions monotonically across the raw span.
|
|
568
|
+
// This preserves ordering across wrapped slices so logicalToVisualMap has
|
|
569
|
+
// increasing startColInLogical and visual cursor mapping remains consistent.
|
|
570
|
+
const logicalLength = Math.max(0, transform.logEnd - transform.logStart);
|
|
571
|
+
for (let i = 0; i < transformedLen; i++) {
|
|
572
|
+
// Map the i-th transformed code point into [logStart, logEnd)
|
|
573
|
+
const transformationToLogicalOffset = logicalLength === 0
|
|
574
|
+
? 0
|
|
575
|
+
: Math.floor((i * logicalLength) / transformedLen);
|
|
576
|
+
const transformationToLogicalIndex = transform.logStart +
|
|
577
|
+
Math.min(transformationToLogicalOffset, Math.max(logicalLength - 1, 0));
|
|
578
|
+
transformedToLogMap.push(transformationToLogicalIndex);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
lastLogPos = transform.logEnd;
|
|
582
|
+
}
|
|
583
|
+
// Append text after last transform
|
|
584
|
+
const remainingUntransformedText = cpSlice(logLine, lastLogPos);
|
|
585
|
+
transformedLine += remainingUntransformedText;
|
|
586
|
+
for (let i = 0; i < cpLen(remainingUntransformedText); i++) {
|
|
587
|
+
transformedToLogMap.push(lastLogPos + i);
|
|
588
|
+
}
|
|
589
|
+
// For a cursor at the very end of the transformed line
|
|
590
|
+
transformedToLogMap.push(cpLen(logLine));
|
|
591
|
+
return { transformedLine, transformedToLogMap };
|
|
592
|
+
}
|
|
486
593
|
// Calculates the visual wrapping of lines and the mapping between logical and visual coordinates.
|
|
487
594
|
// This is an expensive operation and should be memoized.
|
|
488
|
-
function calculateLayout(logicalLines, viewportWidth) {
|
|
595
|
+
function calculateLayout(logicalLines, viewportWidth, logicalCursor) {
|
|
489
596
|
const visualLines = [];
|
|
490
597
|
const logicalToVisualMap = [];
|
|
491
598
|
const visualToLogicalMap = [];
|
|
599
|
+
const transformedToLogicalMaps = [];
|
|
600
|
+
const visualToTransformedMap = [];
|
|
492
601
|
logicalLines.forEach((logLine, logIndex) => {
|
|
493
602
|
logicalToVisualMap[logIndex] = [];
|
|
494
|
-
|
|
603
|
+
const transformations = calculateTransformationsForLine(logLine);
|
|
604
|
+
const { transformedLine, transformedToLogMap } = calculateTransformedLine(logLine, logIndex, logicalCursor, transformations);
|
|
605
|
+
transformedToLogicalMaps[logIndex] = transformedToLogMap;
|
|
606
|
+
if (transformedLine.length === 0) {
|
|
495
607
|
// Handle empty logical line
|
|
496
608
|
logicalToVisualMap[logIndex].push([visualLines.length, 0]);
|
|
497
609
|
visualToLogicalMap.push([logIndex, 0]);
|
|
610
|
+
visualToTransformedMap.push(0);
|
|
498
611
|
visualLines.push('');
|
|
499
612
|
}
|
|
500
613
|
else {
|
|
501
614
|
// Non-empty logical line
|
|
502
615
|
let currentPosInLogLine = 0; // Tracks position within the current logical line (code point index)
|
|
503
|
-
const codePointsInLogLine = toCodePoints(
|
|
616
|
+
const codePointsInLogLine = toCodePoints(transformedLine);
|
|
504
617
|
while (currentPosInLogLine < codePointsInLogLine.length) {
|
|
505
618
|
let currentChunk = '';
|
|
506
619
|
let currentChunkVisualWidth = 0;
|
|
@@ -570,11 +683,13 @@ function calculateLayout(logicalLines, viewportWidth) {
|
|
|
570
683
|
currentChunk = codePointsInLogLine[currentPosInLogLine];
|
|
571
684
|
numCodePointsInChunk = 1;
|
|
572
685
|
}
|
|
686
|
+
const logicalStartCol = transformedToLogMap[currentPosInLogLine] ?? 0;
|
|
573
687
|
logicalToVisualMap[logIndex].push([
|
|
574
688
|
visualLines.length,
|
|
575
|
-
|
|
689
|
+
logicalStartCol,
|
|
576
690
|
]);
|
|
577
|
-
visualToLogicalMap.push([logIndex,
|
|
691
|
+
visualToLogicalMap.push([logIndex, logicalStartCol]);
|
|
692
|
+
visualToTransformedMap.push(currentPosInLogLine);
|
|
578
693
|
visualLines.push(currentChunk);
|
|
579
694
|
const logicalStartOfThisChunk = currentPosInLogLine;
|
|
580
695
|
currentPosInLogLine += numCodePointsInChunk;
|
|
@@ -599,18 +714,21 @@ function calculateLayout(logicalLines, viewportWidth) {
|
|
|
599
714
|
logicalToVisualMap[0] = [];
|
|
600
715
|
logicalToVisualMap[0].push([0, 0]);
|
|
601
716
|
visualToLogicalMap.push([0, 0]);
|
|
717
|
+
visualToTransformedMap.push(0);
|
|
602
718
|
}
|
|
603
719
|
}
|
|
604
720
|
return {
|
|
605
721
|
visualLines,
|
|
606
722
|
logicalToVisualMap,
|
|
607
723
|
visualToLogicalMap,
|
|
724
|
+
transformedToLogicalMaps,
|
|
725
|
+
visualToTransformedMap,
|
|
608
726
|
};
|
|
609
727
|
}
|
|
610
728
|
// Calculates the visual cursor position based on a pre-calculated layout.
|
|
611
729
|
// This is a lightweight operation.
|
|
612
730
|
function calculateVisualCursorFromLayout(layout, logicalCursor) {
|
|
613
|
-
const { logicalToVisualMap, visualLines } = layout;
|
|
731
|
+
const { logicalToVisualMap, visualLines, transformedToLogicalMaps } = layout;
|
|
614
732
|
const [logicalRow, logicalCol] = logicalCursor;
|
|
615
733
|
const segmentsForLogicalLine = logicalToVisualMap[logicalRow];
|
|
616
734
|
if (!segmentsForLogicalLine || segmentsForLogicalLine.length === 0) {
|
|
@@ -635,9 +753,26 @@ function calculateVisualCursorFromLayout(layout, logicalCursor) {
|
|
|
635
753
|
}
|
|
636
754
|
}
|
|
637
755
|
const [visualRow, startColInLogical] = segmentsForLogicalLine[targetSegmentIndex];
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
756
|
+
// Find the coordinates in transformed space in order to conver to visual
|
|
757
|
+
const transformedToLogicalMap = transformedToLogicalMaps[logicalRow] ?? [];
|
|
758
|
+
let transformedCol = 0;
|
|
759
|
+
for (let i = 0; i < transformedToLogicalMap.length; i++) {
|
|
760
|
+
if (transformedToLogicalMap[i] > logicalCol) {
|
|
761
|
+
transformedCol = Math.max(0, i - 1);
|
|
762
|
+
break;
|
|
763
|
+
}
|
|
764
|
+
if (i === transformedToLogicalMap.length - 1) {
|
|
765
|
+
transformedCol = transformedToLogicalMap.length - 1;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
let startColInTransformed = 0;
|
|
769
|
+
while (startColInTransformed < transformedToLogicalMap.length &&
|
|
770
|
+
transformedToLogicalMap[startColInTransformed] < startColInLogical) {
|
|
771
|
+
startColInTransformed++;
|
|
772
|
+
}
|
|
773
|
+
const clampedTransformedCol = Math.min(transformedCol, Math.max(0, transformedToLogicalMap.length - 1));
|
|
774
|
+
const visualCol = clampedTransformedCol - startColInTransformed;
|
|
775
|
+
const clampedVisualCol = Math.min(Math.max(visualCol, 0), cpLen(visualLines[visualRow] ?? ''));
|
|
641
776
|
return [visualRow, clampedVisualCol];
|
|
642
777
|
}
|
|
643
778
|
const historyLimit = 100;
|
|
@@ -827,16 +962,25 @@ function textBufferReducerLogic(state, action, options = {}) {
|
|
|
827
962
|
break;
|
|
828
963
|
default: {
|
|
829
964
|
const exhaustiveCheck = dir;
|
|
830
|
-
|
|
965
|
+
debugLogger.error(`Unknown visual movement direction: ${exhaustiveCheck}`);
|
|
831
966
|
return state;
|
|
832
967
|
}
|
|
833
968
|
}
|
|
834
969
|
if (visualToLogicalMap[newVisualRow]) {
|
|
835
|
-
const [logRow,
|
|
970
|
+
const [logRow, logicalStartCol] = visualToLogicalMap[newVisualRow];
|
|
971
|
+
const transformedToLogicalMap = visualLayout.transformedToLogicalMaps?.[logRow] ?? [];
|
|
972
|
+
let transformedStartCol = 0;
|
|
973
|
+
while (transformedStartCol < transformedToLogicalMap.length &&
|
|
974
|
+
transformedToLogicalMap[transformedStartCol] < logicalStartCol) {
|
|
975
|
+
transformedStartCol++;
|
|
976
|
+
}
|
|
977
|
+
const clampedTransformedCol = Math.min(transformedStartCol + newVisualCol, Math.max(0, transformedToLogicalMap.length - 1));
|
|
978
|
+
const newLogicalCol = transformedToLogicalMap[clampedTransformedCol] ??
|
|
979
|
+
cpLen(lines[logRow] ?? '');
|
|
836
980
|
return {
|
|
837
981
|
...state,
|
|
838
982
|
cursorRow: logRow,
|
|
839
|
-
cursorCol:
|
|
983
|
+
cursorCol: newLogicalCol,
|
|
840
984
|
preferredCol: newPreferredCol,
|
|
841
985
|
};
|
|
842
986
|
}
|
|
@@ -1116,18 +1260,35 @@ function textBufferReducerLogic(state, action, options = {}) {
|
|
|
1116
1260
|
return handleVimAction(state, action);
|
|
1117
1261
|
default: {
|
|
1118
1262
|
const exhaustiveCheck = action;
|
|
1119
|
-
|
|
1263
|
+
debugLogger.error(`Unknown action encountered: ${exhaustiveCheck}`);
|
|
1120
1264
|
return state;
|
|
1121
1265
|
}
|
|
1122
1266
|
}
|
|
1123
1267
|
}
|
|
1124
1268
|
export function textBufferReducer(state, action, options = {}) {
|
|
1125
1269
|
const newState = textBufferReducerLogic(state, action, options);
|
|
1270
|
+
const newTransformedLines = newState.lines !== state.lines
|
|
1271
|
+
? calculateTransformations(newState.lines)
|
|
1272
|
+
: state.transformationsByLine;
|
|
1273
|
+
const oldTransform = getTransformUnderCursor(state.cursorRow, state.cursorCol, state.transformationsByLine);
|
|
1274
|
+
const newTransform = getTransformUnderCursor(newState.cursorRow, newState.cursorCol, newTransformedLines);
|
|
1275
|
+
const oldInside = oldTransform !== null;
|
|
1276
|
+
const newInside = newTransform !== null;
|
|
1277
|
+
const movedBetweenTransforms = oldTransform !== newTransform &&
|
|
1278
|
+
(oldTransform !== null || newTransform !== null);
|
|
1126
1279
|
if (newState.lines !== state.lines ||
|
|
1127
|
-
newState.viewportWidth !== state.viewportWidth
|
|
1280
|
+
newState.viewportWidth !== state.viewportWidth ||
|
|
1281
|
+
oldInside !== newInside ||
|
|
1282
|
+
movedBetweenTransforms) {
|
|
1283
|
+
const shouldResetPreferred = oldInside !== newInside || movedBetweenTransforms;
|
|
1128
1284
|
return {
|
|
1129
1285
|
...newState,
|
|
1130
|
-
|
|
1286
|
+
preferredCol: shouldResetPreferred ? null : newState.preferredCol,
|
|
1287
|
+
visualLayout: calculateLayout(newState.lines, newState.viewportWidth, [
|
|
1288
|
+
newState.cursorRow,
|
|
1289
|
+
newState.cursorCol,
|
|
1290
|
+
]),
|
|
1291
|
+
transformationsByLine: newTransformedLines,
|
|
1131
1292
|
};
|
|
1132
1293
|
}
|
|
1133
1294
|
return newState;
|
|
@@ -1137,11 +1298,13 @@ export function useTextBuffer({ initialText = '', initialCursorOffset = 0, viewp
|
|
|
1137
1298
|
const initialState = useMemo(() => {
|
|
1138
1299
|
const lines = initialText.split('\n');
|
|
1139
1300
|
const [initialCursorRow, initialCursorCol] = calculateInitialCursorPosition(lines.length === 0 ? [''] : lines, initialCursorOffset);
|
|
1140
|
-
const
|
|
1301
|
+
const transformationsByLine = calculateTransformations(lines.length === 0 ? [''] : lines);
|
|
1302
|
+
const visualLayout = calculateLayout(lines.length === 0 ? [''] : lines, viewport.width, [initialCursorRow, initialCursorCol]);
|
|
1141
1303
|
return {
|
|
1142
1304
|
lines: lines.length === 0 ? [''] : lines,
|
|
1143
1305
|
cursorRow: initialCursorRow,
|
|
1144
1306
|
cursorCol: initialCursorCol,
|
|
1307
|
+
transformationsByLine,
|
|
1145
1308
|
preferredCol: null,
|
|
1146
1309
|
undoStack: [],
|
|
1147
1310
|
redoStack: [],
|
|
@@ -1153,10 +1316,10 @@ export function useTextBuffer({ initialText = '', initialCursorOffset = 0, viewp
|
|
|
1153
1316
|
};
|
|
1154
1317
|
}, [initialText, initialCursorOffset, viewport.width, viewport.height]);
|
|
1155
1318
|
const [state, dispatch] = useReducer((s, a) => textBufferReducer(s, a, { inputFilter, singleLine }), initialState);
|
|
1156
|
-
const { lines, cursorRow, cursorCol, preferredCol, selectionAnchor, visualLayout, } = state;
|
|
1319
|
+
const { lines, cursorRow, cursorCol, preferredCol, selectionAnchor, visualLayout, transformationsByLine, } = state;
|
|
1157
1320
|
const text = useMemo(() => lines.join('\n'), [lines]);
|
|
1158
1321
|
const visualCursor = useMemo(() => calculateVisualCursorFromLayout(visualLayout, [cursorRow, cursorCol]), [visualLayout, cursorRow, cursorCol]);
|
|
1159
|
-
const { visualLines, visualToLogicalMap } = visualLayout;
|
|
1322
|
+
const { visualLines, visualToLogicalMap, transformedToLogicalMaps, visualToTransformedMap, } = visualLayout;
|
|
1160
1323
|
const [visualScrollRow, setVisualScrollRow] = useState(0);
|
|
1161
1324
|
useEffect(() => {
|
|
1162
1325
|
if (onChange) {
|
|
@@ -1383,7 +1546,7 @@ export function useTextBuffer({ initialText = '', initialCursorOffset = 0, viewp
|
|
|
1383
1546
|
dispatch({ type: 'set_text', payload: newText, pushToUndo: false });
|
|
1384
1547
|
}
|
|
1385
1548
|
catch (err) {
|
|
1386
|
-
|
|
1549
|
+
coreEvents.emitFeedback('error', '[useTextBuffer] external editor error', err);
|
|
1387
1550
|
}
|
|
1388
1551
|
finally {
|
|
1389
1552
|
coreEvents.emit(CoreEvent.ExternalEditorClosed);
|
|
@@ -1494,12 +1657,16 @@ export function useTextBuffer({ initialText = '', initialCursorOffset = 0, viewp
|
|
|
1494
1657
|
dispatch({ type: 'move_to_offset', payload: { offset } });
|
|
1495
1658
|
}, []);
|
|
1496
1659
|
const moveToVisualPosition = useCallback((visRow, visCol) => {
|
|
1497
|
-
const { visualLines, visualToLogicalMap } = visualLayout;
|
|
1660
|
+
const { visualLines, visualToLogicalMap, transformedToLogicalMaps, visualToTransformedMap, } = visualLayout;
|
|
1498
1661
|
// Clamp visRow to valid range
|
|
1499
1662
|
const clampedVisRow = Math.max(0, Math.min(visRow, visualLines.length - 1));
|
|
1500
1663
|
const visualLine = visualLines[clampedVisRow] || '';
|
|
1501
1664
|
if (visualToLogicalMap[clampedVisRow]) {
|
|
1502
|
-
const [logRow
|
|
1665
|
+
const [logRow] = visualToLogicalMap[clampedVisRow];
|
|
1666
|
+
const transformedToLogicalMap = transformedToLogicalMaps?.[logRow] ?? [];
|
|
1667
|
+
// Where does this visual line begin within the transformed line?
|
|
1668
|
+
const startColInTransformed = visualToTransformedMap?.[clampedVisRow] ?? 0;
|
|
1669
|
+
// Handle wide characters: convert visual X position to character offset
|
|
1503
1670
|
const codePoints = toCodePoints(visualLine);
|
|
1504
1671
|
let currentVisX = 0;
|
|
1505
1672
|
let charOffset = 0;
|
|
@@ -1518,8 +1685,10 @@ export function useTextBuffer({ initialText = '', initialCursorOffset = 0, viewp
|
|
|
1518
1685
|
}
|
|
1519
1686
|
// Clamp charOffset to length
|
|
1520
1687
|
charOffset = Math.min(charOffset, codePoints.length);
|
|
1688
|
+
// Map character offset through transformations to get logical position
|
|
1689
|
+
const transformedCol = Math.min(startColInTransformed + charOffset, Math.max(0, transformedToLogicalMap.length - 1));
|
|
1521
1690
|
const newCursorRow = logRow;
|
|
1522
|
-
const newCursorCol =
|
|
1691
|
+
const newCursorCol = transformedToLogicalMap[transformedCol] ?? cpLen(lines[logRow] ?? '');
|
|
1523
1692
|
dispatch({
|
|
1524
1693
|
type: 'set_cursor',
|
|
1525
1694
|
payload: {
|
|
@@ -1529,7 +1698,7 @@ export function useTextBuffer({ initialText = '', initialCursorOffset = 0, viewp
|
|
|
1529
1698
|
},
|
|
1530
1699
|
});
|
|
1531
1700
|
}
|
|
1532
|
-
}, [visualLayout]);
|
|
1701
|
+
}, [visualLayout, lines]);
|
|
1533
1702
|
const getOffset = useCallback(() => logicalPosToOffset(lines, cursorRow, cursorCol), [lines, cursorRow, cursorCol]);
|
|
1534
1703
|
const returnValue = useMemo(() => ({
|
|
1535
1704
|
lines,
|
|
@@ -1542,6 +1711,9 @@ export function useTextBuffer({ initialText = '', initialCursorOffset = 0, viewp
|
|
|
1542
1711
|
visualCursor,
|
|
1543
1712
|
visualScrollRow,
|
|
1544
1713
|
visualToLogicalMap,
|
|
1714
|
+
transformedToLogicalMaps,
|
|
1715
|
+
visualToTransformedMap,
|
|
1716
|
+
transformationsByLine,
|
|
1545
1717
|
setText,
|
|
1546
1718
|
insert,
|
|
1547
1719
|
newline,
|
|
@@ -1605,6 +1777,10 @@ export function useTextBuffer({ initialText = '', initialCursorOffset = 0, viewp
|
|
|
1605
1777
|
renderedVisualLines,
|
|
1606
1778
|
visualCursor,
|
|
1607
1779
|
visualScrollRow,
|
|
1780
|
+
visualToLogicalMap,
|
|
1781
|
+
transformedToLogicalMaps,
|
|
1782
|
+
visualToTransformedMap,
|
|
1783
|
+
transformationsByLine,
|
|
1608
1784
|
setText,
|
|
1609
1785
|
insert,
|
|
1610
1786
|
newline,
|
|
@@ -1656,7 +1832,6 @@ export function useTextBuffer({ initialText = '', initialCursorOffset = 0, viewp
|
|
|
1656
1832
|
vimMoveToLastLine,
|
|
1657
1833
|
vimMoveToLine,
|
|
1658
1834
|
vimEscapeInsertMode,
|
|
1659
|
-
visualToLogicalMap,
|
|
1660
1835
|
]);
|
|
1661
1836
|
return returnValue;
|
|
1662
1837
|
}
|