diff --git a/README.md b/README.md index 77c0e25e..4619b437 100644 --- a/README.md +++ b/README.md @@ -156,4 +156,7 @@ More details in [Getting Started](docs/getting_started.md) - Search/Replace - fix e.g. dw deleting one character to much because it's inclusive +## LSP +- Handle rust lsp paths with lowercase letter like d:/path/to/file + [Test report](https://nimaoth.github.io/Absytree/testresults.html) diff --git a/config/keybindings_vim.nim b/config/keybindings_vim.nim index ce535e91..b43d63eb 100644 --- a/config/keybindings_vim.nim +++ b/config/keybindings_vim.nim @@ -900,7 +900,6 @@ proc loadVimKeybindings*() {.scriptActionWasmNims("load-vim-keybindings").} = addTextCommand "", "X", vimDeleteLeft addTextCommand "", "u", "undo" - addTextCommand "", "U", "redo" addTextCommand "", "", "redo" addTextCommand "", "p", "vim-paste" @@ -914,9 +913,6 @@ proc loadVimKeybindings*() {.scriptActionWasmNims("load-vim-keybindings").} = editor.vimChangeSelection(true) # Insert mode - addTextCommand "insert", "", "insert-text", "\n" - addTextCommand "insert", "", "insert-text", "\n" - addTextCommand "insert", "", "set-mode", "insert-register" setHandleInputs "editor.text.insert-register", true setTextInputHandler "insert-register", proc(editor: TextDocumentEditor, input: string): bool = @@ -985,4 +981,13 @@ proc loadVimKeybindings*() {.scriptActionWasmNims("load-vim-keybindings").} = addTextCommand "visual-line", "", """vim-select-move <#count>""" addTextCommand "visual-line", "\\>", "indent" - addTextCommand "visual-line", "\\<", "unindent" \ No newline at end of file + addTextCommand "visual-line", "\\<", "unindent" + + # todo: not really vim keybindings + addTextCommand "", "gd", "goto-definition" + addTextCommand "", "gs", "goto-symbol" + addTextCommand "", "", "get-completions" + addTextCommand "", "", "show-hover-for-current" + addTextCommand "", "", "select-prev" + addTextCommand "", "", "select-next" + addTextCommand "", "U", "redo" \ No newline at end of file diff --git a/scripting/absytree_internal.nim b/scripting/absytree_internal.nim index 180e5d57..85080c5d 100644 --- a/scripting/absytree_internal.nim +++ b/scripting/absytree_internal.nim @@ -297,6 +297,12 @@ proc editor_text_showHoverForCurrent_void_TextDocumentEditor_impl*( discard proc editor_text_hideHover_void_TextDocumentEditor_impl*(self: TextDocumentEditor) = discard +proc editor_text_cancelDelayedHideHover_void_TextDocumentEditor_impl*( + self: TextDocumentEditor) = + discard +proc editor_text_hideHoverDelayed_void_TextDocumentEditor_impl*( + self: TextDocumentEditor) = + discard proc editor_text_isRunningSavedCommands_bool_TextDocumentEditor_impl*( self: TextDocumentEditor): bool = discard diff --git a/scripting/absytree_internal_wasm.nim b/scripting/absytree_internal_wasm.nim index ada8833c..9bd13ad2 100644 --- a/scripting/absytree_internal_wasm.nim +++ b/scripting/absytree_internal_wasm.nim @@ -204,6 +204,10 @@ proc editor_text_showHoverFor_void_TextDocumentEditor_Cursor_impl( proc editor_text_showHoverForCurrent_void_TextDocumentEditor_impl( self: TextDocumentEditor) {.importc.} proc editor_text_hideHover_void_TextDocumentEditor_impl(self: TextDocumentEditor) {.importc.} +proc editor_text_cancelDelayedHideHover_void_TextDocumentEditor_impl( + self: TextDocumentEditor) {.importc.} +proc editor_text_hideHoverDelayed_void_TextDocumentEditor_impl( + self: TextDocumentEditor) {.importc.} proc editor_text_isRunningSavedCommands_bool_TextDocumentEditor_impl( self: TextDocumentEditor): bool {.importc.} proc editor_text_runSavedCommands_void_TextDocumentEditor_impl( diff --git a/scripting/editor_text_api.nim b/scripting/editor_text_api.nim index 3e28556e..3c74deb0 100644 --- a/scripting/editor_text_api.nim +++ b/scripting/editor_text_api.nim @@ -300,6 +300,11 @@ proc showHoverForCurrent*(self: TextDocumentEditor) = proc hideHover*(self: TextDocumentEditor) = ## Hides the hover information. editor_text_hideHover_void_TextDocumentEditor_impl(self) +proc cancelDelayedHideHover*(self: TextDocumentEditor) = + editor_text_cancelDelayedHideHover_void_TextDocumentEditor_impl(self) +proc hideHoverDelayed*(self: TextDocumentEditor) = + ## Hides the hover information after a delay. + editor_text_hideHoverDelayed_void_TextDocumentEditor_impl(self) proc isRunningSavedCommands*(self: TextDocumentEditor): bool = editor_text_isRunningSavedCommands_bool_TextDocumentEditor_impl(self) proc runSavedCommands*(self: TextDocumentEditor) = diff --git a/scripting/editor_text_api_wasm.nim b/scripting/editor_text_api_wasm.nim index 049f2aaf..9152a9e6 100644 --- a/scripting/editor_text_api_wasm.nim +++ b/scripting/editor_text_api_wasm.nim @@ -1975,6 +1975,34 @@ proc hideHover*(self: TextDocumentEditor) = argsJsonString.cstring) +proc editor_text_cancelDelayedHideHover_void_TextDocumentEditor_wasm( + arg: cstring): cstring {.importc.} +proc cancelDelayedHideHover*(self: TextDocumentEditor) = + var argsJson = newJArray() + argsJson.add block: + when TextDocumentEditor is JsonNode: + self + else: + self.toJson() + let argsJsonString = $argsJson + let res {.used.} = editor_text_cancelDelayedHideHover_void_TextDocumentEditor_wasm( + argsJsonString.cstring) + + +proc editor_text_hideHoverDelayed_void_TextDocumentEditor_wasm(arg: cstring): cstring {. + importc.} +proc hideHoverDelayed*(self: TextDocumentEditor) = + var argsJson = newJArray() + argsJson.add block: + when TextDocumentEditor is JsonNode: + self + else: + self.toJson() + let argsJsonString = $argsJson + let res {.used.} = editor_text_hideHoverDelayed_void_TextDocumentEditor_wasm( + argsJsonString.cstring) + + proc editor_text_isRunningSavedCommands_bool_TextDocumentEditor_wasm( arg: cstring): cstring {.importc.} proc isRunningSavedCommands*(self: TextDocumentEditor): bool = diff --git a/src/text/text_editor.nim b/src/text/text_editor.nim index 289cccdc..c57051e5 100644 --- a/src/text/text_editor.nim +++ b/src/text/text_editor.nim @@ -54,6 +54,7 @@ type TextDocumentEditor* = ref object of DocumentEditor # hover showHoverTask: DelayedTask # for showing hover info after a delay + hideHoverTask: DelayedTask # for hiding hover info after a delay currentHoverLocation: Cursor # the location of the mouse hover showHover*: bool # whether to show hover info in ui hoverText*: string # the text to show in the hover info @@ -1518,6 +1519,9 @@ proc applySelectedCompletion*(self: TextDocumentEditor) {.expose("editor.text"). self.hideCompletions() proc showHoverForAsync(self: TextDocumentEditor, cursor: Cursor): Future[void] {.async.} = + if self.hideHoverTask.isNotNil: + self.hideHoverTask.pause() + let languageServer = await self.document.getLanguageServer() if languageServer.getSome(ls): @@ -1547,12 +1551,31 @@ proc hideHover*(self: TextDocumentEditor) {.expose("editor.text").} = self.showHover = false self.markDirty() +proc cancelDelayedHideHover*(self: TextDocumentEditor) {.expose("editor.text").} = + if self.hideHoverTask.isNotNil: + self.hideHoverTask.pause() + +proc hideHoverDelayed*(self: TextDocumentEditor) {.expose("editor.text").} = + ## Hides the hover information after a delay. + if self.showHoverTask.isNotNil: + self.showHoverTask.pause() + + let hoverDelayMs = self.configProvider.getValue("text.hover-delay", 200) + if self.hideHoverTask.isNil: + self.hideHoverTask = startDelayed(hoverDelayMs, repeat=false): + self.hideHover() + else: + self.hideHoverTask.interval = hoverDelayMs + self.hideHoverTask.reschedule() + proc showHoverForDelayed*(self: TextDocumentEditor, cursor: Cursor) = ## Show hover information for the given cursor after a delay. self.currentHoverLocation = cursor - let hoverDelayMs = self.configProvider.getValue("text.hover-delay", 200) + if self.hideHoverTask.isNotNil: + self.hideHoverTask.pause() + let hoverDelayMs = self.configProvider.getValue("text.hover-delay", 200) if self.showHoverTask.isNil: self.showHoverTask = startDelayed(hoverDelayMs, repeat=false): self.showHoverFor(self.currentHoverLocation) diff --git a/src/ui/node.nim b/src/ui/node.nim index af657d76..3d05d96b 100644 --- a/src/ui/node.nim +++ b/src/ui/node.nim @@ -110,8 +110,8 @@ type mHandlePressed: proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2): bool mHandleReleased: proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2): bool mHandleDrag: proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2, delta: Vec2): bool - mHandleBeginHover: proc(node: UINode): bool - mHandleEndHover: proc(node: UINode): bool + mHandleBeginHover: proc(node: UINode, pos: Vec2): bool + mHandleEndHover: proc(node: UINode, pos: Vec2): bool mHandleHover: proc(node: UINode, pos: Vec2): bool mHandleScroll: proc(node: UINode, pos: Vec2, delta: Vec2, modifiers: set[Modifier]): bool @@ -200,16 +200,16 @@ proc `textColor=`*(node: UINode, value: Color) {.inline.} = (let changed = func handlePressed* (node: UINode): (proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2): bool) {.inline.} = node.mHandlePressed func handleReleased* (node: UINode): (proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2): bool) {.inline.} = node.mHandleReleased func handleDrag* (node: UINode): (proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2, delta: Vec2): bool) {.inline.} = node.mHandleDrag -func handleBeginHover*(node: UINode): (proc(node: UINode): bool) {.inline.} = node.mHandleBeginHover -func handleEndHover* (node: UINode): (proc(node: UINode): bool) {.inline.} = node.mHandleEndHover +func handleBeginHover*(node: UINode): (proc(node: UINode, pos: Vec2): bool) {.inline.} = node.mHandleBeginHover +func handleEndHover* (node: UINode): (proc(node: UINode, pos: Vec2): bool) {.inline.} = node.mHandleEndHover func handleHover* (node: UINode): (proc(node: UINode, pos: Vec2): bool) {.inline.} = node.mHandleHover func handleScroll* (node: UINode): (proc(node: UINode, pos: Vec2, delta: Vec2, modifiers: set[Modifier]): bool) {.inline.} = node.mHandleScroll func `handlePressed=`* (node: UINode, value: proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2): bool) {.inline.} = node.mHandlePressed = value func `handleReleased=`* (node: UINode, value: proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2): bool) {.inline.} = node.mHandleReleased = value func `handleDrag=`* (node: UINode, value: proc(node: UINode, button: MouseButton, modifiers: set[Modifier], pos: Vec2, delta: Vec2): bool) {.inline.} = node.mHandleDrag = value -func `handleBeginHover=`*(node: UINode, value: proc(node: UINode): bool) {.inline.} = node.mHandleBeginHover = value -func `handleEndHover=`* (node: UINode, value: proc(node: UINode): bool) {.inline.} = node.mHandleEndHover = value +func `handleBeginHover=`*(node: UINode, value: proc(node: UINode, pos: Vec2): bool) {.inline.} = node.mHandleBeginHover = value +func `handleEndHover=`* (node: UINode, value: proc(node: UINode, pos: Vec2): bool) {.inline.} = node.mHandleEndHover = value func `handleHover=`* (node: UINode, value: proc(node: UINode, pos: Vec2): bool) {.inline.} = node.mHandleHover = value func `handleScroll=`* (node: UINode, value: proc(node: UINode, pos: Vec2, delta: Vec2, modifiers: set[Modifier]): bool) {.inline.} = node.mHandleScroll = value @@ -326,18 +326,18 @@ proc handleMouseMoved*(builder: UINodeBuilder, pos: Vec2, buttons: set[MouseButt result = a.handleHover()(a, pos - a.boundsAbsolute.xy) or result else: if a.handleEndHover.isNotNil: - result = a.handleEndHover()(a) or result + result = a.handleEndHover()(a, pos - a.boundsAbsolute.xy) or result if b.handleBeginHover.isNotNil: - result = b.handleBeginHover()(b) or result + result = b.handleBeginHover()(b, pos - b.boundsAbsolute.xy) or result result = true of (None(), Some(@b)): if b.handleBeginHover.isNotNil: - result = b.handleBeginHover()(b) or result + result = b.handleBeginHover()(b, pos - b.boundsAbsolute.xy) or result result = true of (Some(@a), None()): if a.handleEndHover.isNotNil: - result = a.handleEndHover()(a) or result + result = a.handleEndHover()(a, pos - a.boundsAbsolute.xy) or result result = true of (None(), None()): discard @@ -1288,11 +1288,11 @@ macro panel*(builder: UINodeBuilder, inFlags: UINodeFlags, args: varargs[untyped onDragBody template onBeginHover(onBody: untyped) {.used.} = - currentNode.handleBeginHover = proc(node: UINode): bool = + currentNode.handleBeginHover = proc(node: UINode, pos {.inject.}: Vec2): bool = onBody template onEndHover(onBody: untyped) {.used.} = - currentNode.handleEndHover = proc(node: UINode): bool = + currentNode.handleEndHover = proc(node: UINode, pos {.inject.}: Vec2): bool = onBody template onHover(onBody: untyped) {.used.} = diff --git a/src/ui/widget_builder_text_document.nim b/src/ui/widget_builder_text_document.nim index 3c9fe7f6..c4631015 100644 --- a/src/ui/widget_builder_text_document.nim +++ b/src/ui/widget_builder_text_document.nim @@ -186,14 +186,18 @@ proc renderLine*( self.app.tryActivateEditor(self) self.markDirty() + onBeginHover: + let offset = self.getCursorPos(textRuneLen, line.index, startRune.RuneIndex, pos) + self.lastHoverLocationBounds = partNode.boundsAbsolute.some + self.showHoverForDelayed (line.index, offset) + onHover: let offset = self.getCursorPos(textRuneLen, line.index, startRune.RuneIndex, pos) self.lastHoverLocationBounds = partNode.boundsAbsolute.some self.showHoverForDelayed (line.index, offset) onEndHover: - # todo: hide after delay so the user has time to hover over the hover window for e.g. scrolling - self.hideHover() + self.hideHoverDelayed() if addBackgroundAsChildren: # Add separate background colors for selections/highlights @@ -512,7 +516,7 @@ proc createHover(self: TextDocumentEditor, builder: UINodeBuilder, app: App, cur clampedX = max(builder.root.w - totalWidth, 0) var hoverPanel: UINode = nil - builder.panel(&{SizeToContentX, MaskContent, FillBackground, DrawBorder, MouseHover}, x = clampedX, y = top, h = height, pivot = vec2(0, 0), backgroundColor = backgroundColor, borderColor = scopeColor, userId = self.hoverId.newPrimaryId): + builder.panel(&{SizeToContentX, MaskContent, FillBackground, DrawBorder, MouseHover, SnapInitialBounds, AnimateBounds}, x = clampedX, y = top, h = height, pivot = vec2(0, 0), backgroundColor = backgroundColor, borderColor = scopeColor, userId = self.hoverId.newPrimaryId): hoverPanel = currentNode var textNode: UINode = nil # todo: height @@ -525,6 +529,12 @@ proc createHover(self: TextDocumentEditor, builder: UINodeBuilder, app: App, cur self.hoverScrollOffset = clamp(self.hoverScrollOffset + delta.y * scrollSpeed, -1000, 0) self.markDirty() + onBeginHover: + self.cancelDelayedHideHover() + + onEndHover: + self.hideHoverDelayed() + hoverPanel.rawY = cursorBounds.y hoverPanel.pivot = vec2(0, 1)