about summary refs log tree commit diff stats
path: root/modules/home.legacy/conf/nvim/plgs/luasnip/lua/snippets/all.lua
blob: 2b923f209a556c73c3c7ca69d2b9e2aff2604e14 (plain) (blame)
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
local ls = require("luasnip")
-- auto_pairs  {{{
local get_visual = function(args, parent)
  if #parent.snippet.env.SELECT_RAW > 0 then
    return sn(nil, i(1, parent.snippet.env.SELECT_RAW))
  else
    return sn(nil, i(1, ""))
  end
end
local function char_count_same(c1, c2)
  local line = vim.api.nvim_get_current_line()
  -- '%'-escape chars to force explicit match (gsub accepts patterns).
  -- second return value is number of substitutions.
  local _, ct1 = string.gsub(line, "%" .. c1, "")
  local _, ct2 = string.gsub(line, "%" .. c2, "")
  return ct1 == ct2
end

local function even_count(c, ...)
  local line = vim.api.nvim_get_current_line()
  local _, ct = string.gsub(line, c, "")
  return ct % 2 == 0
end

-- This makes creation of pair-type snippets easier.
local function pair(pair_begin, pair_end, file_types, condition_function)
  -- FIXME(@Soispha): This only works if file_types == nil, otherwise the snippet does not expand.
  -- It would be nice, if it would support both an empty array (`{}`) and nil <2023-08-27>
  -- file_types = file_types or {};

  return s(
    { trig = pair_begin, wordTrig = false, snippetType = "autosnippet" },
    { t({ pair_begin }), d(1, get_visual), t({ pair_end }) },
    {
      condition = function()
        local filetype_check = true
        if file_types ~= nil then filetype_check = file_types[vim.bo.filetype] or false end
        return (not condition_function(pair_begin, pair_end)) and filetype_check
      end,
    }
  )
end

local auto_pairs = {
  pair("(", ")", nil, char_count_same),
  pair("{", "}", nil, char_count_same),
  pair("[", "]", nil, char_count_same),
  pair("<", ">", { ["rust"] = true, ["tex"] = true }, char_count_same),
  pair("'", "'", nil, even_count),
  pair("\"", "\"", nil, even_count),
  pair("`", "`", nil, even_count),
}

ls.add_snippets("all", auto_pairs, { type = "snippets", key = "auto_pairs" })
-- }}}

-- todo_comments {{{
local calculate_comment_string = require("Comment.ft").calculate
local utils = require("Comment.utils")

local read_git_config = function(config_value)
  local command = string.format("git config \"%s\"", config_value)
  local handle = io.popen(command)
  if handle == nil then return error(string.format("Failed to call `%s`.", command)) end
  local result = handle:read("*a")
  handle:close()
  -- stripped = string.gsub(str, '%s+', '')
  return string.gsub(result, "\n", "")
end

local name_to_handle = function(name)
  -- from: https://stackoverflow.com/a/7615129
  local split = function(inputstr, sep)
    local t = {}
    for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
      table.insert(t, str)
    end
    return t
  end

  local parts = split(name, "%s")

  local output_name = ""
  if #parts > 2 then
    for _, val in ipairs(parts) do
      output_name = string.format("%s%s", output_name, val:sub(1, 1))
    end
  elseif #parts == 2 then
    output_name = string.format("%s%s", parts[1]:sub(1, 1), parts[2])
  elseif #parts == 1 then
    output_name = parts[1]
  else
    -- parts is 0
    output_name = "<NoName>"
  end
  return string.format("@%s", output_name:lower())
end

_G.luasnip = {}
_G.luasnip.vars = {
  username = function() return name_to_handle(read_git_config("user.name")) end,
  email = function() return read_git_config("user.email") end,
}

--- Get the comment string {beg,end} table
---@param ctype integer 1 for `line`-comment and 2 for `block`-comment
---@return table comment_strings {begcstring, endcstring}
local get_cstring = function(ctype)
  -- use the `Comments.nvim` API to fetch the comment string for the region (eq. '--%s' or '--[[%s]]' for `lua`)
  local cstring = calculate_comment_string({ ctype = ctype, range = utils.get_region() }) or vim.bo.commentstring
  -- as we want only the strings themselves and not strings ready for using `format` we want to split the left and right side
  local left, right = utils.unwrap_cstr(cstring)
  -- create a `{left, right}` table for it
  return { left, right }
end

--- Options for marks to be used in a TODO comment
---@return table,table: The first table contains a node for the date, the second for the signature
local marks = {
  signature = function() return t("(" .. _G.luasnip.vars:username() .. ")"), t("") end,
  date_signature = function() return t("<" .. os.date("%Y-%m-%d") .. ">"), t("(" .. _G.luasnip.vars:username() .. ")") end,
  date = function() return t("<" .. os.date("%Y-%m-%d") .. ">"), t("") end,
  empty = function() return t(""), t("") end,
}

---@param alias string
---@param opts table
---@param mark_function function: This function should return two nodes
---@return table: Returns the comment node
local todo_snippet_nodes = function(alias, opts, mark_function)
  local date_node, signature_node = mark_function()
  -- format them into the actual snippet
  local comment_node = fmta("<> <><>: <> <> <>", {
    f(function()
      return get_cstring(opts.ctype)[1] -- get <comment-string[1]>
    end),
    t(alias), -- [name-of-comment]
    signature_node,
    i(0), -- {comment-text}
    date_node,
    f(function()
      return get_cstring(opts.ctype)[2] -- get <comment-string[2]>
    end),
  })
  return comment_node
end

--- Generate a TODO comment snippet with an automatic description and docstring
---@param context table merged with the generated context table `trig` must be specified
---@param alias string of aliases for the todo comment (ex.: {FIX, ISSUE, FIXIT, BUG})
---@param opts table merged with the snippet opts table
---@param mark_function function: The function used to get the marks
local todo_snippet = function(context, alias, opts, mark_function)
  opts = opts or {}
  context = context or {}
  if not context.trig then
    return error("context doesn't include a `trig` key which is mandatory", 2) -- all we need from the context is the trigger
  end
  opts.ctype = opts.ctype or 1 -- comment type can be passed in the `opts` table, but if it is not, we have to ensure, it is defined
  local alias_string = alias -- `choice_node` documentation
  context.name = context.name or (alias_string .. " comment") -- generate the `name` of the snippet if not defined
  context.dscr = context.dscr or (alias_string .. " comment with a signature-mark") -- generate the `dscr` if not defined
  context.docstring = context.docstring or (" {1:" .. alias_string .. "}: {3} <{2:mark}>{0} ") -- generate the `docstring` if not defined
  local comment_node = todo_snippet_nodes(alias, opts, mark_function)
  return s(context, comment_node, opts) -- the final todo-snippet constructed from our parameters
end

---@param context table: The luasnip context
---@param opts table: The luasnip opts table, needs to have a ctype set
---@param aliases string: All aliases for a name
---@param marks table: Possible marks to account in snipped generation
---@return table: All possible snippets build from the marks
local process_marks = function(context, aliases, opts, marks)
  local output = {}
  for mark_name, mark_function in pairs(marks) do
    local contex_trig_local = context.trig
    context.trig = context.trig .. "-" .. mark_name
    output[#output + 1] = todo_snippet(context, aliases, opts, mark_function)
    context.trig = contex_trig_local
  end
  return output
end

local todo_snippet_specs = {
  { { trig = "todo" }, { "TODO" }, { ctype = 1 } },
  { { trig = "fix" }, { "FIXME", "ISSUE" }, { ctype = 1 } },
  { { trig = "hack" }, { "HACK" }, { ctype = 1 } },
  { { trig = "warn" }, { "WARNING" }, { ctype = 1 } },
  { { trig = "perf" }, { "PERFORMANCE", "OPTIMIZE" }, { ctype = 1 } },
  { { trig = "note" }, { "NOTE", "INFO" }, { ctype = 1 } },

  -- NOTE: Block commented todo-comments
  { { trig = "todob" }, { "TODO" }, { ctype = 2 } },
  { { trig = "fixb" }, { "FIXME", "ISSUE" }, { ctype = 2 } },
  { { trig = "hackb" }, { "HACK" }, { ctype = 2 } },
  { { trig = "warnb" }, { "WARNING" }, { ctype = 2 } },
  { { trig = "perfb" }, { "PERF", "PERFORMANCE", "OPTIM", "OPTIMIZE" }, { ctype = 2 } },
  { { trig = "noteb" }, { "NOTE", "INFO" }, { ctype = 2 } },
}

local todo_comment_snippets = {}
for _, v in ipairs(todo_snippet_specs) do
  local snippets = process_marks(v[1], v[2][1], v[3], marks)
  for _, value in pairs(snippets) do
    table.insert(todo_comment_snippets, value)
  end
end

ls.add_snippets("all", todo_comment_snippets, { type = "snippets", key = "todo_comments" })

-- }}}