;;; init.fnl - primary init file ;; Initialize global `zander' prefix (set _G.zander {}) ;; Helpful keymaping functions (macro bind! [mode key cmd ?a1 ?a2] (let [desc (or ?a2 ?a1) buf (if (and ?a1 ?a2) ?a1 ?a2) opts { :noremap true :silent true :buffer buf }] (when desc (tset opts :desc desc)) `(vim.keymap.set ,mode ,key ,cmd ,opts))) ;; Better autocommands (macro hook! [hooks ?patterns callback] (let [hook_table (if (= (type hooks) :table) hooks [ hooks ]) pattern_table (if (not ?patterns) [] (= (type ?patterns) :table) ?patterns [ ?patterns ])] (var group "config-hook") (each [_ hook (ipairs hook_table)] (set group (.. group "-" hook))) (each [_ pattern (ipairs pattern_table)] (set group (.. group "-" pattern))) `(vim.api.nvim_create_autocmd ,hook_table { :group (vim.api.nvim_create_augroup ,group { :clear true }) :pattern ,pattern_table :callback ,callback }))) ;; Make space leader (set vim.g.mapleader " ") (bind! :n : :) ;; Enable lua filetype (set vim.g.do_filetype_lua true) ;; Some options ; Make the mouse work (set vim.opt.mouse :a) (set vim.opt.mousemodel :extend) ; Ignore case when a query is all lower case (set vim.opt.ignorecase true) (set vim.opt.smartcase true) ; Enable line numbering (set vim.opt.number true) ; Make insert 4 spaces (set vim.opt.expandtab true) (set vim.opt.tabstop 4) (set vim.opt.shiftwidth 4) (set vim.opt.shiftround true) ; Auto-indent (set vim.opt.autoindent true) (set vim.opt.smartindent true) ; Give context while scrolling (set vim.opt.scrolloff 4) (set vim.opt.sidescrolloff 8) ; Make splitting better (set vim.opt.splitbelow true) (set vim.opt.splitright true) ; 24 bit color support (set vim.opt.termguicolors true) ; Shell like completions (set vim.opt.wildmode [ :list :longest ]) ; Set completion options (set vim.opt.completeopt [ :menu :menuone :noselect ]) ; Make buffers hide when abandoned (set vim.opt.hidden true) ; Save undo data (set vim.opt.undofile true) ; Text width (set vim.opt.textwidth 80) ; Spell options (set vim.opt.spelllang :en_us) (set vim.opt.spelloptions :camel) ; Allow more freedom with the cursor (set vim.opt.virtualedit :block) ;; Disable netrw (set vim.g.loaded_netrw 1) (set vim.g.loaded_netrwPlugin 1) ;; Remove trailing whitespace (fn remove_trailing_whitespace [] "Remove trailing whitespace from the whole file." (let [res (pcall vim.cmd "%s/ \\+$//gc")] (vim.cmd.nohlsearch) (when (not res) (print "File is clean")))) (vim.api.nvim_create_user_command :RemoveTrailingWhitespace remove_trailing_whitespace {}) (bind! :n :ws remove_trailing_whitespace) ;; Spell keybindings (bind! :n :l (fn [] (let [new_spell (not (vim.opt_local.spell:get))] (set vim.opt_local.spell new_spell) (print (if new_spell "Enabled Spellcheck" "Disabled Spellcheck"))))) (fn jump_or_open_terminal [] "If no terminal buffer exists, create one. Otherwise, open new one." (var term_count 0) (var last_id 0) (var terms [ ]) (each [_ id (pairs (vim.api.nvim_list_bufs))] (let [name (vim.api.nvim_buf_get_name id)] (when (vim.startswith name "term://") (table.insert terms name) (set term_count (+ term_count 1)) (set last_id id)))) (if (= term_count 0) (vim.cmd.terminal) (= term_count 1) (vim.cmd.buffer last_id) (vim.ui.select terms { :prompt "Terminal Buffers" } (fn [choice] (when choice (vim.cmd.buffer choice)))))) (bind! :n :t jump_or_open_terminal) ;; Some keybindings for diagnostics (bind! :n :e vim.diagnostic.open_float) (bind! :n "[d" vim.diagnostic.goto_prev) (bind! :n "]d" vim.diagnostic.goto_next) (bind! :n :q vim.diagnostic.setloclist) ;; Some filetype settings (vim.filetype.add { :filename { ".latexmkrc" :perl "Jenkinsfile" :groovy } :extensions { "nasm" :nasm "h" :c "ui" :xml }}) ;; Plugins (with packer.nvim) (fn packer_init_callback [use] ;; Nicer macro for use (macro use! [repo ...] (local output [ repo ]) (var last_key nil) (each [_ val (ipairs [...])] (if last_key (do (tset output last_key val) (set last_key nil)) (set last_key val))) `(use ,output)) ;; Can be passed to :config of `use!' ;; to call a plugin's `setup' function (macro setup! [pkg ...] (local output [ ]) (var last_key nil) (each [_ val (ipairs [...])] (if last_key (do (tset output last_key val) (set last_key nil)) (set last_key val))) (let [req_var (gensym)] `((. (require ,pkg) :setup) ,output))) ;; Packer (use! :wbthomason/packer.nvim) ;; tangerine.nvim (auto fennel compile) (use! :udayvir-singh/tangerine.nvim) ;; Treesitter (use! :nvim-treesitter/nvim-treesitter :config #(setup! "nvim-treesitter.configs" :ensure_installed :all :sync_install false :auto_install true :highlight { :enable true :additional_vim_regex_highlighting false } :indent { :enable true })) ;; nightfox.nvim (colorscheme) (use! :EdenEast/nightfox.nvim :after :nvim-treesitter :config #(vim.cmd.colorscheme :carbonfox)) ;; Devicons (use! :kyazdani42/nvim-web-devicons) ;; lualine.nvim (use! :nvim-lualine/lualine.nvim :after [ :nightfox.nvim :nvim-web-devicons ] :config #(setup! :lualine :options { :section_separators "" :component_separators "│" } :sections { :lualine_x [ { 1 #(.. "recording @" (vim.fn.reg_recording)) :cond #(not= (# (vim.fn.reg_recording)) 0) } :encoding :fileformat :filetype ]})) ;; bufferline.nvim (use! :akinsho/bufferline.nvim :after [ :nvim-web-devicons :nightfox.nvim ] :config #(setup! :bufferline :options { :always_show_bufferline false :mode :tabs :numbers :buffer-id :show_buffer_close_icons false :show_close_icon false })) ;; noice.nvim (ui overhaul) (use! :folke/noice.nvim :requires :MunifTanjim/nui.nvim :config #(setup! :noice :lsp { :override { "vim.lsp.util.convert_input_to_markdown_lines" true "vim.lsp.util.stylize_markdown" true "cmp.entry.get_documentation" true }} :presets { :bottom_search true :command_palette false :long_message_to_split true :inc_rename false :lsp_doc_border false } :cmdline { :view :cmdline })) ;; dressing.nvim (better prmpts) (use! :stevearc/dressing.nvim :after :telescope.nvim :config #(setup! :dressing :input { :insert_only true })) ;; Telescope (use! :nvim-telescope/telescope.nvim :requires :nvim-lua/plenary.nvim :after :noice.nvim :config (fn [] (setup! :telescope :defaults { :mappings { :i { : :close : :move_selection_next : :move_selection_previous } :n { : :move_selection_next : :move_selection_previous }}} :pickers { :buffers { :sort_lastused true }} :extensions { :fzf { :fuzzy true :override_generic_sorter true :override_file_sorter true :case_mode :smart_case }}) (let [builtin (require :telescope.builtin)] (bind! :n :fq builtin.quickfix) (bind! :n :fr builtin.registers) (bind! :n :fj builtin.jumplist) (bind! :n :fa builtin.marks) (bind! :n :fh builtin.help_tags) (bind! :n :g builtin.live_grep) (bind! :n :fg builtin.live_grep) (bind! :n :G builtin.grep_string) (bind! :n :fG builtin.grep_string) (bind! :n :b builtin.buffers) (bind! :n :gb builtin.buffers) (bind! :n :fu builtin.git_status) (bind! :n :fm builtin.man_pages) (bind! :n :fe builtin.diagnostics) (bind! :n :fp builtin.spell_suggest) (bind! :n :i builtin.find_files) (bind! :n :z= builtin.spell_suggest) (bind! :n :ff (fn [] (let [code (os.execute "git rev-parse --is-inside-work-tree >/dev/null 2>&1")] (if (= code 0) (builtin.git_files) (builtin.find_files)))))) ((. (require :telescope) :load_extension) :noice))) ;; Telescope fzf (use! :nvim-telescope/telescope-fzf-native.nvim :after :telescope.nvim :run "make" :config #((. (require :telescope) :load_extension) :fzf)) ;; Nvim surround (use! :kylechui/nvim-surround :config #(setup! :nvim-surround)) ;; Leap (use! :ggandor/leap.nvim :after :nvim-treesitter :config (fn [] ((. (require :leap) :add_default_mappings)) (bind! :o :z "(leap-forward-to)") (bind! :o :Z "(leap-backward-to)") (vim.api.nvim_set_hl 0 :LeapBackdrop { :link :Comment }))) ;; Leap spooky (remote operations) (use! :ggandor/leap-spooky.nvim :after :leap.nvim :config #(setup! :leap-spooky)) ;; Flit (leap for 'till' and 'forward' motions) (use! :ggandor/flit.nvim :after :leap.nvim :config #(setup! :flit)) ;; Comment.nvim (use! :numToStr/Comment.nvim :config #(setup! :Comment)) ;; nvim-find-other (use! "https://git.zander.im/Zander671/nvim-find-other.git" :config (fn [] (setup! :nvim-find-other :c [ "h" "H" ] :h [ "c" "C" "cpp" "cxx" "c++" "cc" ] :cpp [ "hpp" "hxx" "h++" "hh" "h" "H" ] :hpp [ "cpp" "cxx" "c++" "cc" ]) (let [find_other (require :nvim-find-other)] (vim.api.nvim_create_user_command "FindOtherFile" find_other.find_other_current_buffer {}) (bind! :n :go find_other.find_other_current_buffer)))) (use! :RRethy/vim-illuminate :config (fn [] ((. (require :illuminate) :configure) { :providers [ "lsp" "treesitter" "ragex" ] :modes_denylist [ :markdown :text :gitconfig :gitignore ] :delay 0 } (hook! :FileType :lua #(vim.api.nvim_set_hl 0 :IlluminatedWordText {}))))) ;; Conjure (better lisp support) (use! :Olical/conjure :config #(tset vim.g :conjure#filetype#fennel "conjure.client.fennel.stdio")) ;; Snippy (use! :dcampos/nvim-snippy :config #(let [map (require :snippy.mapping)] (bind! [ :i :s ] : ((. map :next) :)) (bind! [ :i :s ] : (map.previous :)))) ;; nvim-cmp (autocomplete) (use! :hrsh7th/nvim-cmp :requires [ :hrsh7th/cmp-buffer :hrsh7th/cmp-path :hrsh7th/cmp-cmdline :hrsh7th/cmp-nvim-lsp :PaterJason/cmp-conjure :dcampos/cmp-snippy ] :config #(let [cmp (require :cmp) compare (require :cmp.config.compare)] (cmp.setup { :snippet { :expand #((. (require :snippy) :expand_snippet) $1.body) } :mapping { : (cmp.mapping (cmp.mapping.scroll_docs -4) [ :i :c ]) : (cmp.mapping (cmp.mapping.scroll_docs 4) [ :i :c ]) : (cmp.mapping (cmp.mapping.complete) [ :i :c ]) : (cmp.mapping (cmp.mapping.abort) [ :i :c ]) : (cmp.mapping (cmp.mapping.confirm { :select false }) [ :i :c ]) : (cmp.mapping (cmp.mapping.select_next_item { :behavior cmp.SelectBehavior.Select }) [ :i :c ]) : (cmp.mapping (cmp.mapping.select_prev_item { :behavior cmp.SelectBehavior.Select }) [ :i :c ]) : (cmp.mapping (cmp.mapping.select_next_item { :behavior cmp.SelectBehavior.Select }) [ :i :c ]) : (cmp.mapping (cmp.mapping.select_prev_item { :behavior cmp.SelectBehavior.Select }) [ :i :c ]) : (cmp.mapping (cmp.mapping.select_next_item { :behavior cmp.SelectBehavior.Select }) [ :i :c ]) : (cmp.mapping (cmp.mapping.select_prev_item { :behavior cmp.SelectBehavior.Select }) [ :i :c ]) } :sources (cmp.config.sources [{ :name "nvim_lsp" :priority 2 } { :name "conjure" :priority 2 } { :name "snippy" :priority 1 } { :name "buffer" :priority 0 } { :name "path" :priority 0 }]) :sorting { :priority_weight 2 :comparators [ compare.score compare.locality compare.recently_used compare.offset compare.order ; compare.scopes ; compare.exact ; compare.sort_text ; compare.kind ; compare.length ]}}) (cmp.setup.cmdline "/" { :mapping (cmp.mapping.preset.cmdline) :sources (cmp.config.sources [{ :name "buffer" }])}) (cmp.setup.cmdline ":" { :mapping (cmp.mapping.preset.cmdline) :sources (cmp.config.sources [{ :name "cmdline" } { :name "path" }])}))) ;; lspconfig (fn lsp_config_callback [] (fn _G.zander.lsp_on_attach [_ buf] (bind! :n :gD vim.lsp.buf.declaration buf) (bind! :n :gd vim.lsp.buf.definition buf) (bind! :n :K vim.lsp.buf.hover buf) (bind! :n :gI vim.lsp.buf.implementation buf) (bind! :n : vim.lsp.buf.signature_help buf) (bind! :n :wa vim.lsp.buf.add_workspace_folder buf) (bind! :n :wr vim.lsp.buf.remove_workspace_folder buf) (bind! :n :wl #(print (vim.inspect (vim.lsp.buf.list_workspace_folders))) buf) (bind! :n :D vim.lsp.buf.type_definition buf) (bind! :n :rn vim.lsp.buf.rename buf) (bind! :n :cn vim.lsp.buf.code_action buf) ; (bind! :n :gr vim.lsp.buf.references buf) (bind! :n :o #(vim.lsp.buf.format { :async true }) buf) ;; Some telescope commands (let [builtin (require :telescope.builtin)] (bind! :n :gr builtin.lsp_references buf) (bind! :n :s builtin.lsp_dynamic_workspace_symbols buf) (bind! :n :fs builtin.lsp_dynamic_workspace_symbols buf) (bind! :n :fS builtin.lsp_workspace_symbols buf) (bind! :n :d builtin.lsp_document_symbols buf) (bind! :n :fd builtin.lsp_document_symbols buf))) (fn _G.zander.lsp_get_data_dir [server root] (let [resolved_path (vim.fn.resolve root) joined_path (vim.fn.substitute resolved_path "\\/" "@" :g)] (.. (vim.fn.fnamemodify (.. "~/.local/nvim/lsp-cache/" server "/") ":p") joined_path))) (let [lsp (require :lspconfig) lsp_cap ((. (require :cmp_nvim_lsp) :default_capabilities))] (macro setup_server! [name ...] (let [opts { :on_attach `_G.zander.lsp_on_attach :capabilities `lsp_cap }] (var last_key nil) (each [_ val (ipairs [...])] (if last_key (do (tset opts last_key val) (set last_key nil)) (set last_key val))) `((. (. lsp ,name) :setup) ,opts))) (setup_server! :ccls) (setup_server! :cmake) (setup_server! :gopls) (setup_server! :rust_analyzer) (setup_server! :texlab) (setup_server! :sumneko_lua :settings { :Lua { :runtime { :version "LuaJIT" } :diagnostics { :globals [ "vim" ] } :workspace { :checkThirdParty false :library (vim.api.nvim_get_runtime_file "" true) } :telemetry { :enable false }}}))) (use! :neovim/nvim-lspconfig :after :cmp-nvim-lsp :config lsp_config_callback) ;; nvim-jdtls (fn jdtls_config_callback [] (hook! :FileType :java #(let [root_dir ((. (require :jdtls.setup) :find_root) [ ".git" "mvnw" "gradlew" "build.gradle" ]) lsp_cap ((. (require :cmp_nvim_lsp) :default_capabilities))] ((. (require :jdtls) :start_or_attach) { :capabilities lsp_cap :on_attach _G.zander.lsp_on_attach :root_dir root_dir :cmd [ "jdtls" "-data" (_G.zander.lsp_get_data_dir :jdtls root_dir) ]})))) (use! :mfussenegger/nvim-jdtls :after :nvim-lspconfig :config jdtls_config_callback) ;; Sync all packages on first launch (if _G.zander.first_launch ((. (require :packer) :sync)))) (let [packer (require :packer)] (packer.startup packer_init_callback))