Last active
November 14, 2022 19:22
-
-
Save henrycatalinismith/987542d02f8560b412f78dcfa02ad9fb to your computer and use it in GitHub Desktop.
init.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 💾 cosy 1 file nvim cfg -- | |
-- 🧙 drag+ drop install -- | |
-- 📦 0 package management -- | |
-------------------------------- | |
-- plugins --------------------- | |
-------------------------------- | |
local plugins = {} | |
local function plugin_register(name) | |
local dir = string.format( | |
"%s/site/pack/paqs/", | |
vim.fn.stdpath("data") | |
) | |
local url = string.format( | |
"https://github.com/%s", | |
name | |
) | |
plugins[name] = { | |
name = name, | |
dir = dir, | |
url = url, | |
} | |
end | |
local function plugin_install(name) | |
local plugin = plugins[name] | |
local args = { | |
"clone", | |
plugin.url, | |
"--depth=1", | |
"--recurse-submodules", | |
"--shallow-submodules", | |
plugin.dir, | |
} | |
end | |
-------------------------------- | |
-- tables ---------------------- | |
-------------------------------- | |
-- Table index search | |
local function tbl_index(array, value) | |
for i, v in ipairs(array) do | |
if v == value then | |
return i | |
end | |
end | |
return nil | |
end | |
-------------------------------- | |
-- editor ---------------------- | |
-------------------------------- | |
local function editor_keymaps() | |
-- Use space as the leader key | |
-- | |
-- Space is the biggest key on | |
-- a standard keyboard and | |
-- doesn't do anything by | |
-- default in normal mode. | |
vim.g.mapleader = " " | |
end | |
-------------------------------- | |
-- indentation ----------------- | |
-------------------------------- | |
-- Configure columns per indent | |
local function indent_size(s, v) | |
s.shiftwidth = v | |
s.tabstop = v | |
end | |
-- Toggle between tabs & spaces | |
local function indent_style(s, v) | |
if v == "tab" then | |
s.expandtab = false | |
elseif v == "space" then | |
s.expandtab = true | |
end | |
end | |
-- 1 space indents by default | |
-- | |
-- This is intentionally small | |
-- in order to optimize for | |
-- shorter lines. Code that fits | |
-- in a narrow column is more | |
-- legible in space-constrained | |
-- contexts like mobile devices, | |
-- refreshable braille displays, | |
-- and split diffs. | |
-- | |
-- Default to spaces, not tabs | |
-- | |
-- Indentation using spaces is | |
-- more ubiquitous than tabs so | |
-- this is a better starting | |
-- point. | |
local function indent_defaults() | |
indent_size(vim.o, 1) | |
indent_style(vim.o, "space") | |
end | |
-------------------------------- | |
-- autocommands ---------------- | |
-------------------------------- | |
-- Autogroup shorthand | |
local function augroup(name) | |
vim.api.nvim_create_augroup( | |
name, | |
{ clear = true } | |
) | |
end | |
local augroups = { | |
editorconfig = augroup("editorconfig"), | |
gist = augroup("gist"), | |
lsp = augroup("lsp"), | |
terminal = augroup("terminal"), | |
} | |
-------------------------------- | |
-- themes ---------------------- | |
-------------------------------- | |
local function theme_apply(groups) | |
for g,c in pairs(groups) do | |
for n,v in pairs(c) do | |
local cmd = string.format( | |
"hi %s %s=%s gui=none", | |
g, n, v | |
) | |
vim.cmd(cmd) | |
end | |
end | |
end | |
local colors = { | |
gray = "#808080", | |
snow = "#FFFAFA", | |
crimson = "#DC143C", | |
darkolivegreen = "#556B2F", | |
darkslategray = "#2F4F4F", | |
deeppink = "#FF1493", | |
dodgerblue = "#1E90FF", | |
firebrick = "#B22222", | |
green = "#008000", | |
honeydew = "#F0FFF0", | |
lavender = "#E6E6FA", | |
lavenderblush = "#FFF0F5", | |
lightcyan = "#E0FFFF", | |
lightgray = "#D3D3D3", | |
lightskyblue = "#87CEFA", | |
lightyellow = "#FFFFE0", | |
orangered = "#FF4500", | |
palegreen = "#98FB98", | |
palevioletred = "#DB7093", | |
powderblue = "#B0E0E6", | |
royalblue = "#4169E1", | |
seagreen = "#2E8B57", | |
teal = "#008080", | |
thistle = "#D8BFD8", | |
violet = "#EE82EE", | |
yellow = "#FFFF00", | |
} | |
local function theme_light() | |
theme_apply({ | |
Normal = { | |
guibg = colors.darkslategray, | |
guifg = colors.snow, | |
}, | |
SignColumn = { | |
guibg = colors.darkslategray, | |
}, | |
LineNr = { | |
guibg = colors.darkslategray, | |
guifg = colors.thistle, | |
}, | |
CursorLine = { | |
guibg = "none", | |
}, | |
CursorLineNR = { | |
guifg = colors.deeppink, | |
}, | |
PMenu = { | |
guibg = colors.darkslategray, | |
guifg = colors.snow, | |
}, | |
PMenuSel = { | |
guibg = colors.deeppink, | |
guifg = colors.snow, | |
}, | |
IncSearch = { | |
guibg = colors.yellow, | |
}, | |
TabLineFill = { | |
guifg = colors.snow, | |
}, | |
TabLineBuffer = { | |
guibg = colors.lavender, | |
guifg = colors.deeppink, | |
}, | |
TabLineBufferSel = { | |
guibg = colors.deeppink, | |
guifg = colors.snow, | |
}, | |
TabLineNC = { | |
guibg = colors.lavender, | |
}, | |
TabLine = { | |
guibg = colors.powderblue, | |
guifg = colors.royalblue, | |
}, | |
TabLineSel = { | |
guibg = colors.royalblue, | |
guifg = colors.snow, | |
}, | |
DiagnosticVirtualTextError = { | |
guibg = colors.lavenderblush, | |
guifg = colors.crimson, | |
}, | |
DiagnosticVirtualTextWarn = { | |
guibg = colors.lightyellow, | |
guifg = colors.orangered, | |
}, | |
DiagnosticVirtualTextInfo = { | |
guibg = colors.honeydew, | |
guifg = colors.darkolivegreen, | |
}, | |
DiagnosticVirtualTextHint = { | |
guibg = colors.lightcyan, | |
guifg = colors.teal, | |
}, | |
Comment = { | |
guifg = colors.lightgray, | |
}, | |
PreProc = { | |
guifg = colors.violet, | |
}, | |
Special = { | |
guifg = colors.violet, | |
}, | |
String = { | |
guifg = colors.lightskyblue, | |
}, | |
Constant = { | |
guifg = colors.lightskyblue, | |
}, | |
Statement = { | |
guifg = colors.violet, | |
}, | |
Identifier = { | |
guifg = colors.palegreen, | |
}, | |
Type = { | |
guifg = colors.palegreen, | |
}, | |
StatusLine = { | |
guibg = colors.snow, | |
guifg = colors.darkslategray, | |
}, | |
}) | |
end | |
-------------------------------- | |
-- path ------------------------ | |
-------------------------------- | |
-- Return parent directory name | |
local function path_dirname(path) | |
return vim.fn.fnamemodify(path, ":h") | |
end | |
-- Check the file's there | |
local function path_exists(path) | |
local f = io.open(path, "r") | |
if f ~= nil then | |
io.close(f) | |
return true | |
else | |
return false | |
end | |
end | |
-- Remove path's trailing slash | |
local function path_strip(path) | |
local p, _ = path:gsub("/$", '') | |
return p | |
end | |
-- Just get the filename | |
local function path_basename(path) | |
path = path_strip(path) | |
local i = path:match("^.*()/") | |
if not i then return path end | |
return path:sub(i + 1, #path) | |
end | |
-------------------------------- | |
-- buffers --------------------- | |
-------------------------------- | |
-- List buffers belonging to tab | |
-- | |
-- This many-to-one relationship | |
-- doesn't really exist in nvim | |
-- itself but the concept is | |
-- useful so this function bolts | |
-- it on heuristically. A buffer | |
-- is considered to "belong to" | |
-- a tab if the tab's cwd is a | |
-- prefix of the file's path. | |
-- So a buffer at | |
-- /var/www/index.html would be | |
-- considered as "belonging to" | |
-- a tab with cwd /var/www. | |
local function tab_buffers(tabnr) | |
local bufnrs = {} | |
local bufs = vim.api.nvim_list_bufs() | |
for _,i in pairs(bufs) do | |
if not vim.api.nvim_buf_is_loaded(i) then | |
goto continue | |
end | |
local bl = vim.api.nvim_buf_get_option( | |
i, | |
"buflisted" | |
) | |
if not bl then | |
goto continue | |
end | |
local path = vim.api.nvim_buf_get_name(i) | |
local cwd = vim.fn.getcwd(-1, tabnr) | |
local prefix = path:match("^"..cwd) | |
local inside = prefix ~= nil | |
if not inside then | |
goto continue | |
end | |
table.insert(bufnrs, i) | |
::continue:: | |
end | |
return bufnrs | |
end | |
-- Close the current buffer | |
-- | |
-- Equivalent to :tabclose | |
-- except it preserves the tab, | |
-- unloads the currently focused | |
-- buffer, and switches the tab | |
-- to point at another buffer. | |
local function buffer_close() | |
local buf = vim.api.nvim_get_current_buf() | |
local tab = vim.fn.tabpagenr() | |
local bl = vim.api.nvim_buf_get_option( | |
buf, | |
"buflisted" | |
) | |
if not bl then | |
vim.cmd [[ :quit! ]] | |
return | |
end | |
vim.cmd [[ bd ]] | |
local bufs = tab_buffers(tab) | |
if #bufs < 1 then | |
return | |
end | |
local cmd = string.format( | |
"buffer %d", | |
bufs[1] | |
) | |
vim.cmd(cmd) | |
end | |
local function buffer_filename() | |
return vim.api.nvim_buf_get_name( | |
vim.api.nvim_get_current_buf() | |
) | |
end | |
-- Open the next buffer | |
-- | |
-- Equivalent to :tabnext except | |
-- it moves between the buffers | |
-- considered as "belonging to" | |
-- the current tab. | |
local function buffer_next() | |
local buf = vim.api.nvim_get_current_buf() | |
local tab = vim.fn.tabpagenr() | |
local bufs = tab_buffers(tab) | |
if #bufs < 2 then | |
return | |
end | |
if not vim.tbl_contains(bufs, buf) then | |
return | |
end | |
local cur_index = tbl_index(bufs, buf) | |
local nxt_index = cur_index + 1 | |
if nxt_index > #bufs then | |
nxt_index = 1 | |
end | |
local nxt_buf = bufs[nxt_index] | |
local cmd = string.format( | |
"buffer %d", | |
nxt_buf | |
) | |
vim.cmd(cmd) | |
end | |
local function buffer_write() | |
vim.cmd [[ write ]] | |
end | |
-- Keyboard shortcus for buffers | |
local function buffer_keymaps() | |
vim.keymap.set( | |
"n", | |
"<Tab>", | |
buffer_next, | |
{ noremap = true } | |
) | |
vim.keymap.set( | |
"n", | |
"<leader>q", | |
buffer_close, | |
{ noremap = true } | |
) | |
vim.keymap.set( | |
"n", | |
"<leader>w", | |
buffer_write, | |
{ noremap = true } | |
) | |
end | |
-------------------------------- | |
-- tabs ------------------------ | |
-------------------------------- | |
-- Open a new tab | |
local function tab_new() | |
-- TODO: project picker? | |
vim.cmd [[ :tabnew ]] | |
end | |
-- Keyboard shortcuts for tabs | |
local function tab_keymaps() | |
vim.keymap.set( | |
"n", | |
"<leader>t", | |
tab_new, | |
{ noremap = true } | |
) | |
end | |
-------------------------------- | |
-- lsp ------------------------- | |
-------------------------------- | |
-- Handle LSP attach event | |
local function lsp_attach() | |
-- TODO: Inform of this event | |
-- somehow. Ambiguity about | |
-- which buffers are attached | |
-- to an LSP and which aren't | |
-- is annoying. | |
vim.keymap.set( | |
"n", | |
"gd", | |
"<cmd>lua vim.lsp.buf.definition()<CR>", | |
{ | |
noremap = true, | |
buffer = true, | |
} | |
) | |
vim.keymap.set( | |
"n", | |
"gh", | |
"<cmd>lua vim.lsp.buf.hover()<CR>", | |
{ | |
noremap = true, | |
buffer = true, | |
} | |
) | |
vim.keymap.set( | |
"n", | |
"gr", | |
"<cmd>lua vim.lsp.buf.references()<CR>", | |
{ | |
noremap = true, | |
buffer = true, | |
} | |
) | |
vim.keymap.set( | |
"n", | |
"<leader>[", | |
"<cmd>lua vim.diagnostic.goto_prev()<CR>", | |
{ | |
noremap = true, | |
buffer = true, | |
} | |
) | |
vim.keymap.set( | |
"n", | |
"<leader>]", | |
"<cmd>lua vim.diagnostic.goto_next()<CR>", | |
{ | |
noremap = true, | |
buffer = true, | |
} | |
) | |
end | |
-- Start Lua server | |
local function lsp_lua() | |
local cmd = { | |
"lua-language-server" | |
} | |
-- TODO: fix root_dir | |
local root_dir = vim.fs.dirname( | |
buffer_filename() | |
) | |
local settings = { | |
completion = { | |
enable = true, | |
showWord = "Disable", | |
}, | |
runtime = { | |
version = "LuaJIT", | |
}, | |
telemetry = { | |
enable = false, | |
}, | |
} | |
vim.lsp.start({ | |
name = "lua", | |
cmd = cmd, | |
root_dir = root_dir, | |
settings = settings, | |
}) | |
end | |
-- Start TypeScript server | |
local function lsp_typescript() | |
local cmd = { | |
"typescript-language-server", | |
"--stdio", | |
} | |
-- TODO: fix root_dir | |
local root_dir = vim.fs.dirname( | |
buffer_filename() | |
) | |
local settings = { | |
completion = { | |
enable = true, | |
}, | |
completions = { | |
completeFunctionCalls = true, | |
}, | |
typescript = { | |
inlayHints = { | |
includeInlayParameterNameHints = "all", | |
includeInlayVariableTypeHints = true, | |
includeInlayEnumMemberValueHints = true, | |
}, | |
}, | |
javascript = { | |
inlayHints = { | |
includeInlayParameterNameHints = "all", | |
includeInlayVariableTypeHints = true, | |
includeInlayEnumMemberValueHints = true, | |
}, | |
}, | |
telemetry = { | |
enable = false, | |
}, | |
} | |
-- root_dir = vim.fs.dirname( | |
-- vim.fs.find( | |
-- { | |
-- "package.json", | |
-- "tsconfig.json", | |
-- }, | |
-- { | |
-- path = buffer_filename(), | |
-- upward = true, | |
-- } | |
-- ) | |
-- ), | |
vim.lsp.start({ | |
name = "tsserver", | |
cmd = cmd, | |
root_dir = root_dir, | |
settings = settings, | |
}) | |
end | |
-- Hook up LSP events | |
local function lsp_autocommands() | |
vim.api.nvim_create_autocmd( | |
{ "FileType" }, | |
{ | |
pattern = "lua", | |
callback = lsp_lua, | |
group = augroups.lsp, | |
} | |
) | |
vim.api.nvim_create_autocmd( | |
{ "FileType" }, | |
{ | |
pattern = { | |
"javascript", | |
"javascriptreact", | |
"typescript", | |
"typescriptreact", | |
}, | |
callback = lsp_typescript, | |
group = augroups.lsp, | |
} | |
) | |
vim.api.nvim_create_autocmd( | |
{ "LspAttach" }, | |
{ | |
callback = lsp_attach, | |
group = augroups.lsp, | |
} | |
) | |
end | |
-------------------------------- | |
-- zsh ------------------------- | |
-------------------------------- | |
-- Configure zsh | |
-- | |
-- Setting these options here | |
-- instead of in a config file | |
-- is limiting but also keeps | |
-- the entire development setup | |
-- within a single file. | |
local function zsh_args() | |
local args = {"zsh"} | |
-- Skip typing cd and go to | |
-- directories by typing just | |
-- their names. | |
table.insert(args, "--autocd") | |
-- Storing timestamps in shell | |
-- history is useful when | |
-- reconstructing incident | |
-- timelines. | |
table.insert(args, "--extendedhistory") | |
-- Skip loading any zsh config | |
-- files so that the shell's | |
-- environment can be fully | |
-- configured from nvim. | |
table.insert(args, "--no-rcs") | |
-- Write to history immediately | |
-- instead of waiting for exit. | |
table.insert( | |
args, "--incappendhistorytime" | |
) | |
return args | |
end | |
-- Configure zsh environment | |
-- | |
-- Setting these options here | |
-- instead of in a config file | |
-- is limiting but also keeps | |
-- the entire development setup | |
-- within a single file. | |
local function zsh_env() | |
local env = {} | |
-- Without CLICOLOR commands | |
-- like ls will print output | |
-- in plain text. | |
env.CLICOLOR = "1" | |
-- Just have a couple of | |
-- directory names' worth of | |
-- context in the prompt. | |
env.PROMPT = "%2~ $ " | |
return env | |
end | |
-- When zsh exits the intent to | |
-- close the terminal is crystal | |
-- clear but by default nvim | |
-- will keep the buffer open and | |
-- display the exit message | |
-- "[Process exited 0]". So by | |
-- default it requires a | |
-- sequence of two keystrokes to | |
-- close a terminal and this | |
-- call to buf_delete automates | |
-- the second one. | |
local function zsh_on_exit(buf) | |
return function() | |
vim.api.nvim_buf_delete( | |
buf, { force = true } | |
) | |
end | |
end | |
-------------------------------- | |
-- terminal -------------------- | |
-------------------------------- | |
-- Set up newly opened terminal | |
local function terminal_opened() | |
-- Line numbers and signs are | |
-- useless in terminals so turn | |
-- them off. | |
vim.opt_local.number = false | |
vim.opt_local.signcolumn = "no" | |
-- Opening a terminal already | |
-- took one keystroke and the | |
-- intent to enter a terminal | |
-- command next is crystal | |
-- clear so enter insert mode | |
-- automatically. | |
vim.cmd [[ startinsert ]] | |
end | |
-- Entering a terminal | |
local function terminal_entered() | |
-- For now most terminal | |
-- usage is via the <leader>` | |
-- shortcut so there's never | |
-- a circumstance where a | |
-- terminal is entered without | |
-- immediate intent to enter a | |
-- command. | |
vim.cmd [[ startinsert ]] | |
end | |
-- Leaving terminal insert mode | |
local function terminal_escaped() | |
-- For now most terminal usage | |
-- is in a horizontal split at | |
-- the bottom of the screen. | |
-- The next interaction after | |
-- leaving insert mode will | |
-- probably be to edit text so | |
-- restoring focus to the | |
-- editor window above removes | |
-- a kestroke from the journey. | |
vim.cmd [[ wincmd k]] | |
end | |
-- Hook up terminal events | |
local function terminal_autocommands() | |
vim.api.nvim_create_autocmd( | |
{ "TermOpen" }, | |
{ | |
callback = terminal_opened, | |
group = augroups.terminal, | |
} | |
) | |
vim.api.nvim_create_autocmd( | |
{ "BufEnter" }, | |
{ | |
pattern = "term://*", | |
callback = terminal_entered, | |
group = augroups.terminal, | |
} | |
) | |
vim.api.nvim_create_autocmd( | |
{ "ModeChanged" }, | |
{ | |
pattern = "t:nt", | |
callback = terminal_escaped, | |
group = augroups.terminal, | |
} | |
) | |
end | |
-- Open a zsh terminal | |
local function terminal_open() | |
local cmd = zsh_args() | |
local env = zsh_env() | |
local on_exit = zsh_on_exit( | |
vim.api.nvim_get_current_buf() | |
) | |
local opts = { | |
env = env, | |
on_exit = on_exit, | |
} | |
vim.fn.termopen(cmd, opts) | |
end | |
-- New splitscreen terminal | |
local function terminal_split() | |
vim.cmd [[ split terminal ]] | |
terminal_open() | |
end | |
-- Terminal keyboard shortcuts | |
local function terminal_keymaps() | |
vim.keymap.set( | |
"n", | |
"<leader>`", | |
terminal_split, | |
{ noremap = true } | |
) | |
vim.keymap.set( | |
"t", | |
"<Esc>", | |
"<C-\\><C-n>", | |
{ noremap = true } | |
) | |
end | |
-------------------------------- | |
-- gist ------------------------ | |
-------------------------------- | |
-- Upload init.lua to GitHub | |
local function gist_update() | |
local command = string.format( | |
"gist %s -u %s", | |
"~/.config/nvim/init.lua", | |
"987542d02f8560b412f78dcfa02ad9fb" | |
) | |
if vim.fn.executable("gist") then | |
io.popen(command) | |
end | |
end | |
-- Autoupdate gist on save | |
-- | |
-- These editor config files | |
-- are unusually prone to data | |
-- loss due to mistakes while | |
-- working on them. To avoid | |
-- losing an entire day's work | |
-- ever again, this autocmd | |
-- uploads the whole file to | |
-- GitHub after every save. | |
local function gist_autocommands() | |
vim.api.nvim_create_autocmd( | |
{ "BufWrite" }, | |
{ | |
pattern = { "init.lua" }, | |
callback = gist_update, | |
group = augroups.gist, | |
} | |
) | |
end | |
-------------------------------- | |
-- editorconfig ---------------- | |
-------------------------------- | |
-- List editorconfigs for path | |
-- | |
-- Scans each parent directory | |
-- for .editorconfig files and | |
-- returns the final list. | |
local function ec_find(path) | |
local files = {} | |
local dir = path_dirname(path) | |
local done = false | |
while not done do | |
local filename = string.format( | |
"%s.editorconfig", | |
vim.fn.fnamemodify(dir, ":p") | |
) | |
if path_exists(filename) then | |
table.insert(files, filename) | |
end | |
local parent = path_dirname(dir) | |
if parent == dir then | |
done = true | |
end | |
dir = parent | |
end | |
return files | |
end | |
-- Magics a working regex | |
-- | |
-- The pattern format that | |
-- editorconfig files use isn't | |
-- the same as nvim's regexes. | |
-- This code comes from | |
-- somewhere on GitHub and it | |
-- magics the editorconfig ones | |
-- into nvim ones. | |
local function ec_glob2regpat(glob) | |
local placeholder = "@@PLACEHOLDER@@" | |
return string.gsub( | |
vim.fn.glob2regpat( | |
vim.fn.substitute( | |
string.gsub( | |
glob, | |
"{(%d+)%.%.(%d+)}", | |
"[%1-%2]" | |
), | |
"\\*\\@<!\\*\\*\\@!", | |
placeholder, | |
"g" | |
) | |
), | |
placeholder, | |
"[^/]*" | |
) | |
end | |
-- Extract editorconfig line | |
-- | |
-- Returns 3 pieces. | |
-- 1. Pattern: | |
-- This is the contents of | |
-- the square brackets on | |
-- lines that set a pattern. | |
-- 2. Key: | |
-- This is the bit before the | |
-- equals sign on lines that | |
-- configure a setting. | |
-- 3. Value: | |
-- This is the bit after the | |
-- equals sign on lines that | |
-- configure a setting. | |
local function ec_parse(line) | |
local pregex = "^%s*%[(.*)%]%s*$" | |
local kvregx = table.concat({ | |
"^", | |
"([^:= ][^:=]-)", | |
"[:=]", | |
"(.-)", | |
"$" | |
}, "%s*") | |
local pi = (line:match("%b[]") or "") | |
local pattern = pi:match(pregex) | |
local key,value = line:match(kvregx) | |
if pattern ~= nil then | |
pattern = vim.regex( | |
ec_glob2regpat(pattern) | |
) | |
end | |
return pattern, key, value | |
end | |
-- Gets the options in the file | |
local function ec_read(filename, path) | |
local options = {} | |
local pattern = nil | |
local f = io.open(path) | |
for line in f:lines() do | |
local p, k, v = ec_parse(line) | |
if p then | |
pattern = p | |
end | |
if k ~= nil and v ~= nil then | |
local in_pattern = ( | |
pattern ~= nil | |
and pattern:match_str(filename) | |
) | |
if k == "root" then | |
options["root"] = (v == "true") | |
elseif in_pattern then | |
options[k] = v | |
end | |
end | |
end | |
return options | |
end | |
-- Apply editorconfig to buffer | |
local function ec_apply(bufnr, options) | |
for k,v in pairs(options) do | |
if k == "indent_size" then | |
indent_size(vim.bo[bufnr], tonumber(v)) | |
elseif k == "indent_style" then | |
indent_style(vim.bo[bufnr], v) | |
end | |
end | |
end | |
local function ec_init() | |
local b = vim.api.nvim_get_current_buf() | |
local buf = vim.bo[b] | |
local filename = buffer_filename() | |
local can_configure = ( | |
buf.buftype == "" | |
and buf.modifiable | |
and filename ~= "" | |
) | |
if can_configure == false then | |
return nil | |
end | |
local options = {} | |
local editorconfigs = ec_find(filename) | |
for i,path in pairs(editorconfigs) do | |
options = vim.tbl_extend( | |
"force", | |
options, | |
ec_read(path_basename(filename), path) | |
) | |
end | |
ec_apply(b, options) | |
end | |
-- Hook up editorconfig events | |
local function ec_autocommands() | |
vim.api.nvim_create_autocmd( | |
{ "BufEnter" }, | |
{ | |
callback = ec_init, | |
group = augroups.editorconfig, | |
} | |
) | |
end | |
-------------------------------- | |
-- public functions ------------ | |
-------------------------------- | |
-- Identify groups under cursor | |
-- | |
-- It can be really helpful to | |
-- inspect the highlight groups | |
-- applied to the code under the | |
-- cursor but nvim doesn't | |
-- surface this information by | |
-- default. This function | |
-- generates a string for use | |
-- in the status line that lists | |
-- the groups. | |
function HighlightGroup() | |
local pos = vim.api.nvim_win_get_cursor(0) | |
local line = pos[1] | |
local col = pos[2] + 1 | |
local synID = vim.fn.synID | |
local synIDattr = vim.fn.synIDattr | |
local synIDtrans = vim.fn.synIDtrans | |
local hi = synIDattr( | |
synID(line, col, true), | |
"name" | |
) | |
local trans = synIDattr( | |
synID(line, col, false), | |
"name" | |
) | |
local lo = synIDattr( | |
synIDtrans(synID(line, col, true)), | |
"name" | |
) | |
local groups = {} | |
local show_hi = hi and hi ~= "" | |
local show_trans = ( | |
trans | |
and trans ~= "" | |
and trans ~= hi | |
) | |
local show_lo = ( | |
lo | |
and lo ~= "" | |
and lo ~= trans | |
and lo ~= hi | |
) | |
if show_hi then | |
table.insert( | |
groups, | |
string.format("[%s]", hi) | |
) | |
end | |
if show_trans then | |
table.insert( | |
groups, | |
string.format("[%s]", trans) | |
) | |
end | |
if show_lo then | |
table.insert( | |
groups, | |
string.format("[%s]", lo) | |
) | |
end | |
return table.concat(groups, "") | |
end | |
-- Shorthand mode string | |
-- | |
-- Instead of long form stuff | |
-- like "NORMAL" or "INSERT" | |
-- this returns the real short | |
-- ones that nvim provides like | |
-- "n" and "i". | |
function Mode() | |
return vim.api.nvim_get_mode().mode | |
end | |
-- Custom statusline string | |
function StatusLine() | |
local parts = {} | |
-- Apply the correct highlight | |
-- group to the line. | |
table.insert( | |
parts, | |
"%#StatusLine#" | |
) | |
-- Nothing in the left hand | |
-- side. It's nice if that | |
-- whole side of the screen is | |
-- just for code. | |
table.insert( | |
parts, | |
"%=" | |
) | |
-- Nothing in the center. | |
-- Center-aligned stuff moves | |
-- around too much. | |
table.insert( | |
parts, | |
"%=" | |
) | |
-- Show the highlight group | |
-- under the cursor. This | |
-- changes size very | |
-- dramatically as the cursor | |
-- moves. So positioning it | |
-- furthest to the left stops | |
-- it from shifting the layout | |
-- of the rest of the line. | |
table.insert( | |
parts, | |
"%{%v:lua.HighlightGroup()%}" | |
) | |
-- File status flags. These | |
-- change a lot so they can't | |
-- go on the right, but they | |
-- don't change as much as the | |
-- highlight group so they can | |
-- go right of that. | |
table.insert( | |
parts, | |
"%y%h%w%m%r" | |
) | |
-- Cursor position. This | |
-- changes almost continually | |
-- so to avoid thrashing the | |
-- layout it has enough padding | |
-- for three digits on both | |
-- axes. | |
table.insert( | |
parts, | |
"[%-3l,%-03c]" | |
) | |
-- Add the mode last. This | |
-- changes least often of | |
-- everything so putting it | |
-- next to the right-hand edge | |
-- won't shift the layout too | |
-- much. | |
table.insert( | |
parts, | |
"[%{%v:lua.Mode()%}] " | |
) | |
local line = table.concat( | |
parts, | |
"" | |
) | |
return line | |
end | |
-- Custom TabLine string | |
function TabLine() | |
local bufn = vim.api.nvim_get_current_buf() | |
local tabnr = vim.fn.tabpagenr() | |
local tabmax = vim.fn.tabpagenr("$") | |
local bufnrs = tab_buffers(tabnr) | |
local parts = {} | |
-- Left-hand side | |
for _,b in pairs(bufnrs) do | |
local bpath = vim.api.nvim_buf_get_name(b) | |
local bname = path_basename(bpath) | |
local bgroup = "%#TabLineBuffer#" | |
if b == bufn then | |
bgroup = "%#TabLineBufferSel#" | |
end | |
table.insert(parts, bgroup) | |
table.insert(parts, " ") | |
table.insert(parts, bname) | |
table.insert(parts, " ") | |
end | |
table.insert(parts, "%#TabLineNC#") | |
table.insert(parts, " ") | |
table.insert(parts, "%=") | |
-- Right-hand side | |
if tabmax > 1 then | |
for t = 1,tabmax do | |
local tcwd = vim.fn.getcwd(-1, t) | |
local tname = path_basename(tcwd) | |
local tgroup = "%#TabLine#" | |
if t == tabnr then | |
tgroup = "%#TabLineSel#" | |
end | |
table.insert(parts, tgroup) | |
table.insert(parts, " ") | |
table.insert(parts, tname) | |
table.insert(parts, " ") | |
end | |
end | |
return table.concat(parts, "") | |
end | |
-------------------------------- | |
-- ui -------------------------- | |
-------------------------------- | |
-- Terminal colors on | |
-- | |
-- Without this none of the | |
-- colors work properly in the | |
-- terminal. | |
local function ui_enable_guicolors() | |
vim.o.termguicolors = true | |
end | |
-- Open new splits below | |
-- | |
-- Most split usage involves | |
-- terminals and those benefit | |
-- from having the full screen | |
-- width. | |
local function ui_enable_splitbelow() | |
vim.o.splitbelow = true | |
end | |
-- No signcolumn LSP diagnostics | |
-- | |
-- The inline ones are plenty | |
-- prominent on their own. | |
local function ui_hide_diagnostic_signs() | |
vim.lsp.handlers[ | |
"textDocument/publishDiagnostics" | |
] = vim.lsp.with( | |
vim.lsp.diagnostic.on_publish_diagnostics, | |
{ | |
signs = false, | |
} | |
) | |
end | |
-- Hide the startup tildes | |
local function ui_hide_tildes() | |
vim.opt.fillchars:append { | |
eob = " ", | |
} | |
end | |
-- Highlight the cursor line | |
-- | |
-- Even if the whole line | |
-- isn't being highlighted it's | |
-- useful to at least highlight | |
-- the current line number. | |
local function ui_show_cursorline() | |
vim.o.cursorline = true | |
end | |
-- Line numbers up to 9999 | |
-- | |
-- Setting a wide column for the | |
-- line numbers minimizes UI | |
-- movement when file lengths | |
-- change. | |
local function ui_show_numbers() | |
vim.o.number = true | |
vim.o.numberwidth = 4 | |
end | |
-- Max out the signcolumn | |
-- | |
-- Using both available columns | |
-- for this allows for a little | |
-- bit of whitespace between an | |
-- icon and the left of the | |
-- window. | |
local function ui_show_signcolumn() | |
vim.o.signcolumn = "yes:2" | |
end | |
local function ui_show_statusline() | |
vim.o.statusline = "%{%v:lua.StatusLine()%}" | |
end | |
-- Always show the tabline | |
-- | |
-- There's always exactly one | |
-- tab open on startup and then | |
-- 99% of use cases involve | |
-- opening more. Hiding the | |
-- tabline therefore causes a | |
-- layout shift almost every | |
-- time so just always show it | |
-- so the layout is stable. | |
local function ui_show_tabline() | |
vim.o.showtabline = 2 | |
vim.o.tabline = "%!v:lua.TabLine()" | |
end | |
-------------------------------- | |
-- neovide --------------------- | |
-------------------------------- | |
-- Use Iosevka | |
local function neovide_font() | |
vim.o.guifont = "Iosevka Term:h22" | |
end | |
-- Tweak animation timing | |
-- | |
-- The defaults are a bit slow | |
-- in order to make the | |
-- animations prominent and | |
-- noticeable. These tweaked | |
-- numbers reduce the | |
-- distraction a little. | |
local function neovide_cursor() | |
vim.g.neovide_cursor_animation_length = 0.07 | |
vim.g.neovide_cursor_trail_size = 0.7 | |
end | |
-- Fix copypaste | |
-- | |
-- See issue 113 on neovide's | |
-- GitHub repo for more context. | |
local function neovide_keymaps() | |
vim.api.nvim_set_keymap( | |
"", | |
"<D-v>", | |
"+p<CR>", | |
{ | |
noremap = true, | |
silent = true, | |
} | |
) | |
vim.api.nvim_set_keymap( | |
"!", | |
"<D-v>", | |
"<C-R>+", | |
{ | |
noremap = true, | |
silent = true, | |
} | |
) | |
vim.api.nvim_set_keymap( | |
"t", | |
"<D-v>", | |
"<C-R>+", | |
{ | |
noremap = true, | |
silent = true, | |
} | |
) | |
vim.api.nvim_set_keymap( | |
"v", | |
"<D-v>", | |
"<C-R>+", | |
{ | |
noremap = true, | |
silent = true, | |
} | |
) | |
end | |
local function init() | |
theme_light() | |
indent_defaults() | |
editor_keymaps() | |
buffer_keymaps() | |
tab_keymaps() | |
terminal_keymaps() | |
ui_enable_guicolors() | |
ui_enable_splitbelow() | |
ui_hide_diagnostic_signs() | |
ui_hide_tildes() | |
ui_show_cursorline() | |
ui_show_numbers() | |
ui_show_signcolumn() | |
ui_show_statusline() | |
ui_show_tabline() | |
ec_autocommands() | |
gist_autocommands() | |
lsp_autocommands() | |
terminal_autocommands() | |
if vim.g.neovide then | |
neovide_font() | |
neovide_cursor() | |
neovide_keymaps() | |
end | |
plugin_register("nvim-tree/nvim-tree.lua") | |
end | |
init() | |
-------------------------------- | |
-- experimental ---------------- | |
-------------------------------- | |
local function xy(x, y) | |
local p = { | |
x = x or 0, | |
y = y or 0, | |
} | |
setmetatable(p, { | |
__add = function(p1, p2) | |
return xy( | |
p1.x + p2.x, | |
p1.y + p2.y | |
) | |
end, | |
__sub = function(p1, p2) | |
return xy( | |
p1.x - p2.x, | |
p1.y - p2.y | |
) | |
end, | |
__mul = function(p1, v) | |
return xy( | |
p1.x * v, | |
p1.y * v | |
) | |
end, | |
__div = function(p1, v) | |
return xy( | |
p1.x / v, | |
p1.y / v | |
) | |
end, | |
__lt = function(p1, p2) | |
return ( | |
p1.x < p2.x and | |
p1.y < p2.y | |
) | |
end, | |
__gt = function(p1, p2) | |
return ( | |
p1.x > p2.x and | |
p1.y > p2.y | |
) | |
end, | |
}) | |
return p | |
end | |
local function center(p, c) | |
return (p - c) / 2 | |
end | |
local function editor_size() | |
return xy( | |
vim.o.columns, | |
vim.o.lines | |
) | |
end | |
local function window_size() | |
return xy( | |
vim.api.nvim_win_get_width(0), | |
vim.api.nvim_win_get_height(0) | |
) | |
end | |
winid = nil | |
function popup_show() | |
local size = xy(80, 20) | |
local pos = xy(0,0) | |
local bufn = vim.api.nvim_get_current_buf() | |
winid = vim.api.nvim_open_win( | |
bufn, | |
false, | |
{ | |
relative = "win", | |
width = size.x, | |
height = size.y, | |
row = pos.x, | |
col = pos.y, | |
} | |
) | |
end | |
function popup_hide() | |
if vim.api.nvim_win_is_valid(winid) then | |
vim.api.nvim_win_close(winid, true) | |
end | |
end | |
vim.keymap.set( | |
"n", | |
"<Space>w", | |
popup_hide | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment