ink-prompt 0.2.2 → 0.2.4
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 +2 -2
- package/dist/components/MultilineInput/AtomicBlocks.d.ts +10 -20
- package/dist/components/MultilineInput/AtomicBlocks.js +33 -42
- package/dist/components/MultilineInput/BlockMarker.d.ts +19 -0
- package/dist/components/MultilineInput/BlockMarker.js +83 -0
- package/dist/components/MultilineInput/BlockRegistry.d.ts +39 -0
- package/dist/components/MultilineInput/BlockRegistry.js +236 -0
- package/dist/components/MultilineInput/BlockTypes.d.ts +22 -0
- package/dist/components/MultilineInput/ImageTypes.d.ts +0 -2
- package/dist/components/MultilineInput/ImageTypes.js +1 -2
- package/dist/components/MultilineInput/ImageValidator.js +1 -1
- package/dist/components/MultilineInput/KeyHandler.d.ts +1 -1
- package/dist/components/MultilineInput/TextBuffer.d.ts +5 -31
- package/dist/components/MultilineInput/TextBuffer.js +91 -161
- package/dist/components/MultilineInput/TextRenderer.d.ts +6 -7
- package/dist/components/MultilineInput/TextRenderer.js +7 -7
- package/dist/components/MultilineInput/__tests__/BlockMarker.test.js +130 -0
- package/dist/components/MultilineInput/__tests__/BlockRegistry.test.js +225 -0
- package/dist/components/MultilineInput/__tests__/Placeholder.integration.test.js +44 -65
- package/dist/components/MultilineInput/__tests__/TextBuffer_images.test.js +10 -31
- package/dist/components/MultilineInput/__tests__/TextRenderer_images.test.js +27 -13
- package/dist/components/MultilineInput/__tests__/integration_images.test.js +2 -4
- package/dist/components/MultilineInput/__tests__/useTextInput_images.test.js +30 -29
- package/dist/components/MultilineInput/index.d.ts +6 -6
- package/dist/components/MultilineInput/index.js +56 -13
- package/dist/components/MultilineInput/types.d.ts +0 -20
- package/dist/components/MultilineInput/useTextInput.d.ts +4 -11
- package/dist/components/MultilineInput/useTextInput.js +79 -76
- package/package.json +1 -1
- package/dist/components/MultilineInput/Placeholder.d.ts +0 -30
- package/dist/components/MultilineInput/Placeholder.js +0 -200
- package/dist/components/MultilineInput/__tests__/Placeholder.test.js +0 -235
- package/dist/examples/examples/basic.js +0 -9
- package/dist/examples/src/components/MultilineInput/KeyHandler.d.ts +0 -15
- package/dist/examples/src/components/MultilineInput/KeyHandler.js +0 -97
- package/dist/examples/src/components/MultilineInput/TextBuffer.d.ts +0 -34
- package/dist/examples/src/components/MultilineInput/TextBuffer.js +0 -127
- package/dist/examples/src/components/MultilineInput/TextRenderer.d.ts +0 -24
- package/dist/examples/src/components/MultilineInput/TextRenderer.js +0 -72
- package/dist/examples/src/components/MultilineInput/__tests__/KeyHandler.test.js +0 -115
- package/dist/examples/src/components/MultilineInput/__tests__/TextBuffer.test.d.ts +0 -1
- package/dist/examples/src/components/MultilineInput/__tests__/TextBuffer.test.js +0 -254
- package/dist/examples/src/components/MultilineInput/__tests__/TextRenderer.test.d.ts +0 -1
- package/dist/examples/src/components/MultilineInput/__tests__/TextRenderer.test.js +0 -176
- package/dist/examples/src/components/MultilineInput/__tests__/integration.test.d.ts +0 -1
- package/dist/examples/src/components/MultilineInput/__tests__/integration.test.js +0 -71
- package/dist/examples/src/components/MultilineInput/__tests__/useTextInput.test.d.ts +0 -1
- package/dist/examples/src/components/MultilineInput/__tests__/useTextInput.test.js +0 -65
- package/dist/examples/src/components/MultilineInput/index.d.ts +0 -39
- package/dist/examples/src/components/MultilineInput/index.js +0 -82
- package/dist/examples/src/components/MultilineInput/types.d.ts +0 -55
- package/dist/examples/src/components/MultilineInput/types.js +0 -1
- package/dist/examples/src/components/MultilineInput/useTextInput.d.ts +0 -16
- package/dist/examples/src/components/MultilineInput/useTextInput.js +0 -82
- package/dist/examples/src/hello.test.d.ts +0 -1
- package/dist/examples/src/hello.test.js +0 -13
- package/dist/examples/src/index.d.ts +0 -2
- package/dist/examples/src/index.js +0 -2
- /package/dist/components/MultilineInput/{__tests__/Placeholder.test.d.ts → BlockTypes.js} +0 -0
- /package/dist/{examples/examples/basic.d.ts → components/MultilineInput/__tests__/BlockMarker.test.d.ts} +0 -0
- /package/dist/{examples/src/components/MultilineInput/__tests__/KeyHandler.test.d.ts → components/MultilineInput/__tests__/BlockRegistry.test.d.ts} +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { useState, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
2
|
-
import { createBuffer, insertText as bufferInsertText, deleteChar as bufferDeleteChar, deleteCharForward as bufferDeleteCharForward, insertNewLine as bufferInsertNewLine, moveCursor as bufferMoveCursor,
|
|
3
|
-
import {
|
|
4
|
-
import { createSentinel, parseSentinels } from './ImageSentinel.js';
|
|
2
|
+
import { createBuffer, insertText as bufferInsertText, deleteChar as bufferDeleteChar, deleteCharForward as bufferDeleteCharForward, insertNewLine as bufferInsertNewLine, moveCursor as bufferMoveCursor, } from './TextBuffer.js';
|
|
3
|
+
import { createBlockState, createPasteBlockEntry, createImageBlockEntry, removeBlock, getValue, getValueCursorOffset, getCursorFromValueOffset, } from './BlockRegistry.js';
|
|
5
4
|
import { findAtomicBlockBefore, findAtomicBlockAfter } from './AtomicBlocks.js';
|
|
6
5
|
import { log } from '../../utils/logger.js';
|
|
7
|
-
const defaultFormatPlaceholder = (
|
|
6
|
+
const defaultFormatPlaceholder = (displayNumber) => `[Paste text #${displayNumber}]`;
|
|
8
7
|
export function useTextInput({ initialValue = '', width, historyLimit = 100, undoDebounceMs = 200, pasteThreshold, formatPastePlaceholder = defaultFormatPlaceholder, } = {}) {
|
|
9
8
|
const [buffer, setBuffer] = useState(() => createBuffer(initialValue));
|
|
10
9
|
const [cursor, setCursor] = useState(() => {
|
|
@@ -14,9 +13,7 @@ export function useTextInput({ initialValue = '', width, historyLimit = 100, und
|
|
|
14
13
|
column: lines[lines.length - 1].length,
|
|
15
14
|
};
|
|
16
15
|
});
|
|
17
|
-
const [
|
|
18
|
-
const [images, setImages] = useState({});
|
|
19
|
-
const nextDisplayNumberRef = useRef(1);
|
|
16
|
+
const [blockState, setBlockState] = useState(() => createBlockState());
|
|
20
17
|
const [undoStack, setUndoStack] = useState([]);
|
|
21
18
|
const [redoStack, setRedoStack] = useState([]);
|
|
22
19
|
const pendingInsertBatchRef = useRef({});
|
|
@@ -56,13 +53,12 @@ export function useTextInput({ initialValue = '', width, historyLimit = 100, und
|
|
|
56
53
|
pendingInsertBatchRef.current.startState = {
|
|
57
54
|
buffer: currentBuffer,
|
|
58
55
|
cursor: currentCursor,
|
|
59
|
-
|
|
60
|
-
images,
|
|
56
|
+
blockState,
|
|
61
57
|
};
|
|
62
58
|
setRedoStack([]);
|
|
63
59
|
}
|
|
64
60
|
schedulePendingInsertCommit();
|
|
65
|
-
}, [schedulePendingInsertCommit,
|
|
61
|
+
}, [schedulePendingInsertCommit, blockState]);
|
|
66
62
|
const flushPendingInsertBatch = useCallback(() => {
|
|
67
63
|
commitPendingInsertBatch();
|
|
68
64
|
}, [commitPendingInsertBatch]);
|
|
@@ -70,11 +66,10 @@ export function useTextInput({ initialValue = '', width, historyLimit = 100, und
|
|
|
70
66
|
appendUndoState({
|
|
71
67
|
buffer: currentBuffer,
|
|
72
68
|
cursor: currentCursor,
|
|
73
|
-
|
|
74
|
-
images,
|
|
69
|
+
blockState,
|
|
75
70
|
});
|
|
76
71
|
setRedoStack([]);
|
|
77
|
-
}, [appendUndoState,
|
|
72
|
+
}, [appendUndoState, blockState]);
|
|
78
73
|
useEffect(() => {
|
|
79
74
|
return () => {
|
|
80
75
|
clearPendingInsertTimer();
|
|
@@ -92,16 +87,16 @@ export function useTextInput({ initialValue = '', width, historyLimit = 100, und
|
|
|
92
87
|
}
|
|
93
88
|
}, [buffer, cursor, flushPendingInsertBatch, pushToHistory]);
|
|
94
89
|
const insert = useCallback((char) => {
|
|
95
|
-
log(`[INSERT] char="${char.replace(/[\x00-\x1F\x7F
|
|
90
|
+
log(`[INSERT] char="${char.replace(/[\x00-\x1F\x7F-\uFFFF]/g, c => `\\x${c.charCodeAt(0).toString(16)}`)}" len=${char.length} cursor={line:${cursor.line},col:${cursor.column}} linesBefore=${buffer.lines.length}`);
|
|
96
91
|
const normalized = char.replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\x00/g, '');
|
|
97
92
|
if (normalized.length === 0)
|
|
98
93
|
return;
|
|
99
|
-
// Check if this is a paste exceeding the threshold
|
|
100
94
|
if (pasteThreshold !== undefined && pasteThreshold > 0 && normalized.length > pasteThreshold) {
|
|
101
95
|
flushPendingInsertBatch();
|
|
102
96
|
pushToHistory(buffer, cursor);
|
|
103
|
-
const
|
|
104
|
-
|
|
97
|
+
const displayText = formatPastePlaceholder(blockState.nextPasteNumber);
|
|
98
|
+
const { marker, state: newBlockState } = createPasteBlockEntry(blockState, normalized, displayText);
|
|
99
|
+
setBlockState(newBlockState);
|
|
105
100
|
const result = bufferInsertText(buffer, cursor, marker);
|
|
106
101
|
setBuffer(result.buffer);
|
|
107
102
|
setCursor(result.cursor);
|
|
@@ -120,70 +115,61 @@ export function useTextInput({ initialValue = '', width, historyLimit = 100, und
|
|
|
120
115
|
const result = bufferInsertText(buffer, cursor, normalized);
|
|
121
116
|
setBuffer(result.buffer);
|
|
122
117
|
setCursor(result.cursor);
|
|
123
|
-
}, [beginOrRefreshInsertBatch, buffer, cursor, flushPendingInsertBatch, pushToHistory, undoDebounceMs, pasteThreshold,
|
|
118
|
+
}, [beginOrRefreshInsertBatch, buffer, cursor, flushPendingInsertBatch, pushToHistory, undoDebounceMs, pasteThreshold, blockState, formatPastePlaceholder]);
|
|
124
119
|
const cleanupBlockRegistry = useCallback((block) => {
|
|
125
120
|
if (!block)
|
|
126
121
|
return;
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
else if (block.kind === 'sentinel' && images[block.id]) {
|
|
131
|
-
const next = { ...images };
|
|
132
|
-
delete next[block.id];
|
|
133
|
-
setImages(next);
|
|
134
|
-
}
|
|
135
|
-
}, [images]);
|
|
122
|
+
setBlockState((prev) => removeBlock(prev, block.id));
|
|
123
|
+
}, []);
|
|
136
124
|
const deleteChar = useCallback(() => {
|
|
137
125
|
applyEdit(() => {
|
|
138
126
|
const line = buffer.lines[cursor.line];
|
|
139
|
-
cleanupBlockRegistry(findAtomicBlockBefore(line, cursor.column,
|
|
140
|
-
return bufferDeleteChar(buffer, cursor,
|
|
127
|
+
cleanupBlockRegistry(findAtomicBlockBefore(line, cursor.column, blockState.entries));
|
|
128
|
+
return bufferDeleteChar(buffer, cursor, blockState.entries);
|
|
141
129
|
});
|
|
142
|
-
}, [applyEdit, buffer, cursor,
|
|
130
|
+
}, [applyEdit, buffer, cursor, blockState, cleanupBlockRegistry]);
|
|
143
131
|
const deleteCharForward = useCallback(() => {
|
|
144
132
|
applyEdit(() => {
|
|
145
133
|
const line = buffer.lines[cursor.line];
|
|
146
|
-
cleanupBlockRegistry(findAtomicBlockAfter(line, cursor.column,
|
|
147
|
-
return bufferDeleteCharForward(buffer, cursor,
|
|
134
|
+
cleanupBlockRegistry(findAtomicBlockAfter(line, cursor.column, blockState.entries));
|
|
135
|
+
return bufferDeleteCharForward(buffer, cursor, blockState.entries);
|
|
148
136
|
});
|
|
149
|
-
}, [applyEdit, buffer, cursor,
|
|
137
|
+
}, [applyEdit, buffer, cursor, blockState, cleanupBlockRegistry]);
|
|
150
138
|
const newLine = useCallback(() => {
|
|
151
139
|
applyEdit(() => bufferInsertNewLine(buffer, cursor));
|
|
152
140
|
}, [applyEdit, buffer, cursor]);
|
|
153
141
|
const deleteAndNewLine = useCallback(() => {
|
|
154
142
|
applyEdit(() => {
|
|
155
|
-
const afterDelete = bufferDeleteChar(buffer, cursor,
|
|
143
|
+
const afterDelete = bufferDeleteChar(buffer, cursor, blockState.entries);
|
|
156
144
|
return bufferInsertNewLine(afterDelete.buffer, afterDelete.cursor);
|
|
157
145
|
});
|
|
158
|
-
}, [applyEdit, buffer, cursor,
|
|
146
|
+
}, [applyEdit, buffer, cursor, blockState]);
|
|
159
147
|
const moveCursor = useCallback((direction) => {
|
|
160
148
|
flushPendingInsertBatch();
|
|
161
|
-
const newCursor = bufferMoveCursor(buffer, cursor, direction, width,
|
|
149
|
+
const newCursor = bufferMoveCursor(buffer, cursor, direction, width, blockState.entries);
|
|
162
150
|
setCursor(newCursor);
|
|
163
|
-
}, [buffer, cursor, flushPendingInsertBatch, width,
|
|
151
|
+
}, [buffer, cursor, flushPendingInsertBatch, width, blockState]);
|
|
164
152
|
const undo = useCallback(() => {
|
|
165
153
|
const pendingStartState = pendingInsertBatchRef.current.startState;
|
|
166
154
|
if (pendingStartState) {
|
|
167
155
|
clearPendingInsertTimer();
|
|
168
156
|
pendingInsertBatchRef.current.startState = undefined;
|
|
169
|
-
setRedoStack((prev) => [...prev, { buffer, cursor,
|
|
157
|
+
setRedoStack((prev) => [...prev, { buffer, cursor, blockState }]);
|
|
170
158
|
setBuffer(pendingStartState.buffer);
|
|
171
159
|
setCursor(pendingStartState.cursor);
|
|
172
|
-
|
|
173
|
-
setImages(pendingStartState.images);
|
|
160
|
+
setBlockState(pendingStartState.blockState);
|
|
174
161
|
return;
|
|
175
162
|
}
|
|
176
163
|
if (undoStack.length === 0)
|
|
177
164
|
return;
|
|
178
165
|
const previousState = undoStack[undoStack.length - 1];
|
|
179
166
|
const newUndoStack = undoStack.slice(0, -1);
|
|
180
|
-
setRedoStack((prev) => [...prev, { buffer, cursor,
|
|
167
|
+
setRedoStack((prev) => [...prev, { buffer, cursor, blockState }]);
|
|
181
168
|
setBuffer(previousState.buffer);
|
|
182
169
|
setCursor(previousState.cursor);
|
|
183
|
-
|
|
184
|
-
setImages(previousState.images);
|
|
170
|
+
setBlockState(previousState.blockState);
|
|
185
171
|
setUndoStack(newUndoStack);
|
|
186
|
-
}, [buffer, clearPendingInsertTimer, cursor, undoStack,
|
|
172
|
+
}, [buffer, clearPendingInsertTimer, cursor, undoStack, blockState]);
|
|
187
173
|
const redo = useCallback(() => {
|
|
188
174
|
if (pendingInsertBatchRef.current.startState) {
|
|
189
175
|
return;
|
|
@@ -192,53 +178,70 @@ export function useTextInput({ initialValue = '', width, historyLimit = 100, und
|
|
|
192
178
|
return;
|
|
193
179
|
const nextState = redoStack[redoStack.length - 1];
|
|
194
180
|
const newRedoStack = redoStack.slice(0, -1);
|
|
195
|
-
setUndoStack((prev) => [...prev, { buffer, cursor,
|
|
181
|
+
setUndoStack((prev) => [...prev, { buffer, cursor, blockState }]);
|
|
196
182
|
setBuffer(nextState.buffer);
|
|
197
183
|
setCursor(nextState.cursor);
|
|
198
|
-
|
|
199
|
-
setImages(nextState.images);
|
|
184
|
+
setBlockState(nextState.blockState);
|
|
200
185
|
setRedoStack(newRedoStack);
|
|
201
|
-
}, [buffer, cursor, redoStack,
|
|
186
|
+
}, [buffer, cursor, redoStack, blockState]);
|
|
202
187
|
const setText = useCallback((text) => {
|
|
203
188
|
applyEdit(() => {
|
|
204
|
-
|
|
189
|
+
setBlockState(createBlockState());
|
|
205
190
|
const newBuffer = createBuffer(text);
|
|
206
191
|
const lines = text.split('\n');
|
|
207
192
|
const newCursor = { line: lines.length - 1, column: lines[lines.length - 1].length };
|
|
208
|
-
// Clean up orphaned images
|
|
209
|
-
const usedIds = new Set(parseSentinels(getTextContent(newBuffer)).map((s) => s.id));
|
|
210
|
-
setImages((prev) => {
|
|
211
|
-
const next = {};
|
|
212
|
-
for (const [id, ref] of Object.entries(prev)) {
|
|
213
|
-
if (usedIds.has(id))
|
|
214
|
-
next[id] = ref;
|
|
215
|
-
}
|
|
216
|
-
return next;
|
|
217
|
-
});
|
|
218
193
|
return { buffer: newBuffer, cursor: newCursor };
|
|
219
194
|
});
|
|
220
195
|
}, [applyEdit]);
|
|
221
196
|
const insertImage = useCallback((imageRef) => {
|
|
222
197
|
applyEdit(() => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
return bufferInsertText(buffer, cursor, createSentinel(imageRef.id, imageRef.displayNumber));
|
|
198
|
+
const { marker, state: newBlockState } = createImageBlockEntry(blockState, imageRef, imageRef.id);
|
|
199
|
+
setBlockState(newBlockState);
|
|
200
|
+
return bufferInsertText(buffer, cursor, marker);
|
|
228
201
|
});
|
|
229
|
-
}, [applyEdit, buffer, cursor]);
|
|
230
|
-
const value = useMemo(() => getValue(buffer.lines,
|
|
231
|
-
const cursorOffset = useMemo(() => getValueCursorOffset(buffer.lines, cursor,
|
|
232
|
-
const imagesList = useMemo(() =>
|
|
202
|
+
}, [applyEdit, buffer, cursor, blockState]);
|
|
203
|
+
const value = useMemo(() => getValue(buffer.lines, blockState.entries), [buffer.lines, blockState.entries]);
|
|
204
|
+
const cursorOffset = useMemo(() => getValueCursorOffset(buffer.lines, cursor, blockState.entries), [buffer.lines, cursor, blockState.entries]);
|
|
205
|
+
const imagesList = useMemo(() => {
|
|
206
|
+
const result = [];
|
|
207
|
+
for (const entry of blockState.entries.values()) {
|
|
208
|
+
if (entry.kind === 'image') {
|
|
209
|
+
result.push({
|
|
210
|
+
id: entry.id,
|
|
211
|
+
data: entry.data,
|
|
212
|
+
mimeType: entry.mimeType,
|
|
213
|
+
byteSize: entry.byteSize,
|
|
214
|
+
displayNumber: entry.displayNumber,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
}, [blockState.entries]);
|
|
233
220
|
const getImagesCallback = useCallback(() => {
|
|
234
221
|
return imagesList;
|
|
235
222
|
}, [imagesList]);
|
|
236
223
|
const setImagesCallback = useCallback((newImages) => {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
224
|
+
setBlockState((prev) => {
|
|
225
|
+
const newEntries = new Map(prev.entries);
|
|
226
|
+
for (const [id, entry] of prev.entries) {
|
|
227
|
+
if (entry.kind === 'image') {
|
|
228
|
+
newEntries.delete(id);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
let nextImageNumber = prev.nextImageNumber;
|
|
232
|
+
for (const img of newImages) {
|
|
233
|
+
nextImageNumber = Math.max(nextImageNumber, img.displayNumber + 1);
|
|
234
|
+
newEntries.set(img.id, {
|
|
235
|
+
kind: 'image',
|
|
236
|
+
id: img.id,
|
|
237
|
+
displayNumber: img.displayNumber,
|
|
238
|
+
data: img.data,
|
|
239
|
+
mimeType: img.mimeType,
|
|
240
|
+
byteSize: img.byteSize,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
return { ...prev, entries: newEntries, nextImageNumber };
|
|
244
|
+
});
|
|
242
245
|
}, []);
|
|
243
246
|
return {
|
|
244
247
|
value,
|
|
@@ -256,9 +259,9 @@ export function useTextInput({ initialValue = '', width, historyLimit = 100, und
|
|
|
256
259
|
cursorOffset,
|
|
257
260
|
setCursorOffset: useCallback((offset) => {
|
|
258
261
|
flushPendingInsertBatch();
|
|
259
|
-
setCursor(getCursorFromValueOffset(buffer.lines, offset,
|
|
260
|
-
}, [buffer.lines, flushPendingInsertBatch,
|
|
261
|
-
|
|
262
|
+
setCursor(getCursorFromValueOffset(buffer.lines, offset, blockState.entries));
|
|
263
|
+
}, [buffer.lines, flushPendingInsertBatch, blockState.entries]),
|
|
264
|
+
blockState,
|
|
262
265
|
insertImage,
|
|
263
266
|
images: imagesList,
|
|
264
267
|
getImages: getImagesCallback,
|
package/package.json
CHANGED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { PlaceholderInfo, PlaceholderState } from './types.js';
|
|
2
|
-
export declare const MARKER_REGEX: RegExp;
|
|
3
|
-
export declare function createMarker(id: number): string;
|
|
4
|
-
export declare function createPlaceholderState(): PlaceholderState;
|
|
5
|
-
export declare function addPlaceholder(state: PlaceholderState, originalText: string, displayText: string): {
|
|
6
|
-
id: number;
|
|
7
|
-
marker: string;
|
|
8
|
-
state: PlaceholderState;
|
|
9
|
-
};
|
|
10
|
-
export declare function removePlaceholder(state: PlaceholderState, id: number): PlaceholderState;
|
|
11
|
-
export declare function getDisplayLine(line: string, placeholders: Map<number, PlaceholderInfo>): string;
|
|
12
|
-
export declare function getValue(lines: string[], placeholders: Map<number, PlaceholderInfo>): string;
|
|
13
|
-
export interface MarkerRange {
|
|
14
|
-
id: number;
|
|
15
|
-
start: number;
|
|
16
|
-
end: number;
|
|
17
|
-
}
|
|
18
|
-
export declare function findPlaceholderAt(line: string, column: number): MarkerRange | null;
|
|
19
|
-
export declare function findPlaceholderAfter(line: string, column: number): MarkerRange | null;
|
|
20
|
-
export declare function findPlaceholderBefore(line: string, column: number): MarkerRange | null;
|
|
21
|
-
export declare function bufferColToDisplayCol(line: string, column: number, placeholders: Map<number, PlaceholderInfo>): number;
|
|
22
|
-
export declare function displayColToBufferCol(line: string, displayColumn: number, placeholders: Map<number, PlaceholderInfo>): number;
|
|
23
|
-
export declare function getValueCursorOffset(lines: string[], cursor: {
|
|
24
|
-
line: number;
|
|
25
|
-
column: number;
|
|
26
|
-
}, placeholders: Map<number, PlaceholderInfo>): number;
|
|
27
|
-
export declare function getCursorFromValueOffset(lines: string[], offset: number, placeholders: Map<number, PlaceholderInfo>): {
|
|
28
|
-
line: number;
|
|
29
|
-
column: number;
|
|
30
|
-
};
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
export const MARKER_REGEX = /\x00P(\d+)\x00/g;
|
|
2
|
-
export function createMarker(id) {
|
|
3
|
-
return `\x00P${id}\x00`;
|
|
4
|
-
}
|
|
5
|
-
export function createPlaceholderState() {
|
|
6
|
-
return { placeholders: new Map(), nextId: 0 };
|
|
7
|
-
}
|
|
8
|
-
export function addPlaceholder(state, originalText, displayText) {
|
|
9
|
-
const id = state.nextId;
|
|
10
|
-
const marker = createMarker(id);
|
|
11
|
-
const newPlaceholders = new Map(state.placeholders);
|
|
12
|
-
newPlaceholders.set(id, { id, originalText, displayText });
|
|
13
|
-
return { id, marker, state: { placeholders: newPlaceholders, nextId: id + 1 } };
|
|
14
|
-
}
|
|
15
|
-
export function removePlaceholder(state, id) {
|
|
16
|
-
const newPlaceholders = new Map(state.placeholders);
|
|
17
|
-
newPlaceholders.delete(id);
|
|
18
|
-
return { ...state, placeholders: newPlaceholders };
|
|
19
|
-
}
|
|
20
|
-
export function getDisplayLine(line, placeholders) {
|
|
21
|
-
return line.replace(MARKER_REGEX, (_, idStr) => {
|
|
22
|
-
const info = placeholders.get(Number(idStr));
|
|
23
|
-
return info ? info.displayText : '';
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
export function getValue(lines, placeholders) {
|
|
27
|
-
const text = lines.join('\n');
|
|
28
|
-
return text.replace(MARKER_REGEX, (_, idStr) => {
|
|
29
|
-
const info = placeholders.get(Number(idStr));
|
|
30
|
-
return info ? info.originalText : '';
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
export function findPlaceholderAt(line, column) {
|
|
34
|
-
MARKER_REGEX.lastIndex = 0;
|
|
35
|
-
let match;
|
|
36
|
-
while ((match = MARKER_REGEX.exec(line)) !== null) {
|
|
37
|
-
const start = match.index;
|
|
38
|
-
const end = start + match[0].length;
|
|
39
|
-
if (column > start && column < end) {
|
|
40
|
-
return { id: Number(match[1]), start, end };
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
export function findPlaceholderAfter(line, column) {
|
|
46
|
-
MARKER_REGEX.lastIndex = 0;
|
|
47
|
-
let match;
|
|
48
|
-
while ((match = MARKER_REGEX.exec(line)) !== null) {
|
|
49
|
-
if (match.index === column) {
|
|
50
|
-
return { id: Number(match[1]), start: match.index, end: match.index + match[0].length };
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
export function findPlaceholderBefore(line, column) {
|
|
56
|
-
MARKER_REGEX.lastIndex = 0;
|
|
57
|
-
let match;
|
|
58
|
-
while ((match = MARKER_REGEX.exec(line)) !== null) {
|
|
59
|
-
const end = match.index + match[0].length;
|
|
60
|
-
if (end === column) {
|
|
61
|
-
return { id: Number(match[1]), start: match.index, end };
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
export function bufferColToDisplayCol(line, column, placeholders) {
|
|
67
|
-
if (!placeholders || placeholders.size === 0)
|
|
68
|
-
return column;
|
|
69
|
-
let displayCol = 0;
|
|
70
|
-
let lastEnd = 0;
|
|
71
|
-
MARKER_REGEX.lastIndex = 0;
|
|
72
|
-
let match;
|
|
73
|
-
while ((match = MARKER_REGEX.exec(line)) !== null) {
|
|
74
|
-
const markerStart = match.index;
|
|
75
|
-
const markerEnd = markerStart + match[0].length;
|
|
76
|
-
if (column <= markerStart) {
|
|
77
|
-
return displayCol + (column - lastEnd);
|
|
78
|
-
}
|
|
79
|
-
displayCol += markerStart - lastEnd;
|
|
80
|
-
const info = placeholders.get(Number(match[1]));
|
|
81
|
-
const displayLen = info ? info.displayText.length : 0;
|
|
82
|
-
if (column <= markerEnd) {
|
|
83
|
-
return displayCol + displayLen;
|
|
84
|
-
}
|
|
85
|
-
displayCol += displayLen;
|
|
86
|
-
lastEnd = markerEnd;
|
|
87
|
-
}
|
|
88
|
-
return displayCol + (column - lastEnd);
|
|
89
|
-
}
|
|
90
|
-
export function displayColToBufferCol(line, displayColumn, placeholders) {
|
|
91
|
-
if (!placeholders || placeholders.size === 0)
|
|
92
|
-
return displayColumn;
|
|
93
|
-
let bufPos = 0;
|
|
94
|
-
let dispPos = 0;
|
|
95
|
-
MARKER_REGEX.lastIndex = 0;
|
|
96
|
-
let match;
|
|
97
|
-
while ((match = MARKER_REGEX.exec(line)) !== null) {
|
|
98
|
-
const markerStart = match.index;
|
|
99
|
-
const markerEnd = markerStart + match[0].length;
|
|
100
|
-
const textLen = markerStart - bufPos;
|
|
101
|
-
if (displayColumn <= dispPos + textLen) {
|
|
102
|
-
return bufPos + (displayColumn - dispPos);
|
|
103
|
-
}
|
|
104
|
-
dispPos += textLen;
|
|
105
|
-
bufPos = markerStart;
|
|
106
|
-
const info = placeholders.get(Number(match[1]));
|
|
107
|
-
const displayLen = info ? info.displayText.length : 0;
|
|
108
|
-
if (displayColumn <= dispPos + displayLen) {
|
|
109
|
-
return markerEnd;
|
|
110
|
-
}
|
|
111
|
-
dispPos += displayLen;
|
|
112
|
-
bufPos = markerEnd;
|
|
113
|
-
}
|
|
114
|
-
return bufPos + (displayColumn - dispPos);
|
|
115
|
-
}
|
|
116
|
-
export function getValueCursorOffset(lines, cursor, placeholders) {
|
|
117
|
-
let offset = 0;
|
|
118
|
-
for (let i = 0; i < cursor.line; i++) {
|
|
119
|
-
offset += getExpandedLineLength(lines[i], placeholders) + 1;
|
|
120
|
-
}
|
|
121
|
-
const line = lines[cursor.line];
|
|
122
|
-
let bufPos = 0;
|
|
123
|
-
MARKER_REGEX.lastIndex = 0;
|
|
124
|
-
let match;
|
|
125
|
-
while ((match = MARKER_REGEX.exec(line)) !== null) {
|
|
126
|
-
const markerStart = match.index;
|
|
127
|
-
const markerEnd = markerStart + match[0].length;
|
|
128
|
-
if (cursor.column <= markerStart) {
|
|
129
|
-
offset += cursor.column - bufPos;
|
|
130
|
-
return offset;
|
|
131
|
-
}
|
|
132
|
-
offset += markerStart - bufPos;
|
|
133
|
-
const info = placeholders.get(Number(match[1]));
|
|
134
|
-
offset += info ? info.originalText.length : 0;
|
|
135
|
-
bufPos = markerEnd;
|
|
136
|
-
if (cursor.column <= markerEnd) {
|
|
137
|
-
return offset;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
offset += cursor.column - bufPos;
|
|
141
|
-
return offset;
|
|
142
|
-
}
|
|
143
|
-
export function getCursorFromValueOffset(lines, offset, placeholders) {
|
|
144
|
-
let currentOffset = 0;
|
|
145
|
-
const lineCount = lines.length;
|
|
146
|
-
for (let i = 0; i < lineCount; i++) {
|
|
147
|
-
const lineLen = getExpandedLineLength(lines[i], placeholders);
|
|
148
|
-
if (i === lineCount - 1) {
|
|
149
|
-
if (offset <= currentOffset + lineLen) {
|
|
150
|
-
const colInExpanded = offset - currentOffset;
|
|
151
|
-
return { line: i, column: valueOffsetToBufferColumn(lines[i], colInExpanded, placeholders) };
|
|
152
|
-
}
|
|
153
|
-
return { line: i, column: lines[i].length };
|
|
154
|
-
}
|
|
155
|
-
if (offset <= currentOffset + lineLen) {
|
|
156
|
-
const colInExpanded = offset - currentOffset;
|
|
157
|
-
return { line: i, column: valueOffsetToBufferColumn(lines[i], colInExpanded, placeholders) };
|
|
158
|
-
}
|
|
159
|
-
currentOffset += lineLen + 1;
|
|
160
|
-
}
|
|
161
|
-
const lastIdx = lines.length - 1;
|
|
162
|
-
return { line: lastIdx, column: lines[lastIdx].length };
|
|
163
|
-
}
|
|
164
|
-
function getExpandedLineLength(line, placeholders) {
|
|
165
|
-
let len = 0;
|
|
166
|
-
let lastEnd = 0;
|
|
167
|
-
MARKER_REGEX.lastIndex = 0;
|
|
168
|
-
let match;
|
|
169
|
-
while ((match = MARKER_REGEX.exec(line)) !== null) {
|
|
170
|
-
len += match.index - lastEnd;
|
|
171
|
-
const info = placeholders.get(Number(match[1]));
|
|
172
|
-
len += info ? info.originalText.length : 0;
|
|
173
|
-
lastEnd = match.index + match[0].length;
|
|
174
|
-
}
|
|
175
|
-
len += line.length - lastEnd;
|
|
176
|
-
return len;
|
|
177
|
-
}
|
|
178
|
-
function valueOffsetToBufferColumn(line, offsetInExpanded, placeholders) {
|
|
179
|
-
let bufPos = 0;
|
|
180
|
-
let expandedPos = 0;
|
|
181
|
-
MARKER_REGEX.lastIndex = 0;
|
|
182
|
-
let match;
|
|
183
|
-
while ((match = MARKER_REGEX.exec(line)) !== null) {
|
|
184
|
-
const markerStart = match.index;
|
|
185
|
-
const textLen = markerStart - bufPos;
|
|
186
|
-
if (offsetInExpanded <= expandedPos + textLen) {
|
|
187
|
-
return bufPos + (offsetInExpanded - expandedPos);
|
|
188
|
-
}
|
|
189
|
-
expandedPos += textLen;
|
|
190
|
-
bufPos = markerStart;
|
|
191
|
-
const info = placeholders.get(Number(match[1]));
|
|
192
|
-
const originalLen = info ? info.originalText.length : 0;
|
|
193
|
-
if (offsetInExpanded <= expandedPos + originalLen) {
|
|
194
|
-
return markerStart + match[0].length;
|
|
195
|
-
}
|
|
196
|
-
expandedPos += originalLen;
|
|
197
|
-
bufPos = markerStart + match[0].length;
|
|
198
|
-
}
|
|
199
|
-
return bufPos + (offsetInExpanded - expandedPos);
|
|
200
|
-
}
|