aboutsummaryrefslogtreecommitdiff
path: root/.nvim.lua
blob: 0748af9c5752e917a25215e9a79a0bf3b15003f4 (plain)
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
local workspace_folder = vim.fn.getcwd()

-- Formatting
vim.g.autoformat = true
vim.opt.fixeol = false

-- Enable Doxygen
vim.g.load_doxygen_syntax = true

local function safe_require(module)
  local ok, mod = pcall(require, module)
  if not ok then return nil end
  return mod
end

-- C++
local default_clangd_config = vim.deepcopy(vim.lsp.config["clangd"]) or {}
default_clangd_config.cmd = {
  "clangd",
  "--background-index",
  "--clang-tidy",
  "--header-insertion=iwyu",
  "--completion-style=detailed",
  string.format("--compile-commands-dir=%s/build", workspace_folder),
  "--query-driver=**/x86_64-pc-elf-g++"
}

vim.lsp.config("clangd", default_clangd_config)

vim.filetype.add({
  pattern = {
    [".*/include/kstd/.*"] = function(path, _)
      if not path:match("%.[^/]+$") then
        return "cpp"
      end
    end
  }
})

-- File Browser
local neo_tree = safe_require("neo-tree")
if neo_tree then
  local current_config = neo_tree.config or {}
  local project_config = vim.tbl_deep_extend("force", current_config, {
    nesting_rules = {
      ['*.hpp'] = {
        pattern = "(.*).hpp",
        files = { "%1.cpp", "%1.test.cpp", "%1.tests.cpp" }
      }
    }
  })
  neo_tree.setup(project_config)
end

-- Debugging
local dap = require("dap")
local qemu_job_id = nil
local qemu_bufnr = nil

local function resolve_build_paths(artifact_path)
  local base_path = string.gsub(artifact_path, "%.[%w]+$", "")
  local build_type = artifact_path:match("bin/([^/]+)/") or "Debug"
  return {
    iso = base_path .. ".iso",
    elf = base_path .. ".elf",
    sym = base_path .. ".sym",
    build_type = build_type,
  }
end

local function launch_qemu(artifact_path, is_debug)
  if qemu_job_id then
    vim.fn.jobstop(qemu_job_id)
    qemu_job_id = nil
  end

  local paths = resolve_build_paths(artifact_path)
  local debug_flag = is_debug and "1" or "0"
  local shell_cmd = string.format("%s/scripts/qemu-wrapper.sh '%s' '%s' '%s' '%s'",
    workspace_folder, workspace_folder, paths.build_type, paths.iso, debug_flag)

  vim.cmd("botright vsplit | enew")
  qemu_bufnr = vim.api.nvim_get_current_buf()
  vim.api.nvim_buf_set_name(qemu_bufnr, "TeachOS QEMU (" .. paths.build_type .. ")")
  vim.opt_local.number = false
  vim.opt_local.relativenumber = false
  vim.opt_local.signcolumn = "no"

  qemu_job_id = vim.fn.termopen(shell_cmd, {
    on_exit = function() qemu_job_id = nil end
  })

  vim.cmd("wincmd p")
end

dap.adapters.teachos_qemu_mi = function(callback, config)
  local artifact_path = config.program
  if not artifact_path then
    vim.notify("Fatal: No artifact path resolved by CMake", vim.log.levels.ERROR)
    return
  end

  launch_qemu(artifact_path, true)

  local paths = resolve_build_paths(artifact_path)
  config.setupCommands = {
    { description = "Enable pretty-printing for gdb", text = "-enable-pretty-printing" },
    { description = "Load code",                      text = string.format("-file-exec-file %s", paths.elf) },
    { description = "Load symbols",                   text = string.format("-file-exec-and-symbols %s", paths.sym) },
  }
  config.program = paths.sym

  vim.defer_fn(function()
    callback({
      type = "executable",
      command = vim.fn.stdpath("data") .. "/mason/bin/OpenDebugAD7",
      id = "cppdbg",
    })
  end, 500)
end

local function teardown_qemu()
  if qemu_job_id then
    vim.fn.jobstop(qemu_job_id)
    qemu_job_id = nil
  end

  if qemu_bufnr and vim.api.nvim_buf_is_valid(qemu_bufnr) then
    vim.api.nvim_buf_delete(qemu_bufnr, { force = true })
    qemu_bufnr = nil
  end
end

dap.listeners.after.event_terminated["teachos_qemu_teardown"] = teardown_qemu
dap.listeners.after.event_exited["teachos_qemu_teardown"] = teardown_qemu
dap.listeners.after.disconnect["teachos_qemu_teardown"] = teardown_qemu

local cmake_tools = safe_require("cmake-tools")
if cmake_tools then
  require("cmake-tools").setup({
    cmake_compile_commands_options = {
      action = "copy",
      target = string.format("%s/build", workspace_folder),
    },
    cmake_dap_configuration = {
      name = "(gdb) QEMU MI Attach",
      type = "teachos_qemu_mi",
      request = "launch",
      MIMode = "gdb",
      cwd = workspace_folder,
      miDebuggerServerAddress = "localhost:1234",
      miDebuggerPath = "x86_64-pc-elf-gdb",
      stopAtEntry = true,
    }
  })

  vim.api.nvim_create_user_command("TeachOSRun", function()
    local cmake = safe_require("cmake-tools")
    if not cmake then return end
    local is_ready, target = pcall(cmake.get_launch_target_path)
    if is_ready and target then
      launch_qemu(target, false)
    else
      vim.notify("Fatal: Cannot determine CMake target path.", vim.log.levels.ERROR)
    end
  end, {})

  vim.keymap.set('n', '<S-F5>', '<cmd>TeachOSRun<CR>', { noremap = true, desc = "Run TeachOS QEMU (No Debug)" })
end