Skip to content

Commit 8e0a179

Browse files
authored
Add jump back from goto definition (#2201)
1 parent 5f7f5f7 commit 8e0a179

File tree

10 files changed

+270
-9
lines changed

10 files changed

+270
-9
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.. _#2201: https://github.com/fox0430/moe/pull/2201
2+
3+
Added
4+
.....
5+
6+
- `#2201`_ Add jump back from goto definition
7+

documents/howtouse.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
| <kbd>**z**</kbd> <kbd>**R**</kbd></br> | Delete fold lines |
140140
| <kbd>**Ctrl**</kbd> <kbd>**s**</kbd></br> | Selection Range (LSP) |
141141
| <kbd>**Space**</kbd> <kbd>**o**</kbd></br> | Document Symbol (LSP) |
142+
| <kbd>**Ctrl**</kbd> <kbd>**o**</kbd></br> | Jump Back from LSP Goto Definition |
142143

143144
</details>
144145

src/moepkg/bufferstatus.nim

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ type
101101
codeLenses*: seq[CodeLens] # Lsp CodeLens
102102
selectionRanges*: seq[SelectionRange] # Lsp SelectionRange
103103
documentSymbols*: seq[DocumentSymbol] # Lsp DocumentSybol
104+
gotoDefinitionSource: Option[BufferLocation] # The position before LSP Goto definition
104105

105106
var
106107
countAddedBuffer = 0
@@ -316,10 +317,14 @@ proc isUpdate*(bufStatuses: seq[BufferStatus]): bool =
316317
for b in bufStatuses:
317318
if b.isUpdate: return true
318319

319-
proc checkBufferExist*(bufStatus: seq[BufferStatus], path: Runes): Option[int] =
320-
for index, buf in bufStatus:
321-
if buf.path == path:
322-
return some(index)
320+
proc checkBufferExist*(
321+
bufStatus: seq[BufferStatus],
322+
path: Runes | string): Option[int] =
323+
324+
let runes = path.toRunes
325+
for index, buf in bufStatus:
326+
if buf.path == runes:
327+
return some(index)
323328

324329
proc absolutePath*(bufStatus: BufferStatus): Runes =
325330
if isAbsolute($bufStatus.path):
@@ -384,7 +389,7 @@ proc initBufferStatus*(
384389
return Result[BufferStatus, string].ok b
385390

386391
proc initBufferStatus*(
387-
mode: Mode): Result[BufferStatus, string] =
392+
mode: Mode = Mode.normal): Result[BufferStatus, string] =
388393
## Return a BufferStatus for a new empty buffer.
389394

390395
var b = BufferStatus(
@@ -451,3 +456,16 @@ proc updateSyntaxCheckerResults*(
451456
bufStatus.syntaxCheckResults = r.get
452457

453458
return Result[(), string].ok ()
459+
460+
proc setGotoDefinitionSource*(b: BufferStatus, l: BufferLocation) {.inline.} =
461+
b.gotoDefinitionSource = some(l)
462+
463+
proc getGotoDefinitionSource*(
464+
b: BufferStatus,
465+
clear: bool = true): Option[BufferLocation] =
466+
467+
if b.gotoDefinitionSource.isSome:
468+
let l = b.gotoDefinitionSource
469+
if clear:
470+
b.gotoDefinitionSource = none(BufferLocation)
471+
return l

src/moepkg/helputils.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ zd - Delete folding lines
141141
zD - Delete all folding lines
142142
Ctrl-s - Selection Range (LSP)
143143
Space o - Document Symbol (LSP)
144+
Ctrl-o - Jump Back from LSP Goto Definition
144145
145146
# Register
146147

src/moepkg/independentutils.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#[###################### GNU General Public License 3.0 ######################]#
22
# #
3-
# Copyright (C) 2017─2023 Shuhei Nogawa #
3+
# Copyright (C) 2017─2024 Shuhei Nogawa #
44
# #
55
# This program is free software: you can redistribute it and/or modify #
66
# it under the terms of the GNU General Public License as published by #

src/moepkg/lsp/handler.nim

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,10 @@ proc openWindowAndGotoDefinition(
607607
l: BufferLocation): Result[(), string] =
608608
## Goto definition and Goto TypeDefinition.
609609

610+
let
611+
beforePath = $currentBufStatus.path
612+
beforePosition = currentMainWindowNode.bufferPosition
613+
610614
if l.path == $currentBufStatus.absolutePath:
611615
currentMainWindowNode.currentLine = l.range.first.line
612616
currentMainWindowNode.currentColumn = l.range.first.column
@@ -634,6 +638,16 @@ proc openWindowAndGotoDefinition(
634638
jumpLine(currentBufStatus, currentMainWindowNode, l.range.first.line)
635639
currentMainWindowNode.currentColumn = l.range.first.column
636640

641+
block:
642+
let p = BufferPosition(
643+
line: beforePosition.line,
644+
column: beforePosition.column)
645+
currentBufStatus.setGotoDefinitionSource(BufferLocation(
646+
path: beforePath,
647+
range: BufferRange(
648+
first: p,
649+
last: p)))
650+
637651
return Result[(), string].ok ()
638652

639653
proc lspDeclaration(

src/moepkg/normalmode.nim

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,32 @@ proc requestGotoTypeDefinition(status: var EditorStatus) =
600600
if r.isErr:
601601
status.commandLine.writeLspTypeDefinitionError(r.error)
602602

603+
proc jumpBackFromGotoDefinitionSource(status: var EditorStatus) =
604+
let location = currentBufStatus.getGotoDefinitionSource
605+
if location.isNone:
606+
# Not found
607+
return
608+
609+
let
610+
path = location.get.path
611+
bufferIndex = status.bufStatus.checkBufferExist(path)
612+
if bufferIndex.isSome:
613+
status.changeCurrentBuffer(bufferIndex.get)
614+
else:
615+
let err = status.addNewBufferInCurrentWin($path)
616+
if err.isErr:
617+
status.commandLine.writeFileOpenError(location.get.path)
618+
return
619+
620+
status.resize
621+
622+
currentMainWindowNode.currentLine = min(
623+
currentBufStatus.buffer.high,
624+
location.get.range.first.line)
625+
currentMainWindowNode.currentColumn = min(
626+
currentBufStatus.buffer[currentMainWindowNode.currentLine].high,
627+
location.get.range.first.column)
628+
603629
proc requestGotoImplementation(status: var EditorStatus) =
604630
if not status.lspClients.contains(currentBufStatus.langId):
605631
debug "lsp client is not ready"
@@ -1656,6 +1682,8 @@ proc normalCommand(status: var EditorStatus, commands: Runes): Option[Rune] =
16561682
status.requestDocumentLink
16571683
elif key == ord('G'):
16581684
currentBufStatus.moveToLastLine(currentMainWindowNode)
1685+
elif isCtrlO(key):
1686+
status.jumpBackFromGotoDefinitionSource
16591687
elif isCtrlU(key):
16601688
return status.halfPageUpCommand
16611689
elif isCtrlD(key):
@@ -1955,7 +1983,8 @@ proc isNormalModeCommand*(
19551983
$command == "L" or
19561984
$command == "%" or
19571985
$command == "K" or
1958-
isCtrlS(command):
1986+
isCtrlS(command) or
1987+
isCtrlO(command):
19591988
result = InputState.Valid
19601989

19611990
elif isDigit(command[0]):

tests/tbufferstatus.nim

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#[###################### GNU General Public License 3.0 ######################]#
2+
# #
3+
# Copyright (C) 2017─2024 Shuhei Nogawa #
4+
# #
5+
# This program is free software: you can redistribute it and/or modify #
6+
# it under the terms of the GNU General Public License as published by #
7+
# the Free Software Foundation, either version 3 of the License, or #
8+
# (at your option) any later version. #
9+
# #
10+
# This program is distributed in the hope that it will be useful, #
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
13+
# GNU General Public License for more details. #
14+
# #
15+
# You should have received a copy of the GNU General Public License #
16+
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
17+
# #
18+
#[############################################################################]#
19+
20+
import std/[unittest, importutils, options]
21+
22+
import pkg/results
23+
24+
import moepkg/independentutils
25+
26+
import moepkg/bufferstatus {.all.}
27+
28+
suite "setGotoDefinitionSource":
29+
privateAccess(BufferStatus)
30+
31+
test "Basic":
32+
var b = initBufferStatus().get
33+
let l = BufferLocation(
34+
path: "/path",
35+
range: BufferRange(
36+
first: BufferPosition(line: 0, column: 1),
37+
last: BufferPosition(line: 0, column: 1)))
38+
39+
b.setGotoDefinitionSource(l)
40+
41+
check b.gotoDefinitionSource.get == l
42+
43+
test "Overwrite":
44+
var b = initBufferStatus().get
45+
46+
b.setGotoDefinitionSource(BufferLocation(
47+
path: "/path",
48+
range: BufferRange(
49+
first: BufferPosition(line: 0, column: 1),
50+
last: BufferPosition(line: 0, column: 1))))
51+
52+
let l = BufferLocation(
53+
path: "/dir/path/",
54+
range: BufferRange(
55+
first: BufferPosition(line: 1, column: 2),
56+
last: BufferPosition(line: 1, column: 2)))
57+
58+
b.setGotoDefinitionSource(l)
59+
60+
check b.gotoDefinitionSource.get == l
61+
62+
suite "getGotoDefinitionSource":
63+
privateAccess(BufferStatus)
64+
65+
test "Empty":
66+
var b = initBufferStatus().get
67+
68+
check b.getGotoDefinitionSource.isNone
69+
70+
test "Basic":
71+
var b = initBufferStatus().get
72+
73+
let l = BufferLocation(
74+
path: "/path",
75+
range: BufferRange(
76+
first: BufferPosition(line: 0, column: 1),
77+
last: BufferPosition(line: 0, column: 1)))
78+
79+
b.gotoDefinitionSource = some(l)
80+
81+
check b.getGotoDefinitionSource.get == l
82+
check b.getGotoDefinitionSource.isNone
83+
84+
test "Peek":
85+
var b = initBufferStatus().get
86+
87+
let l = BufferLocation(
88+
path: "/path",
89+
range: BufferRange(
90+
first: BufferPosition(line: 0, column: 1),
91+
last: BufferPosition(line: 0, column: 1)))
92+
93+
b.gotoDefinitionSource = some(l)
94+
95+
check b.getGotoDefinitionSource(clear = false).get == l
96+
check b.getGotoDefinitionSource.isSome

tests/tlsphandler.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1883,7 +1883,7 @@ suite "lsp: handleLspResponse":
18831883
assert status.lspInitialize(workspaceRoot, LangId).isOk
18841884

18851885
var isTimeout = true
1886-
for _ in 0 .. 120:
1886+
for _ in 0 .. 180:
18871887
const Timeout = 1000
18881888
let readable = lspClient.readable(Timeout).get
18891889
if not readable:

tests/tnormalmode.nim

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import pkg/results
2424
import moepkg/syntax/highlite
2525
import moepkg/[registers, settings, editorstatus, gapbuffer, unicodeext,
2626
bufferstatus, ui, windownode, quickrunutils, viewhighlight,
27-
folding, editorview]
27+
folding, editorview, independentutils]
2828

2929
import utils
3030

@@ -4428,3 +4428,98 @@ suite "Normal mode: requestGotoDefinition":
44284428
status.requestGotoDefinition
44294429

44304430
status.update
4431+
4432+
suite "Normal mode: jumpBackFromGotoDefinitionSource":
4433+
const TestFileBuffer = "123\n123\n"
4434+
4435+
var status: EditorStatus
4436+
4437+
let
4438+
currentDir = getCurrentDir()
4439+
testDir = currentDir / "jumpBackTest"
4440+
testFilePath1 = testDir / "file1"
4441+
testFilePath2 = testDir / "file2"
4442+
4443+
setup:
4444+
createDir(testDir)
4445+
4446+
4447+
writeFile(testFilePath1, TestFileBuffer)
4448+
writeFile(testFilePath2, "a\n")
4449+
4450+
status = initEditorStatus()
4451+
status.settings.lsp.enable = false
4452+
4453+
teardown:
4454+
removeDir(testDir)
4455+
4456+
test "Empty":
4457+
assert status.addNewBufferInCurrentWin(testFilePath1).isOk
4458+
assert status.addNewBufferInCurrentWin(testFilePath2).isOk
4459+
4460+
status.resize(100, 100)
4461+
status.update
4462+
4463+
status.jumpBackFromGotoDefinitionSource
4464+
4465+
status.update
4466+
4467+
check currentBufStatus.path == testFilePath2.toRunes
4468+
check currentBufStatus.getGotoDefinitionSource.isNone
4469+
4470+
check currentMainWindowNode.bufferPosition == BufferPosition(
4471+
line: 0,
4472+
column: 0)
4473+
4474+
test "Basic":
4475+
assert status.addNewBufferInCurrentWin(testFilePath1).isOk
4476+
assert status.addNewBufferInCurrentWin(testFilePath2).isOk
4477+
4478+
status.resize(100, 100)
4479+
status.update
4480+
4481+
let l = BufferLocation(
4482+
path: testFilePath1,
4483+
range: BufferRange(
4484+
first: BufferPosition(line: 0, column: 1),
4485+
last: BufferPosition(line: 0, column: 1)))
4486+
4487+
currentBufStatus.setGotoDefinitionSource(l)
4488+
4489+
status.jumpBackFromGotoDefinitionSource
4490+
4491+
status.update
4492+
4493+
check currentBufStatus.path == testFilePath1.toRunes
4494+
check currentBufStatus.buffer.toRunes == TestFileBuffer.toRunes
4495+
check currentBufStatus.getGotoDefinitionSource.isNone
4496+
4497+
check currentMainWindowNode.bufferPosition == BufferPosition(
4498+
line: 0,
4499+
column: 1)
4500+
4501+
test "Basic 2":
4502+
assert status.addNewBufferInCurrentWin(testFilePath2).isOk
4503+
4504+
status.resize(100, 100)
4505+
status.update
4506+
4507+
let l = BufferLocation(
4508+
path: testFilePath1,
4509+
range: BufferRange(
4510+
first: BufferPosition(line: 0, column: 1),
4511+
last: BufferPosition(line: 0, column: 1)))
4512+
4513+
currentBufStatus.setGotoDefinitionSource(l)
4514+
4515+
status.jumpBackFromGotoDefinitionSource
4516+
4517+
status.update
4518+
4519+
check currentBufStatus.path == testFilePath1.toRunes
4520+
check currentBufStatus.buffer.toRunes == TestFileBuffer.toRunes
4521+
check currentBufStatus.getGotoDefinitionSource.isNone
4522+
4523+
check currentMainWindowNode.bufferPosition == BufferPosition(
4524+
line: 0,
4525+
column: 1)

0 commit comments

Comments
 (0)