Skip to content

Commit

Permalink
fixed some issues with vim keybindings
Browse files Browse the repository at this point in the history
  • Loading branch information
Nimaoth committed Mar 14, 2024
1 parent 446f24b commit 4be42b8
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 30 deletions.
59 changes: 43 additions & 16 deletions config/keybindings_vim.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ var vimMotionNextMode = initTable[EditorId, string]()
var yankedLines: bool = false
var selectLines: bool = false
var deleteInclusiveEnd: bool = true
var vimCursorIncludeEol = false
var vimCurrentUndoCheckpoint = "insert"

type VimTextObjectRange* = enum Inner, Outer, CurrentToEnd

Expand Down Expand Up @@ -76,6 +78,12 @@ proc vimSelect(editor: TextDocumentEditor, move: string, count: int = 1) {.expos
for i in 0..<max(count, 1):
editor.runAction(action, arg)

proc vimUndo(editor: TextDocumentEditor) {.expose("vim-undo").} =
editor.undo(vimCurrentUndoCheckpoint)

proc vimRedo(editor: TextDocumentEditor) {.expose("vim-redo").} =
editor.redo(vimCurrentUndoCheckpoint)

proc copySelection(editor: TextDocumentEditor): Selections =
yankedLines = selectLines
editor.copy(inclusiveEnd=true)
Expand Down Expand Up @@ -182,14 +190,14 @@ proc vimMoveTo*(editor: TextDocumentEditor, target: string, before: bool, count:
editor.moveCursorColumn(-1)

proc vimClamp*(self: TextDocumentEditor, cursor: Cursor): Cursor =
let lineLen = self.lineLength(cursor.line)
if cursor.column > 0 and cursor.column >= lineLen:
return (cursor.line, lineLen - 1)
return cursor
var lineLen = self.lineLength(cursor.line)
if not vimCursorIncludeEol and lineLen > 0: lineLen.dec
result = (cursor.line, min(cursor.column, lineLen))

proc vimMotionLine*(editor: TextDocumentEditor, cursor: Cursor, count: int): Selection =
let lineLen = editor.lineLength(cursor.line)
result = ((cursor.line, 0), (cursor.line, max(0, lineLen - 1)))
var lineLen = editor.lineLength(cursor.line)
if not vimCursorIncludeEol and lineLen > 0: lineLen.dec
result = ((cursor.line, 0), (cursor.line, lineLen))

proc vimMotionWord*(editor: TextDocumentEditor, cursor: Cursor, count: int): Selection =
const AlphaNumeric = {'A'..'Z', 'a'..'z', '0'..'9', '_'}
Expand Down Expand Up @@ -487,12 +495,12 @@ proc vimDeleteRight*(editor: TextDocumentEditor) =
expose "vim-delete-left", vimDeleteLeft

proc vimMoveCursorColumn(editor: TextDocumentEditor, direction: int, count: int = 1) {.expose("vim-move-cursor-column").} =
editor.moveCursorColumn(direction * max(count, 1), wrap=false, includeAfter=false)
editor.moveCursorColumn(direction * max(count, 1), wrap=false, includeAfter=vimCursorIncludeEol)
if selectLines:
editor.vimSelectLine()

proc vimMoveCursorLine(editor: TextDocumentEditor, direction: int, count: int = 1) {.expose("vim-move-cursor-line").} =
editor.moveCursorLine(direction * max(count, 1), includeAfter=false)
editor.moveCursorLine(direction * max(count, 1), includeAfter=vimCursorIncludeEol)
if selectLines:
editor.vimSelectLine()

Expand Down Expand Up @@ -587,23 +595,24 @@ proc loadVimKeybindings*() {.scriptActionWasmNims("load-vim-keybindings").} =
startRecordingCommands(".")
editor.clearCurrentCommandHistory(retainLast=true)

if newMode == "visual-line":
selectLines = true
else:
selectLines = false
selectLines = newMode == "visual-line"

vimCursorIncludeEol = newMode == "insert"
vimCurrentUndoCheckpoint = if newMode == "insert": "word" else: "insert"

case newMode
of "normal":
setOption "editor.text.vim-motion-action", "vim-select-last-cursor"
setOption "editor.text.inclusive-selection", false
vimMotionNextMode[editor.id] = "normal"
editor.selections = editor.selections.mapIt(it.last.toSelection)
editor.selections = editor.selections.mapIt(editor.vimClamp(it.last).toSelection)
editor.saveCurrentCommandHistory()

of "insert":
setOption "editor.text.inclusive-selection", false
setOption "editor.text.vim-motion-action", ""
vimMotionNextMode[editor.id] = "insert"
editor.addNextCheckpoint("insert")

of "visual":
setOption "editor.text.inclusive-selection", true
Expand Down Expand Up @@ -908,13 +917,28 @@ proc loadVimKeybindings*() {.scriptActionWasmNims("load-vim-keybindings").} =
editor.vimYankSelection()
selectLines = false

addTextCommandBlock "", "D":
editor.selections = editor.selections.mapIt (it.last, editor.vimMotionLine(it.last, 0).last)
editor.vimDeleteSelection(true)
selectLines = false

addTextCommandBlock "", "C":
editor.selections = editor.selections.mapIt (it.last, editor.vimMotionLine(it.last, 0).last)
editor.vimChangeSelection(true)
selectLines = false

addTextCommandBlock "", "Y":
editor.selections = editor.selections.mapIt (it.last, editor.vimMotionLine(it.last, 0).last)
editor.vimYankSelection()
selectLines = false

# Deleting text
addTextCommand "", "x", vimDeleteRight
addTextCommand "", "<DELETE>", vimDeleteRight
addTextCommand "", "X", vimDeleteLeft

addTextCommand "", "u", "undo"
addTextCommand "", "<C-r>", "redo"
addTextCommand "", "u", "vim-undo"
addTextCommand "", "<C-r>", "vim-redo"
addTextCommand "", "p", "vim-paste"

addTextCommand "", "<ENTER>", "insert-text", "\n"
Expand Down Expand Up @@ -1004,4 +1028,7 @@ proc loadVimKeybindings*() {.scriptActionWasmNims("load-vim-keybindings").} =
addTextCommand "", "<C-r>", "select-prev"
addTextCommand "", "<C-m>", "select-next"
addTextCommand "", "U", "redo"
addTextCommand "", "<C-k><C-c>", "toggle-line-comment"
addTextCommand "", "<C-z>", "vim-undo"
addTextCommand "", "<C-y>", "vim-redo"
addTextCommand "", "<C-k><C-c>", "toggle-line-comment"
addTextCommand "", "<C-k><C-u>", "print-undo-history"
7 changes: 5 additions & 2 deletions scripting/absytree_internal.nim
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,17 @@ proc editor_text_indent_void_TextDocumentEditor_impl*(self: TextDocumentEditor)
proc editor_text_unindent_void_TextDocumentEditor_impl*(self: TextDocumentEditor) =
discard
proc editor_text_undo_void_TextDocumentEditor_string_impl*(
self: TextDocumentEditor; checkpoint: string = "word") =
self: TextDocumentEditor; checkpoint: string = "move") =
discard
proc editor_text_redo_void_TextDocumentEditor_string_impl*(
self: TextDocumentEditor; checkpoint: string = "word") =
self: TextDocumentEditor; checkpoint: string = "move") =
discard
proc editor_text_addNextCheckpoint_void_TextDocumentEditor_string_impl*(
self: TextDocumentEditor; checkpoint: string) =
discard
proc editor_text_printUndoHistory_void_TextDocumentEditor_int_impl*(
self: TextDocumentEditor; max: int = 50) =
discard
proc editor_text_copy_void_TextDocumentEditor_string_bool_impl*(
self: TextDocumentEditor; register: string = ""; inclusiveEnd: bool = false) =
discard
Expand Down
6 changes: 4 additions & 2 deletions scripting/absytree_internal_wasm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,13 @@ proc editor_text_insertText_void_TextDocumentEditor_string_bool_impl(
proc editor_text_indent_void_TextDocumentEditor_impl(self: TextDocumentEditor) {.importc.}
proc editor_text_unindent_void_TextDocumentEditor_impl(self: TextDocumentEditor) {.importc.}
proc editor_text_undo_void_TextDocumentEditor_string_impl(
self: TextDocumentEditor; checkpoint: string = "word") {.importc.}
self: TextDocumentEditor; checkpoint: string = "move") {.importc.}
proc editor_text_redo_void_TextDocumentEditor_string_impl(
self: TextDocumentEditor; checkpoint: string = "word") {.importc.}
self: TextDocumentEditor; checkpoint: string = "move") {.importc.}
proc editor_text_addNextCheckpoint_void_TextDocumentEditor_string_impl(
self: TextDocumentEditor; checkpoint: string) {.importc.}
proc editor_text_printUndoHistory_void_TextDocumentEditor_int_impl(
self: TextDocumentEditor; max: int = 50) {.importc.}
proc editor_text_copy_void_TextDocumentEditor_string_bool_impl(
self: TextDocumentEditor; register: string = ""; inclusiveEnd: bool = false) {.importc.}
proc editor_text_paste_void_TextDocumentEditor_string_impl(
Expand Down
6 changes: 4 additions & 2 deletions scripting/editor_text_api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,15 @@ proc indent*(self: TextDocumentEditor) =
editor_text_indent_void_TextDocumentEditor_impl(self)
proc unindent*(self: TextDocumentEditor) =
editor_text_unindent_void_TextDocumentEditor_impl(self)
proc undo*(self: TextDocumentEditor; checkpoint: string = "word") =
proc undo*(self: TextDocumentEditor; checkpoint: string = "move") =
editor_text_undo_void_TextDocumentEditor_string_impl(self, checkpoint)
proc redo*(self: TextDocumentEditor; checkpoint: string = "word") =
proc redo*(self: TextDocumentEditor; checkpoint: string = "move") =
editor_text_redo_void_TextDocumentEditor_string_impl(self, checkpoint)
proc addNextCheckpoint*(self: TextDocumentEditor; checkpoint: string) =
editor_text_addNextCheckpoint_void_TextDocumentEditor_string_impl(self,
checkpoint)
proc printUndoHistory*(self: TextDocumentEditor; max: int = 50) =
editor_text_printUndoHistory_void_TextDocumentEditor_int_impl(self, max)
proc copy*(self: TextDocumentEditor; register: string = "";
inclusiveEnd: bool = false) =
editor_text_copy_void_TextDocumentEditor_string_bool_impl(self, register,
Expand Down
23 changes: 21 additions & 2 deletions scripting/editor_text_api_wasm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ proc unindent*(self: TextDocumentEditor) =

proc editor_text_undo_void_TextDocumentEditor_string_wasm(arg: cstring): cstring {.
importc.}
proc undo*(self: TextDocumentEditor; checkpoint: string = "word") =
proc undo*(self: TextDocumentEditor; checkpoint: string = "move") =
var argsJson = newJArray()
argsJson.add block:
when TextDocumentEditor is JsonNode:
Expand All @@ -552,7 +552,7 @@ proc undo*(self: TextDocumentEditor; checkpoint: string = "word") =

proc editor_text_redo_void_TextDocumentEditor_string_wasm(arg: cstring): cstring {.
importc.}
proc redo*(self: TextDocumentEditor; checkpoint: string = "word") =
proc redo*(self: TextDocumentEditor; checkpoint: string = "move") =
var argsJson = newJArray()
argsJson.add block:
when TextDocumentEditor is JsonNode:
Expand Down Expand Up @@ -588,6 +588,25 @@ proc addNextCheckpoint*(self: TextDocumentEditor; checkpoint: string) =
argsJsonString.cstring)


proc editor_text_printUndoHistory_void_TextDocumentEditor_int_wasm(arg: cstring): cstring {.
importc.}
proc printUndoHistory*(self: TextDocumentEditor; max: int = 50) =
var argsJson = newJArray()
argsJson.add block:
when TextDocumentEditor is JsonNode:
self
else:
self.toJson()
argsJson.add block:
when int is JsonNode:
max
else:
max.toJson()
let argsJsonString = $argsJson
let res {.used.} = editor_text_printUndoHistory_void_TextDocumentEditor_int_wasm(
argsJsonString.cstring)


proc editor_text_copy_void_TextDocumentEditor_string_bool_wasm(arg: cstring): cstring {.
importc.}
proc copy*(self: TextDocumentEditor; register: string = "";
Expand Down
14 changes: 9 additions & 5 deletions src/text/text_document.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import std/[os, strutils, sequtils, sugar, options, json, strformat, tables]
import scripting_api except DocumentEditor, TextDocumentEditor, AstDocumentEditor
from scripting_api as api import nil
import patty, bumpy
import misc/[id, util, event, custom_logger, custom_async, custom_unicode, myjsonutils, regex]
import misc/[id, util, event, custom_logger, custom_async, custom_unicode, myjsonutils, regex, array_set]
import platform/[filesystem]
import language/[languages, language_server_base]
import workspaces/[workspace]
Expand Down Expand Up @@ -30,7 +30,7 @@ type
children: seq[UndoOp]

proc `$`*(op: UndoOp): string =
result = fmt"{{{op.kind} ({op.oldSelection})"
result = fmt"{{{op.kind} (old: {op.oldSelection}, checkpoints: {op.checkpoints})"
if op.kind == Delete: result.add fmt", selections = {op.selection}}}"
if op.kind == Insert: result.add fmt", selections = {op.cursor}, text: '{op.text}'}}"
if op.kind == Nested: result.add fmt", {op.children}}}"
Expand Down Expand Up @@ -745,7 +745,7 @@ proc delete*(self: TextDocument, selections: openArray[Selection], oldSelection:
self.undoOps.add undoOp
self.redoOps = @[]

self.nextCheckpoints = @[]
self.nextCheckpoints = @[]

proc getNodeRange*(self: TextDocument, selection: Selection, parentIndex: int = 0, siblingIndex: int = 0): Option[Selection] =
result = Selection.none
Expand Down Expand Up @@ -863,6 +863,10 @@ proc traverse*(line, column: int, text: openArray[char]): (int, int) =
return (line, column)

proc insert*(self: TextDocument, selections: openArray[Selection], oldSelection: openArray[Selection], texts: openArray[string], notify: bool = true, record: bool = true): seq[Selection] =
# be careful with logging inside this function, because the logs are written to another document using this function to insert, which can cause infinite recursion
# when inserting a log line logs something.
# Use echo for debugging instead

result = self.clampAndMergeSelections selections

var undoOp = UndoOp(kind: Nested, children: @[], oldSelection: @oldSelection)
Expand Down Expand Up @@ -965,7 +969,7 @@ proc insert*(self: TextDocument, selections: openArray[Selection], oldSelection:
self.undoOps.add undoOp
self.redoOps = @[]

self.nextCheckpoints = @[]
self.nextCheckpoints = @[]

proc edit*(self: TextDocument, selections: openArray[Selection], oldSelection: openArray[Selection], texts: openArray[string], notify: bool = true, record: bool = true): seq[Selection] =
let selections = selections.map (s) => s.normalized
Expand Down Expand Up @@ -1044,7 +1048,7 @@ proc redo*(self: TextDocument, oldSelection: openArray[Selection], useOldSelecti
break

proc addNextCheckpoint*(self: TextDocument, checkpoint: string) =
self.nextCheckpoints.add checkpoint
self.nextCheckpoints.incl checkpoint

proc isLineEmptyOrWhitespace*(self: TextDocument, line: int): bool =
if line > self.lines.high:
Expand Down
16 changes: 15 additions & 1 deletion src/text/text_editor.nim
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,15 @@ proc `selections=`*(self: TextDocumentEditor, selections: Selections) =
self.selectionHistory.addLast self.selectionsInternal
if self.selectionHistory.len > 100:
discard self.selectionHistory.popFirst

self.selectionsInternal = selections
self.cursorVisible = true

if self.blinkCursorTask.isNotNil and self.active:
self.blinkCursorTask.reschedule()

self.showHover = false
# self.document.addNextCheckpoint("move")
self.markDirty()

proc `selection=`*(self: TextDocumentEditor, selection: Selection) =
Expand All @@ -147,11 +151,15 @@ proc `selection=`*(self: TextDocumentEditor, selection: Selection) =
self.selectionHistory.addLast self.selectionsInternal
if self.selectionHistory.len > 100:
discard self.selectionHistory.popFirst

self.selectionsInternal = @[self.clampSelection selection]
self.cursorVisible = true

if self.blinkCursorTask.isNotNil and self.active:
self.blinkCursorTask.reschedule()

self.showHover = false
# self.document.addNextCheckpoint("move")
self.markDirty()

proc `targetSelection=`*(self: TextDocumentEditor, selection: Selection) =
Expand Down Expand Up @@ -442,7 +450,7 @@ proc doMoveCursorColumn(self: TextDocumentEditor, cursor: Cursor, offset: int, w

template currentLine: openArray[char] = self.document.lines[cursor.line].toOpenArray

let lastIndex = self.document.lastValidIndex(cursor.line, includeAfter)
var lastIndex = self.document.lastValidIndex(cursor.line, includeAfter)

if offset > 0:
for i in 0..<offset:
Expand All @@ -452,6 +460,7 @@ proc doMoveCursorColumn(self: TextDocumentEditor, cursor: Cursor, offset: int, w
if cursor.line < self.document.lines.high:
cursor.line = cursor.line + 1
cursor.column = 0
lastIndex = self.document.lastValidIndex(cursor.line, includeAfter)
continue
else:
cursor.column = lastIndex
Expand All @@ -466,6 +475,7 @@ proc doMoveCursorColumn(self: TextDocumentEditor, cursor: Cursor, offset: int, w
break
if cursor.line > 0:
cursor.line = cursor.line - 1
lastIndex = self.document.lastValidIndex(cursor.line, includeAfter)
cursor.column = lastIndex
continue
else:
Expand Down Expand Up @@ -769,6 +779,10 @@ proc redo*(self: TextDocumentEditor, checkpoint: string = "word") {.expose("edit
proc addNextCheckpoint*(self: TextDocumentEditor, checkpoint: string) {.expose("editor.text").} =
self.document.addNextCheckpoint checkpoint

proc printUndoHistory*(self: TextDocumentEditor, max: int = 50) {.expose("editor.text").} =
for i in countdown(self.document.undoOps.high, 0):
debugf"undo: {self.document.undoOps[i]}"

proc copyAsync*(self: TextDocumentEditor, register: string, inclusiveEnd: bool): Future[void] {.async.} =
var text = ""
for i, selection in self.selections:
Expand Down

0 comments on commit 4be42b8

Please sign in to comment.