티스토리 뷰

etc/Vim

neovim에서 dap 세팅하기 (자바스크립트)

기억용블로그 2023. 6. 23. 02:17
728x90

DAP 세팅

lunarvim(혹은 neovim)에서 DAP를 세팅하는 방법에 대해 알아봅니다.

DAP란?

DAP는 마이크로소프트에서 본인들의 제품인 vscode에 적용하기 위해 작성한 명세서LSP 와 같은 개념으로 이해할 수 있습니다.

다만 대부분의 텍스트 에디터에서 LSP는 중요한 기능이므로 first-class 취급을 받는데 반해 DAP는 상대적으로 등한시되는 부분이 분명히 존재합니다. 실제로 neovim에서 DAP는 built-in이 되어있지 않기도 하며 이로 인해 기본적인 세팅을 하는데 상당한 어려움이 존재하여 이에 대한 가이드를 작성합니다.

DAP 구조 이해하기

DAP는 크게 아래 3개의 컴포넌트로 구성되어 있습니다.

DAP Adapter

위에서도 언급하였듯이 DAP는 실체가 없는 명세서이므로 이를 실제로 구현한 구현체가 먼저 필요합니다. DAP를 구현한 구현체는 DAP Adapter라 불리며 이 어댑터는 유저가 조작하는 DAP Client와 실제로 디버깅을 진행하는 Debugger를 연결하는 bridge로서 동작하게 됩니다. 이 구현체들의 목록은 여기에서 확인하실 수 있습니다.

DAP Client

DAP Adapterplatform agnostic합니다. 이는 어댑터라는 구현체는 vscode, neovim과 같은 텍스트 에디터 하나에 종속된 구현체가 아니라는 의미이며 이 어댑터를 neovim에서 사용하기 위해선 DAP Client가 필요합니다. 위에서 neovim에선 DAP가 first-class가 아니라는 말을 했었는데 이 말은 neovim이 lsp 플러그인을 built-in으로 가지고 있는 것과 다르게 DAP 플러그인을 유저가 직접 다운로드하고 설치해서 사용해야 한다는 의미입니다.

neovim에서 이 클라이언트의 역할을 하는 것이 nvim-dapnvim-dap-ui입니다. 자세한 설명은 밑에서 계속 하겠습니다.

Debugger

DAP Adapter에 의해 실제로 코드를 분석하고 디버깅을 진행하는 컴포넌트인 디버거 직접 다운로드해서 사용해야 합니다.

간단 정리

만약 DAP를 처음 접하시는 분들은 이게 도대체 무슨 소리를 하는건지 감도 잡히지 않으실 것같아 한 번 더 간단하게 정리해보겠습니다. 쉽게 말해 디버깅 기능은 이쪽에서 완전 곁다리 취급을 받고 있고 단 하나의 플러그인이나 익스텐션을 어디서도 관리해주지 않기 때문에 DAP client, DAP adapter, Debugger과 같은 요소들을 모두 유저가 직접 다운받고 설치하고 경로까지 다 일일히 설정해주어야 한다고 보시면 됩니다.

위에서 설명한 요소들의 의존성을 간략하게 모식화해보면 다음과 같습니다:

User -> DAP Client UI (extension) -> DAP Client (Plugin)
-> DAP Adapter (Component) -> Debugger (Component)

디버깅을 진행하고자 하는 유저는 UI를 통해서 조작하며 UI는 DAP client의 익스텐션이고 DAP Client는 neovim의 플러그인으로 neovim 밖에 존재하는 DAP Adapter와 소통하는 역할을 하며 DAP Adapter는 실제 디버깅을 진행하는 Debugger와 소통하는 브리지 역할을 하게 됩니다.

DAP 설치

위에 설명을 굳이 길게 한 이유는 이 구조를 이해하지 않고는 도대체 왜 이렇게 많은 것들을 다운받아야 하나 생각이 들기 때문입니다. 위 설명이 잘 이해가 되지 않으시더라도 아~ 아무튼 뭐 이것저것 받아야 되는구나 정도만 이해가 되셨어도 충분합니다.

DAP Client 설치

먼저 DAP client부터 설치를 시작합니다. 이때 lunarvim을 사용하시는 분들은 client와 ui가 이미 설치가 되어 있으므로 간단하게 다음 설정을 통해 사용할 것을 명시하시기만 하면 됩니다.

-- ~/.config/lvim/config.lua

lvim.builtin.dap.active = true

만약 lunarvim이 뭔지 궁금하시거나 이걸 사용하고 싶으신 분들은 제가 작성해둔 가이드 를 참고하시면 되겠습니다.

neovim을 사용하시는 분들은 사용하시는 패키지 매니저에 따라 다음 2개의 플러그인을 설치하시면 됩니다:

그 다음 플러그인을 사용하기 위한 키매핑을 진행하시면 되는데 편하신대로 세팅하시면 되며 밑에 간단한 예시를 첨부하겠습니다:

  • neovim을 사용하시는 경우
-- https://gist.github.com/mengwangk/9be5794515f0c56016a5b1fe4a2297e1

local M = {}

local whichkey = require "which-key"

-- local function keymap(lhs, rhs, desc)
--   vim.keymap.set("n", lhs, rhs, { silent = true, desc = desc })
-- end

function M.setup()
  local keymap = {
    d = {
      name = "Debug",
      R = { "<cmd>lua require'dap'.run_to_cursor()<cr>", "Run to Cursor" },
      E = { "<cmd>lua require'dapui'.eval(vim.fn.input '[Expression] > ')<cr>", "Evaluate Input" },
      C = { "<cmd>lua require'dap'.set_breakpoint(vim.fn.input '[Condition] > ')<cr>", "Conditional Breakpoint" },
      U = { "<cmd>lua require'dapui'.toggle()<cr>", "Toggle UI" },
      b = { "<cmd>lua require'dap'.step_back()<cr>", "Step Back" },
      c = { "<cmd>lua require'dap'.continue()<cr>", "Continue" },
      d = { "<cmd>lua require'dap'.disconnect()<cr>", "Disconnect" },
      e = { "<cmd>lua require'dapui'.eval()<cr>", "Evaluate" },
      g = { "<cmd>lua require'dap'.session()<cr>", "Get Session" },
      h = { "<cmd>lua require'dap.ui.widgets'.hover()<cr>", "Hover Variables" },
      S = { "<cmd>lua require'dap.ui.widgets'.scopes()<cr>", "Scopes" },
      i = { "<cmd>lua require'dap'.step_into()<cr>", "Step Into" },
      o = { "<cmd>lua require'dap'.step_over()<cr>", "Step Over" },
      p = { "<cmd>lua require'dap'.pause.toggle()<cr>", "Pause" },
      q = { "<cmd>lua require'dap'.close()<cr>", "Quit" },
      r = { "<cmd>lua require'dap'.repl.toggle()<cr>", "Toggle Repl" },
      s = { "<cmd>lua require'dap'.continue()<cr>", "Start" },
      t = { "<cmd>lua require'dap'.toggle_breakpoint()<cr>", "Toggle Breakpoint" },
      x = { "<cmd>lua require'dap'.terminate()<cr>", "Terminate" },
      u = { "<cmd>lua require'dap'.step_out()<cr>", "Step Out" },
    },
  }

  whichkey.register(keymap, {
    mode = "n",
    prefix = "<leader>",
    buffer = nil,
    silent = true,
    noremap = true,
    nowait = false,
  })

  local keymap_v = {
    name = "Debug",
    e = { "<cmd>lua require'dapui'.eval()<cr>", "Evaluate" },
  }
  whichkey.register(keymap_v, {
    mode = "v",
    prefix = "<leader>",
    buffer = nil,
    silent = true,
    noremap = true,
    nowait = false,
  })
end

return M
  • lunarvim을 사용하시는 경우:
lvim.builtin.which_key.mappings["d"] = {
    name = "Debug",
    R = { "<cmd>lua require'dap'.run_to_cursor()<cr>", "Run to Cursor" },
    E = { "<cmd>lua require'dapui'.eval(vim.fn.input '[Expression] > ')<cr>", "Evaluate Input" },
    C = { "<cmd>lua require'dap'.set_breakpoint(vim.fn.input '[Condition] > ')<cr>", "Conditional Breakpoint" },
    U = { "<cmd>lua require'dapui'.toggle()<cr>", "Toggle UI" },
    b = { "<cmd>lua require'dap'.step_back()<cr>", "Step Back" },
    c = { "<cmd>lua require'dap'.continue()<cr>", "Continue" },
    d = { "<cmd>lua require'dap'.disconnect()<cr>", "Disconnect" },
    e = { "<cmd>lua require'dapui'.eval()<cr>", "Evaluate" },
    g = { "<cmd>lua require'dap'.session()<cr>", "Get Session" },
    h = { "<cmd>lua require'dap.ui.widgets'.hover()<cr>", "Hover Variables" },
    S = { "<cmd>lua require'dap.ui.widgets'.scopes()<cr>", "Scopes" },
    i = { "<cmd>lua require'dap'.step_into()<cr>", "Step Into" },
    o = { "<cmd>lua require'dap'.step_over()<cr>", "Step Over" },
    p = { "<cmd>lua require'dap'.pause.toggle()<cr>", "Pause" },
    q = { "<cmd>lua require'dap'.close()<cr>", "Quit" },
    r = { "<cmd>lua require'dap'.repl.toggle()<cr>", "Toggle Repl" },
    s = { "<cmd>lua require'dap'.continue()<cr>", "Start" },
    t = { "<cmd>lua require'dap'.toggle_breakpoint()<cr>", "Toggle Breakpoint" },
    x = { "<cmd>lua require'dap'.terminate()<cr>", "Terminate" },
    u = { "<cmd>lua require'dap'.step_out()<cr>", "Step Out" },
}

그리고 dap-ui를 조작하기 위한 다음 설정도 따로 키매핑을 하시는 것을 추천합니다:

require("dapui").open()
require("dapui").close()
require("dapui").toggle()

DAP Adapter & Debugger 설치

여기서부터 설정이 상당히 난해해지는데 사용하시려는 언어마다 전부 다른 설정과 다른 다운로드 방법을 가지고 있으므로 모든 언어에 맞는 설정과 다운로드를 직접 각각 다 진행하셔야 합니다. 여기서는 자바스크립트를 기준으로 설명하도록 하겠습니다.

자세한 설치 방법과 리스트는 여기에서 확인하실 수 있습니다.

Lazy 패키지 매니저 기준 다음과 같이 설정합니다:

{
    -- DAP Client
    "mfussenegger/nvim-dap",
    lazy = true,
    dependencies = {
        -- DAP Client UI
        "rcarriga/nvim-dap-ui",
        -- DAP Adapter
        "mxsdev/nvim-dap-vscode-js",
        -- Debugger
        {
            "microsoft/vscode-js-debug",
            version = "1.x",
            -- 커맨드 꼭 이대로 사용하기
            build = "npm i && npm run compile vsDebugServerBundle && mv dist out"
        }
    },
    keys = { ... },
    -- DAP Adater 설정
    config = function()
        require("dap-vscode-js").setup({
            debugger_path =
            -- vscode-js-debug의 `절대 경로`. 꼭 직접 확인 후 설정해주셔야 합니다.
            "/home/{user}/.local/share/lunarvim/site/pack/lazy/opt/vscode-js-debug",
            -- 사용하고자 하는 어댑터.
            adapters = { 'pwa-node', 'pwa-chrome', 'pwa-msedge', 'node-terminal', 'pwa-extensionHost' },
        })
        require("dapui").setup()
    end
},

전부 복사 붙여넣기로 사용하시면 되는데 debugger_path를 유념해서 확인하셔야 합니다. 저는 lunarvim의 패키지 매니저인 lazy를 통해 vscode-js-debug를 다운받았으므로 해당 플러그인의 경로를 확인 후 위와 같이 작성해주었습니다.

neovim을 사용하시거나 다른 패키지 매니저를 사용하시는 경우 꼭 직접 확인하시고나서 아래와 같이 절대 경로로 넣어주시면 되겠습니다.

"/home/{user}/.local/share/lunarvim/site/pack/lazy/opt/vscode-js-debug"

그리고 제 경우 build = "npm i && npm run compile vsDebugServerBundle && mv dist out" 옵션이 매번 에디터를 켤때마다 실행되는 문제가 있어 build를 따로 주지 않고 직접 경로로 찾아가서 커맨드를 실행해주었습니다.

설정

마지막으로 다음 설정을 통해 dap를 사용하도록 설정하시면 됩니다. neovim이나 lunarvim 모두 동일합니다.

더 자세한 설정은 여기에서 확인하실 수 있습니다.

-- 적용하고자 하는 언어
local js_based_languages = { "typescript", "javascript", "typescriptreact" }

for _, language in ipairs(js_based_languages) do
    require("dap").configurations[language] = {
        {
            type = "pwa-node",
            request = "launch",
            name = "Launch file",
            program = "${file}",
            cwd = "${workspaceFolder}",
        },
        {
            type = "pwa-node",
            request = "attach",
            name = "Attach",
            processId = require 'dap.utils'.pick_process,
            cwd = "${workspaceFolder}",
        },
        {
            type = "pwa-chrome",
            request = "launch",
            name = "Start Chrome with \"localhost\"",
            url = "http://localhost:3000",
            webRoot = "${workspaceFolder}",
            userDataDir = "${workspaceFolder}/.vscode/vscode-chrome-debug-userdatadir"
        }
    }
end

여기까지 설정이 전부 끝나셨다면 이제 소스 코드에 들어가서 위에서 키매핑을 해둔대로 toggle을 걸고 start를 해보는걸로 DAP 설정이 된 것을 확인해볼 수 있습니다!

References

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함