summaryrefslogtreecommitdiffstatshomepage
path: root/runtime/lua/vim/_core/util.lua
blob: 29ba8c5720f4819524f2059311b1089bee212f35 (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
-- Nursery for random things that may later find their way into stdlib if they mature.

local M = {}

--- Adds one or more blank lines above or below the cursor.
-- TODO: move to _core/defaults.lua once it is possible to assign a Lua function to options #25672
--- @param above? boolean Place blank line(s) above the cursor
local function add_blank(above)
  local offset = above and 1 or 0
  local repeated = vim.fn['repeat']({ '' }, vim.v.count1)
  local linenr = vim.api.nvim_win_get_cursor(0)[1]
  vim.api.nvim_buf_set_lines(0, linenr - offset, linenr - offset, true, repeated)
end

-- TODO: move to _core/defaults.lua once it is possible to assign a Lua function to options #25672
function M.space_above()
  add_blank(true)
end

-- TODO: move to _core/defaults.lua once it is possible to assign a Lua function to options #25672
function M.space_below()
  add_blank()
end

--- Gets a buffer by name
--- @param name string
--- @return integer?
function M.get_buf_by_name(name)
  for _, buf in ipairs(vim.api.nvim_list_bufs()) do
    if vim.api.nvim_buf_get_name(buf) == name then
      return buf
    end
  end
end

--- Edit a file in a specific window
--- @param winnr number
--- @param file string
--- @return number buffer number of the edited buffer
M.edit_in = function(winnr, file)
  local function resolved_path(path)
    if not path or path == '' then
      return ''
    end
    return vim.fn.resolve(vim.fs.abspath(path))
  end

  return vim.api.nvim_win_call(winnr, function()
    local current_buf = vim.api.nvim_win_get_buf(winnr)
    local current = resolved_path(vim.api.nvim_buf_get_name(current_buf))

    -- Check if the current buffer is already the target file
    if current == resolved_path(file) then
      return current_buf
    end

    -- Read the file into the buffer
    vim.cmd.edit(vim.fn.fnameescape(file))
    return vim.api.nvim_get_current_buf()
  end)
end

--- :edit, but it respects commands like :hor, :vert, :tab, etc.
--- @param file string
--- @param mods string|vim.api.keyset.cmd_mods Modifier string ("vertical") or structured mods table.
function M.wrapped_edit(file, mods)
  assert(mods)
  if type(mods) == 'string' then
    mods = vim.api.nvim_parse_cmd(mods .. ' edit', {}).mods --[[@as vim.api.keyset.cmd_mods]]
  end
  --- @cast mods vim.api.keyset.cmd_mods
  if (mods.tab or 0) > 0 or (mods.split or '') ~= '' or mods.horizontal or mods.vertical then
    local buf = M.get_buf_by_name(file)
    if buf == nil then
      buf = vim.api.nvim_create_buf(true, false)
    end
    vim.cmd.sbuffer { buf, mods = mods }
  end
  vim.cmd.edit { file }
end

--- Read a chunk of data from a file
--- @param file string
--- @param size number
--- @return string? chunk or nil on error
function M.read_chunk(file, size)
  local fd = io.open(file, 'rb')
  if not fd then
    return nil
  end
  local chunk = fd:read(size)
  fd:close()
  return tostring(chunk)
end

--- Check if a range in a buffer is inside a Lua codeblock via treesitter injection.
--- Used by :source to detect Lua code in non-Lua files (e.g., vimdoc).
--- @param bufnr integer Buffer number
--- @param line1 integer Start line (1-indexed)
--- @param line2 integer End line (1-indexed)
--- @return boolean True if the range is in a Lua injection
function M.source_is_lua(bufnr, line1, line2)
  local parser = vim.treesitter.get_parser(bufnr)
  if not parser then
    return false
  end
  -- Parse from buffer start through one line past line2 to include injection closing markers
  local range = { line1 - 1, 0, line2 - 1, -1 }
  parser:parse({ 0, 0, line2, -1 })
  local lang_tree = parser:language_for_range(range)
  return lang_tree:lang() == 'lua'
end

--- Returns the exit code string for the current buffer, given:
--- - Channel is attached to the current buffer
--- - Current buffer is a terminal buffer
---
--- @return string
function M.term_exitcode()
  local chan_id = vim.bo.channel
  if chan_id == 0 or vim.bo.buftype ~= 'terminal' then
    return ''
  end

  local info = vim.api.nvim_get_chan_info(chan_id)
  if info.exitcode and info.exitcode >= 0 then
    return string.format('[Exit: %d]', info.exitcode)
  end
  return ''
end

return M