Initial commit

master v0.1.0
Michele Guerini Rocco 2022-09-06 17:09:28 +02:00
commit f2c0a5f8c6
Signed by: rnhmjoj
GPG Key ID: BFBAF4C975F76450
4 changed files with 387 additions and 0 deletions

21
LICENSE Normal file
View 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
View 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
View 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
View 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