plugins.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. -- add extra filetypes for plenary
  2. require('plenary.filetype').add_table({
  3. extension = {
  4. ['elv'] = [[elvish]]
  5. }
  6. })
  7. -- add extra builtin filetypes
  8. vim.filetype.add({
  9. pattern = {
  10. ['.*%.ts$'] = 'typescript'
  11. },
  12. })
  13. -- helpers
  14. local function resolve_git_path(buf_id)
  15. local bufname = vim.api.nvim_buf_get_name(buf_id)
  16. if not vim.startswith(bufname, "fugitive://") then
  17. return false
  18. end
  19. local parsed = vim.fn.FugitiveParse(bufname)
  20. local resolved_path = parsed[1]
  21. local repo = parsed[2]
  22. if resolved_path == "" then
  23. return false
  24. end
  25. local parts = vim.split(resolved_path, ':')
  26. local commit = parts[1]
  27. local path = parts[2]
  28. return {
  29. repo = repo,
  30. commit = commit,
  31. path = path,
  32. }
  33. end
  34. -- file/buffer/etc picker
  35. local telescope_actions = require('telescope.actions')
  36. local action_state = require('telescope.actions.state')
  37. local actions = {}
  38. actions.git_show_commit = function(prompt_bufnr)
  39. local selection = action_state.get_selected_entry()
  40. local path = vim.fn.FugitiveFind(selection.value)
  41. telescope_actions.close(prompt_bufnr)
  42. vim.cmd.edit(path)
  43. end
  44. local commit_mappings = {
  45. i = {
  46. ['<CR>'] = actions.git_show_commit,
  47. ['<C-r>c'] = telescope_actions.git_checkout,
  48. }
  49. }
  50. require('telescope').setup({
  51. defaults = {
  52. mappings = {
  53. i = {
  54. ['jj'] = 'close',
  55. },
  56. },
  57. layout_config = {
  58. prompt_position = 'top',
  59. },
  60. sorting_strategy = 'ascending',
  61. -- use filename as preview window title
  62. dynamic_preview_title = true,
  63. path_display = {
  64. -- shorten directory names of everything but the last 3 parts
  65. -- foo/bar/baz/file.txt -> f/boo/bar/file.txt
  66. shorten = { len = 1, exclude = { -3, -2, -1 } },
  67. -- truncate the beginning of the file name if wider than the window
  68. truncate = true,
  69. },
  70. preview = {
  71. -- don't preview files larger than 1MB
  72. filesize_limit = 1,
  73. timeout = 500,
  74. },
  75. vimgrep_arguments = {
  76. -- defaults
  77. "rg",
  78. "--color=never",
  79. "--no-heading",
  80. "--with-filename",
  81. "--line-number",
  82. "--column",
  83. "--smart-case",
  84. -- search "hidden" files except git folder
  85. "--hidden",
  86. "--iglob=!.git"
  87. },
  88. -- ignore things we're likely not to edit
  89. file_ignore_patterns = {
  90. "%.zip$",
  91. "%.yarn/releases/",
  92. "%.yarn/plugins/"
  93. },
  94. -- picker history
  95. cache_picker = {
  96. num_pickers = 10,
  97. },
  98. },
  99. pickers = {
  100. buffers = {
  101. sort_lastused = true,
  102. sort_mru = true,
  103. mappings = {
  104. i = {
  105. ['<C-k>'] = 'delete_buffer'
  106. },
  107. },
  108. },
  109. find_files = {
  110. find_command = { "fd", "--type", "f", "--strip-cwd-prefix" }
  111. },
  112. git_commits = {
  113. mappings = commit_mappings,
  114. },
  115. git_bcommits = {
  116. mappings = commit_mappings,
  117. },
  118. },
  119. })
  120. -- use native sorter for better performance
  121. require('telescope').load_extension('fzf')
  122. local telescope_builtin = require('telescope.builtin')
  123. local telescope_pickers = require('telescope.pickers')
  124. local telescope_finders = require('telescope.finders')
  125. local telescope_previewers = require('telescope.previewers')
  126. local telescope_putils = require('telescope.previewers.utils')
  127. local telescope_conf = require('telescope.config').values
  128. -- custom picker to fallback to files if no git
  129. _G.project_files = function()
  130. local ok = pcall(telescope_builtin.git_files, { show_untracked = true })
  131. if not ok then telescope_builtin.find_files({}) end
  132. end
  133. -- custom picker for files within a commit
  134. _G.commit_files = function(opts)
  135. local resolved = resolve_git_path(0)
  136. if not resolved then
  137. vim.print("current file is not a fugitive path")
  138. return
  139. end
  140. opts = opts or {}
  141. telescope_pickers.new(opts, {
  142. prompt_title = resolved.commit,
  143. finder = telescope_finders.new_oneshot_job({ "git", "ls-tree", "--name-only", "-r", resolved.commit }, {
  144. entry_maker = function(entry)
  145. local path = string.format("fugitive://%s//%s/%s", resolved.repo, resolved.commit, entry)
  146. return {
  147. path = path,
  148. value = entry,
  149. display = entry,
  150. ordinal = entry,
  151. }
  152. end,
  153. }),
  154. sorter = telescope_conf.file_sorter(opts),
  155. -- the builtin previewer has fancy async loading which doesn't work for
  156. -- fugitive paths so we have to define our own
  157. previewer = telescope_previewers.new_buffer_previewer({
  158. title = function(self)
  159. return 'Commit Files'
  160. end,
  161. dyn_title = function(self, entry)
  162. return entry.value
  163. end,
  164. define_preview = function(self, entry, status)
  165. -- the builtin previewer does more things like using mime type
  166. -- fallbacks as well as binary file detection which ours doesn't do
  167. local ft = telescope_putils.filetype_detect(entry.value)
  168. vim.api.nvim_buf_call(self.state.bufnr, function()
  169. vim.cmd('Gread ' .. entry.path)
  170. telescope_putils.highlighter(self.state.bufnr, ft, opts)
  171. end)
  172. end,
  173. }),
  174. }):find()
  175. end
  176. -- shows added/removed/changed lines
  177. local MiniDiff = require('mini.diff')
  178. MiniDiff.setup({
  179. view = {
  180. style = 'sign',
  181. signs = {
  182. delete = '_',
  183. },
  184. },
  185. source = {
  186. MiniDiff.gen_source.git(),
  187. -- handle fugitive paths
  188. {
  189. name = "fugitive",
  190. attach = function(buf_id)
  191. local resolved = resolve_git_path(buf_id)
  192. if not resolved or resolved.path == "" then
  193. return false
  194. end
  195. local source = vim.fn.FugitiveFind(string.format("%s~1:%s", resolved.commit, resolved.path))
  196. local text = vim.fn['fugitive#readfile'](source)
  197. MiniDiff.set_ref_text(buf_id, text)
  198. end
  199. }
  200. }
  201. })
  202. local function section_git(args)
  203. if MiniStatusline.is_truncated(args.trunc_width) then return '' end
  204. return vim.fn.FugitiveHead()
  205. end
  206. require('mini.statusline').setup({
  207. content = {
  208. -- copy-pasted from default, we just want to remove the icon
  209. active = function()
  210. local mode, mode_hl = MiniStatusline.section_mode({ trunc_width = 120 })
  211. local git = section_git({ trunc_width = 75 })
  212. local diagnostics = MiniStatusline.section_diagnostics({ trunc_width = 75, icon = '' })
  213. local filename = MiniStatusline.section_filename({ trunc_width = 140 })
  214. local fileinfo = MiniStatusline.section_fileinfo({ trunc_width = 120 })
  215. local location = MiniStatusline.section_location({ trunc_width = 75 })
  216. return MiniStatusline.combine_groups({
  217. { hl = mode_hl, strings = { mode } },
  218. { hl = 'MiniStatuslineDevinfo', strings = { git, diagnostics } },
  219. '%<', -- Mark general truncate point
  220. { hl = 'MiniStatuslineFilename', strings = { filename } },
  221. '%=', -- End left alignment
  222. { hl = 'MiniStatuslineFileinfo', strings = { fileinfo } },
  223. { hl = mode_hl, strings = { location } },
  224. })
  225. end
  226. },
  227. })
  228. -- delete buffer while preserving layout
  229. require('mini.bufremove').setup()
  230. -- shows a line indicating the current indentation scope
  231. require('mini.indentscope').setup()
  232. -- comment actions
  233. require('mini.comment').setup()
  234. -- surround actions
  235. require('mini.surround').setup({
  236. custom_surroundings = {
  237. -- js template string
  238. ['$'] = {
  239. output = {
  240. left = '`${',
  241. right = '}`',
  242. },
  243. },
  244. },
  245. })
  246. local spec_treesitter = require('mini.ai').gen_spec.treesitter
  247. require('mini.ai').setup({
  248. -- only consider the current location
  249. search_method = 'cover',
  250. custom_textobjects = {
  251. ['.'] = spec_treesitter({
  252. a = '@call.outer',
  253. i = '@call.inner',
  254. }),
  255. [','] = spec_treesitter({
  256. a = '@parameter.outer',
  257. i = '@parameter.inner',
  258. }),
  259. },
  260. });
  261. -- align actions
  262. require('mini.align').setup()
  263. -- repeatable f/t
  264. require('mini.jump').setup({
  265. mappings = {
  266. repeat_jump = '',
  267. },
  268. delay = {
  269. highlight = 10000000,
  270. },
  271. })
  272. -- autopair brackets
  273. require('mini.pairs').setup({
  274. mappings = {
  275. -- default config includes (, [ and {
  276. -- autopair <> if preceded by a character, otherwise it might be a regular
  277. -- comparison operation
  278. ['<'] = { action = 'open', pair = '<>', neigh_pattern = '%w.' },
  279. ['>'] = { action = 'close', pair = '<>' },
  280. },
  281. })
  282. -- notifications
  283. require('mini.notify').setup()
  284. vim.notify = require('mini.notify').make_notify()
  285. -- file explorer
  286. require('mini.files').setup({
  287. content = {
  288. -- remove icons
  289. prefix = function() end,
  290. }
  291. })
  292. -- rest client
  293. require('kulala').setup({
  294. global_keymaps = true,
  295. global_keymaps_prefix = '<leader>r'
  296. })
  297. -- Use Treesitter for syntax highlighting
  298. require('nvim-treesitter.configs').setup({
  299. highlight = {
  300. enable = true,
  301. },
  302. indent = {
  303. enable = true,
  304. },
  305. incremental_selection = {
  306. enable = true,
  307. keymaps = {
  308. init_selection = "]t",
  309. node_incremental = "]t",
  310. node_decremental = "[t",
  311. },
  312. },
  313. textobjects = {
  314. swap = {
  315. enable = true,
  316. swap_next = {
  317. ['>,'] = '@parameter.inner',
  318. },
  319. swap_previous = {
  320. ['<,'] = '@parameter.inner',
  321. },
  322. },
  323. },
  324. })
  325. local tsj_utils = require('treesj.langs.utils')
  326. -- Treesitter-aware split/join
  327. require('treesj').setup({
  328. use_default_keymaps = false,
  329. })
  330. -- Treesitter context
  331. require('treesitter-context').setup({
  332. enable = true,
  333. multiline_threshold = 5,
  334. })
  335. -- Treesitter navigation
  336. require('treewalker').setup()
  337. -- completion
  338. require('blink.cmp').setup({
  339. cmdline = {
  340. enabled = false,
  341. },
  342. completion = {
  343. accept = {
  344. auto_brackets = {
  345. enabled = false,
  346. },
  347. },
  348. documentation = {
  349. auto_show = true,
  350. auto_show_delay_ms = 500,
  351. },
  352. trigger = {
  353. prefetch_on_insert = false,
  354. show_on_keyword = false,
  355. show_on_trigger_character = false,
  356. },
  357. menu = {
  358. draw = {
  359. columns = {
  360. { "label", "label_description", gap = 1 },
  361. { "kind" },
  362. },
  363. },
  364. },
  365. },
  366. keymap = {
  367. ['<Enter>'] = { 'select_and_accept', 'fallback' },
  368. ['<C-u>'] = { 'scroll_documentation_up', 'fallback' },
  369. ['<C-d>'] = { 'scroll_documentation_down', 'fallback' },
  370. },
  371. })
  372. -- typescript-vim compiler options
  373. vim.g.typescript_compiler_options = '--incremental --noEmit'
  374. -- stuff
  375. _G.toggle_end_char = function(char)
  376. local cursor = vim.api.nvim_win_get_cursor(0)
  377. local row = cursor[1] - 1
  378. local end_char = vim.api.nvim_buf_get_text(0, row, -2, row, -1, {})[1]
  379. if end_char == char then
  380. vim.api.nvim_buf_set_text(0, row, -2, row, -1, {})
  381. else
  382. vim.api.nvim_buf_set_text(0, row, -1, row, -1, { char })
  383. end
  384. end