プログラミングする時の一番大事な道具は、いうまでもなくエディタだ。僕は、neovim を使っていて、その設定をまとめました。

mkdir -p ~/.config/nvim/lua
mkdir -p ~/.config/nvim/plugin
mkdir -p ~/.config/nvim/after/plugin

init.lua

これは、 ~/.config/nvim/init.lua で、みての通り、他の設定をまとめているだけ。neovim は、lua 言語(でも)設定する。

require('options')
require('keymaps')
require('packages')

ディレクトリはこんな感じ。linux なら、一番上は home として読み代えてください。

Users/
├─ yasushi/
│  ├─ .config/
│  │  ├─ nvim/
│  │  │  ├─ init.lua
│  │  │  ├─ lua/
│  │  │  ├─ options.lua
│  │  │  ├─ keymaps.lua
│  │  │  ├─ packages.lua

options.lua

vim.opt.tabstop = 8
vim.opt.shiftwidth = 8
vim.opt.softtabstop = 8

-- I don't know why this is not working with 'vim.opt'
vim.cmd[[set noexpandtab]]

vim.opt.encoding = 'utf-8'
vim.scriptencoding = 'utf-8'
-- vim.opt.ambiwidth= 'double'

vim.opt.timeoutlen=300
vim.opt.updatetime=300
vim.opt.signcolumn="yes"
vim.opt.splitright = true
vim.opt.splitbelow = true
vim.opt.mouse="a"
vim.opt.undodir= vim.fn.getenv("HOME") .. "/.vimdid"
vim.opt.undofile = true
vim.opt.scrolloff = 10
vim.opt.laststatus = 2
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.gdefault = true

vim.opt.relativenumber = true
vim.opt.number = true

vim.ayucolor="dark"

if vim.fn.has('macunix') then
  vim.opt.clipboard:append { 'unnamedplus' }
end


-- au TextYankPost * silent! lua vim.highlight.on_yank()
vim.cmd('au TextYankPost * silent! lua.highlight.on_yank()')

-- neomutt text-flowed option
vim.cmd('au BufNewFile,BufRead neomutt-* setf mail')

-- color scheme
vim.g.tokyonight_style = "night"
vim.cmd[[colorscheme tokyonight]]

ファイルタイプ個別対応

rust

vim.cmd('setl colorcolumn=100')
vim.cmd('setl expandtab')
vim.cmd('setl tabstop=4')
vim.cmd('setl shiftwidth=4')
vim.cmd('setl softtabstop=4')


local tools = require"rust-tools";

require"which-key".register(
  {
    m = {
      name = 'Language specific tools (Rust)',
      e = {tools.expand_macro.expand_macro, 'expand macro'},
      c = {tools.open_cargo_toml.open_cargo_toml, 'open Cargo.toml'},
      h = {'<cmd>RustHoverActions<cr><cmd>RustHoverActions<cr>', 'hover actions'},
      
      -- want to toggle....
      d = {'<cmd>RustDisableInlayHints<cr>', 'disable inlay hints'},
      i = {'<cmd>RustEnableInlayHints<cr>', 'enable inlay hints'},
    }
  },
  {prefix = "<leader>"}
)

mail

Mail の設定参照の事たまーに、一行 72 文字で書きましょうみたいなメーリスがおたく界隈ではあるので、それに対応している。でも、英語圏でしか通用しないんだけどね。

vim.cmd('setl tw=72')
vim.cmd('setl fo=awq')
vim.cmd('setl comments+=nb:>')

keymaps

プラグインの関数と結びつけていたり、特殊なものは whichkey を使っているので、そっち参照

local keymap = vim.api.nvim_set_keymap
local opts = {noremap=true}

local function nkeymap (key, target)
    keymap('n', key, target, opts)
end

vim.g.mapleader = " "
keymap('i', 'jk', '<Esc>', opts)
nkeymap('j','gj')
nkeymap('k','gk')
nkeymap(';',':')

-- window motion
nkeymap('ff', '<C-w>w')
nkeymap('fg', ':vsplit<cr><C-w>w')
nkeymap('fv', ':split<cr><C-w>w')
nkeymap('fh', '<C-w>h')
nkeymap('fj', '<C-w>j')
nkeymap('fk', '<C-w>k')
nkeymap('fl', '<C-w>l')
nkeymap('<C-f>h', '<C-w><')
nkeymap('<C-f>j','<C-w>>')
nkeymap('<C-f>k', '<C-w>+')
nkeymap('<C-f>l', '<C-w>-')

-- increment / decrement
nkeymap('+', '<C-a>')
nkeymap('-', '<C-x>')

-- terminal
keymap('t', 'jk', '<c-\\><c-n>', {})

packages.lua

packer ってのが lua 製のパッケージマネージャらしいので使う。

最初に packer 自体を入れないといけないのはここに書いてあるけど、ようするに下記を実行する。

git clone --depth 1 https://github.com/wbthomason/packer.nvim\ ~/.local/share/nvim/site/pack/packer/start/packer.nvim

設定をなにか変えたら下記を実行で、同期してくれる。

nvim --headless -c 'autocmd User PackerComplete quitall' -c 'PackerSync'

:PackerInstall をすれば、他のをインストールしてくれる。他のコマンドも用途に応じてどうぞ。

-- make sure packer is there
local status, packer = pcall(require, "packer")
if (not status) then
  print("Can't find Packer")
  return
end

vim.cmd[[packadd packer.nvim]]

require('packer').startup(function()

  use 'wbthomason/packer.nvim'
  use 'kyazdani42/nvim-web-devicons'
  use 'jose-elias-alvarez/null-ls.nvim'
  use 'glepnir/lspsaga.nvim' -- better LSP UI
  use 'neovim/nvim-lspconfig'
  --- after 'plugins'

  -- auto pairs
  use 'windwp/nvim-autopairs'
  use 'windwp/nvim-ts-autotag'

  -- completion
  use 'hrsh7th/nvim-cmp'
  use 'L3MON4D3/LuaSnip'
  use "rafamadriz/friendly-snippets"
  use 'onsails/lspkind-nvim'
  use 'hrsh7th/cmp-nvim-lsp'
  use 'hrsh7th/cmp-path'
  use 'hrsh7th/cmp-buffer'

  -- git
  use { 'TimUntersberger/neogit',
        requires = {'nvim-lua/plenary.nvim' }
  }

  use { 'nvim-lualine/lualine.nvim',
        requires = { 'kyazdani42/nvim-web-devicons', opt = true }
  }

  use 'MunifTanjim/prettier.nvim'

  use { 'nvim-telescope/telescope.nvim',
        requires = {'nvim-lua/plenary.nvim'},
  }
  use 'nvim-telescope/telescope-file-browser.nvim'

  use { 'nvim-treesitter/nvim-treesitter',
        run = ':TSUpdate'
  }

  use 'airblade/vim-rooter'
  use 'ggandor/lightspeed.nvim'
    --  use leap?

  use 'simrat39/rust-tools.nvim'

  use 'famiu/bufdelete.nvim'
  use 'folke/tokyonight.nvim'

  -- this comes last
  use {'folke/which-key.nvim',
      config = function()
      require("which-key").setup { spelling = { enabled = true }}
      end
  }

  use 'haya14busa/vim-migemo'

end)

null-ls

local status, config = pcall(require, "null-ls")
if (not status) then return end

config.setup({
  sources = {
    config.builtins.diagnostics.eslint_d.with({
      diagnostics_format = '[eslint] #{m}\n(#{c})'
    })
  }
})

lspsaga

local status, saga = pcall(require, "lspsaga")
if (not status) then return end

saga.init_lsp_saga {
  server_filetype_map = {
    typescript = 'typescript'
  },
  finder_action_keys = {
    open = "<CR>",
    quit = "<esc>"
  }
}

lspconfig

--vim.lsp.set_log_level("debug")

local status, nvim_lsp = pcall(require, "lspconfig")
if (not status) then return end

local protocol = require('vim.lsp.protocol')

-- Use an on_attach function to only map the following keys
-- after the language server attaches to the current buffer
local on_attach = function(client, bufnr)
  local function buf_set_option(...) vim.api.nvim_buf_set_option(bufnr, ...) end

  --Enable completion triggered by <c-x><c-o>
  buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc')

  -- Mappings.
  local opts = { noremap = true, silent = true }

  -- formatting
  if client.server_capabilities.documentFormattingProvider then
    vim.api.nvim_create_autocmd("BufWritePre", {
      group = vim.api.nvim_create_augroup("Format", { clear = true }),
      buffer = bufnr,
      callback = function() vim.lsp.buf.formatting_seq_sync() end
    })
  end
end

protocol.CompletionItemKind = {
  '', -- Text
  '', -- Method
  '', -- Function
  '', -- Constructor
  '', -- Field
  '', -- Variable
  '', -- Class
  'ﰮ', -- Interface
  '', -- Module
  '', -- Property
  '', -- Unit
  '', -- Value
  '', -- Enum
  '', -- Keyword
  '﬌', -- Snippet
  '', -- Color
  '', -- File
  '', -- Reference
  '', -- Folder
  '', -- EnumMember
  '', -- Constant
  '', -- Struct
  '', -- Event
  'ﬦ', -- Operator
  '', -- TypeParameter
}

-- Set up completion using nvim_cmp with LSP source
local capabilities = require('cmp_nvim_lsp').update_capabilities(
  vim.lsp.protocol.make_client_capabilities()
)

nvim_lsp.tsserver.setup {
  on_attach = on_attach,
  filetypes = { "typescript", "typescriptreact", "typescript.tsx" },
  cmd = { "typescript-language-server", "--stdio" },
  capabilities = capabilities
}

nvim_lsp.sumneko_lua.setup {
  on_attach = on_attach,
  settings = {
    Lua = {
      diagnostics = {
        -- Get the language server to recognize the `vim` global
        globals = { 'vim' },
      },

      workspace = {
        -- Make the server aware of Neovim runtime files
        library = vim.api.nvim_get_runtime_file("", true),
        checkThirdParty = false
      },
    },
  },
}

require("rust-tools").setup({
  server = {
    on_attach = on_attach,
    capabilities = capabilities
  },
})

nvim_lsp.tailwindcss.setup {}

vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
  vim.lsp.diagnostic.on_publish_diagnostics, {
  underline = true,
  update_in_insert = false,
  virtual_text = { spacing = 4, prefix = "●" },
  severity_sort = true,
}
)

-- Diagnostic symbols in the sign column (gutter)
local signs = { Error = " ", Warn = " ", Hint = " ", Info = " " }
for type, icon in pairs(signs) do
  local hl = "DiagnosticSign" .. type
  vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = "" })
end

vim.diagnostic.config({
  virtual_text = {
    prefix = '●'
  },
  update_in_insert = true,
  float = {
    source = "always", -- Or "if_many"
  },
})

これいこうはそれぞれの Plugin の設定は、 ~/.config/nvim/after/plugin/XXX.rc.lua の中にかいておくと、

autopairs

local status, config = pcall(require, "nvim-autopairs")
if(not status) then return end
config.setup({
    disable_filetype = {"TelescopePrompt", "vim"}
})

cmp

local status, cmp = pcall(require, "cmp")
if(not status) then return end

local has_words_before = function()
  local line, col = unpack(vim.api.nvim_win_get_cursor(0))
  return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
end
local lspkind = require 'lspkind'
local luasnip = require 'luasnip'

-- lazy load
require("luasnip.loaders.from_vscode").lazy_load()

cmp.setup({
    snippet = {
      expand = function(args)
        luasnip.lsp_expand(args.body)
      end,
    },
    
    mapping = cmp.mapping.preset.insert({
        ['<C-d>'] = cmp.mapping.scroll_docs(-4),
        ['<C-f>'] = cmp.mapping.scroll_docs(-4),
        ['<C-Space>'] = cmp.mapping.complete(),
        ['<C-e>'] = cmp.mapping.close(),
        ['<CR>'] = cmp.mapping.confirm({
            behaivior = cmp.ConfirmBehavior.Replace,
            select = true
        }),
        ["<Tab>"] = cmp.mapping(function(fallback)
            if cmp.visible() then
              cmp.select_next_item()
            elseif luasnip.expand_or_jumpable() then
              luasnip.expand_or_jump()
            elseif has_words_before() then
              cmp.complete()
            else
              fallback()
            end
        end, { "i", "s" }),
        ["<S-Tab>"] = cmp.mapping(function(fallback)
            if cmp.visible() then
              cmp.select_prev_item()
            elseif luasnip.jumpable(-1) then
              luasnip.jump(-1)
            else
              fallback()
            end
        end, { "i", "s" })
    }),
    sources = cmp.config.sources({
        {name = 'nvim_lsp'},
        {name = 'buffer'},
        {name = 'path'},

    }),
    formatting = {
      format = lspkind.cmp_format({mode = 'symbol', maxwidth = 50})
    }
})

lspkind

local status, config = pcall(require, "lspkind")
if (not status) then return end

config.init({
    mode='symbol',
})

lualine

local status, config = pcall(require, "lualine")
if (not status) then return end

config.setup{
  options = {
    theme = 'tokyonight'
  }
}

prettier

local status, config = pcall(require, "prettier")
if (not status) then return end

config.setup {
  bin = 'prettierd',
  filetypes = {
    "css",
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "json",
    "scss",
    "less"
  }
}

telescope

local status, config = pcall(require, "telescope")
if (not status) then return end

local actions = require('telescope.actions')

config.setup {
  defaults = {
    mappings = {
      n = {
        ["q"] = actions.close
      },
    },
  },
  extensions = {
    file_browser = {
      theme = "dropdown",
      -- disables netrw and use telescope-file-browser in its place
      hijack_netrw = true,
    },
  },
}

config.load_extension("file_browser")

treesitter

local status, config = pcall(require, "nvim-treesitter.configs")
if (not status) then return end

config.setup {
  autotag = {
    enable = true
  },
  highlight = {
    enable = true,
    disable = {},
  },
  indent = {
    enable = true,
    disable = {},
  },
  ensure_installed = {
    "tsx",
    "toml",
    "php",
    "json",
    "yaml",
    "css",
    "html",
    "lua",
    "rust"
  },
  autotag = {
    enable = true,
  },
}

local parser_config = require "nvim-treesitter.parsers".get_parser_configs()
parser_config.tsx.filetype_to_parsername = { "javascript", "typescript.tsx" }

whichkey

local status, config = pcall(require, "which-key")
if(not status) then return end

config.setup{
  spelling = {enabled = true}
}

config.register(
  {
  f = {'<cmd>Telescope find_files<cr>', 'Find files'},
  r = {'<cmd>Telescope live_grep<cr>', 'Grep'},
  h = {'<cmd>Telescope help_tags<cr>', 'Help tags'},
  j = {'<cmd>Migemo<cr>', 'search Japanese text'},
  b = {
    name = 'buffer',
    b = {"<cmd>Telescope buffers<cr>", "Buffer list"},
    d = {"<cmd>Bdelete<cr>", "Delete buffer"}
  },
  g = {
    name = 'LSP functions',
    g = {'<cmd>Lspsaga lsp_finder<cr>', 'LSP find file'},
    n = {'<cmd>Lspsaga dignostic_jump_next<cr>', 'next diagnostic'},
    p = {'<cmd>Lspsaga dignostic_jump_prev<cr>', 'prev diagnostic'},
    d = {'<cmd>Lspsaga preview_deifinition<cr>', 'preview definition'},
    D = {'<cmd>lua vim.lsp.buf.definition<cr>', 'jump to definition'},
    a = {'<cmd>Lspsaga code_action<cr>', 'code action'},
    k = {'<cmd>Lspsaga hover_doc<cr>', 'documentation'},
    K = {'<cmd>Lspsaga signature_help<cr>', 'signature help'},
    i = {'<cmd>lua vim.lsp.buf.implementation<cr>', 'list implementation'},
    r = {'<cmd>Lspsaga rename<cr>', 'rename'}
  },
  s = {'<cmd>Neogit kind=split<cr>', 'Git'},
  t = {'<cmd>terminal<cr>', 'Open Terminal'},
  w = {'<cmd>w<cr>', 'write'},
  e = {'<cmd>e .<cr>', 'edit'}
  },
  {prefix = "<leader>"}
)


migemo

cmigemo の辞書ファイルがみつからなくて :Migemo コマンドから怒られる。

vim.g.migemodict='/opt/homebrew/Cellar/cmigemo/20110227/share/migemo/utf-8/migemo-dict'

vscode

neovim じゃないけど、vscode を Mac で使(わないといけない悲しい状況があり)う時に、 hjkl がリピートしない。

defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false
defaults delete -g ApplePressAndHoldEnabled

上をやれって言われてるけど、ならない。謎

Date: 2022-07-26 Tue 10:00