Skip to content

Commit

Permalink
More progress on debugger
Browse files Browse the repository at this point in the history
  • Loading branch information
Nimaoth committed Jun 5, 2024
1 parent c344209 commit ff950fa
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 13 deletions.
6 changes: 6 additions & 0 deletions config/default_config.nim
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@ proc loadDefaultKeybindings*(clearExisting: bool = false) =
addCommand "popup.selector.file-explorer", "<C-UP>", "go-up"
addCommand "popup.selector.file-explorer", "<C-r>", "go-up"

addCommand "editor", "<LEADER>al", "run-configuration", "test1"
addCommand "editor", "<LEADER>ac", "continue-execution"
addCommand "editor", "<LEADER>ar", "step-over"
addCommand "editor", "<LEADER>at", "step-in"
addCommand "editor", "<LEADER>an", "step-out"

# addCommand "editor.text", "<C-SPACE>ts", "reload-treesitter"

# setHandleInputs("editor.model", true)
Expand Down
30 changes: 30 additions & 0 deletions scripting/debugger_api_wasm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,33 @@ proc addBreakpoint*(file: string; line: int) =
let res {.used.} = debugger_addBreakpoint_void_Debugger_string_int_wasm(
argsJsonString.cstring)


proc debugger_continueExecution_void_Debugger_wasm(arg: cstring): cstring {.
importc.}
proc continueExecution*() =
var argsJson = newJArray()
let argsJsonString = $argsJson
let res {.used.} = debugger_continueExecution_void_Debugger_wasm(
argsJsonString.cstring)


proc debugger_stepOver_void_Debugger_wasm(arg: cstring): cstring {.importc.}
proc stepOver*() =
var argsJson = newJArray()
let argsJsonString = $argsJson
let res {.used.} = debugger_stepOver_void_Debugger_wasm(argsJsonString.cstring)


proc debugger_stepIn_void_Debugger_wasm(arg: cstring): cstring {.importc.}
proc stepIn*() =
var argsJson = newJArray()
let argsJsonString = $argsJson
let res {.used.} = debugger_stepIn_void_Debugger_wasm(argsJsonString.cstring)


proc debugger_stepOut_void_Debugger_wasm(arg: cstring): cstring {.importc.}
proc stepOut*() =
var argsJson = newJArray()
let argsJsonString = $argsJson
let res {.used.} = debugger_stepOut_void_Debugger_wasm(argsJsonString.cstring)

7 changes: 7 additions & 0 deletions src/app.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1830,6 +1830,13 @@ proc openWorkspaceFile*(self: App, path: string, folder: WorkspaceFolder): Optio
defer:
self.platform.requestRender()

let folder = if folder.isNotNil:
folder
elif self.workspace.folders.len > 0:
self.workspace.folders[0]
else:
return DocumentEditor.none

let path = folder.getAbsolutePath(path)

log lvlInfo, fmt"[openWorkspaceFile] Open file '{path}' in workspace {folder.name} ({folder.id})"
Expand Down
4 changes: 2 additions & 2 deletions src/text/language/dap_client.nim
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ type
algorithm*: ChecksumAlgorithm
checksum*: string

Thread* = object
ThreadInfo* = object
id*: int
name*: string

Threads* = object
threads*: seq[Thread]
threads*: seq[ThreadInfo]

Source* = object
name*: Option[string]
Expand Down
111 changes: 105 additions & 6 deletions src/text/language/debugger.nim
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
import std/[strutils, options, json, asynchttpserver, tables, sugar]
import misc/[id, custom_async, custom_logger, util, connection, myjsonutils, event]
import misc/[id, custom_async, custom_logger, util, connection, myjsonutils, event, response]
import scripting/expose
import dap_client, dispatch_tables, app_interface, config_provider
import text/text_editor

import chroma

import scripting_api except DocumentEditor, TextDocumentEditor, AstDocumentEditor
from scripting_api as api import nil

logCategory "debugger"

let debuggerCurrentLineId = newId()

type
DebuggerConnectionKind = enum Tcp = "tcp", Stdio = "stdio", Websocket = "websocket"

Debugger* = ref object
app: AppInterface
client: Option[DapClient]

# Data setup in the editor and sent to the server
breakpoints: seq[SourceBreakpoint]

client: Option[DapClient]
# Cached data from server
threads: seq[ThreadInfo]
stackTraces: Table[int, StackTraceResponse]

# Other stuff
currentThreadIndex: int

lastEditor: Option[TextDocumentEditor]

var gDebugger: Debugger = nil

Expand All @@ -26,6 +44,11 @@ static:
proc createDebugger*(app: AppInterface) =
gDebugger = Debugger(app: app)

proc currentThread*(self: Debugger): Option[ThreadInfo] =
if self.currentThreadIndex >= 0 and self.currentThreadIndex < self.threads.len:
return self.threads[self.currentThreadIndex].some
return ThreadInfo.none

proc stopDebugSession*(self: Debugger) {.expose("debugger").} =
debugf"[stopDebugSession] Stopping session"
if self.client.isNone:
Expand Down Expand Up @@ -89,20 +112,73 @@ proc createConnectionWithType(self: Debugger, name: string): Future[Option[Conne

return Connection.none

proc updateStackTrace(self: Debugger, threadId: Option[int]): Future[Option[int]] {.async.} =
let threadId = if threadId.getSome(id):
id
elif self.currentThread.getSome(thread):
thread.id
else:
return int.none

if self.client.getSome(client):
let stackTrace = await client.stackTrace(threadId)
if stackTrace.isError:
return int.none
self.stackTraces[threadId] = stackTrace.result

return threadId.some

proc handleStoppedAsync(self: Debugger, data: OnStoppedData) {.async.} =
log(lvlInfo, &"onStopped {data}")

if self.lastEditor.isSome:
self.lastEditor.get.clearCustomHighlights(debuggerCurrentLineId)
self.lastEditor = TextDocumentEditor.none

if self.currentThread.getSome(thread) and self.client.getSome(client):
let threadId = await self.updateStackTrace(data.threadId)

if threadId.getSome(threadId) and self.stackTraces.contains(threadId):
let stack {.cursor.} = self.stackTraces[threadId]

if stack.stackFrames.len == 0:
return

let frame {.cursor.} = stack.stackFrames[0]

if frame.source.isSome and frame.source.get.path.getSome(path):
let editor = self.app.openWorkspaceFile(path, nil)

if editor.getSome(editor) and editor of TextDocumentEditor:
let textEditor = editor.TextDocumentEditor
let location: Cursor = (frame.line - 1, frame.column - 1)
textEditor.targetSelection = location.toSelection

let lineSelection = ((location.line, 0), (location.line, textEditor.lineLength(location.line)))
textEditor.addCustomHighlight(debuggerCurrentLineId, lineSelection, "editorError.foreground", color(1, 1, 1, 0.3))
self.lastEditor = textEditor.some

proc handleStopped(self: Debugger, data: OnStoppedData) =
asyncCheck self.handleStoppedAsync(data)

proc handleTerminated(self: Debugger, data: Option[OnTerminatedData]) =
log(lvlInfo, &"onTerminated {data}")
if self.lastEditor.isSome:
self.lastEditor.get.clearCustomHighlights(debuggerCurrentLineId)
self.lastEditor = TextDocumentEditor.none

proc setClient(self: Debugger, client: DAPClient) =
assert self.client.isNone
self.client = client.some

discard client.onInitialized.subscribe (data: OnInitializedData) =>
log(lvlInfo, &"onInitialized")
discard client.onStopped.subscribe (data: OnStoppedData) =>
log(lvlInfo, &"onStopped {data}")
discard client.onStopped.subscribe (data: OnStoppedData) => self.handleStopped(data)
discard client.onContinued.subscribe (data: OnContinuedData) =>
log(lvlInfo, &"onContinued {data}")
discard client.onExited.subscribe (data: OnExitedData) =>
log(lvlInfo, &"onExited {data}")
discard client.onTerminated.subscribe (data: Option[OnTerminatedData]) =>
log(lvlInfo, &"onTerminated {data}")
discard client.onTerminated.subscribe (data: Option[OnTerminatedData]) => self.handleTerminated(data)
discard client.onThread.subscribe (data: OnThreadData) =>
log(lvlInfo, &"onThread {data}")
discard client.onOutput.subscribe (data: OnOutputData) =>
Expand Down Expand Up @@ -184,6 +260,13 @@ proc runConfigurationAsync(self: Debugger, name: string) {.async.} =
]
)

let threads = await client.getThreads
if threads.isError:
log lvlError, &"Failed to get threads: {threads}"
return

self.threads = threads.result.threads

await client.configurationDone()

proc runConfiguration*(self: Debugger, name: string) {.expose("debugger").} =
Expand All @@ -192,6 +275,22 @@ proc runConfiguration*(self: Debugger, name: string) {.expose("debugger").} =
proc addBreakpoint*(self: Debugger, file: string, line: int) {.expose("debugger").} =
debugf"[addBreakpoint] '{file}' in line {line}"

proc continueExecution*(self: Debugger) {.expose("debugger").} =
if self.currentThread.getSome(thread) and self.client.getSome(client):
asyncCheck client.continueExecution(thread.id)

proc stepOver*(self: Debugger) {.expose("debugger").} =
if self.currentThread.getSome(thread) and self.client.getSome(client):
asyncCheck client.next(thread.id)

proc stepIn*(self: Debugger) {.expose("debugger").} =
if self.currentThread.getSome(thread) and self.client.getSome(client):
asyncCheck client.stepIn(thread.id)

proc stepOut*(self: Debugger) {.expose("debugger").} =
if self.currentThread.getSome(thread) and self.client.getSome(client):
asyncCheck client.stepOut(thread.id)

genDispatcher("debugger")
addGlobalDispatchTable "debugger", genDispatchTable("debugger")

Expand Down
10 changes: 5 additions & 5 deletions src/text/text_editor.nim
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,9 @@ proc updateTargetColumn*(self: TextDocumentEditor, cursor: SelectionCursor = Las
proc updateInlayHints*(self: TextDocumentEditor)
proc updateDiagnosticsForCurrent*(self: TextDocumentEditor)
proc visibleTextRange*(self: TextDocumentEditor, buffer: int = 0): Selection
proc addCustomHighlight(self: TextDocumentEditor, id: Id, selection: Selection, color: string,
proc addCustomHighlight*(self: TextDocumentEditor, id: Id, selection: Selection, color: string,
tint: Color = color(1, 1, 1))
proc clearCustomHighlights(self: TextDocumentEditor, id: Id)
proc clearCustomHighlights*(self: TextDocumentEditor, id: Id)
proc updateSearchResults(self: TextDocumentEditor)
proc centerCursor*(self: TextDocumentEditor, cursor: SelectionCursor = SelectionCursor.Config)
proc centerCursor*(self: TextDocumentEditor, cursor: Cursor)
Expand Down Expand Up @@ -481,15 +481,15 @@ iterator splitSelectionIntoLines(self: TextDocumentEditor, selection: Selection,

yield ((selection.last.line, 0), selection.last)

proc clearCustomHighlights(self: TextDocumentEditor, id: Id) =
proc clearCustomHighlights*(self: TextDocumentEditor, id: Id) =
## Removes all custom highlights associated with the given id

for highlights in self.customHighlights.mvalues:
for i in countdown(highlights.high, 0):
if highlights[i].id == id:
highlights.removeSwap(i)

proc addCustomHighlight(self: TextDocumentEditor, id: Id, selection: Selection, color: string,
proc addCustomHighlight*(self: TextDocumentEditor, id: Id, selection: Selection, color: string,
tint: Color = color(1, 1, 1)) =
# customHighlights*: Table[int, seq[(Id, Selection, Color)]]
for lineSelection in self.splitSelectionIntoLines(selection):
Expand Down Expand Up @@ -750,7 +750,7 @@ proc fromJsonHook*(t: var api.TextDocumentEditor, jsonNode: JsonNode) =
proc lineCount(self: TextDocumentEditor): int {.expose: "editor.text".} =
return self.document.lines.len

proc lineLength(self: TextDocumentEditor, line: int): int {.expose: "editor.text".} =
proc lineLength*(self: TextDocumentEditor, line: int): int {.expose: "editor.text".} =
return self.document.lineLength(line)

proc screenLineCount(self: TextDocumentEditor): int {.expose: "editor.text".} =
Expand Down

0 comments on commit ff950fa

Please sign in to comment.