cliedit 0.1.2 → 0.1.3

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 CHANGED
@@ -4,7 +4,7 @@ A lightweight, zero-dependency, raw-mode terminal editor component for Node.js.
4
4
 
5
5
  `cliedit` is designed to be imported into your own CLI application to provide a full-featured, TTY-based text editing experience. It's perfect for applications that need to ask the user for multi-line input, edit configuration files, or write commit messages.
6
6
 
7
- It includes line wrapping, visual navigation, undo/redo, text selection, and clipboard support.
7
+ It includes line wrapping, visual navigation, smart auto-indentation, undo/redo, text selection, Find/Replace, and cross-platform clipboard support.
8
8
 
9
9
  ## Features
10
10
 
@@ -13,18 +13,21 @@ It includes line wrapping, visual navigation, undo/redo, text selection, and cli
13
13
  - **Visual Navigation:** `Up`/`Down` arrows move by visual rows, not logical lines.
14
14
  - **Undo/Redo:** `Ctrl+Z` / `Ctrl+Y` for persistent history.
15
15
  - **Text Selection:** `Ctrl+Arrow` keys to select text.
16
- - **Clipboard Support:** `Ctrl+C` (Copy), `Ctrl+X` (Cut), `Ctrl+V` (Paste) for system clipboard (macOS/Windows).
16
+ - **Clipboard Support:** `Ctrl+C` (Copy), `Ctrl+X` (Cut), `Ctrl+V` (Paste) for system clipboard (macOS, Windows, **and Linux** via `xclip`).
17
17
  - **File I/O:** Loads from and saves to the filesystem.
18
- - **Search:** `Ctrl+W` to find text.
18
+ - **Search & Replace:** `Ctrl+W` to find text, `Ctrl+R` to find and replace interactively.
19
+ - **Go to Line:** `Ctrl+L` to quickly jump to a specific line number.
20
+ - **Smart Auto-Indentation:** Automatically preserves indentation level when pressing Enter.
19
21
 
20
22
  ## Installation
21
23
  ```bash
22
24
  npm install cliedit
23
- ```
25
+ ````
24
26
 
25
27
  ## Usage
26
28
 
27
29
  The package exports an `async` function `openEditor` that returns a `Promise`. The promise resolves when the user quits the editor.
30
+
28
31
  ```javascript
29
32
  import { openEditor } from 'cliedit';
30
33
  import path from 'path';
@@ -60,13 +63,15 @@ getCommitMessage();
60
63
  `openEditor(filepath: string)`
61
64
 
62
65
  Opens the editor for the specified file. If the file doesn't exist, it will be created upon saving.
63
- - **Returns:** `Promise<{ saved: boolean; content: string }>`
64
- * `saved`: `true` if the user saved (Ctrl+S), `false` otherwise (Ctrl+Q).
65
- * `content`: The final content of the file as a string.
66
+
67
+ - **Returns:** `Promise<{ saved: boolean; content: string }>`
68
+ * `saved`: `true` if the user saved (Ctrl+S), `false` otherwise (Ctrl+Q).
69
+ * `content`: The final content of the file as a string.
66
70
 
67
71
  `CliEditor`
68
72
 
69
73
  The main editor class. You can import this directly if you need to extend or instantiate the editor with custom logic.
74
+
70
75
  ```javascript
71
76
  import { CliEditor } from 'cliedit';
72
77
  ```
@@ -74,6 +79,7 @@ import { CliEditor } from 'cliedit';
74
79
  ### Types
75
80
 
76
81
  Key types are also exported for convenience:
82
+
77
83
  ```javascript
78
84
  import type {
79
85
  DocumentState,
@@ -29,6 +29,14 @@ declare function deleteBackward(this: CliEditor): void;
29
29
  * Deletes the character after the cursor, or joins the current line with the next one.
30
30
  */
31
31
  declare function deleteForward(this: CliEditor): void;
32
+ /**
33
+ * Handles auto-pairing of brackets and quotes.
34
+ * If text is selected, it wraps the selection.
35
+ * Otherwise, it inserts the pair and places the cursor in the middle.
36
+ * @param openChar The opening character that was typed (e.g., '(', '[', '{').
37
+ * @param closeChar The corresponding closing character (e.g., ')', ']', '}').
38
+ */
39
+ declare function handleAutoPair(this: CliEditor, openChar: string, closeChar: string): void;
32
40
  export declare const editingMethods: {
33
41
  insertContentAtCursor: typeof insertContentAtCursor;
34
42
  insertCharacter: typeof insertCharacter;
@@ -36,5 +44,6 @@ export declare const editingMethods: {
36
44
  insertNewLine: typeof insertNewLine;
37
45
  deleteBackward: typeof deleteBackward;
38
46
  deleteForward: typeof deleteForward;
47
+ handleAutoPair: typeof handleAutoPair;
39
48
  };
40
49
  export {};
@@ -110,6 +110,36 @@ function deleteForward() {
110
110
  }
111
111
  this.setDirty();
112
112
  }
113
+ /**
114
+ * Handles auto-pairing of brackets and quotes.
115
+ * If text is selected, it wraps the selection.
116
+ * Otherwise, it inserts the pair and places the cursor in the middle.
117
+ * @param openChar The opening character that was typed (e.g., '(', '[', '{').
118
+ * @param closeChar The corresponding closing character (e.g., ')', ']', '}').
119
+ */
120
+ function handleAutoPair(openChar, closeChar) {
121
+ if (this.selectionAnchor) {
122
+ // There is a selection, so we need to wrap it.
123
+ const selection = this.getNormalizedSelection();
124
+ if (!selection)
125
+ return; // Should not happen if anchor exists, but good practice
126
+ const selectedText = this.getSelectedText();
127
+ // The deleteSelectedText() function automatically moves the cursor to the start
128
+ // of the selection, so we don't need to set it manually.
129
+ this.deleteSelectedText();
130
+ // Wrap the original selected text
131
+ const wrappedText = openChar + selectedText + closeChar;
132
+ this.insertContentAtCursor(wrappedText.split('\n'));
133
+ // The selection is already cancelled by deleteSelectedText().
134
+ }
135
+ else {
136
+ // No selection, just insert the opening and closing characters
137
+ this.insertCharacter(openChar + closeChar);
138
+ // Move cursor back one position to be in between the pair
139
+ this.cursorX--;
140
+ }
141
+ this.setDirty();
142
+ }
113
143
  export const editingMethods = {
114
144
  insertContentAtCursor,
115
145
  insertCharacter,
@@ -117,4 +147,5 @@ export const editingMethods = {
117
147
  insertNewLine,
118
148
  deleteBackward,
119
149
  deleteForward,
150
+ handleAutoPair,
120
151
  };
@@ -1,5 +1,12 @@
1
1
  // src/editor.keys.ts
2
2
  import { KEYS } from './constants.js';
3
+ const PAIR_MAP = {
4
+ '(': ')',
5
+ '[': ']',
6
+ '{': '}',
7
+ "'": "'",
8
+ '"': '"',
9
+ };
3
10
  /**
4
11
  * Main router for standardized keypress events from the 'keypress' library.
5
12
  */
@@ -175,10 +182,24 @@ function handleEditKeys(key) {
175
182
  this.insertNewLine();
176
183
  return true;
177
184
  case KEYS.BACKSPACE:
178
- if (this.selectionAnchor)
179
- this.deleteSelectedText();
180
- else
181
- this.deleteBackward();
185
+ // Handle auto-pair deletion
186
+ const line = this.lines[this.cursorY] || '';
187
+ const charBefore = line[this.cursorX - 1];
188
+ const charAfter = line[this.cursorX];
189
+ if (!this.selectionAnchor &&
190
+ charBefore && charAfter &&
191
+ PAIR_MAP[charBefore] === charAfter) {
192
+ // Delete both characters of the pair
193
+ this.lines[this.cursorY] = line.slice(0, this.cursorX - 1) + line.slice(this.cursorX + 1);
194
+ this.cursorX--; // Move cursor back
195
+ this.setDirty();
196
+ }
197
+ else {
198
+ if (this.selectionAnchor)
199
+ this.deleteSelectedText();
200
+ else
201
+ this.deleteBackward();
202
+ }
182
203
  return true;
183
204
  case KEYS.DELETE:
184
205
  if (this.selectionAnchor)
@@ -238,11 +259,26 @@ function handleEditKeys(key) {
238
259
  * Handles insertion of a character, deleting selection first if it exists.
239
260
  */
240
261
  function handleCharacterKey(ch) {
241
- if (this.selectionAnchor) {
242
- this.deleteSelectedText();
262
+ const line = this.lines[this.cursorY] || '';
263
+ const charAfter = line[this.cursorX];
264
+ // If user types a closing character and it's what we expect, just move the cursor.
265
+ if (!this.selectionAnchor &&
266
+ (ch === ')' || ch === ']' || ch === '}' || ch === "'" || ch === '"') &&
267
+ charAfter === ch) {
268
+ this.cursorX++;
269
+ return;
270
+ }
271
+ const closeChar = PAIR_MAP[ch];
272
+ if (closeChar) {
273
+ this.handleAutoPair(ch, closeChar);
274
+ }
275
+ else {
276
+ if (this.selectionAnchor) {
277
+ this.deleteSelectedText();
278
+ }
279
+ this.insertCharacter(ch);
280
+ this.setDirty();
243
281
  }
244
- this.insertCharacter(ch);
245
- this.setDirty();
246
282
  }
247
283
  /**
248
284
  * Handles Ctrl+Q (Quit) sequence.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cliedit",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "A lightweight, raw-mode terminal editor utility for Node.js CLI applications, with line wrapping and undo/redo support.",
5
5
  "repository": "https://github.com/CodeTease/cliedit",
6
6
  "type": "module",