Heim > Backend-Entwicklung > Golang > Golang-Debugging in Emacs beherrschen

Golang-Debugging in Emacs beherrschen

Linda Hamilton
Freigeben: 2024-12-02 11:56:09
Original
215 Leute haben es durchsucht

Einführung

Seit ich angefangen habe, in Golang zu entwickeln, habe ich den Debugger nicht mehr wirklich verwendet. Stattdessen habe ich naiverweise überall fmt.Print-Anweisungen hinzugefügt, um meinen Code zu validieren? Während Druckanweisungen und Protokolle möglicherweise auch Ihr erster Debugging-Instinkt sind, versagen sie oft, wenn Sie mit einer großen und komplexen Codebasis arbeiten, mit ausgefeiltem Laufzeitverhalten und (natürlich!) komplexen Parallelitätsproblemen, die scheinbar unmöglich zu reproduzieren sind.

Nachdem ich begonnen hatte, an komplexeren Projekten zu arbeiten (wie diesem: https://github.com/cloudoperators/heureka), musste ich mich dazu zwingen, einen tieferen Blick auf delve (den Golang-Debugger) zu werfen und zu sehen, was Emacs zu bieten hat damit interagieren. Obwohl das Go-Ökosystem hervorragende Debugging-Tools bietet, kann es eine Herausforderung sein, diese in einen komfortablen Entwicklungsworkflow zu integrieren.

In diesem Beitrag werde ich die leistungsstarke Kombination von Emacs, Delve und Dape näher erläutern. Zusammen schaffen diese Tools ein Debugging-Erlebnis, das herkömmliche IDEs nachahmt (und oft übertrifft), während gleichzeitig die Flexibilität und Erweiterbarkeit erhalten bleibt, für die Emacs berühmt ist.

Das können Sie erwarten:

  • Delve mit Dape einrichten und konfigurieren
  • Debuggen Sie sowohl Standardanwendungen als auch Ginkgo-Tests (das verwende ich derzeit?)
  • Optimieren Sie Ihren Debugging-Workflow mit Emacs-spezifischen Anpassungen

Einrichten der Entwicklungsumgebung

In diesem Beitrag gehe ich davon aus, dass Sie bereits Erfahrung mit Emacs haben und nun wissen, wie man Pakete konfiguriert und kleine Elisp-Snippets schreibt. Ich persönlich verwende Straight.el als Paketmanager, minimal-emacs.d als minimale Vanilla-Emacs-Konfiguration (zusammen mit meinen eigenen Anpassungen), dape als Debug-Adapter-Client und eglot als meinen LSP-Client.

Erforderliche Emacs-Pakete

Für Emacs 29-Benutzer ist eglot integriert. Schauen Sie sich die Konfiguration von eglot für gopls und einige erweiterte Gopls-Einstellungen an. Wir fügen zuerst dape:
hinzu

(use-package dape
  :straight t
  :config
  ;; Pulse source line (performance hit)
  (add-hook 'dape-display-source-hook 'pulse-momentary-highlight-one-line)

  ;; To not display info and/or buffers on startup
  ;; (remove-hook 'dape-start-hook 'dape-info)
  (remove-hook 'dape-start-hook 'dape-repl))
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Und Go-Modus:

(use-package go-mode
  :straight t
  :mode "\.go\'"
  :hook ((before-save . gofmt-before-save))
  :bind (:map go-mode-map
              ("M-?" . godoc-at-point)
              ("M-." . xref-find-definitions)
              ("M-_" . xref-find-references)
              ;; ("M-*" . pop-tag-mark) ;; Jump back after godef-jump
              ("C-c m r" . go-run))
  :custom
  (gofmt-command "goimports"))
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Installieren der erforderlichen Go-Tools

Installieren Sie Delve und gopls, den LSP-Server:

# Install Delve
go install github.com/go-delve/delve/cmd/dlv@latest

# Install gopls
go install golang.org/x/tools/gopls@latest
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Außerdem habe ich eine Reihe anderer Tools, die ich von Zeit zu Zeit verwende:

go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/onsi/ginkgo/v2/ginkgo@latest

go install -v golang.org/x/tools/cmd/godoc@latest
go install -v golang.org/x/tools/cmd/goimports@latest
go install -v github.com/stamblerre/gocode@latest
go install -v golang.org/x/tools/cmd/gorename@latest
go install -v golang.org/x/tools/cmd/guru@latest
go install -v github.com/cweill/gotests/...@latest

go install -v github.com/davidrjenni/reftools/cmd/fillstruct@latest
go install -v github.com/fatih/gomodifytags@latest
go install -v github.com/godoctor/godoctor@latest
go install -v github.com/haya14busa/gopkgs/cmd/gopkgs@latest
go install -v github.com/josharian/impl@latest
go install -v github.com/rogpeppe/godef@latest
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Dann müssen Sie die entsprechenden Emacs-Pakete konfigurieren:

(use-package ginkgo
  :straight (:type git :host github :repo "garslo/ginkgo-mode")
  :init
  (setq ginkgo-use-pwd-as-test-dir t
        ginkgo-use-default-keys t))

(use-package gotest
  :straight t
  :after go-mode
  :bind (:map go-mode-map
              ("C-c t f" . go-test-current-file)
              ("C-c t t" . go-test-current-test)
              ("C-c t j" . go-test-current-project)
              ("C-c t b" . go-test-current-benchmark)
              ("C-c t c" . go-test-current-coverage)
              ("C-c t x" . go-run)))

(use-package go-guru
  :straight t
  :hook
  (go-mode . go-guru-hl-identifier-mode))

(use-package go-projectile
  :straight t
  :after (projectile go-mode))

(use-package flycheck-golangci-lint
  :straight t
  :hook
  (go-mode . flycheck-golangci-lint-setup))

(use-package go-eldoc
  :straight t
  :hook
  (go-mode . go-eldoc-setup))

(use-package go-tag
  :straight t
  :bind (:map go-mode-map
              ("C-c t a" . go-tag-add)
              ("C-c t r" . go-tag-remove))
  :init (setq go-tag-args (list "-transform" "camelcase")))

(use-package go-fill-struct
  :straight t)

(use-package go-impl
  :straight t)

(use-package go-playground
  :straight t)
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Dape-Konfiguration

Es gibt keinen besonderen Grund, warum ich Dape anstelle von Dap verwende. Als ich noch MinEmacs benutzte, gehörte es dazu und ich habe mich einfach daran gewöhnt. In der Dokumentation heißt es:

  • Dape unterstützt keine launch.json-Dateien. Wenn eine Projektkonfiguration erforderlich ist, verwenden Sie dir-locals und dape-command.
  • Dape verbessert die Ergonomie innerhalb des Minipuffers, indem es Benutzern ermöglicht, PLIST-Einträge mithilfe von Optionen zu ändern oder zu einer vorhandenen Konfiguration hinzuzufügen.
  • Keine Magie, keine speziellen Variablen wie ${workspaceFolder}. Stattdessen werden Funktionen und Variablen aufgelöst, bevor eine neue Sitzung gestartet wird.
  • Versucht sich vorzustellen, wie Debug-Adapterkonfigurationen in Emacs implementiert würden, wenn vscode nie existieren würde.

Wenn Sie jemals mit VSCode gearbeitet haben, wissen Sie bereits, dass es eine launch.json verwendet, um verschiedene Debugging-Profile zu speichern:

(use-package dape
  :straight t
  :config
  ;; Pulse source line (performance hit)
  (add-hook 'dape-display-source-hook 'pulse-momentary-highlight-one-line)

  ;; To not display info and/or buffers on startup
  ;; (remove-hook 'dape-start-hook 'dape-info)
  (remove-hook 'dape-start-hook 'dape-repl))
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Sie haben verschiedene Felder/Eigenschaften, die Sie gemäß dieser Seite in Ihrer Debugging-Konfiguration anpassen können:

Property Description
name Name for your configuration that appears in the drop down in the Debug viewlet
type Always set to "go". This is used by VS Code to figure out which extension should be used for debugging your code
request Either of launch or attach. Use attach when you want to attach to an already running process
mode For launch requests, either of auto, debug, remote, test, exec. For attach requests, use either local or remote
program Absolute path to the package or file to debug when in debug & test mode, or to the pre-built binary file to debug in exec mode
env Environment variables to use when debugging. Example: { "ENVNAME": "ENVVALUE" }
envFile Absolute path to a file containing environment variable definitions
args Array of command line arguments that will be passed to the program being debugged
showLog Boolean indicating if logs from delve should be printed in the debug console
logOutput Comma separated list of delve components for debug output
buildFlags Build flags to be passed to the Go compiler
remotePath Absolute path to the file being debugged on the remote machine
processId ID of the process that needs debugging (for attach request with local mode)

Beispielanwendung

Lassen Sie uns nun unser Wissen in die Praxis umsetzen, indem wir eine echte Anwendung debuggen, die eine REST-API implementiert.

Projektstruktur

Bei unserem Beispiel handelt es sich um eine REST API zur Aufgabenverwaltung mit folgendem Aufbau:

(use-package dape
  :straight t
  :config
  ;; Pulse source line (performance hit)
  (add-hook 'dape-display-source-hook 'pulse-momentary-highlight-one-line)

  ;; To not display info and/or buffers on startup
  ;; (remove-hook 'dape-start-hook 'dape-info)
  (remove-hook 'dape-start-hook 'dape-repl))
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Kernkomponenten

Werfen wir einen Blick auf die Kernkomponenten.

Die Aufgabe stellt unser Kerndomänenmodell dar:

(use-package go-mode
  :straight t
  :mode "\.go\'"
  :hook ((before-save . gofmt-before-save))
  :bind (:map go-mode-map
              ("M-?" . godoc-at-point)
              ("M-." . xref-find-definitions)
              ("M-_" . xref-find-references)
              ;; ("M-*" . pop-tag-mark) ;; Jump back after godef-jump
              ("C-c m r" . go-run))
  :custom
  (gofmt-command "goimports"))
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Der TaskStore verwaltet unsere In-Memory-Datenoperationen:

# Install Delve
go install github.com/go-delve/delve/cmd/dlv@latest

# Install gopls
go install golang.org/x/tools/gopls@latest
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

REST-API

Die API stellt folgende Endpunkte bereit:

  • POST /task/create – Erstellt eine neue Aufgabe
  • GET /task/get?id= – Ruft eine Aufgabe nach ID ab
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/onsi/ginkgo/v2/ginkgo@latest

go install -v golang.org/x/tools/cmd/godoc@latest
go install -v golang.org/x/tools/cmd/goimports@latest
go install -v github.com/stamblerre/gocode@latest
go install -v golang.org/x/tools/cmd/gorename@latest
go install -v golang.org/x/tools/cmd/guru@latest
go install -v github.com/cweill/gotests/...@latest

go install -v github.com/davidrjenni/reftools/cmd/fillstruct@latest
go install -v github.com/fatih/gomodifytags@latest
go install -v github.com/godoctor/godoctor@latest
go install -v github.com/haya14busa/gopkgs/cmd/gopkgs@latest
go install -v github.com/josharian/impl@latest
go install -v github.com/rogpeppe/godef@latest
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Server

Hier ist die Serverimplementierung:

(use-package ginkgo
  :straight (:type git :host github :repo "garslo/ginkgo-mode")
  :init
  (setq ginkgo-use-pwd-as-test-dir t
        ginkgo-use-default-keys t))

(use-package gotest
  :straight t
  :after go-mode
  :bind (:map go-mode-map
              ("C-c t f" . go-test-current-file)
              ("C-c t t" . go-test-current-test)
              ("C-c t j" . go-test-current-project)
              ("C-c t b" . go-test-current-benchmark)
              ("C-c t c" . go-test-current-coverage)
              ("C-c t x" . go-run)))

(use-package go-guru
  :straight t
  :hook
  (go-mode . go-guru-hl-identifier-mode))

(use-package go-projectile
  :straight t
  :after (projectile go-mode))

(use-package flycheck-golangci-lint
  :straight t
  :hook
  (go-mode . flycheck-golangci-lint-setup))

(use-package go-eldoc
  :straight t
  :hook
  (go-mode . go-eldoc-setup))

(use-package go-tag
  :straight t
  :bind (:map go-mode-map
              ("C-c t a" . go-tag-add)
              ("C-c t r" . go-tag-remove))
  :init (setq go-tag-args (list "-transform" "camelcase")))

(use-package go-fill-struct
  :straight t)

(use-package go-impl
  :straight t)

(use-package go-playground
  :straight t)
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Schauen wir uns unsere Hauptfunktion an:

{
    "name": "Launch file",
    "type": "go",
    "request": "launch",
    "mode": "auto",
    "program": "${file}"
}
Nach dem Login kopieren
Nach dem Login kopieren

Anwendung erstellen

Lass uns den Server starten:

taskapi/
├── go.mod
├── go.sum
├── main.go
├── task_store.go
└── task_test.go
Nach dem Login kopieren
Nach dem Login kopieren

Jetzt von einem anderen Terminal aus eine neue Aufgabe erstellen
:

import (
    "fmt"
)

type Task struct {
    ID          int    `json:"id"`
    Title       string `json:"title"`
    Description string `json:"description"`
    Done        bool   `json:"done"`
}
Nach dem Login kopieren
Nach dem Login kopieren


Antwort:

type TaskStore struct {
    tasks  map[int]Task
    nextID int
}

func NewTaskStore() *TaskStore {
    return &TaskStore{
        tasks:  make(map[int]Task),
        nextID: 1,
    }
}
Nach dem Login kopieren
Nach dem Login kopieren


Mal sehen, ob wir es holen können:

// CreateTask stores a given Task internally
func (ts *TaskStore) CreateTask(task Task) Task {
    task.ID = ts.nextID
    ts.tasks[task.ID] = task
    ts.nextID++
    return task
}

// GetTask retrieves a Task by ID
func (ts *TaskStore) GetTask(id int) (Task, error) {
    task, exists := ts.tasks[id]
    if !exists {
        return Task{}, fmt.Errorf("task with id %d not found", id)
    }
    return task, nil
}

// UpdateTask updates task ID with a new Task object
func (ts *TaskStore) UpdateTask(id int, task Task) error {
    if _, exists := ts.tasks[id]; !exists {
        return fmt.Errorf("task with id %d not found", id)
    }
    task.ID = id
    ts.tasks[id] = task
    return nil
}
Nach dem Login kopieren
Nach dem Login kopieren


Antwort:

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

// Server implements a web application for managing tasks
type Server struct {
    store *TaskStore
}

func (s *Server) handleCreateTask(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }
    var task Task
    if err := json.NewDecoder(r.Body).Decode(&task); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    createdTask := s.store.CreateTask(task)
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(createdTask)
}

func (s *Server) handleGetTask(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }
    id := 0
    fmt.Sscanf(r.URL.Query().Get("id"), "%d", &id)

    task, err := s.store.GetTask(id)
    if err != nil {
        http.Error(w, err.Error(), http.StatusNotFound)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(task)
}
Nach dem Login kopieren

Unit-Tests


Im Folgenden finden Sie einige Unit-Tests (geschrieben in Ginkgo) für den TaskStore:

package main

import (
    "log"
    "net/http"
)

func main() {
    store := NewTaskStore()
    server := &Server{store: store}
    http.HandleFunc("/task/create", server.handleCreateTask)
    http.HandleFunc("/task/get", server.handleGetTask)

    log.Printf("Starting server on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
Nach dem Login kopieren
go build -o taskapi *.go
./taskapi
2024/11/14 07:03:48 Starting server on :8080
Nach dem Login kopieren

In Emacs würde ich dann ginkgo-run-this-container aufrufen, wie in diesem Screenshot gezeigt:

Mastering Golang Debugging in Emacs

Grundlegendes Debuggen mit Delve und Dape

Um unsere Task-API zu debuggen, haben wir folgende Ansätze:
  • Wir können die Anwendung direkt starten
  • und debuggen
  • wir können an einen laufenden Prozess anhängen
  • wir können an eine laufende Debugging-Sitzung anhängen
Hier sind die Optionen für verschiedene Anfragetypen:

Anfrage Modus erforderlich optional
Start
request mode required optional
launch debug program dlvCwd, env, backend, args, cwd, buildFlags, output, noDebug
test program dlvCwd, env, backend, args, cwd, buildFlags, output, noDebug
exec program dlvCwd, env, backend, args, cwd, noDebug
core program, corefilePath dlvCwd, env
replay traceDirPath dlvCwd, env
attach local processId backend
remote
Debug Programm dlvCwd, env, backend, args, cwd, buildFlags, Ausgabe, noDebug Test Programm dlvCwd, env, backend, args, cwd, buildFlags, Ausgabe, noDebug exec Programm dlvCwd, env, backend, args, cwd, noDebug Kern Programm, corefilePath dlvCwd, env Wiedergabe traceDirPath dlvCwd, env anhängen lokal Prozess-ID Backend remote

Profil 1: Anwendung starten

Hier ist unser erstes Debugging-Profil für .dir-locals.el:

(use-package dape
  :straight t
  :config
  ;; Pulse source line (performance hit)
  (add-hook 'dape-display-source-hook 'pulse-momentary-highlight-one-line)

  ;; To not display info and/or buffers on startup
  ;; (remove-hook 'dape-start-hook 'dape-info)
  (remove-hook 'dape-start-hook 'dape-repl))
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

? Möglicherweise möchten Sie einen anderen Wert für command-cwd verwenden. In meinem Fall wollte ich den Debugger in einem Verzeichnis starten, das derzeit kein Projekt ist. Standardverzeichnis ist eine Variable, die das Arbeitsverzeichnis für den aktuellen Puffer enthält, in dem Sie sich gerade befinden.

Debuggen starten:

  • Führen Sie dape-info aus, um Debugging-Informationen anzuzeigen

Mastering Golang Debugging in Emacs

  • Haltepunkt erstellen mit dape-breakpoint-toggle:

Mastering Golang Debugging in Emacs

Nachdem Sie den Debugger mit diesem Profil gestartet haben, sollten Sie im dape-repl-Puffer Folgendes sehen:

(use-package go-mode
  :straight t
  :mode "\.go\'"
  :hook ((before-save . gofmt-before-save))
  :bind (:map go-mode-map
              ("M-?" . godoc-at-point)
              ("M-." . xref-find-definitions)
              ("M-_" . xref-find-references)
              ;; ("M-*" . pop-tag-mark) ;; Jump back after godef-jump
              ("C-c m r" . go-run))
  :custom
  (gofmt-command "goimports"))
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Beachten Sie, dass wir keine Binärdatei/Datei zum Debuggen angegeben haben (wir hatten :program „.“ in .dir-locals.el). delve erstellt die Binärdatei automatisch, bevor die Anwendung gestartet wird:

# Install Delve
go install github.com/go-delve/delve/cmd/dlv@latest

# Install gopls
go install golang.org/x/tools/gopls@latest
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Profil 2: An einen externen Debugger anschließen

Fügen wir ein Profil hinzu, um eine Verbindung zu einer vorhandenen Debugging-Sitzung herzustellen:

go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/onsi/ginkgo/v2/ginkgo@latest

go install -v golang.org/x/tools/cmd/godoc@latest
go install -v golang.org/x/tools/cmd/goimports@latest
go install -v github.com/stamblerre/gocode@latest
go install -v golang.org/x/tools/cmd/gorename@latest
go install -v golang.org/x/tools/cmd/guru@latest
go install -v github.com/cweill/gotests/...@latest

go install -v github.com/davidrjenni/reftools/cmd/fillstruct@latest
go install -v github.com/fatih/gomodifytags@latest
go install -v github.com/godoctor/godoctor@latest
go install -v github.com/haya14busa/gopkgs/cmd/gopkgs@latest
go install -v github.com/josharian/impl@latest
go install -v github.com/rogpeppe/godef@latest
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Jetzt starten wir den Debugger auf der CLI:

(use-package ginkgo
  :straight (:type git :host github :repo "garslo/ginkgo-mode")
  :init
  (setq ginkgo-use-pwd-as-test-dir t
        ginkgo-use-default-keys t))

(use-package gotest
  :straight t
  :after go-mode
  :bind (:map go-mode-map
              ("C-c t f" . go-test-current-file)
              ("C-c t t" . go-test-current-test)
              ("C-c t j" . go-test-current-project)
              ("C-c t b" . go-test-current-benchmark)
              ("C-c t c" . go-test-current-coverage)
              ("C-c t x" . go-run)))

(use-package go-guru
  :straight t
  :hook
  (go-mode . go-guru-hl-identifier-mode))

(use-package go-projectile
  :straight t
  :after (projectile go-mode))

(use-package flycheck-golangci-lint
  :straight t
  :hook
  (go-mode . flycheck-golangci-lint-setup))

(use-package go-eldoc
  :straight t
  :hook
  (go-mode . go-eldoc-setup))

(use-package go-tag
  :straight t
  :bind (:map go-mode-map
              ("C-c t a" . go-tag-add)
              ("C-c t r" . go-tag-remove))
  :init (setq go-tag-args (list "-transform" "camelcase")))

(use-package go-fill-struct
  :straight t)

(use-package go-impl
  :straight t)

(use-package go-playground
  :straight t)
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Jetzt können Sie in Emacs dape starten und das Profil go-attach-taskapi auswählen:

Mastering Golang Debugging in Emacs

Profil 3: An einen laufenden Prozess anhängen

In diesem Szenario wird die Anwendung bereits ausgeführt, aber Sie möchten den Debugger daran anhängen. Starten Sie zuerst die Anwendung:

{
    "name": "Launch file",
    "type": "go",
    "request": "launch",
    "mode": "auto",
    "program": "${file}"
}
Nach dem Login kopieren
Nach dem Login kopieren

Finden Sie die Prozess-ID (PID) heraus:

taskapi/
├── go.mod
├── go.sum
├── main.go
├── task_store.go
└── task_test.go
Nach dem Login kopieren
Nach dem Login kopieren

Fügen wir ein weiteres Debug-Profil hinzu:

import (
    "fmt"
)

type Task struct {
    ID          int    `json:"id"`
    Title       string `json:"title"`
    Description string `json:"description"`
    Done        bool   `json:"done"`
}
Nach dem Login kopieren
Nach dem Login kopieren

Wir benötigen eine Hilfsfunktion:

type TaskStore struct {
    tasks  map[int]Task
    nextID int
}

func NewTaskStore() *TaskStore {
    return &TaskStore{
        tasks:  make(map[int]Task),
        nextID: 1,
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

Mastering Golang Debugging in Emacs

Jetzt starte ich den Debugger:

Mastering Golang Debugging in Emacs

Wenn ich jetzt eine POST-Anfrage wie diese sende:

// CreateTask stores a given Task internally
func (ts *TaskStore) CreateTask(task Task) Task {
    task.ID = ts.nextID
    ts.tasks[task.ID] = task
    ts.nextID++
    return task
}

// GetTask retrieves a Task by ID
func (ts *TaskStore) GetTask(id int) (Task, error) {
    task, exists := ts.tasks[id]
    if !exists {
        return Task{}, fmt.Errorf("task with id %d not found", id)
    }
    return task, nil
}

// UpdateTask updates task ID with a new Task object
func (ts *TaskStore) UpdateTask(id int, task Task) error {
    if _, exists := ts.tasks[id]; !exists {
        return fmt.Errorf("task with id %d not found", id)
    }
    task.ID = id
    ts.tasks[id] = task
    return nil
}
Nach dem Login kopieren
Nach dem Login kopieren

Der Debugger sollte automatisch am festgelegten Haltepunkt anhalten:

Mastering Golang Debugging in Emacs

Debuggen von Ginkgo-Tests

Es ist von entscheidender Bedeutung, Tests in Golang debuggen zu können. Zum Durchführen von Ginkgo-Tests verwende ich den Ginkgo-Modus, der mehrere Funktionen bietet:

Mastering Golang Debugging in Emacs

Mastering Golang Debugging in Emacs

Und als Ausgabe erhalte ich:

(use-package dape
  :straight t
  :config
  ;; Pulse source line (performance hit)
  (add-hook 'dape-display-source-hook 'pulse-momentary-highlight-one-line)

  ;; To not display info and/or buffers on startup
  ;; (remove-hook 'dape-start-hook 'dape-info)
  (remove-hook 'dape-start-hook 'dape-repl))
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Dape-Konfiguration für Ginkgo

Dies ist die Grundkonfiguration zum Debuggen von Ginkgo-Tests:

(use-package go-mode
  :straight t
  :mode "\.go\'"
  :hook ((before-save . gofmt-before-save))
  :bind (:map go-mode-map
              ("M-?" . godoc-at-point)
              ("M-." . xref-find-definitions)
              ("M-_" . xref-find-references)
              ;; ("M-*" . pop-tag-mark) ;; Jump back after godef-jump
              ("C-c m r" . go-run))
  :custom
  (gofmt-command "goimports"))
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Wenn ich das Debug-Profil go-test-ginkgo wähle, sollte ich in der Lage sein, die Tests zu debuggen:

Mastering Golang Debugging in Emacs

Jetzt ist die Konfiguration recht statisch und daher kann man den Unit-Test/Container nicht vorab auswählen. Wir müssen den Parameter -ginkgo.focus irgendwie dynamisch machen:

# Install Delve
go install github.com/go-delve/delve/cmd/dlv@latest

# Install gopls
go install golang.org/x/tools/gopls@latest
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Mastering Golang Debugging in Emacs

Wenn ich mir danach die Variable dape-configs anschaue, sollte ich diesen Wert sehen:

go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/onsi/ginkgo/v2/ginkgo@latest

go install -v golang.org/x/tools/cmd/godoc@latest
go install -v golang.org/x/tools/cmd/goimports@latest
go install -v github.com/stamblerre/gocode@latest
go install -v golang.org/x/tools/cmd/gorename@latest
go install -v golang.org/x/tools/cmd/guru@latest
go install -v github.com/cweill/gotests/...@latest

go install -v github.com/davidrjenni/reftools/cmd/fillstruct@latest
go install -v github.com/fatih/gomodifytags@latest
go install -v github.com/godoctor/godoctor@latest
go install -v github.com/haya14busa/gopkgs/cmd/gopkgs@latest
go install -v github.com/josharian/impl@latest
go install -v github.com/rogpeppe/godef@latest
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Nachdem ich den Debugger (mit dem debug-focused-test-Profil) im Dape-Repl-Puffer gestartet habe, erhalte ich Folgendes:

(use-package ginkgo
  :straight (:type git :host github :repo "garslo/ginkgo-mode")
  :init
  (setq ginkgo-use-pwd-as-test-dir t
        ginkgo-use-default-keys t))

(use-package gotest
  :straight t
  :after go-mode
  :bind (:map go-mode-map
              ("C-c t f" . go-test-current-file)
              ("C-c t t" . go-test-current-test)
              ("C-c t j" . go-test-current-project)
              ("C-c t b" . go-test-current-benchmark)
              ("C-c t c" . go-test-current-coverage)
              ("C-c t x" . go-run)))

(use-package go-guru
  :straight t
  :hook
  (go-mode . go-guru-hl-identifier-mode))

(use-package go-projectile
  :straight t
  :after (projectile go-mode))

(use-package flycheck-golangci-lint
  :straight t
  :hook
  (go-mode . flycheck-golangci-lint-setup))

(use-package go-eldoc
  :straight t
  :hook
  (go-mode . go-eldoc-setup))

(use-package go-tag
  :straight t
  :bind (:map go-mode-map
              ("C-c t a" . go-tag-add)
              ("C-c t r" . go-tag-remove))
  :init (setq go-tag-args (list "-transform" "camelcase")))

(use-package go-fill-struct
  :straight t)

(use-package go-impl
  :straight t)

(use-package go-playground
  :straight t)
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

?Beachten Sie, dass nur „1 von 5 Spezifikationen“ (❶) ausgeführt wurde, was bedeutet, dass sich Ginkgo nur auf den von uns angegebenen Container konzentriert hat (❷).

Best Practices und Tipps

Während meiner Debugging-Erfahrung habe ich mehrere Best Practices schätzen gelernt:

  • Verwenden Sie Versionskontrolle zum Debuggen von Konfigurationen
  • Behalten Sie Debug-Konfigurationen in .dir-locals.el bei
  • Verwenden Sie aussagekräftige Namen für Konfigurationen
  • Erstellen Sie projektspezifische Debugging-Hilfsfunktionen
  • Nehmen Sie Anpassungen lokal vor (pufferspezifisch)

Ressourcen und Referenzen

  • vscode-go/docs/debugging.md unter master · golang/vscode-go
  • delve/dlv dap-mode direkt unterstützen · Problem Nr. 318 · emacs-lsp/dap-mode
  • Dape GitHub Repository
  • Delve Debugger
  • Eglot-Dokumentation
  • Ginkgo Testing Framework

Das obige ist der detaillierte Inhalt vonGolang-Debugging in Emacs beherrschen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage