summaryrefslogtreecommitdiff
path: root/lua/nvim-treesitter/utils.lua
blob: 437385a34fce9eedb8a1ee785aa8d3752c982976 (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
170
171
172
173
174
175
176
177
178
179
180
181
182
local api = vim.api
local fn = vim.fn
local luv = vim.loop

local M = {}

function M.setup_commands(mod, commands)
  for command_name, def in pairs(commands) do
    local call_fn = string.format("lua require'nvim-treesitter.%s'.commands.%s['run<bang>'](<f-args>)", mod, command_name)
    local parts = vim.tbl_flatten({
      "command!",
      def.args,
      command_name,
      call_fn,
    })
    api.nvim_command(table.concat(parts, " "))
  end
end

function M.get_path_sep()
  return fn.has('win32') == 1 and '\\' or '/'
end

-- Returns a function that joins the given arguments with separator. Arguments
-- can't be nil. Example:
--[[
print(M.generate_join(" ")("foo", "bar"))
--]]
-- prints "foo bar"
function M.generate_join(separator)
  return function (...)
    return table.concat({...}, separator)
  end
end

M.join_path = M.generate_join(M.get_path_sep())

local join_space = M.generate_join(" ")

function M.get_package_path()
  -- Path to this source file, removing the leading '@'
  local source = string.sub(debug.getinfo(1, 'S').source, 2)

  -- Path to the package root
  return fn.fnamemodify(source, ":p:h:h:h")
end

function M.get_cache_dir()
  local cache_dir = fn.stdpath('data')

  if luv.fs_access(cache_dir, 'RW') then
    return cache_dir
  elseif luv.fs_access('/tmp', 'RW') then
    return '/tmp'
  end

  return nil, join_space('Invalid cache rights,', fn.stdpath('data'), 'or /tmp should be read/write')
end

-- Returns $XDG_DATA_HOME/nvim/site, but could use any directory that is in
-- runtimepath
function M.get_site_dir()
  local path_sep = M.get_path_sep()
  return M.join_path(fn.stdpath('data'), path_sep, 'site')
end

-- Try the package dir of the nvim-treesitter plugin first, followed by the
-- "site" dir from "runtimepath". "site" dir will be created if it doesn't
-- exist. Using only the package dir won't work when the plugin is installed
-- with Nix, since the "/nix/store" is read-only.
function M.get_parser_install_dir(folder_name)
  folder_name = folder_name or "parser"
  local package_path = M.get_package_path()
  local package_path_parser_dir = M.join_path(package_path, folder_name)

  -- If package_path is read/write, use that
  if luv.fs_access(package_path_parser_dir, 'RW') then
    return package_path_parser_dir
  end

  local site_dir = M.get_site_dir()
  local path_sep = M.get_path_sep()
  local parser_dir = M.join_path(site_dir, path_sep, folder_name)

  -- Try creating and using parser_dir if it doesn't exist
  if not luv.fs_stat(parser_dir) then
    local ok, error = pcall(vim.fn.mkdir, parser_dir, "p", "0755")
    if not ok then
      return nil, join_space('Couldn\'t create parser dir', parser_dir, ':', error)
    end

    return parser_dir
  end

  -- parser_dir exists, use it if it's read/write
  if luv.fs_access(parser_dir, 'RW') then
    return parser_dir
  end

  -- package_path isn't read/write, parser_dir exists but isn't read/write
  -- either, give up
  return nil, join_space('Invalid cache rights,', package_path, 'or', parser_dir, 'should be read/write')
end

function M.get_parser_info_dir()
  return M.get_parser_install_dir('parser-info')
end

-- Gets a property at path
-- @param tbl the table to access
-- @param path the '.' separated path
-- @returns the value at path or nil
function M.get_at_path(tbl, path)
  if path == '' then return tbl end
  local segments = vim.split(path, '.', true)
  local result = tbl

  for _, segment in ipairs(segments) do
    if type(result) == 'table' then
      result = result[segment]
    end
  end

  return result
end

-- Prints a warning message
-- @param text the text message
function M.print_warning(text)
  api.nvim_command(string.format([[echohl WarningMsg | echo "%s" | echohl None]], text))
end

function M.set_jump()
  vim.cmd "normal! m'"
end

function M.index_of(tbl, obj)
  for i, o in ipairs(tbl) do
    if o == obj then
      return i
    end
  end
end

-- Filters a list based on the given predicate
-- @param tbl The list to filter
-- @param predicate The predicate to filter with
function M.filter(tbl, predicate)
  local result = {}

  for i, v in ipairs(tbl) do
    if predicate(v, i) then
      table.insert(result, v)
    end
  end

  return result
end

-- Returns a list of all values from the first list
-- that are not present in the second list.
-- @params tbl1 The first table
-- @params tbl2 The second table
function M.difference(tbl1, tbl2)
  return M.filter(tbl1, function(v)
    return not vim.tbl_contains(tbl2, v)
  end)
end

function M.identity(a)
  return a
end

function M.constant(a)
  return function() return a end
end

function M.to_func(a)
  return type(a) == 'function' and a or M.constant(a)
end

return M