@vimee/plugin-monaco 0.1.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 +112 -0
- package/dist/index.cjs +272 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +166 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.js +240 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# @vimee/plugin-monaco
|
|
2
|
+
|
|
3
|
+
**Attach vim editing to any Monaco Editor instance**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@vimee/plugin-monaco)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
Framework-agnostic plugin that adds vim keybindings to a [Monaco Editor](https://microsoft.github.io/monaco-editor/) instance. Works with vanilla JS, React, or any Monaco wrapper.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @vimee/core @vimee/plugin-monaco
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
> **Note:** You also need `monaco-editor` as a peer dependency in your project.
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import * as monaco from "monaco-editor";
|
|
22
|
+
import { attach } from "@vimee/plugin-monaco";
|
|
23
|
+
|
|
24
|
+
const editor = monaco.editor.create(document.getElementById("editor")!, {
|
|
25
|
+
value: 'console.log("Hello, vim!");',
|
|
26
|
+
language: "typescript",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const vim = attach(editor, {
|
|
30
|
+
onChange: (value) => console.log("Content:", value),
|
|
31
|
+
onModeChange: (mode) => console.log("Mode:", mode),
|
|
32
|
+
onSave: (value) => console.log("Saved:", value),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Later...
|
|
36
|
+
vim.destroy();
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## API
|
|
40
|
+
|
|
41
|
+
### `attach(editor, options?)`
|
|
42
|
+
|
|
43
|
+
Attaches vim keybindings to a Monaco Editor instance. Returns a `VimMonaco` handle.
|
|
44
|
+
|
|
45
|
+
The `editor` parameter accepts any object satisfying the `MonacoEditor` interface — typically a `monaco.editor.IStandaloneCodeEditor`.
|
|
46
|
+
|
|
47
|
+
#### Options
|
|
48
|
+
|
|
49
|
+
| Option | Type | Default | Description |
|
|
50
|
+
|--------|------|---------|-------------|
|
|
51
|
+
| `readOnly` | `boolean` | `false` | Read-only mode (motions work, edits blocked) |
|
|
52
|
+
| `onChange` | `(value: string) => void` | — | Content change callback |
|
|
53
|
+
| `onModeChange` | `(mode: VimMode) => void` | — | Mode change callback |
|
|
54
|
+
| `onYank` | `(text: string) => void` | — | Yank callback |
|
|
55
|
+
| `onSave` | `(value: string) => void` | — | `:w` callback |
|
|
56
|
+
| `onAction` | `(action: VimAction, key: string) => void` | — | Action callback |
|
|
57
|
+
| `indentStyle` | `"space" \| "tab"` | `"space"` | Indent character |
|
|
58
|
+
| `indentWidth` | `number` | `2` | Spaces per indent |
|
|
59
|
+
|
|
60
|
+
#### VimMonaco
|
|
61
|
+
|
|
62
|
+
| Method | Return | Description |
|
|
63
|
+
|--------|--------|-------------|
|
|
64
|
+
| `getMode()` | `VimMode` | Current vim mode |
|
|
65
|
+
| `getCursor()` | `CursorPosition` | Current cursor position (0-based) |
|
|
66
|
+
| `getContent()` | `string` | Current editor content |
|
|
67
|
+
| `destroy()` | `void` | Detach all listeners and clean up |
|
|
68
|
+
|
|
69
|
+
## Cursor Style
|
|
70
|
+
|
|
71
|
+
The plugin automatically switches Monaco's cursor style based on vim mode:
|
|
72
|
+
|
|
73
|
+
- **Normal / Visual / Command-line mode** → Block cursor
|
|
74
|
+
- **Insert mode** → Line cursor
|
|
75
|
+
|
|
76
|
+
## Visual Mode Decorations
|
|
77
|
+
|
|
78
|
+
In visual and visual-line mode, the plugin applies a CSS class `vimee-visual-selection` to the selected range. You can style it:
|
|
79
|
+
|
|
80
|
+
```css
|
|
81
|
+
.vimee-visual-selection {
|
|
82
|
+
background-color: rgba(255, 165, 0, 0.3);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Features
|
|
87
|
+
|
|
88
|
+
- Automatic cursor style switching (block ↔ line)
|
|
89
|
+
- Visual mode selection highlighting via decorations
|
|
90
|
+
- H/M/L motions with viewport tracking
|
|
91
|
+
- Ctrl-U/D/B/F page scrolling
|
|
92
|
+
- IME composition support (CJK input)
|
|
93
|
+
- Auto-scrolls to keep cursor visible (`revealLine`)
|
|
94
|
+
- All vim features from `@vimee/core` (motions, operators, text objects, search, macros, marks, etc.)
|
|
95
|
+
|
|
96
|
+
## Utilities
|
|
97
|
+
|
|
98
|
+
The package also exports cursor and viewport utilities for advanced usage:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import {
|
|
102
|
+
cursorToMonacoPosition, // Convert vimee CursorPosition (0-based) → Monaco IPosition (1-based)
|
|
103
|
+
monacoPositionToCursor, // Convert Monaco IPosition (1-based) → vimee CursorPosition (0-based)
|
|
104
|
+
getTopLine, // Get first visible line (0-based)
|
|
105
|
+
getVisibleLines, // Get visible line count
|
|
106
|
+
revealLine, // Scroll to a specific line (0-based)
|
|
107
|
+
} from "@vimee/plugin-monaco";
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
attach: () => attach,
|
|
24
|
+
cursorToMonacoPosition: () => cursorToMonacoPosition,
|
|
25
|
+
getTopLine: () => getTopLine,
|
|
26
|
+
getVisibleLines: () => getVisibleLines,
|
|
27
|
+
monacoPositionToCursor: () => monacoPositionToCursor,
|
|
28
|
+
revealLine: () => revealLine
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
|
|
32
|
+
// src/attach.ts
|
|
33
|
+
var import_core = require("@vimee/core");
|
|
34
|
+
|
|
35
|
+
// src/cursor.ts
|
|
36
|
+
function cursorToMonacoPosition(cursor) {
|
|
37
|
+
return {
|
|
38
|
+
lineNumber: cursor.line + 1,
|
|
39
|
+
column: cursor.col + 1
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function monacoPositionToCursor(position) {
|
|
43
|
+
return {
|
|
44
|
+
line: position.lineNumber - 1,
|
|
45
|
+
col: position.column - 1
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/viewport.ts
|
|
50
|
+
function getTopLine(editor) {
|
|
51
|
+
const ranges = editor.getVisibleRanges();
|
|
52
|
+
if (ranges.length === 0) return 0;
|
|
53
|
+
return ranges[0].startLineNumber - 1;
|
|
54
|
+
}
|
|
55
|
+
function getVisibleLines(editor) {
|
|
56
|
+
const ranges = editor.getVisibleRanges();
|
|
57
|
+
if (ranges.length === 0) return 20;
|
|
58
|
+
const first = ranges[0];
|
|
59
|
+
const last = ranges[ranges.length - 1];
|
|
60
|
+
return last.endLineNumber - first.startLineNumber + 1;
|
|
61
|
+
}
|
|
62
|
+
function revealLine(editor, line) {
|
|
63
|
+
editor.revealLine(line + 1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/attach.ts
|
|
67
|
+
function attach(editor, options = {}) {
|
|
68
|
+
const {
|
|
69
|
+
readOnly = false,
|
|
70
|
+
onChange,
|
|
71
|
+
onModeChange,
|
|
72
|
+
onYank,
|
|
73
|
+
onSave,
|
|
74
|
+
onAction,
|
|
75
|
+
indentStyle,
|
|
76
|
+
indentWidth
|
|
77
|
+
} = options;
|
|
78
|
+
const buffer = new import_core.TextBuffer(editor.getValue());
|
|
79
|
+
const pos = editor.getPosition();
|
|
80
|
+
const initialCursor = pos ? monacoPositionToCursor(pos) : { line: 0, col: 0 };
|
|
81
|
+
let ctx = (0, import_core.createInitialContext)(initialCursor, {
|
|
82
|
+
indentStyle,
|
|
83
|
+
indentWidth
|
|
84
|
+
});
|
|
85
|
+
updateCursorStyle(ctx.mode);
|
|
86
|
+
let isComposing = false;
|
|
87
|
+
const decorationCollection = editor.createDecorationsCollection();
|
|
88
|
+
function updateCursorStyle(mode) {
|
|
89
|
+
const cursorStyle = mode === "insert" ? 1 : 2;
|
|
90
|
+
editor.updateOptions({ cursorStyle });
|
|
91
|
+
}
|
|
92
|
+
function syncContentToEditor() {
|
|
93
|
+
const content = buffer.getContent();
|
|
94
|
+
if (content !== editor.getValue()) {
|
|
95
|
+
editor.setValue(content);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function syncCursorToEditor(cursor) {
|
|
99
|
+
editor.setPosition(cursorToMonacoPosition(cursor));
|
|
100
|
+
}
|
|
101
|
+
function updateVisualDecorations() {
|
|
102
|
+
if (ctx.mode !== "visual" && ctx.mode !== "visual-line" && ctx.mode !== "visual-block") {
|
|
103
|
+
decorationCollection.clear();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const anchor = ctx.visualAnchor ?? ctx.cursor;
|
|
107
|
+
const before = anchor.line < ctx.cursor.line || anchor.line === ctx.cursor.line && anchor.col <= ctx.cursor.col;
|
|
108
|
+
const start = before ? anchor : ctx.cursor;
|
|
109
|
+
const end = before ? ctx.cursor : anchor;
|
|
110
|
+
if (ctx.mode === "visual-line") {
|
|
111
|
+
decorationCollection.set([
|
|
112
|
+
{
|
|
113
|
+
range: {
|
|
114
|
+
startLineNumber: start.line + 1,
|
|
115
|
+
startColumn: 1,
|
|
116
|
+
endLineNumber: end.line + 1,
|
|
117
|
+
endColumn: (buffer.getLineLength(end.line) || 0) + 2
|
|
118
|
+
},
|
|
119
|
+
options: { className: "vimee-visual-selection", isWholeLine: true }
|
|
120
|
+
}
|
|
121
|
+
]);
|
|
122
|
+
} else {
|
|
123
|
+
decorationCollection.set([
|
|
124
|
+
{
|
|
125
|
+
range: {
|
|
126
|
+
startLineNumber: start.line + 1,
|
|
127
|
+
startColumn: start.col + 1,
|
|
128
|
+
endLineNumber: end.line + 1,
|
|
129
|
+
endColumn: end.col + 2
|
|
130
|
+
// +2: Monaco end is exclusive & col is 0-based
|
|
131
|
+
},
|
|
132
|
+
options: { className: "vimee-visual-selection" }
|
|
133
|
+
}
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function processActions(actions, newCtx, key) {
|
|
138
|
+
for (const action of actions) {
|
|
139
|
+
onAction?.(action, key);
|
|
140
|
+
switch (action.type) {
|
|
141
|
+
case "content-change":
|
|
142
|
+
syncContentToEditor();
|
|
143
|
+
onChange?.(action.content);
|
|
144
|
+
break;
|
|
145
|
+
case "mode-change":
|
|
146
|
+
updateCursorStyle(action.mode);
|
|
147
|
+
onModeChange?.(action.mode);
|
|
148
|
+
break;
|
|
149
|
+
case "yank":
|
|
150
|
+
onYank?.(action.text);
|
|
151
|
+
break;
|
|
152
|
+
case "save":
|
|
153
|
+
onSave?.(action.content);
|
|
154
|
+
break;
|
|
155
|
+
case "set-option":
|
|
156
|
+
case "status-message":
|
|
157
|
+
case "scroll":
|
|
158
|
+
case "noop":
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
syncCursorToEditor(newCtx.cursor);
|
|
163
|
+
revealLine(editor, newCtx.cursor.line);
|
|
164
|
+
updateVisualDecorations();
|
|
165
|
+
}
|
|
166
|
+
function syncViewport() {
|
|
167
|
+
const topLine = getTopLine(editor);
|
|
168
|
+
const height = getVisibleLines(editor);
|
|
169
|
+
ctx = {
|
|
170
|
+
...ctx,
|
|
171
|
+
viewportTopLine: topLine,
|
|
172
|
+
viewportHeight: height
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function shouldPreventDefault(e) {
|
|
176
|
+
const mode = ctx.mode;
|
|
177
|
+
if (mode !== "insert") {
|
|
178
|
+
if (e.ctrlKey && (e.key === "c" || e.key === "a")) return false;
|
|
179
|
+
if (e.key === "Shift" || e.key === "Control" || e.key === "Alt" || e.key === "Meta") {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
if (e.key === "Escape") return true;
|
|
185
|
+
if (e.key === "Tab") return true;
|
|
186
|
+
if (e.key === "Backspace") return true;
|
|
187
|
+
if (e.key === "Enter") return true;
|
|
188
|
+
if (e.ctrlKey) {
|
|
189
|
+
const ctrlKeys = ["r", "b", "f", "d", "u", "v", "w"];
|
|
190
|
+
if (ctrlKeys.includes(e.key)) return true;
|
|
191
|
+
}
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
function handleScroll(direction, amount) {
|
|
195
|
+
const visibleLines = getVisibleLines(editor);
|
|
196
|
+
const scrollLines = Math.max(1, Math.floor(visibleLines * amount));
|
|
197
|
+
const newLine = direction === "up" ? Math.max(0, ctx.cursor.line - scrollLines) : Math.min(buffer.getLineCount() - 1, ctx.cursor.line + scrollLines);
|
|
198
|
+
const maxCol = Math.max(0, (buffer.getLineLength(newLine) || 1) - 1);
|
|
199
|
+
const newCursor = {
|
|
200
|
+
line: newLine,
|
|
201
|
+
col: Math.min(ctx.cursor.col, maxCol)
|
|
202
|
+
};
|
|
203
|
+
ctx = { ...ctx, cursor: newCursor };
|
|
204
|
+
syncCursorToEditor(newCursor);
|
|
205
|
+
revealLine(editor, newLine);
|
|
206
|
+
}
|
|
207
|
+
function onKeyDown(e) {
|
|
208
|
+
if (isComposing) return;
|
|
209
|
+
const browserEvent = e.browserEvent;
|
|
210
|
+
syncViewport();
|
|
211
|
+
if (browserEvent.ctrlKey) {
|
|
212
|
+
const scrollKeys = {
|
|
213
|
+
b: { direction: "up", amount: 1 },
|
|
214
|
+
f: { direction: "down", amount: 1 },
|
|
215
|
+
u: { direction: "up", amount: 0.5 },
|
|
216
|
+
d: { direction: "down", amount: 0.5 }
|
|
217
|
+
};
|
|
218
|
+
const scroll = scrollKeys[browserEvent.key];
|
|
219
|
+
if (scroll) {
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
e.stopPropagation();
|
|
222
|
+
handleScroll(scroll.direction, scroll.amount);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (shouldPreventDefault(browserEvent)) {
|
|
227
|
+
e.preventDefault();
|
|
228
|
+
e.stopPropagation();
|
|
229
|
+
}
|
|
230
|
+
const { newCtx, actions } = (0, import_core.processKeystroke)(
|
|
231
|
+
browserEvent.key,
|
|
232
|
+
ctx,
|
|
233
|
+
buffer,
|
|
234
|
+
browserEvent.ctrlKey,
|
|
235
|
+
readOnly
|
|
236
|
+
);
|
|
237
|
+
ctx = newCtx;
|
|
238
|
+
processActions(actions, newCtx, browserEvent.key);
|
|
239
|
+
}
|
|
240
|
+
const disposables = [
|
|
241
|
+
editor.onKeyDown(onKeyDown),
|
|
242
|
+
editor.onDidCompositionStart(() => {
|
|
243
|
+
isComposing = true;
|
|
244
|
+
}),
|
|
245
|
+
editor.onDidCompositionEnd(() => {
|
|
246
|
+
isComposing = false;
|
|
247
|
+
})
|
|
248
|
+
];
|
|
249
|
+
syncViewport();
|
|
250
|
+
return {
|
|
251
|
+
getMode: () => ctx.mode,
|
|
252
|
+
getCursor: () => ({ ...ctx.cursor }),
|
|
253
|
+
getContent: () => buffer.getContent(),
|
|
254
|
+
destroy: () => {
|
|
255
|
+
for (const d of disposables) {
|
|
256
|
+
d.dispose();
|
|
257
|
+
}
|
|
258
|
+
decorationCollection.clear();
|
|
259
|
+
editor.updateOptions({ cursorStyle: 1 });
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
264
|
+
0 && (module.exports = {
|
|
265
|
+
attach,
|
|
266
|
+
cursorToMonacoPosition,
|
|
267
|
+
getTopLine,
|
|
268
|
+
getVisibleLines,
|
|
269
|
+
monacoPositionToCursor,
|
|
270
|
+
revealLine
|
|
271
|
+
});
|
|
272
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/attach.ts","../src/cursor.ts","../src/viewport.ts"],"sourcesContent":["/**\n * @vimee/plugin-monaco\n *\n * Attach vim editing to any Monaco Editor instance.\n * Framework-agnostic — works with vanilla JS, React, or any Monaco wrapper.\n *\n * @example\n * ```ts\n * import { attach } from '@vimee/plugin-monaco'\n *\n * const vim = attach(monacoEditor, {\n * onChange: (value) => console.log(value),\n * onModeChange: (mode) => console.log(mode),\n * })\n *\n * vim.destroy()\n * ```\n */\n\n// Main function\nexport { attach } from \"./attach\";\n\n// Types\nexport type { AttachOptions, VimMonaco, MonacoEditor } from \"./types\";\nexport type {\n IDisposable,\n IPosition,\n IRange,\n IKeyboardEvent,\n ITextModel,\n IModelDeltaDecoration,\n IModelDecorationOptions,\n IEditorDecorationsCollection,\n} from \"./types\";\n\n// Cursor utilities (for advanced usage)\nexport { cursorToMonacoPosition, monacoPositionToCursor } from \"./cursor\";\n\n// Viewport utilities (for advanced usage)\nexport { getTopLine, getVisibleLines, revealLine } from \"./viewport\";\n\n// Re-export commonly used core types for convenience\nexport type { VimMode, VimAction, CursorPosition } from \"@vimee/core\";\n","/**\n * attach.ts\n *\n * Core implementation of the plugin-monaco package.\n * Attaches vim keybindings to an existing Monaco Editor instance.\n */\n\nimport type { VimContext, VimAction, CursorPosition } from \"@vimee/core\";\nimport { TextBuffer, createInitialContext, processKeystroke } from \"@vimee/core\";\nimport type { AttachOptions, VimMonaco, MonacoEditor, IKeyboardEvent } from \"./types\";\nimport { cursorToMonacoPosition, monacoPositionToCursor } from \"./cursor\";\nimport { getTopLine, getVisibleLines, revealLine } from \"./viewport\";\n\n/**\n * Attach vim editing to a Monaco Editor instance.\n *\n * @example\n * ```ts\n * const vim = attach(monacoEditor, {\n * onChange: (value) => console.log(value),\n * onModeChange: (mode) => console.log(mode),\n * });\n *\n * // Later...\n * vim.destroy();\n * ```\n */\nexport function attach(editor: MonacoEditor, options: AttachOptions = {}): VimMonaco {\n const {\n readOnly = false,\n onChange,\n onModeChange,\n onYank,\n onSave,\n onAction,\n indentStyle,\n indentWidth,\n } = options;\n\n // --- Initialize vim engine ---\n const buffer = new TextBuffer(editor.getValue());\n const pos = editor.getPosition();\n const initialCursor: CursorPosition = pos ? monacoPositionToCursor(pos) : { line: 0, col: 0 };\n let ctx: VimContext = createInitialContext(initialCursor, {\n indentStyle,\n indentWidth,\n });\n\n // --- Set initial cursor style (block for normal mode) ---\n updateCursorStyle(ctx.mode);\n\n // --- Track composing state (IME input) ---\n let isComposing = false;\n\n // --- Visual mode decorations ---\n const decorationCollection = editor.createDecorationsCollection();\n\n // --- Update Monaco cursor style based on vim mode ---\n function updateCursorStyle(mode: string): void {\n // Monaco CursorStyle: Line = 1, Block = 2, Underline = 3\n const cursorStyle = mode === \"insert\" ? 1 : 2;\n editor.updateOptions({ cursorStyle });\n }\n\n // --- Sync vimee buffer content to Monaco editor ---\n function syncContentToEditor(): void {\n const content = buffer.getContent();\n if (content !== editor.getValue()) {\n editor.setValue(content);\n }\n }\n\n // --- Sync cursor position to Monaco editor ---\n function syncCursorToEditor(cursor: CursorPosition): void {\n editor.setPosition(cursorToMonacoPosition(cursor));\n }\n\n // --- Update visual mode selection decorations ---\n function updateVisualDecorations(): void {\n if (ctx.mode !== \"visual\" && ctx.mode !== \"visual-line\" && ctx.mode !== \"visual-block\") {\n decorationCollection.clear();\n return;\n }\n\n const anchor = ctx.visualAnchor ?? ctx.cursor;\n const before =\n anchor.line < ctx.cursor.line ||\n (anchor.line === ctx.cursor.line && anchor.col <= ctx.cursor.col);\n const start = before ? anchor : ctx.cursor;\n const end = before ? ctx.cursor : anchor;\n\n if (ctx.mode === \"visual-line\") {\n decorationCollection.set([\n {\n range: {\n startLineNumber: start.line + 1,\n startColumn: 1,\n endLineNumber: end.line + 1,\n endColumn: (buffer.getLineLength(end.line) || 0) + 2,\n },\n options: { className: \"vimee-visual-selection\", isWholeLine: true },\n },\n ]);\n } else {\n decorationCollection.set([\n {\n range: {\n startLineNumber: start.line + 1,\n startColumn: start.col + 1,\n endLineNumber: end.line + 1,\n endColumn: end.col + 2, // +2: Monaco end is exclusive & col is 0-based\n },\n options: { className: \"vimee-visual-selection\" },\n },\n ]);\n }\n }\n\n // --- Process vim actions and sync state ---\n function processActions(actions: VimAction[], newCtx: VimContext, key: string): void {\n for (const action of actions) {\n onAction?.(action, key);\n\n switch (action.type) {\n case \"content-change\":\n syncContentToEditor();\n onChange?.(action.content);\n break;\n\n case \"mode-change\":\n updateCursorStyle(action.mode);\n onModeChange?.(action.mode);\n break;\n\n case \"yank\":\n onYank?.(action.text);\n break;\n\n case \"save\":\n onSave?.(action.content);\n break;\n\n case \"set-option\":\n case \"status-message\":\n case \"scroll\":\n case \"noop\":\n break;\n }\n }\n\n // Always sync cursor and decorations from context\n syncCursorToEditor(newCtx.cursor);\n revealLine(editor, newCtx.cursor.line);\n updateVisualDecorations();\n }\n\n // --- Update viewport info on the VimContext ---\n function syncViewport(): void {\n const topLine = getTopLine(editor);\n const height = getVisibleLines(editor);\n ctx = {\n ...ctx,\n viewportTopLine: topLine,\n viewportHeight: height,\n };\n }\n\n // --- Determine if default behavior should be prevented ---\n function shouldPreventDefault(e: KeyboardEvent): boolean {\n const mode = ctx.mode;\n\n // In normal/visual/command-line mode, prevent all printable keys\n if (mode !== \"insert\") {\n // Allow modifier-only keys and browser shortcuts like Ctrl-C (copy)\n if (e.ctrlKey && (e.key === \"c\" || e.key === \"a\")) return false;\n if (e.key === \"Shift\" || e.key === \"Control\" || e.key === \"Alt\" || e.key === \"Meta\") {\n return false;\n }\n return true;\n }\n\n // In insert mode, prevent special keys handled by vim engine\n if (e.key === \"Escape\") return true;\n if (e.key === \"Tab\") return true;\n if (e.key === \"Backspace\") return true;\n if (e.key === \"Enter\") return true;\n if (e.ctrlKey) {\n const ctrlKeys = [\"r\", \"b\", \"f\", \"d\", \"u\", \"v\", \"w\"];\n if (ctrlKeys.includes(e.key)) return true;\n }\n\n return true; // Prevent all — vim engine handles insert mode character input\n }\n\n // --- Scroll handler for Ctrl-U/D/B/F ---\n function handleScroll(direction: \"up\" | \"down\", amount: number): void {\n const visibleLines = getVisibleLines(editor);\n const scrollLines = Math.max(1, Math.floor(visibleLines * amount));\n const newLine =\n direction === \"up\"\n ? Math.max(0, ctx.cursor.line - scrollLines)\n : Math.min(buffer.getLineCount() - 1, ctx.cursor.line + scrollLines);\n\n const maxCol = Math.max(0, (buffer.getLineLength(newLine) || 1) - 1);\n const newCursor: CursorPosition = {\n line: newLine,\n col: Math.min(ctx.cursor.col, maxCol),\n };\n\n ctx = { ...ctx, cursor: newCursor };\n syncCursorToEditor(newCursor);\n revealLine(editor, newLine);\n }\n\n // --- Keyboard handler ---\n function onKeyDown(e: IKeyboardEvent): void {\n if (isComposing) return;\n\n const browserEvent = e.browserEvent;\n\n // Update viewport before processing (for H/M/L motions)\n syncViewport();\n\n // Handle Ctrl scroll keys at this level\n if (browserEvent.ctrlKey) {\n const scrollKeys: Record<string, { direction: \"up\" | \"down\"; amount: number }> = {\n b: { direction: \"up\", amount: 1.0 },\n f: { direction: \"down\", amount: 1.0 },\n u: { direction: \"up\", amount: 0.5 },\n d: { direction: \"down\", amount: 0.5 },\n };\n const scroll = scrollKeys[browserEvent.key];\n if (scroll) {\n e.preventDefault();\n e.stopPropagation();\n handleScroll(scroll.direction, scroll.amount);\n return;\n }\n }\n\n if (shouldPreventDefault(browserEvent)) {\n e.preventDefault();\n e.stopPropagation();\n }\n\n const { newCtx, actions } = processKeystroke(\n browserEvent.key,\n ctx,\n buffer,\n browserEvent.ctrlKey,\n readOnly,\n );\n\n ctx = newCtx;\n processActions(actions, newCtx, browserEvent.key);\n }\n\n // --- Attach event listeners ---\n const disposables = [\n editor.onKeyDown(onKeyDown),\n editor.onDidCompositionStart(() => {\n isComposing = true;\n }),\n editor.onDidCompositionEnd(() => {\n isComposing = false;\n }),\n ];\n\n // Initial viewport sync\n syncViewport();\n\n // --- Return the VimMonaco handle ---\n return {\n getMode: () => ctx.mode,\n getCursor: () => ({ ...ctx.cursor }),\n getContent: () => buffer.getContent(),\n destroy: () => {\n for (const d of disposables) {\n d.dispose();\n }\n decorationCollection.clear();\n // Reset cursor style to line (default)\n editor.updateOptions({ cursorStyle: 1 });\n },\n };\n}\n","/**\n * cursor.ts\n *\n * Utilities for converting between vimee's 0-based CursorPosition\n * and Monaco's 1-based IPosition.\n */\n\nimport type { CursorPosition } from \"@vimee/core\";\nimport type { IPosition } from \"./types\";\n\n/**\n * Convert a vimee 0-based CursorPosition to a Monaco 1-based IPosition.\n */\nexport function cursorToMonacoPosition(cursor: CursorPosition): IPosition {\n return {\n lineNumber: cursor.line + 1,\n column: cursor.col + 1,\n };\n}\n\n/**\n * Convert a Monaco 1-based IPosition to a vimee 0-based CursorPosition.\n */\nexport function monacoPositionToCursor(position: IPosition): CursorPosition {\n return {\n line: position.lineNumber - 1,\n col: position.column - 1,\n };\n}\n","/**\n * viewport.ts\n *\n * Utilities for computing viewport information from a Monaco Editor.\n * Handles viewport tracking for H/M/L motions and\n * Ctrl-U/D/B/F page scrolling.\n */\n\nimport type { MonacoEditor } from \"./types\";\n\n/**\n * Get the first visible line (0-based) from the Monaco editor.\n */\nexport function getTopLine(editor: MonacoEditor): number {\n const ranges = editor.getVisibleRanges();\n if (ranges.length === 0) return 0;\n return ranges[0].startLineNumber - 1; // Convert to 0-based\n}\n\n/**\n * Get the number of visible lines in the Monaco editor viewport.\n */\nexport function getVisibleLines(editor: MonacoEditor): number {\n const ranges = editor.getVisibleRanges();\n if (ranges.length === 0) return 20; // Fallback\n const first = ranges[0];\n const last = ranges[ranges.length - 1];\n return last.endLineNumber - first.startLineNumber + 1;\n}\n\n/**\n * Scroll the Monaco editor to reveal a specific line (0-based).\n */\nexport function revealLine(editor: MonacoEditor, line: number): void {\n editor.revealLine(line + 1); // Convert to 1-based\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { VimMode, VimAction, CursorPosition } from '@vimee/core';
|
|
2
|
+
export { CursorPosition, VimAction, VimMode } from '@vimee/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* types.ts
|
|
6
|
+
*
|
|
7
|
+
* Public types for @vimee/plugin-monaco.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Disposable handle returned by Monaco event subscriptions. */
|
|
11
|
+
interface IDisposable {
|
|
12
|
+
dispose(): void;
|
|
13
|
+
}
|
|
14
|
+
/** 1-based position in Monaco (lineNumber ≥ 1, column ≥ 1). */
|
|
15
|
+
interface IPosition {
|
|
16
|
+
readonly lineNumber: number;
|
|
17
|
+
readonly column: number;
|
|
18
|
+
}
|
|
19
|
+
/** 1-based range in Monaco. */
|
|
20
|
+
interface IRange {
|
|
21
|
+
readonly startLineNumber: number;
|
|
22
|
+
readonly startColumn: number;
|
|
23
|
+
readonly endLineNumber: number;
|
|
24
|
+
readonly endColumn: number;
|
|
25
|
+
}
|
|
26
|
+
/** Decoration options applied to a range. */
|
|
27
|
+
interface IModelDecorationOptions {
|
|
28
|
+
className?: string | null;
|
|
29
|
+
isWholeLine?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** A single decoration descriptor. */
|
|
32
|
+
interface IModelDeltaDecoration {
|
|
33
|
+
range: IRange;
|
|
34
|
+
options: IModelDecorationOptions;
|
|
35
|
+
}
|
|
36
|
+
/** Monaco keyboard event wrapper. */
|
|
37
|
+
interface IKeyboardEvent {
|
|
38
|
+
readonly browserEvent: KeyboardEvent;
|
|
39
|
+
preventDefault(): void;
|
|
40
|
+
stopPropagation(): void;
|
|
41
|
+
}
|
|
42
|
+
/** Minimal subset of Monaco's ITextModel. */
|
|
43
|
+
interface ITextModel {
|
|
44
|
+
getValue(): string;
|
|
45
|
+
setValue(value: string): void;
|
|
46
|
+
getLineContent(lineNumber: number): string;
|
|
47
|
+
getLineCount(): number;
|
|
48
|
+
getLineMaxColumn(lineNumber: number): number;
|
|
49
|
+
}
|
|
50
|
+
/** Decoration collection (Monaco ≥ 0.34). */
|
|
51
|
+
interface IEditorDecorationsCollection {
|
|
52
|
+
set(decorations: IModelDeltaDecoration[]): void;
|
|
53
|
+
clear(): void;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Minimal subset of `monaco.editor.IStandaloneCodeEditor`.
|
|
57
|
+
*
|
|
58
|
+
* Any object satisfying this interface can be passed to `attach()`.
|
|
59
|
+
*/
|
|
60
|
+
interface MonacoEditor {
|
|
61
|
+
getValue(): string;
|
|
62
|
+
setValue(value: string): void;
|
|
63
|
+
getPosition(): IPosition | null;
|
|
64
|
+
setPosition(position: IPosition): void;
|
|
65
|
+
getModel(): ITextModel | null;
|
|
66
|
+
onKeyDown(listener: (e: IKeyboardEvent) => void): IDisposable;
|
|
67
|
+
onDidCompositionStart(listener: () => void): IDisposable;
|
|
68
|
+
onDidCompositionEnd(listener: () => void): IDisposable;
|
|
69
|
+
createDecorationsCollection(decorations?: IModelDeltaDecoration[]): IEditorDecorationsCollection;
|
|
70
|
+
getVisibleRanges(): IRange[];
|
|
71
|
+
revealLine(lineNumber: number): void;
|
|
72
|
+
updateOptions(options: Record<string, unknown>): void;
|
|
73
|
+
focus(): void;
|
|
74
|
+
}
|
|
75
|
+
/** Options passed to `attach()`. */
|
|
76
|
+
interface AttachOptions {
|
|
77
|
+
/** Read-only mode — vim motions work but no edits. */
|
|
78
|
+
readOnly?: boolean;
|
|
79
|
+
/** Called whenever the editor content changes via vim commands. */
|
|
80
|
+
onChange?: (value: string) => void;
|
|
81
|
+
/** Called whenever the vim mode changes. */
|
|
82
|
+
onModeChange?: (mode: VimMode) => void;
|
|
83
|
+
/** Called when text is yanked. */
|
|
84
|
+
onYank?: (text: string) => void;
|
|
85
|
+
/** Called when `:w` is executed. */
|
|
86
|
+
onSave?: (value: string) => void;
|
|
87
|
+
/** Called for every vim action (low-level). */
|
|
88
|
+
onAction?: (action: VimAction, key: string) => void;
|
|
89
|
+
/** Indent style: "space" or "tab" (default: "space"). */
|
|
90
|
+
indentStyle?: "space" | "tab";
|
|
91
|
+
/** Indent width (default: 2). */
|
|
92
|
+
indentWidth?: number;
|
|
93
|
+
}
|
|
94
|
+
/** The object returned by `attach()` — the handle to the vim-enabled editor. */
|
|
95
|
+
interface VimMonaco {
|
|
96
|
+
/** Current vim mode. */
|
|
97
|
+
getMode(): VimMode;
|
|
98
|
+
/** Current cursor position (0-based line/col). */
|
|
99
|
+
getCursor(): CursorPosition;
|
|
100
|
+
/** Current editor content (from the vim buffer). */
|
|
101
|
+
getContent(): string;
|
|
102
|
+
/** Detach all event listeners and restore the editor to its original state. */
|
|
103
|
+
destroy(): void;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* attach.ts
|
|
108
|
+
*
|
|
109
|
+
* Core implementation of the plugin-monaco package.
|
|
110
|
+
* Attaches vim keybindings to an existing Monaco Editor instance.
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Attach vim editing to a Monaco Editor instance.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* const vim = attach(monacoEditor, {
|
|
119
|
+
* onChange: (value) => console.log(value),
|
|
120
|
+
* onModeChange: (mode) => console.log(mode),
|
|
121
|
+
* });
|
|
122
|
+
*
|
|
123
|
+
* // Later...
|
|
124
|
+
* vim.destroy();
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
declare function attach(editor: MonacoEditor, options?: AttachOptions): VimMonaco;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* cursor.ts
|
|
131
|
+
*
|
|
132
|
+
* Utilities for converting between vimee's 0-based CursorPosition
|
|
133
|
+
* and Monaco's 1-based IPosition.
|
|
134
|
+
*/
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Convert a vimee 0-based CursorPosition to a Monaco 1-based IPosition.
|
|
138
|
+
*/
|
|
139
|
+
declare function cursorToMonacoPosition(cursor: CursorPosition): IPosition;
|
|
140
|
+
/**
|
|
141
|
+
* Convert a Monaco 1-based IPosition to a vimee 0-based CursorPosition.
|
|
142
|
+
*/
|
|
143
|
+
declare function monacoPositionToCursor(position: IPosition): CursorPosition;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* viewport.ts
|
|
147
|
+
*
|
|
148
|
+
* Utilities for computing viewport information from a Monaco Editor.
|
|
149
|
+
* Handles viewport tracking for H/M/L motions and
|
|
150
|
+
* Ctrl-U/D/B/F page scrolling.
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get the first visible line (0-based) from the Monaco editor.
|
|
155
|
+
*/
|
|
156
|
+
declare function getTopLine(editor: MonacoEditor): number;
|
|
157
|
+
/**
|
|
158
|
+
* Get the number of visible lines in the Monaco editor viewport.
|
|
159
|
+
*/
|
|
160
|
+
declare function getVisibleLines(editor: MonacoEditor): number;
|
|
161
|
+
/**
|
|
162
|
+
* Scroll the Monaco editor to reveal a specific line (0-based).
|
|
163
|
+
*/
|
|
164
|
+
declare function revealLine(editor: MonacoEditor, line: number): void;
|
|
165
|
+
|
|
166
|
+
export { type AttachOptions, type IDisposable, type IEditorDecorationsCollection, type IKeyboardEvent, type IModelDecorationOptions, type IModelDeltaDecoration, type IPosition, type IRange, type ITextModel, type MonacoEditor, type VimMonaco, attach, cursorToMonacoPosition, getTopLine, getVisibleLines, monacoPositionToCursor, revealLine };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { VimMode, VimAction, CursorPosition } from '@vimee/core';
|
|
2
|
+
export { CursorPosition, VimAction, VimMode } from '@vimee/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* types.ts
|
|
6
|
+
*
|
|
7
|
+
* Public types for @vimee/plugin-monaco.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Disposable handle returned by Monaco event subscriptions. */
|
|
11
|
+
interface IDisposable {
|
|
12
|
+
dispose(): void;
|
|
13
|
+
}
|
|
14
|
+
/** 1-based position in Monaco (lineNumber ≥ 1, column ≥ 1). */
|
|
15
|
+
interface IPosition {
|
|
16
|
+
readonly lineNumber: number;
|
|
17
|
+
readonly column: number;
|
|
18
|
+
}
|
|
19
|
+
/** 1-based range in Monaco. */
|
|
20
|
+
interface IRange {
|
|
21
|
+
readonly startLineNumber: number;
|
|
22
|
+
readonly startColumn: number;
|
|
23
|
+
readonly endLineNumber: number;
|
|
24
|
+
readonly endColumn: number;
|
|
25
|
+
}
|
|
26
|
+
/** Decoration options applied to a range. */
|
|
27
|
+
interface IModelDecorationOptions {
|
|
28
|
+
className?: string | null;
|
|
29
|
+
isWholeLine?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** A single decoration descriptor. */
|
|
32
|
+
interface IModelDeltaDecoration {
|
|
33
|
+
range: IRange;
|
|
34
|
+
options: IModelDecorationOptions;
|
|
35
|
+
}
|
|
36
|
+
/** Monaco keyboard event wrapper. */
|
|
37
|
+
interface IKeyboardEvent {
|
|
38
|
+
readonly browserEvent: KeyboardEvent;
|
|
39
|
+
preventDefault(): void;
|
|
40
|
+
stopPropagation(): void;
|
|
41
|
+
}
|
|
42
|
+
/** Minimal subset of Monaco's ITextModel. */
|
|
43
|
+
interface ITextModel {
|
|
44
|
+
getValue(): string;
|
|
45
|
+
setValue(value: string): void;
|
|
46
|
+
getLineContent(lineNumber: number): string;
|
|
47
|
+
getLineCount(): number;
|
|
48
|
+
getLineMaxColumn(lineNumber: number): number;
|
|
49
|
+
}
|
|
50
|
+
/** Decoration collection (Monaco ≥ 0.34). */
|
|
51
|
+
interface IEditorDecorationsCollection {
|
|
52
|
+
set(decorations: IModelDeltaDecoration[]): void;
|
|
53
|
+
clear(): void;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Minimal subset of `monaco.editor.IStandaloneCodeEditor`.
|
|
57
|
+
*
|
|
58
|
+
* Any object satisfying this interface can be passed to `attach()`.
|
|
59
|
+
*/
|
|
60
|
+
interface MonacoEditor {
|
|
61
|
+
getValue(): string;
|
|
62
|
+
setValue(value: string): void;
|
|
63
|
+
getPosition(): IPosition | null;
|
|
64
|
+
setPosition(position: IPosition): void;
|
|
65
|
+
getModel(): ITextModel | null;
|
|
66
|
+
onKeyDown(listener: (e: IKeyboardEvent) => void): IDisposable;
|
|
67
|
+
onDidCompositionStart(listener: () => void): IDisposable;
|
|
68
|
+
onDidCompositionEnd(listener: () => void): IDisposable;
|
|
69
|
+
createDecorationsCollection(decorations?: IModelDeltaDecoration[]): IEditorDecorationsCollection;
|
|
70
|
+
getVisibleRanges(): IRange[];
|
|
71
|
+
revealLine(lineNumber: number): void;
|
|
72
|
+
updateOptions(options: Record<string, unknown>): void;
|
|
73
|
+
focus(): void;
|
|
74
|
+
}
|
|
75
|
+
/** Options passed to `attach()`. */
|
|
76
|
+
interface AttachOptions {
|
|
77
|
+
/** Read-only mode — vim motions work but no edits. */
|
|
78
|
+
readOnly?: boolean;
|
|
79
|
+
/** Called whenever the editor content changes via vim commands. */
|
|
80
|
+
onChange?: (value: string) => void;
|
|
81
|
+
/** Called whenever the vim mode changes. */
|
|
82
|
+
onModeChange?: (mode: VimMode) => void;
|
|
83
|
+
/** Called when text is yanked. */
|
|
84
|
+
onYank?: (text: string) => void;
|
|
85
|
+
/** Called when `:w` is executed. */
|
|
86
|
+
onSave?: (value: string) => void;
|
|
87
|
+
/** Called for every vim action (low-level). */
|
|
88
|
+
onAction?: (action: VimAction, key: string) => void;
|
|
89
|
+
/** Indent style: "space" or "tab" (default: "space"). */
|
|
90
|
+
indentStyle?: "space" | "tab";
|
|
91
|
+
/** Indent width (default: 2). */
|
|
92
|
+
indentWidth?: number;
|
|
93
|
+
}
|
|
94
|
+
/** The object returned by `attach()` — the handle to the vim-enabled editor. */
|
|
95
|
+
interface VimMonaco {
|
|
96
|
+
/** Current vim mode. */
|
|
97
|
+
getMode(): VimMode;
|
|
98
|
+
/** Current cursor position (0-based line/col). */
|
|
99
|
+
getCursor(): CursorPosition;
|
|
100
|
+
/** Current editor content (from the vim buffer). */
|
|
101
|
+
getContent(): string;
|
|
102
|
+
/** Detach all event listeners and restore the editor to its original state. */
|
|
103
|
+
destroy(): void;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* attach.ts
|
|
108
|
+
*
|
|
109
|
+
* Core implementation of the plugin-monaco package.
|
|
110
|
+
* Attaches vim keybindings to an existing Monaco Editor instance.
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Attach vim editing to a Monaco Editor instance.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* const vim = attach(monacoEditor, {
|
|
119
|
+
* onChange: (value) => console.log(value),
|
|
120
|
+
* onModeChange: (mode) => console.log(mode),
|
|
121
|
+
* });
|
|
122
|
+
*
|
|
123
|
+
* // Later...
|
|
124
|
+
* vim.destroy();
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
declare function attach(editor: MonacoEditor, options?: AttachOptions): VimMonaco;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* cursor.ts
|
|
131
|
+
*
|
|
132
|
+
* Utilities for converting between vimee's 0-based CursorPosition
|
|
133
|
+
* and Monaco's 1-based IPosition.
|
|
134
|
+
*/
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Convert a vimee 0-based CursorPosition to a Monaco 1-based IPosition.
|
|
138
|
+
*/
|
|
139
|
+
declare function cursorToMonacoPosition(cursor: CursorPosition): IPosition;
|
|
140
|
+
/**
|
|
141
|
+
* Convert a Monaco 1-based IPosition to a vimee 0-based CursorPosition.
|
|
142
|
+
*/
|
|
143
|
+
declare function monacoPositionToCursor(position: IPosition): CursorPosition;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* viewport.ts
|
|
147
|
+
*
|
|
148
|
+
* Utilities for computing viewport information from a Monaco Editor.
|
|
149
|
+
* Handles viewport tracking for H/M/L motions and
|
|
150
|
+
* Ctrl-U/D/B/F page scrolling.
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get the first visible line (0-based) from the Monaco editor.
|
|
155
|
+
*/
|
|
156
|
+
declare function getTopLine(editor: MonacoEditor): number;
|
|
157
|
+
/**
|
|
158
|
+
* Get the number of visible lines in the Monaco editor viewport.
|
|
159
|
+
*/
|
|
160
|
+
declare function getVisibleLines(editor: MonacoEditor): number;
|
|
161
|
+
/**
|
|
162
|
+
* Scroll the Monaco editor to reveal a specific line (0-based).
|
|
163
|
+
*/
|
|
164
|
+
declare function revealLine(editor: MonacoEditor, line: number): void;
|
|
165
|
+
|
|
166
|
+
export { type AttachOptions, type IDisposable, type IEditorDecorationsCollection, type IKeyboardEvent, type IModelDecorationOptions, type IModelDeltaDecoration, type IPosition, type IRange, type ITextModel, type MonacoEditor, type VimMonaco, attach, cursorToMonacoPosition, getTopLine, getVisibleLines, monacoPositionToCursor, revealLine };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// src/attach.ts
|
|
2
|
+
import { TextBuffer, createInitialContext, processKeystroke } from "@vimee/core";
|
|
3
|
+
|
|
4
|
+
// src/cursor.ts
|
|
5
|
+
function cursorToMonacoPosition(cursor) {
|
|
6
|
+
return {
|
|
7
|
+
lineNumber: cursor.line + 1,
|
|
8
|
+
column: cursor.col + 1
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function monacoPositionToCursor(position) {
|
|
12
|
+
return {
|
|
13
|
+
line: position.lineNumber - 1,
|
|
14
|
+
col: position.column - 1
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/viewport.ts
|
|
19
|
+
function getTopLine(editor) {
|
|
20
|
+
const ranges = editor.getVisibleRanges();
|
|
21
|
+
if (ranges.length === 0) return 0;
|
|
22
|
+
return ranges[0].startLineNumber - 1;
|
|
23
|
+
}
|
|
24
|
+
function getVisibleLines(editor) {
|
|
25
|
+
const ranges = editor.getVisibleRanges();
|
|
26
|
+
if (ranges.length === 0) return 20;
|
|
27
|
+
const first = ranges[0];
|
|
28
|
+
const last = ranges[ranges.length - 1];
|
|
29
|
+
return last.endLineNumber - first.startLineNumber + 1;
|
|
30
|
+
}
|
|
31
|
+
function revealLine(editor, line) {
|
|
32
|
+
editor.revealLine(line + 1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/attach.ts
|
|
36
|
+
function attach(editor, options = {}) {
|
|
37
|
+
const {
|
|
38
|
+
readOnly = false,
|
|
39
|
+
onChange,
|
|
40
|
+
onModeChange,
|
|
41
|
+
onYank,
|
|
42
|
+
onSave,
|
|
43
|
+
onAction,
|
|
44
|
+
indentStyle,
|
|
45
|
+
indentWidth
|
|
46
|
+
} = options;
|
|
47
|
+
const buffer = new TextBuffer(editor.getValue());
|
|
48
|
+
const pos = editor.getPosition();
|
|
49
|
+
const initialCursor = pos ? monacoPositionToCursor(pos) : { line: 0, col: 0 };
|
|
50
|
+
let ctx = createInitialContext(initialCursor, {
|
|
51
|
+
indentStyle,
|
|
52
|
+
indentWidth
|
|
53
|
+
});
|
|
54
|
+
updateCursorStyle(ctx.mode);
|
|
55
|
+
let isComposing = false;
|
|
56
|
+
const decorationCollection = editor.createDecorationsCollection();
|
|
57
|
+
function updateCursorStyle(mode) {
|
|
58
|
+
const cursorStyle = mode === "insert" ? 1 : 2;
|
|
59
|
+
editor.updateOptions({ cursorStyle });
|
|
60
|
+
}
|
|
61
|
+
function syncContentToEditor() {
|
|
62
|
+
const content = buffer.getContent();
|
|
63
|
+
if (content !== editor.getValue()) {
|
|
64
|
+
editor.setValue(content);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function syncCursorToEditor(cursor) {
|
|
68
|
+
editor.setPosition(cursorToMonacoPosition(cursor));
|
|
69
|
+
}
|
|
70
|
+
function updateVisualDecorations() {
|
|
71
|
+
if (ctx.mode !== "visual" && ctx.mode !== "visual-line" && ctx.mode !== "visual-block") {
|
|
72
|
+
decorationCollection.clear();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const anchor = ctx.visualAnchor ?? ctx.cursor;
|
|
76
|
+
const before = anchor.line < ctx.cursor.line || anchor.line === ctx.cursor.line && anchor.col <= ctx.cursor.col;
|
|
77
|
+
const start = before ? anchor : ctx.cursor;
|
|
78
|
+
const end = before ? ctx.cursor : anchor;
|
|
79
|
+
if (ctx.mode === "visual-line") {
|
|
80
|
+
decorationCollection.set([
|
|
81
|
+
{
|
|
82
|
+
range: {
|
|
83
|
+
startLineNumber: start.line + 1,
|
|
84
|
+
startColumn: 1,
|
|
85
|
+
endLineNumber: end.line + 1,
|
|
86
|
+
endColumn: (buffer.getLineLength(end.line) || 0) + 2
|
|
87
|
+
},
|
|
88
|
+
options: { className: "vimee-visual-selection", isWholeLine: true }
|
|
89
|
+
}
|
|
90
|
+
]);
|
|
91
|
+
} else {
|
|
92
|
+
decorationCollection.set([
|
|
93
|
+
{
|
|
94
|
+
range: {
|
|
95
|
+
startLineNumber: start.line + 1,
|
|
96
|
+
startColumn: start.col + 1,
|
|
97
|
+
endLineNumber: end.line + 1,
|
|
98
|
+
endColumn: end.col + 2
|
|
99
|
+
// +2: Monaco end is exclusive & col is 0-based
|
|
100
|
+
},
|
|
101
|
+
options: { className: "vimee-visual-selection" }
|
|
102
|
+
}
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function processActions(actions, newCtx, key) {
|
|
107
|
+
for (const action of actions) {
|
|
108
|
+
onAction?.(action, key);
|
|
109
|
+
switch (action.type) {
|
|
110
|
+
case "content-change":
|
|
111
|
+
syncContentToEditor();
|
|
112
|
+
onChange?.(action.content);
|
|
113
|
+
break;
|
|
114
|
+
case "mode-change":
|
|
115
|
+
updateCursorStyle(action.mode);
|
|
116
|
+
onModeChange?.(action.mode);
|
|
117
|
+
break;
|
|
118
|
+
case "yank":
|
|
119
|
+
onYank?.(action.text);
|
|
120
|
+
break;
|
|
121
|
+
case "save":
|
|
122
|
+
onSave?.(action.content);
|
|
123
|
+
break;
|
|
124
|
+
case "set-option":
|
|
125
|
+
case "status-message":
|
|
126
|
+
case "scroll":
|
|
127
|
+
case "noop":
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
syncCursorToEditor(newCtx.cursor);
|
|
132
|
+
revealLine(editor, newCtx.cursor.line);
|
|
133
|
+
updateVisualDecorations();
|
|
134
|
+
}
|
|
135
|
+
function syncViewport() {
|
|
136
|
+
const topLine = getTopLine(editor);
|
|
137
|
+
const height = getVisibleLines(editor);
|
|
138
|
+
ctx = {
|
|
139
|
+
...ctx,
|
|
140
|
+
viewportTopLine: topLine,
|
|
141
|
+
viewportHeight: height
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function shouldPreventDefault(e) {
|
|
145
|
+
const mode = ctx.mode;
|
|
146
|
+
if (mode !== "insert") {
|
|
147
|
+
if (e.ctrlKey && (e.key === "c" || e.key === "a")) return false;
|
|
148
|
+
if (e.key === "Shift" || e.key === "Control" || e.key === "Alt" || e.key === "Meta") {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
if (e.key === "Escape") return true;
|
|
154
|
+
if (e.key === "Tab") return true;
|
|
155
|
+
if (e.key === "Backspace") return true;
|
|
156
|
+
if (e.key === "Enter") return true;
|
|
157
|
+
if (e.ctrlKey) {
|
|
158
|
+
const ctrlKeys = ["r", "b", "f", "d", "u", "v", "w"];
|
|
159
|
+
if (ctrlKeys.includes(e.key)) return true;
|
|
160
|
+
}
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
function handleScroll(direction, amount) {
|
|
164
|
+
const visibleLines = getVisibleLines(editor);
|
|
165
|
+
const scrollLines = Math.max(1, Math.floor(visibleLines * amount));
|
|
166
|
+
const newLine = direction === "up" ? Math.max(0, ctx.cursor.line - scrollLines) : Math.min(buffer.getLineCount() - 1, ctx.cursor.line + scrollLines);
|
|
167
|
+
const maxCol = Math.max(0, (buffer.getLineLength(newLine) || 1) - 1);
|
|
168
|
+
const newCursor = {
|
|
169
|
+
line: newLine,
|
|
170
|
+
col: Math.min(ctx.cursor.col, maxCol)
|
|
171
|
+
};
|
|
172
|
+
ctx = { ...ctx, cursor: newCursor };
|
|
173
|
+
syncCursorToEditor(newCursor);
|
|
174
|
+
revealLine(editor, newLine);
|
|
175
|
+
}
|
|
176
|
+
function onKeyDown(e) {
|
|
177
|
+
if (isComposing) return;
|
|
178
|
+
const browserEvent = e.browserEvent;
|
|
179
|
+
syncViewport();
|
|
180
|
+
if (browserEvent.ctrlKey) {
|
|
181
|
+
const scrollKeys = {
|
|
182
|
+
b: { direction: "up", amount: 1 },
|
|
183
|
+
f: { direction: "down", amount: 1 },
|
|
184
|
+
u: { direction: "up", amount: 0.5 },
|
|
185
|
+
d: { direction: "down", amount: 0.5 }
|
|
186
|
+
};
|
|
187
|
+
const scroll = scrollKeys[browserEvent.key];
|
|
188
|
+
if (scroll) {
|
|
189
|
+
e.preventDefault();
|
|
190
|
+
e.stopPropagation();
|
|
191
|
+
handleScroll(scroll.direction, scroll.amount);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (shouldPreventDefault(browserEvent)) {
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
e.stopPropagation();
|
|
198
|
+
}
|
|
199
|
+
const { newCtx, actions } = processKeystroke(
|
|
200
|
+
browserEvent.key,
|
|
201
|
+
ctx,
|
|
202
|
+
buffer,
|
|
203
|
+
browserEvent.ctrlKey,
|
|
204
|
+
readOnly
|
|
205
|
+
);
|
|
206
|
+
ctx = newCtx;
|
|
207
|
+
processActions(actions, newCtx, browserEvent.key);
|
|
208
|
+
}
|
|
209
|
+
const disposables = [
|
|
210
|
+
editor.onKeyDown(onKeyDown),
|
|
211
|
+
editor.onDidCompositionStart(() => {
|
|
212
|
+
isComposing = true;
|
|
213
|
+
}),
|
|
214
|
+
editor.onDidCompositionEnd(() => {
|
|
215
|
+
isComposing = false;
|
|
216
|
+
})
|
|
217
|
+
];
|
|
218
|
+
syncViewport();
|
|
219
|
+
return {
|
|
220
|
+
getMode: () => ctx.mode,
|
|
221
|
+
getCursor: () => ({ ...ctx.cursor }),
|
|
222
|
+
getContent: () => buffer.getContent(),
|
|
223
|
+
destroy: () => {
|
|
224
|
+
for (const d of disposables) {
|
|
225
|
+
d.dispose();
|
|
226
|
+
}
|
|
227
|
+
decorationCollection.clear();
|
|
228
|
+
editor.updateOptions({ cursorStyle: 1 });
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
export {
|
|
233
|
+
attach,
|
|
234
|
+
cursorToMonacoPosition,
|
|
235
|
+
getTopLine,
|
|
236
|
+
getVisibleLines,
|
|
237
|
+
monacoPositionToCursor,
|
|
238
|
+
revealLine
|
|
239
|
+
};
|
|
240
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/attach.ts","../src/cursor.ts","../src/viewport.ts"],"sourcesContent":["/**\n * attach.ts\n *\n * Core implementation of the plugin-monaco package.\n * Attaches vim keybindings to an existing Monaco Editor instance.\n */\n\nimport type { VimContext, VimAction, CursorPosition } from \"@vimee/core\";\nimport { TextBuffer, createInitialContext, processKeystroke } from \"@vimee/core\";\nimport type { AttachOptions, VimMonaco, MonacoEditor, IKeyboardEvent } from \"./types\";\nimport { cursorToMonacoPosition, monacoPositionToCursor } from \"./cursor\";\nimport { getTopLine, getVisibleLines, revealLine } from \"./viewport\";\n\n/**\n * Attach vim editing to a Monaco Editor instance.\n *\n * @example\n * ```ts\n * const vim = attach(monacoEditor, {\n * onChange: (value) => console.log(value),\n * onModeChange: (mode) => console.log(mode),\n * });\n *\n * // Later...\n * vim.destroy();\n * ```\n */\nexport function attach(editor: MonacoEditor, options: AttachOptions = {}): VimMonaco {\n const {\n readOnly = false,\n onChange,\n onModeChange,\n onYank,\n onSave,\n onAction,\n indentStyle,\n indentWidth,\n } = options;\n\n // --- Initialize vim engine ---\n const buffer = new TextBuffer(editor.getValue());\n const pos = editor.getPosition();\n const initialCursor: CursorPosition = pos ? monacoPositionToCursor(pos) : { line: 0, col: 0 };\n let ctx: VimContext = createInitialContext(initialCursor, {\n indentStyle,\n indentWidth,\n });\n\n // --- Set initial cursor style (block for normal mode) ---\n updateCursorStyle(ctx.mode);\n\n // --- Track composing state (IME input) ---\n let isComposing = false;\n\n // --- Visual mode decorations ---\n const decorationCollection = editor.createDecorationsCollection();\n\n // --- Update Monaco cursor style based on vim mode ---\n function updateCursorStyle(mode: string): void {\n // Monaco CursorStyle: Line = 1, Block = 2, Underline = 3\n const cursorStyle = mode === \"insert\" ? 1 : 2;\n editor.updateOptions({ cursorStyle });\n }\n\n // --- Sync vimee buffer content to Monaco editor ---\n function syncContentToEditor(): void {\n const content = buffer.getContent();\n if (content !== editor.getValue()) {\n editor.setValue(content);\n }\n }\n\n // --- Sync cursor position to Monaco editor ---\n function syncCursorToEditor(cursor: CursorPosition): void {\n editor.setPosition(cursorToMonacoPosition(cursor));\n }\n\n // --- Update visual mode selection decorations ---\n function updateVisualDecorations(): void {\n if (ctx.mode !== \"visual\" && ctx.mode !== \"visual-line\" && ctx.mode !== \"visual-block\") {\n decorationCollection.clear();\n return;\n }\n\n const anchor = ctx.visualAnchor ?? ctx.cursor;\n const before =\n anchor.line < ctx.cursor.line ||\n (anchor.line === ctx.cursor.line && anchor.col <= ctx.cursor.col);\n const start = before ? anchor : ctx.cursor;\n const end = before ? ctx.cursor : anchor;\n\n if (ctx.mode === \"visual-line\") {\n decorationCollection.set([\n {\n range: {\n startLineNumber: start.line + 1,\n startColumn: 1,\n endLineNumber: end.line + 1,\n endColumn: (buffer.getLineLength(end.line) || 0) + 2,\n },\n options: { className: \"vimee-visual-selection\", isWholeLine: true },\n },\n ]);\n } else {\n decorationCollection.set([\n {\n range: {\n startLineNumber: start.line + 1,\n startColumn: start.col + 1,\n endLineNumber: end.line + 1,\n endColumn: end.col + 2, // +2: Monaco end is exclusive & col is 0-based\n },\n options: { className: \"vimee-visual-selection\" },\n },\n ]);\n }\n }\n\n // --- Process vim actions and sync state ---\n function processActions(actions: VimAction[], newCtx: VimContext, key: string): void {\n for (const action of actions) {\n onAction?.(action, key);\n\n switch (action.type) {\n case \"content-change\":\n syncContentToEditor();\n onChange?.(action.content);\n break;\n\n case \"mode-change\":\n updateCursorStyle(action.mode);\n onModeChange?.(action.mode);\n break;\n\n case \"yank\":\n onYank?.(action.text);\n break;\n\n case \"save\":\n onSave?.(action.content);\n break;\n\n case \"set-option\":\n case \"status-message\":\n case \"scroll\":\n case \"noop\":\n break;\n }\n }\n\n // Always sync cursor and decorations from context\n syncCursorToEditor(newCtx.cursor);\n revealLine(editor, newCtx.cursor.line);\n updateVisualDecorations();\n }\n\n // --- Update viewport info on the VimContext ---\n function syncViewport(): void {\n const topLine = getTopLine(editor);\n const height = getVisibleLines(editor);\n ctx = {\n ...ctx,\n viewportTopLine: topLine,\n viewportHeight: height,\n };\n }\n\n // --- Determine if default behavior should be prevented ---\n function shouldPreventDefault(e: KeyboardEvent): boolean {\n const mode = ctx.mode;\n\n // In normal/visual/command-line mode, prevent all printable keys\n if (mode !== \"insert\") {\n // Allow modifier-only keys and browser shortcuts like Ctrl-C (copy)\n if (e.ctrlKey && (e.key === \"c\" || e.key === \"a\")) return false;\n if (e.key === \"Shift\" || e.key === \"Control\" || e.key === \"Alt\" || e.key === \"Meta\") {\n return false;\n }\n return true;\n }\n\n // In insert mode, prevent special keys handled by vim engine\n if (e.key === \"Escape\") return true;\n if (e.key === \"Tab\") return true;\n if (e.key === \"Backspace\") return true;\n if (e.key === \"Enter\") return true;\n if (e.ctrlKey) {\n const ctrlKeys = [\"r\", \"b\", \"f\", \"d\", \"u\", \"v\", \"w\"];\n if (ctrlKeys.includes(e.key)) return true;\n }\n\n return true; // Prevent all — vim engine handles insert mode character input\n }\n\n // --- Scroll handler for Ctrl-U/D/B/F ---\n function handleScroll(direction: \"up\" | \"down\", amount: number): void {\n const visibleLines = getVisibleLines(editor);\n const scrollLines = Math.max(1, Math.floor(visibleLines * amount));\n const newLine =\n direction === \"up\"\n ? Math.max(0, ctx.cursor.line - scrollLines)\n : Math.min(buffer.getLineCount() - 1, ctx.cursor.line + scrollLines);\n\n const maxCol = Math.max(0, (buffer.getLineLength(newLine) || 1) - 1);\n const newCursor: CursorPosition = {\n line: newLine,\n col: Math.min(ctx.cursor.col, maxCol),\n };\n\n ctx = { ...ctx, cursor: newCursor };\n syncCursorToEditor(newCursor);\n revealLine(editor, newLine);\n }\n\n // --- Keyboard handler ---\n function onKeyDown(e: IKeyboardEvent): void {\n if (isComposing) return;\n\n const browserEvent = e.browserEvent;\n\n // Update viewport before processing (for H/M/L motions)\n syncViewport();\n\n // Handle Ctrl scroll keys at this level\n if (browserEvent.ctrlKey) {\n const scrollKeys: Record<string, { direction: \"up\" | \"down\"; amount: number }> = {\n b: { direction: \"up\", amount: 1.0 },\n f: { direction: \"down\", amount: 1.0 },\n u: { direction: \"up\", amount: 0.5 },\n d: { direction: \"down\", amount: 0.5 },\n };\n const scroll = scrollKeys[browserEvent.key];\n if (scroll) {\n e.preventDefault();\n e.stopPropagation();\n handleScroll(scroll.direction, scroll.amount);\n return;\n }\n }\n\n if (shouldPreventDefault(browserEvent)) {\n e.preventDefault();\n e.stopPropagation();\n }\n\n const { newCtx, actions } = processKeystroke(\n browserEvent.key,\n ctx,\n buffer,\n browserEvent.ctrlKey,\n readOnly,\n );\n\n ctx = newCtx;\n processActions(actions, newCtx, browserEvent.key);\n }\n\n // --- Attach event listeners ---\n const disposables = [\n editor.onKeyDown(onKeyDown),\n editor.onDidCompositionStart(() => {\n isComposing = true;\n }),\n editor.onDidCompositionEnd(() => {\n isComposing = false;\n }),\n ];\n\n // Initial viewport sync\n syncViewport();\n\n // --- Return the VimMonaco handle ---\n return {\n getMode: () => ctx.mode,\n getCursor: () => ({ ...ctx.cursor }),\n getContent: () => buffer.getContent(),\n destroy: () => {\n for (const d of disposables) {\n d.dispose();\n }\n decorationCollection.clear();\n // Reset cursor style to line (default)\n editor.updateOptions({ cursorStyle: 1 });\n },\n };\n}\n","/**\n * cursor.ts\n *\n * Utilities for converting between vimee's 0-based CursorPosition\n * and Monaco's 1-based IPosition.\n */\n\nimport type { CursorPosition } from \"@vimee/core\";\nimport type { IPosition } from \"./types\";\n\n/**\n * Convert a vimee 0-based CursorPosition to a Monaco 1-based IPosition.\n */\nexport function cursorToMonacoPosition(cursor: CursorPosition): IPosition {\n return {\n lineNumber: cursor.line + 1,\n column: cursor.col + 1,\n };\n}\n\n/**\n * Convert a Monaco 1-based IPosition to a vimee 0-based CursorPosition.\n */\nexport function monacoPositionToCursor(position: IPosition): CursorPosition {\n return {\n line: position.lineNumber - 1,\n col: position.column - 1,\n };\n}\n","/**\n * viewport.ts\n *\n * Utilities for computing viewport information from a Monaco Editor.\n * Handles viewport tracking for H/M/L motions and\n * Ctrl-U/D/B/F page scrolling.\n */\n\nimport type { MonacoEditor } from \"./types\";\n\n/**\n * Get the first visible line (0-based) from the Monaco editor.\n */\nexport function getTopLine(editor: MonacoEditor): number {\n const ranges = editor.getVisibleRanges();\n if (ranges.length === 0) return 0;\n return ranges[0].startLineNumber - 1; // Convert to 0-based\n}\n\n/**\n * Get the number of visible lines in the Monaco editor viewport.\n */\nexport function getVisibleLines(editor: MonacoEditor): number {\n const ranges = editor.getVisibleRanges();\n if (ranges.length === 0) return 20; // Fallback\n const first = ranges[0];\n const last = ranges[ranges.length - 1];\n return last.endLineNumber - first.startLineNumber + 1;\n}\n\n/**\n * Scroll the Monaco editor to reveal a specific line (0-based).\n */\nexport function revealLine(editor: MonacoEditor, line: number): void {\n editor.revealLine(line + 1); // Convert to 1-based\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vimee/plugin-monaco",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Attach vim editing to a Monaco Editor instance",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"typecheck": "tsgo --noEmit",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"test:coverage": "vitest run --coverage"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"vim",
|
|
28
|
+
"monaco",
|
|
29
|
+
"editor",
|
|
30
|
+
"plugin",
|
|
31
|
+
"framework-agnostic"
|
|
32
|
+
],
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/vimeejs/vimee.git",
|
|
37
|
+
"directory": "packages/plugin-monaco"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@vimee/core": "0.2.0",
|
|
41
|
+
"monaco-editor": ">=0.34.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@vimee/core": "workspace:*",
|
|
45
|
+
"happy-dom": "20.8.4"
|
|
46
|
+
}
|
|
47
|
+
}
|