Initial commit
This commit is contained in:
commit
f2c0a5f8c6
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-2022 TJ DeVries, Michele Guerini Rocco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
125
README.md
Normal file
125
README.md
Normal file
@ -0,0 +1,125 @@
|
||||
# Frameline
|
||||
|
||||
### Very minimal framework to write a status/tabline
|
||||
|
||||
Frameline is a Lua library for writing your own statusline (and also a tabline)
|
||||
for Neovim. This is not a ready-made statusline plugin, it's essential just the
|
||||
*frame*, you have to built the rest by yourself.
|
||||
|
||||
Frameline is around 200 lines of Lua with no external dependencies.
|
||||
|
||||
## Examples
|
||||
|
||||
1. A statusline with the usual components: mode, git branch, filename,
|
||||
encoding, etc.
|
||||
|
||||
```lua
|
||||
local frameline = require 'frameline'
|
||||
local utils = frameline.utils
|
||||
|
||||
-- Some components
|
||||
function mode(win, buf)
|
||||
if not win.is_active then return end
|
||||
|
||||
local k = vim.api.nvim_get_mode().mode
|
||||
local modes = {
|
||||
n={'Normal', 'Normal'},
|
||||
i={'Insert', 'Insert'},
|
||||
R={'Replas', 'Insert'},
|
||||
v={'Visual', 'Visual'},
|
||||
V={'V⋅Line', 'VLine'},
|
||||
t={'Termin', 'Term'},
|
||||
['']={'V⋅Bloc', 'VBloc'}
|
||||
}
|
||||
return utils.highlight('Mode'..modes[k][2], ' '..modes[k][1]..' ')
|
||||
end
|
||||
|
||||
function branch(_, buf)
|
||||
local head = vim.fn.FugitiveHead()
|
||||
if buf.modifiable and head ~= "" then return '⚑ '..head end
|
||||
end
|
||||
|
||||
function filename(_, buf)
|
||||
local delta = ""
|
||||
if buf.modified then delta = 'Δ' end
|
||||
if not buf.modifiable then delta = '∇' end
|
||||
local fname = buf.name ~= "" and utils.filename or 'new-file'
|
||||
return delta..fname
|
||||
end
|
||||
|
||||
function readonly(_, buf)
|
||||
if buf.modifiable and buf.readonly then return '∅' end
|
||||
end
|
||||
|
||||
function encoding(_, buf)
|
||||
return buf.fileencoding ~= "" and buf.fileencoding or nil
|
||||
end
|
||||
|
||||
function filetype(_, buf)
|
||||
return buf.filetype ~= "" and buf.filetype or 'no ft'
|
||||
end
|
||||
|
||||
-- Statusline
|
||||
frameline.setup_statusline(function()
|
||||
local segments = {}
|
||||
|
||||
-- Left section
|
||||
table.insert(segments, utils.subsection{items={mode}})
|
||||
table.insert(segments, utils.subsection{
|
||||
separator=' → ',
|
||||
items={branch, filename, readonly},
|
||||
})
|
||||
table.insert(segments, utils.split)
|
||||
|
||||
-- Right section
|
||||
table.insert(segments, utils.subsection{
|
||||
user=2,
|
||||
separator=':', stop=' ',
|
||||
items={utils.line_number, utils.column_number},
|
||||
})
|
||||
table.insert(segments, utils.subsection{
|
||||
user=1,
|
||||
separator=' ∘ ',
|
||||
items={utils.percent, encoding, filetype}
|
||||
})
|
||||
|
||||
return segments
|
||||
end)
|
||||
```
|
||||
|
||||
2. A tabline that shows the current tab, open tabs and date.
|
||||
|
||||
```lua
|
||||
-- Tabline
|
||||
frameline.setup_tabline(function()
|
||||
local segments = {}
|
||||
local api = vim.api
|
||||
local color = '%#StatusLine#'
|
||||
|
||||
-- Tabs
|
||||
local current = api.nvim_get_current_tabpage()
|
||||
local label = ' %d %s '
|
||||
for i, tab in pairs(api.nvim_list_tabpages()) do
|
||||
-- tab -> active win -> active buf -> name
|
||||
local active_buf = api.nvim_win_get_buf(api.nvim_tabpage_get_win(tab))
|
||||
local name = api.nvim_buf_get_name(active_buf)
|
||||
name = vim.fn.fnamemodify(name, ':t') -- filename only
|
||||
|
||||
local group = tab == current and 'TablineTabCur' or 'TablineTab'
|
||||
table.insert(segments, utils.highlight(group, label:format(i, name)))
|
||||
end
|
||||
|
||||
table.insert(segments, color)
|
||||
table.insert(segments, utils.split)
|
||||
|
||||
-- Current date
|
||||
table.insert(segments, vim.fn.strftime("%a %H:%M"))
|
||||
|
||||
return table.concat(segments)
|
||||
end)
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Frameline is licensed under the MIT license.
|
||||
See the accompanying file LICENSE or https://mit-license.org/.
|
17
default.nix
Normal file
17
default.nix
Normal file
@ -0,0 +1,17 @@
|
||||
{ lib, runCommand }:
|
||||
|
||||
runCommand "nvim-frameline"
|
||||
{ pname = "nvim-frameline";
|
||||
version = "0.1.0";
|
||||
|
||||
meta = with lib; {
|
||||
description = "Very minimal framework to write a status/tabline";
|
||||
homepage = "https://maxwell.ydns.eu/git/rnhmjoj/nvim-frameline";
|
||||
license = licenses.mit;
|
||||
maintainers = [ maintainers.rnhmjoj ];
|
||||
platforms = platforms.all;
|
||||
};
|
||||
}
|
||||
''
|
||||
install -m644 -D ${./frameline.lua} "$out/lua/frameline/init.lua"
|
||||
''
|
224
frameline.lua
Normal file
224
frameline.lua
Normal file
@ -0,0 +1,224 @@
|
||||
-- Very minimal framework to write a status/tabline
|
||||
-- Based on a (literally) stripped-down express_line.
|
||||
-- License: MIT
|
||||
-- Copyright © 2022 TJ DeVries, Michele Guerini Rocco
|
||||
|
||||
|
||||
local fl = {}
|
||||
|
||||
-- Misc utilities
|
||||
fl.utils = {
|
||||
-- Built-in segments
|
||||
line_number = '%l',
|
||||
column_number = '%c',
|
||||
percent = "%p%%",
|
||||
filename = '%t',
|
||||
split = '%=',
|
||||
|
||||
-- Highlights a string
|
||||
highlight = function(group, content)
|
||||
return ('%%#%s#'):format(group) .. content .. '%*'
|
||||
end,
|
||||
|
||||
-- Creates a subsection {highlight, items, separator, stop}
|
||||
subsection = function(cfg)
|
||||
local segments = {}
|
||||
|
||||
if cfg.user ~= nil then
|
||||
table.insert(segments, ('%%%s*'):format(cfg.user))
|
||||
end
|
||||
if cfg.highlight ~= nil then
|
||||
table.insert(segments, ('%%#%s#'):format(cfg.highlight))
|
||||
end
|
||||
|
||||
table.insert(segments, function(win, buf)
|
||||
local res, last_active = "", false
|
||||
|
||||
for i, item in pairs(cfg.items) do
|
||||
local part = type(item) == 'string' and item or item(win, buf)
|
||||
if part and last_active then
|
||||
res = res .. (cfg.separator or ' ')
|
||||
end
|
||||
if part then
|
||||
res = res .. part
|
||||
last_active = true
|
||||
end
|
||||
end
|
||||
return res
|
||||
end)
|
||||
table.insert(segments, cfg.stop or ' ')
|
||||
|
||||
if highlight ~= nil or user ~= nil then
|
||||
table.insert(segments, '%*')
|
||||
end
|
||||
|
||||
return segments
|
||||
end
|
||||
}
|
||||
|
||||
-- Neovim Buffer class
|
||||
local Buffer = {}
|
||||
local buf_props = {
|
||||
name = function(buf) return vim.api.nvim_buf_get_name(buf.bufnr) end,
|
||||
is_active = function(buf) return buf.bufnr == vim.api.nvim_get_current_buf() end,
|
||||
}
|
||||
function Buffer:new(bufnr)
|
||||
if bufnr == 0 then
|
||||
bufnr = vim.api.nvim_buf_get_number(0)
|
||||
end
|
||||
|
||||
return setmetatable({ bufnr = bufnr, }, { __index=function(t, k)
|
||||
if Buffer[k] ~= nil then
|
||||
t[k] = Buffer[k]
|
||||
elseif buf_props[k] ~= nil then
|
||||
t[k] = buf_props[k](t)
|
||||
else
|
||||
t[k] = vim.api.nvim_buf_get_option(t.bufnr, k)
|
||||
end
|
||||
return t[k]
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
-- Neovim Window class
|
||||
local Window = {}
|
||||
local win_props = {
|
||||
width = function(win) return vim.api.nvim_win_get_width(win.winid) end,
|
||||
height = function(win) return vim.api.nvim_win_get_height(win.winid) end,
|
||||
is_active = function(win) return win.winid == vim.api.nvim_get_current_win() end
|
||||
}
|
||||
function Window:new(winid)
|
||||
return setmetatable({winid=winid}, { __index=function(t, k)
|
||||
local result
|
||||
if Window[k] ~= nil then
|
||||
result = Window[k]
|
||||
elseif win_props[k] ~= nil then
|
||||
result = win_props[k](t)
|
||||
end
|
||||
return result
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
-- Evaluates the statusline segments
|
||||
local processor = function(items, window, buffer)
|
||||
local winid = window.winid
|
||||
|
||||
return function()
|
||||
if not vim.api.nvim_win_is_valid(winid) then return end
|
||||
|
||||
buffer = Buffer:new(buffer.bufnr)
|
||||
local waiting = {}
|
||||
local statusline = {}
|
||||
local effects = {}
|
||||
|
||||
for k, v in ipairs(items) do
|
||||
local ok, result, effect
|
||||
if type(v) == 'string' then
|
||||
ok, result = true, v
|
||||
elseif type(v) == 'function' then
|
||||
ok, result, effect = pcall(v, window, buffer)
|
||||
else
|
||||
ok = false
|
||||
end
|
||||
|
||||
if not ok then
|
||||
statusline[k] = ''
|
||||
else
|
||||
if type(result) == 'thread' then
|
||||
table.insert(waiting, {index=k, thread=result, effect=effect})
|
||||
else
|
||||
statusline[k], effects[k] = result, effect
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local remaining = table.getn(waiting)
|
||||
local completed = 0
|
||||
|
||||
local start = os.time()
|
||||
while start + 2 > os.time() do
|
||||
if remaining == completed then
|
||||
break
|
||||
end
|
||||
|
||||
for i = 1, remaining do
|
||||
local wait_val = waiting[i]
|
||||
|
||||
if wait_val ~= nil then
|
||||
local index, thread = wait_val.index, wait_val.thread
|
||||
local _, res = coroutine.resume(thread, window, buffer)
|
||||
|
||||
if coroutine.status(thread) == 'dead' then
|
||||
statusline[index] = res
|
||||
-- Remove
|
||||
completed = completed + 1
|
||||
waiting[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Filter nils and concat
|
||||
local final = {}
|
||||
for k, v in ipairs(statusline) do
|
||||
if effects[k] then v = effects[k](v) end
|
||||
if v then table.insert(final, v) end
|
||||
end
|
||||
|
||||
return table.concat(final, "")
|
||||
end
|
||||
end
|
||||
|
||||
-- Creates a table of all window/buffers
|
||||
local get_new_windows_table = function()
|
||||
return setmetatable({}, {__index = function(self, winid)
|
||||
local val = setmetatable({}, {
|
||||
__index = function(win_table, bufnr)
|
||||
|
||||
if not fl.generator then return function() return '' end end
|
||||
|
||||
local window = Window:new(winid)
|
||||
local buffer = Buffer:new(bufnr)
|
||||
local items = vim.tbl_flatten(fl.generator(window, buffer))
|
||||
local p = processor(items, window, buffer)
|
||||
|
||||
rawset(win_table, bufnr, p)
|
||||
return p
|
||||
end,
|
||||
})
|
||||
|
||||
rawset(self, winid, val)
|
||||
return val
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
-- Draws the status/tabline
|
||||
function draw_statusline(winid)
|
||||
local bufnr = vim.api.nvim_win_get_buf(winid)
|
||||
return fl._window_statuslines[winid][bufnr]()
|
||||
end
|
||||
function draw_tabline()
|
||||
return fl._tabline()
|
||||
end
|
||||
|
||||
-- Set up a statusline per window
|
||||
fl.setup_statusline = function(generator)
|
||||
fl._window_statuslines = get_new_windows_table()
|
||||
fl.generator = generator
|
||||
vim.api.nvim_create_autocmd(
|
||||
{'BufWinEnter','WinEnter'},
|
||||
{pattern='*', callback=function()
|
||||
vim.wo.statusline = "%!luaeval('draw_statusline("..vim.api.nvim_get_current_win()..")')"
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
-- Set up a global tabline
|
||||
fl.setup_tabline = function(generator)
|
||||
fl._tabline = generator
|
||||
vim.cmd('set tabline=%!v:lua.draw_tabline()')
|
||||
end
|
||||
|
||||
return fl
|
Loading…
Reference in New Issue
Block a user