cliedit 0.3.5 → 0.4.0
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 +3 -0
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +4 -0
- package/dist/editor.editing.d.ts +21 -0
- package/dist/editor.editing.js +127 -0
- package/dist/editor.keys.js +29 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -19,7 +19,10 @@ It includes line wrapping, visual navigation, smart auto-indentation, undo/redo,
|
|
|
19
19
|
- **Search & Replace:** `Ctrl+W` to find text, `Ctrl+R` to find and replace interactively.
|
|
20
20
|
- **Go to Line:** `Ctrl+L` to quickly jump to a specific line number.
|
|
21
21
|
- **Smart Auto-Indentation:** Automatically preserves indentation level when pressing Enter.
|
|
22
|
+
- **Block Indentation:** Use `Tab` / `Shift+Tab` to indent or outdent selected blocks of text.
|
|
22
23
|
- **Smart Navigation:** `Alt + Left/Right` to jump by words, `Ctrl + M` to jump between matching brackets.
|
|
24
|
+
- **Line Moving:** `Alt + Up/Down` to move the current line or selection up and down.
|
|
25
|
+
- **Line Duplication:** `Ctrl+D` to duplicate the current line or selection.
|
|
23
26
|
- **Piping Support:** Works with standard Unix pipes (e.g. `cat file.txt | cliedit`).
|
|
24
27
|
- **Crash Recovery:** Automatically saves changes to a hidden swap file (e.g. `.filename.swp`) to prevent data loss.
|
|
25
28
|
|
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
|
@@ -56,4 +56,8 @@ export const KEYS = {
|
|
|
56
56
|
PAGE_UP: 'pageup',
|
|
57
57
|
PAGE_DOWN: 'pagedown',
|
|
58
58
|
TAB: '\t',
|
|
59
|
+
SHIFT_TAB: 'SHIFT_TAB', // Internal representation
|
|
60
|
+
ALT_UP: 'ALT_UP', // Internal representation
|
|
61
|
+
ALT_DOWN: 'ALT_DOWN', // Internal representation
|
|
62
|
+
CTRL_D: '\x04',
|
|
59
63
|
};
|
package/dist/editor.editing.d.ts
CHANGED
|
@@ -37,6 +37,23 @@ declare function deleteForward(this: CliEditor): void;
|
|
|
37
37
|
* @param closeChar The corresponding closing character (e.g., ')', ']', '}').
|
|
38
38
|
*/
|
|
39
39
|
declare function handleAutoPair(this: CliEditor, openChar: string, closeChar: string): void;
|
|
40
|
+
/**
|
|
41
|
+
* Indents the selected lines (Block Indentation).
|
|
42
|
+
*/
|
|
43
|
+
declare function indentSelection(this: CliEditor): void;
|
|
44
|
+
/**
|
|
45
|
+
* Outdents the selected lines (Block Outdent).
|
|
46
|
+
*/
|
|
47
|
+
declare function outdentSelection(this: CliEditor): void;
|
|
48
|
+
/**
|
|
49
|
+
* Moves the current line or selection up or down.
|
|
50
|
+
* @param direction -1 for Up, 1 for Down
|
|
51
|
+
*/
|
|
52
|
+
declare function moveLines(this: CliEditor, direction: -1 | 1): void;
|
|
53
|
+
/**
|
|
54
|
+
* Duplicates the current line or selection.
|
|
55
|
+
*/
|
|
56
|
+
declare function duplicateLineOrSelection(this: CliEditor): void;
|
|
40
57
|
export declare const editingMethods: {
|
|
41
58
|
insertContentAtCursor: typeof insertContentAtCursor;
|
|
42
59
|
insertCharacter: typeof insertCharacter;
|
|
@@ -45,5 +62,9 @@ export declare const editingMethods: {
|
|
|
45
62
|
deleteBackward: typeof deleteBackward;
|
|
46
63
|
deleteForward: typeof deleteForward;
|
|
47
64
|
handleAutoPair: typeof handleAutoPair;
|
|
65
|
+
indentSelection: typeof indentSelection;
|
|
66
|
+
outdentSelection: typeof outdentSelection;
|
|
67
|
+
moveLines: typeof moveLines;
|
|
68
|
+
duplicateLineOrSelection: typeof duplicateLineOrSelection;
|
|
48
69
|
};
|
|
49
70
|
export {};
|
package/dist/editor.editing.js
CHANGED
|
@@ -147,6 +147,129 @@ function handleAutoPair(openChar, closeChar) {
|
|
|
147
147
|
}
|
|
148
148
|
this.setDirty();
|
|
149
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Indents the selected lines (Block Indentation).
|
|
152
|
+
*/
|
|
153
|
+
function indentSelection() {
|
|
154
|
+
this.saveState(); // Save state before modification for Undo
|
|
155
|
+
const selection = this.getNormalizedSelection();
|
|
156
|
+
if (!selection)
|
|
157
|
+
return;
|
|
158
|
+
for (let i = selection.start.y; i <= selection.end.y; i++) {
|
|
159
|
+
const line = this.lines[i];
|
|
160
|
+
this.lines[i] = ' '.repeat(this.tabSize) + line;
|
|
161
|
+
}
|
|
162
|
+
// Adjust selection anchors
|
|
163
|
+
if (this.selectionAnchor) {
|
|
164
|
+
this.selectionAnchor.x += this.tabSize;
|
|
165
|
+
this.cursorX += this.tabSize;
|
|
166
|
+
}
|
|
167
|
+
this.setDirty();
|
|
168
|
+
this.invalidateSyntaxCache();
|
|
169
|
+
this.recalculateVisualRows();
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Outdents the selected lines (Block Outdent).
|
|
173
|
+
*/
|
|
174
|
+
function outdentSelection() {
|
|
175
|
+
this.saveState(); // Save state before modification for Undo
|
|
176
|
+
// If no selection, try to outdent current line
|
|
177
|
+
let startY = this.cursorY;
|
|
178
|
+
let endY = this.cursorY;
|
|
179
|
+
if (this.selectionAnchor) {
|
|
180
|
+
const selection = this.getNormalizedSelection();
|
|
181
|
+
if (selection) {
|
|
182
|
+
startY = selection.start.y;
|
|
183
|
+
endY = selection.end.y;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
let changed = false;
|
|
187
|
+
for (let i = startY; i <= endY; i++) {
|
|
188
|
+
const line = this.lines[i];
|
|
189
|
+
// Remove up to tabSize spaces
|
|
190
|
+
const match = line.match(/^(\s+)/);
|
|
191
|
+
if (match) {
|
|
192
|
+
const spaces = match[1].length;
|
|
193
|
+
const toRemove = Math.min(spaces, this.tabSize);
|
|
194
|
+
this.lines[i] = line.slice(toRemove);
|
|
195
|
+
changed = true;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (changed) {
|
|
199
|
+
if (this.selectionAnchor) {
|
|
200
|
+
// Approximation: shift anchor and cursor left
|
|
201
|
+
this.selectionAnchor.x = Math.max(0, this.selectionAnchor.x - this.tabSize);
|
|
202
|
+
this.cursorX = Math.max(0, this.cursorX - this.tabSize);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
this.cursorX = Math.max(0, this.cursorX - this.tabSize);
|
|
206
|
+
}
|
|
207
|
+
this.setDirty();
|
|
208
|
+
this.invalidateSyntaxCache();
|
|
209
|
+
this.recalculateVisualRows();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Moves the current line or selection up or down.
|
|
214
|
+
* @param direction -1 for Up, 1 for Down
|
|
215
|
+
*/
|
|
216
|
+
function moveLines(direction) {
|
|
217
|
+
this.saveState(); // Save state before modification for Undo
|
|
218
|
+
let startY = this.cursorY;
|
|
219
|
+
let endY = this.cursorY;
|
|
220
|
+
if (this.selectionAnchor) {
|
|
221
|
+
const selection = this.getNormalizedSelection();
|
|
222
|
+
if (selection) {
|
|
223
|
+
startY = selection.start.y;
|
|
224
|
+
endY = selection.end.y;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Boundary checks
|
|
228
|
+
if (direction === -1 && startY === 0)
|
|
229
|
+
return; // Top
|
|
230
|
+
if (direction === 1 && endY >= this.lines.length - 1)
|
|
231
|
+
return; // Bottom
|
|
232
|
+
// Extract lines to move
|
|
233
|
+
const count = endY - startY + 1;
|
|
234
|
+
const linesToMove = this.lines.splice(startY, count);
|
|
235
|
+
// Insert at new position
|
|
236
|
+
const newStart = startY + direction;
|
|
237
|
+
this.lines.splice(newStart, 0, ...linesToMove);
|
|
238
|
+
// Update selection/cursor
|
|
239
|
+
this.cursorY += direction;
|
|
240
|
+
if (this.selectionAnchor) {
|
|
241
|
+
this.selectionAnchor.y += direction;
|
|
242
|
+
}
|
|
243
|
+
this.setDirty();
|
|
244
|
+
this.recalculateVisualRows();
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Duplicates the current line or selection.
|
|
248
|
+
*/
|
|
249
|
+
function duplicateLineOrSelection() {
|
|
250
|
+
this.saveState(); // Save state before modification for Undo
|
|
251
|
+
if (this.selectionAnchor) {
|
|
252
|
+
const selection = this.getNormalizedSelection();
|
|
253
|
+
if (!selection)
|
|
254
|
+
return;
|
|
255
|
+
const text = this.getSelectedText();
|
|
256
|
+
// We need to move cursor to end of selection.
|
|
257
|
+
// Normalized selection end:
|
|
258
|
+
this.cursorX = selection.end.x;
|
|
259
|
+
this.cursorY = selection.end.y;
|
|
260
|
+
const contentLines = text.split('\n');
|
|
261
|
+
this.insertContentAtCursor(contentLines);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
// Single line duplication
|
|
265
|
+
const line = this.lines[this.cursorY];
|
|
266
|
+
this.lines.splice(this.cursorY + 1, 0, line);
|
|
267
|
+
this.cursorY++; // Move down to the new line
|
|
268
|
+
// CursorX stays same? Usually yes.
|
|
269
|
+
}
|
|
270
|
+
this.setDirty();
|
|
271
|
+
this.recalculateVisualRows();
|
|
272
|
+
}
|
|
150
273
|
export const editingMethods = {
|
|
151
274
|
insertContentAtCursor,
|
|
152
275
|
insertCharacter,
|
|
@@ -155,4 +278,8 @@ export const editingMethods = {
|
|
|
155
278
|
deleteBackward,
|
|
156
279
|
deleteForward,
|
|
157
280
|
handleAutoPair,
|
|
281
|
+
indentSelection,
|
|
282
|
+
outdentSelection,
|
|
283
|
+
moveLines,
|
|
284
|
+
duplicateLineOrSelection,
|
|
158
285
|
};
|
package/dist/editor.keys.js
CHANGED
|
@@ -80,11 +80,15 @@ function handleKeypressEvent(ch, key) {
|
|
|
80
80
|
else if (key.name === 'return')
|
|
81
81
|
keyName = KEYS.ENTER;
|
|
82
82
|
else if (key.name === 'tab')
|
|
83
|
-
keyName = KEYS.TAB;
|
|
83
|
+
keyName = key.shift ? KEYS.SHIFT_TAB : KEYS.TAB;
|
|
84
84
|
else if (key.meta && key.name === 'left')
|
|
85
85
|
keyName = 'ALT_LEFT';
|
|
86
86
|
else if (key.meta && key.name === 'right')
|
|
87
87
|
keyName = 'ALT_RIGHT';
|
|
88
|
+
else if (key.meta && key.name === 'up')
|
|
89
|
+
keyName = KEYS.ALT_UP;
|
|
90
|
+
else if (key.meta && key.name === 'down')
|
|
91
|
+
keyName = KEYS.ALT_DOWN;
|
|
88
92
|
// Handle Mouse Scroll events explicitly
|
|
89
93
|
else if (key.name === 'scrollup')
|
|
90
94
|
keyName = 'SCROLL_UP';
|
|
@@ -277,8 +281,30 @@ function handleEditKeys(key) {
|
|
|
277
281
|
return true;
|
|
278
282
|
case KEYS.TAB:
|
|
279
283
|
this.clearSearchResults();
|
|
280
|
-
this.
|
|
281
|
-
|
|
284
|
+
if (this.selectionAnchor) {
|
|
285
|
+
this.indentSelection();
|
|
286
|
+
return false; // Manually saved state
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
this.insertSoftTab();
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
case KEYS.SHIFT_TAB:
|
|
293
|
+
this.clearSearchResults();
|
|
294
|
+
this.outdentSelection();
|
|
295
|
+
return false; // Manually saved state
|
|
296
|
+
case KEYS.ALT_UP:
|
|
297
|
+
this.clearSearchResults();
|
|
298
|
+
this.moveLines(-1);
|
|
299
|
+
return false; // Manually saved state
|
|
300
|
+
case KEYS.ALT_DOWN:
|
|
301
|
+
this.clearSearchResults();
|
|
302
|
+
this.moveLines(1);
|
|
303
|
+
return false; // Manually saved state
|
|
304
|
+
case KEYS.CTRL_D:
|
|
305
|
+
this.clearSearchResults();
|
|
306
|
+
this.duplicateLineOrSelection();
|
|
307
|
+
return false; // Manually saved state
|
|
282
308
|
// --- Search & History ---
|
|
283
309
|
case KEYS.CTRL_W:
|
|
284
310
|
this.enterFindMode();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cliedit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "A zero-dependency, embeddable TUI text editor for Node.js CLI applications.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/CodeTease/cliedit.git"
|