opencode-vim 0.0.12 → 0.0.13
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/package.json
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import type { HashtagTriggerMatch, SnippetInfo } from "./types"
|
|
2
2
|
|
|
3
|
-
const HASHTAG_TRIGGER = /(^|\s)#([^\s#]*)$/
|
|
4
|
-
|
|
5
3
|
export function findTrailingHashtagTrigger(input: string): HashtagTriggerMatch | undefined {
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
let start = -1
|
|
5
|
+
for (let i = input.length - 1; i >= 0; i--) {
|
|
6
|
+
const char = input[i]
|
|
7
|
+
if (char === "#") {
|
|
8
|
+
start = i
|
|
9
|
+
break
|
|
10
|
+
}
|
|
11
|
+
if (/\s/.test(char)) return undefined
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (start < 0) return undefined
|
|
15
|
+
if (start > 0 && !/\s/.test(input[start - 1])) return undefined
|
|
8
16
|
|
|
9
|
-
const query =
|
|
17
|
+
const query = input.slice(start + 1)
|
|
10
18
|
const token = `#${query}`
|
|
11
19
|
|
|
12
20
|
return {
|
|
13
|
-
start
|
|
21
|
+
start,
|
|
14
22
|
end: input.length,
|
|
15
23
|
query,
|
|
16
24
|
token,
|
|
@@ -82,11 +82,12 @@ export function SnippetAutocomplete(props: SnippetAutocompleteProps) {
|
|
|
82
82
|
syncInterval = setInterval(() => {
|
|
83
83
|
const ref = props.ctx.prompt()
|
|
84
84
|
if (!ref) {
|
|
85
|
-
setInput("")
|
|
85
|
+
if (input()) setInput("")
|
|
86
86
|
setSyncingPrompt(false)
|
|
87
87
|
return
|
|
88
88
|
}
|
|
89
|
-
|
|
89
|
+
const next = ref.current.input ?? ""
|
|
90
|
+
if (next !== input()) setInput(next)
|
|
90
91
|
}, PROMPT_SYNC_MS)
|
|
91
92
|
|
|
92
93
|
onCleanup(() => {
|
|
@@ -75,8 +75,6 @@ function VimKeyboard(props: { ctx: PromptContext; config: VimConfig; state: Retu
|
|
|
75
75
|
|
|
76
76
|
if (passThroughKey(event, key, props.state.mode())) {
|
|
77
77
|
if (!preparedEvents.delete(event)) preparePassThroughKey(props.ctx, key, props.state.mode())
|
|
78
|
-
syncCursorStyle(true)
|
|
79
|
-
props.ctx.requestRender()
|
|
80
78
|
return
|
|
81
79
|
}
|
|
82
80
|
|
|
@@ -90,9 +88,9 @@ function VimKeyboard(props: { ctx: PromptContext; config: VimConfig; state: Retu
|
|
|
90
88
|
if (consumed) {
|
|
91
89
|
event.preventDefault()
|
|
92
90
|
event.stopPropagation()
|
|
91
|
+
syncCursorStyle(true)
|
|
92
|
+
props.ctx.requestRender()
|
|
93
93
|
}
|
|
94
|
-
syncCursorStyle(true)
|
|
95
|
-
props.ctx.requestRender()
|
|
96
94
|
})
|
|
97
95
|
|
|
98
96
|
onCleanup(() => {
|
package/src/modules/vim/vimee.ts
CHANGED
|
@@ -10,6 +10,8 @@ import type { createVimState } from "./state"
|
|
|
10
10
|
|
|
11
11
|
type VimState = ReturnType<typeof createVimState>
|
|
12
12
|
type HostAction = VimeeAction | { type: "submit" }
|
|
13
|
+
type HostKeybindAction = "normal" | "submit"
|
|
14
|
+
type HostKeybindDefinition = KeybindDefinition & { hostAction?: HostKeybindAction }
|
|
13
15
|
type HostRange = { start: number; end: number }
|
|
14
16
|
|
|
15
17
|
const YANK_FLASH_MS = 250
|
|
@@ -33,11 +35,6 @@ export function createVimeeAdapter(state: VimState, config: VimConfig, log: VimL
|
|
|
33
35
|
const ref = ctx.prompt()
|
|
34
36
|
if (!ref) return false
|
|
35
37
|
|
|
36
|
-
const input = focusedInput(ctx)
|
|
37
|
-
const text = input?.plainText ?? ref.current.input
|
|
38
|
-
const offset = clamp(input?.cursorOffset ?? text.length, 0, text.length)
|
|
39
|
-
const map = mapForHostText(text, input)
|
|
40
|
-
const cursor = hostPosition(map, offset)
|
|
41
38
|
let vimeeKey = keyForVimee(event, key)
|
|
42
39
|
if (!vimeeKey) return false
|
|
43
40
|
|
|
@@ -46,16 +43,29 @@ export function createVimeeAdapter(state: VimState, config: VimConfig, log: VimL
|
|
|
46
43
|
return true
|
|
47
44
|
}
|
|
48
45
|
|
|
49
|
-
if (state.mode() === "insert" && key === "<CR>") {
|
|
50
|
-
cancelPendingInsert(ctx, offset)
|
|
51
|
-
return false
|
|
52
|
-
}
|
|
53
|
-
|
|
54
46
|
if (state.mode() === "insert") {
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
if (key === "<CR>") {
|
|
48
|
+
cancelPendingInsert(ctx)
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
if (vimeeKey === "Escape") {
|
|
52
|
+
cancelPendingInsert(ctx, undefined, false)
|
|
53
|
+
enterNormal(ctx)
|
|
54
|
+
state.setPending("")
|
|
55
|
+
updateTimeout(ctx)
|
|
56
|
+
log("vimee.key", { key, mode: vim.mode, phase: vim.phase, cursor: vim.cursor, actions: ["mode-change"] })
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
return handleInsertKeybind(event, vimeeKey, ctx)
|
|
57
60
|
}
|
|
58
61
|
|
|
62
|
+
const input = focusedInput(ctx)
|
|
63
|
+
const text = input?.plainText ?? ref.current.input
|
|
64
|
+
const offset = clamp(input?.cursorOffset ?? text.length, 0, text.length)
|
|
65
|
+
|
|
66
|
+
const map = mapForHostText(text, input)
|
|
67
|
+
const cursor = hostPosition(map, offset)
|
|
68
|
+
|
|
59
69
|
const wasPending = keybinds?.isPending() ?? false
|
|
60
70
|
const pendingBefore = pendingInsert
|
|
61
71
|
sync(map, cursor)
|
|
@@ -172,20 +182,8 @@ export function createVimeeAdapter(state: VimState, config: VimConfig, log: VimL
|
|
|
172
182
|
syncVisualSelection(input, currentMap, ctx)
|
|
173
183
|
}
|
|
174
184
|
|
|
175
|
-
function
|
|
176
|
-
if (
|
|
177
|
-
cancelPendingInsert(ctx, offset, false)
|
|
178
|
-
sync(map, cursor)
|
|
179
|
-
const result = processKeystroke(key, vim, buffer, event.ctrl, false)
|
|
180
|
-
vim = result.newCtx
|
|
181
|
-
applyActions(result.actions as HostAction[], ctx, map)
|
|
182
|
-
syncMode(state, vim.mode)
|
|
183
|
-
state.setPending("")
|
|
184
|
-
log("vimee.key", { key, mode: vim.mode, phase: vim.phase, cursor: vim.cursor, actions: result.actions.map((action) => action.type) })
|
|
185
|
-
return true
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (!keybinds?.hasKeybinds("insert") && !keybinds?.isPending()) return undefined
|
|
185
|
+
function handleInsertKeybind(event: KeyEvent, key: string, ctx: PromptContext) {
|
|
186
|
+
if (!keybinds?.hasKeybinds("insert") && !keybinds?.isPending()) return false
|
|
189
187
|
|
|
190
188
|
const wasPending = keybinds.isPending()
|
|
191
189
|
const pendingBefore = pendingInsert
|
|
@@ -198,25 +196,67 @@ export function createVimeeAdapter(state: VimState, config: VimConfig, log: VimL
|
|
|
198
196
|
updateTimeout(ctx)
|
|
199
197
|
return true
|
|
200
198
|
case "matched": {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
if (applyInsertKeybind(resolved.definition, ctx)) {
|
|
200
|
+
pendingInsert = ""
|
|
201
|
+
state.setPending("")
|
|
202
|
+
updateTimeout(ctx)
|
|
203
|
+
log("vimee.keybind", { key, mode: vim.mode, phase: vim.phase, cursor: vim.cursor, actions: ["mode-change"] })
|
|
204
|
+
return true
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (wasPending && pendingBefore) flushPendingInsert(ctx, pendingBefore)
|
|
205
208
|
pendingInsert = ""
|
|
206
209
|
state.setPending("")
|
|
207
210
|
updateTimeout(ctx)
|
|
208
|
-
log("vimee.keybind", { key
|
|
209
|
-
return
|
|
211
|
+
log("vimee.keybind.unsupported", { key })
|
|
212
|
+
return false
|
|
210
213
|
}
|
|
211
214
|
case "none":
|
|
212
|
-
if (wasPending && pendingBefore) flushPendingInsert(ctx, pendingBefore
|
|
215
|
+
if (wasPending && pendingBefore) flushPendingInsert(ctx, pendingBefore)
|
|
213
216
|
pendingInsert = ""
|
|
214
217
|
state.setPending("")
|
|
215
218
|
updateTimeout(ctx)
|
|
216
|
-
return
|
|
219
|
+
return false
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function applyInsertKeybind(definition: KeybindDefinition, ctx: PromptContext) {
|
|
224
|
+
switch (insertHostAction(definition)) {
|
|
225
|
+
case "normal":
|
|
226
|
+
enterNormal(ctx)
|
|
227
|
+
return true
|
|
228
|
+
case "submit":
|
|
229
|
+
ctx.prompt()?.submit()
|
|
230
|
+
return true
|
|
231
|
+
default:
|
|
232
|
+
return false
|
|
217
233
|
}
|
|
218
234
|
}
|
|
219
235
|
|
|
236
|
+
function enterNormal(ctx: PromptContext) {
|
|
237
|
+
const ref = ctx.prompt()
|
|
238
|
+
const input = focusedInput(ctx)
|
|
239
|
+
const text = input?.plainText ?? ref?.current.input ?? ""
|
|
240
|
+
const offset = clamp(input?.cursorOffset ?? text.length, 0, text.length)
|
|
241
|
+
|
|
242
|
+
if (input && text.length > 0) {
|
|
243
|
+
input.cursorOffset = Math.max(0, offset - 1)
|
|
244
|
+
clampNormalCursor(input)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
vim = { ...resetContext(vim), mode: "normal", statusMessage: "" }
|
|
248
|
+
nativeInsertUndoSaved = false
|
|
249
|
+
syncMode(state, "normal")
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function insertHostAction(definition: KeybindDefinition): HostKeybindAction | undefined {
|
|
253
|
+
const action = (definition as HostKeybindDefinition).hostAction
|
|
254
|
+
if (action) return action
|
|
255
|
+
if ("execute" in definition) return undefined
|
|
256
|
+
if (definition.keys === "<Esc>" || definition.keys === "<C-[>" || definition.keys === "Escape") return "normal"
|
|
257
|
+
return undefined
|
|
258
|
+
}
|
|
259
|
+
|
|
220
260
|
function applyKeybind(definition: KeybindDefinition, map: PromptMap) {
|
|
221
261
|
if ("execute" in definition) {
|
|
222
262
|
const actions = definition.execute(vim, buffer) as HostAction[]
|
|
@@ -299,12 +339,8 @@ export function createVimeeAdapter(state: VimState, config: VimConfig, log: VimL
|
|
|
299
339
|
const currentOffset = input?.cursorOffset ?? insertAt
|
|
300
340
|
const next = text.slice(0, insertAt) + value + text.slice(insertAt)
|
|
301
341
|
setInput(ref, next)
|
|
302
|
-
activeMap = createPromptMap(next, input)
|
|
303
|
-
rememberMap(activeMap)
|
|
304
|
-
buffer.replaceContent(activeMap.vimText)
|
|
305
342
|
const nextOffset = currentOffset >= insertAt ? currentOffset + value.length : currentOffset
|
|
306
343
|
if (input) input.cursorOffset = nextOffset
|
|
307
|
-
vim = { ...vim, cursor: hostPosition(activeMap, nextOffset) }
|
|
308
344
|
}
|
|
309
345
|
|
|
310
346
|
function cursorOffset(map: PromptMap, position: CursorPosition) {
|
|
@@ -553,14 +589,14 @@ function createKeybinds(config: VimConfig, log: VimLog): KeybindMap | undefined
|
|
|
553
589
|
return count > 0 ? map : undefined
|
|
554
590
|
}
|
|
555
591
|
|
|
556
|
-
function keybindAction(action: string):
|
|
592
|
+
function keybindAction(action: string): HostKeybindDefinition {
|
|
557
593
|
switch (action) {
|
|
558
594
|
case "normal":
|
|
559
|
-
return { keys: "<Esc>" }
|
|
595
|
+
return { keys: "<Esc>", hostAction: "normal" }
|
|
560
596
|
case "insert":
|
|
561
597
|
return { keys: "i" }
|
|
562
598
|
case "submit":
|
|
563
|
-
return { execute: () => [{ type: "submit" } as unknown as VimeeAction] }
|
|
599
|
+
return { execute: () => [{ type: "submit" } as unknown as VimeeAction], hostAction: "submit" }
|
|
564
600
|
default:
|
|
565
601
|
return { keys: action }
|
|
566
602
|
}
|