diff --git a/README.md b/README.md index 96fe96a..b442820 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,13 @@ require('wlsample.wind') ### [luffy](./lua/wlsample/airline_luffy.lua) ![luffy animation](https://github.com/windwp/windline.nvim/wiki/screenshot/airline_luffy.gif) +### [cava] +![cava animation](https://github.com/windwp/windline.nvim/wiki/screenshot/cava.gif) +```lua +-- only support linux it need install cava +lua require("windline.components.cava").toggle() +``` + ```lua require('wlsample.airline_luffy') ``` @@ -390,9 +397,3 @@ A command to benchmark current status line by rendering it 10.000 time. ## Document [wiki](https://github.com/windwp/windline.nvim/wiki/) - -## Floating window statusline (Deprecated) -__If you are using nvim > 0.7 you should use set laststatus=3 to enable global -statusline__ - - [MoreInfo](https://github.com/windwp/windline.nvim/wiki/Floating-statusline) diff --git a/lua/windline/components/cava.lua b/lua/windline/components/cava.lua new file mode 100644 index 0000000..4477566 --- /dev/null +++ b/lua/windline/components/cava.lua @@ -0,0 +1,112 @@ +---@diagnostic disable: need-check-nil +local windline = require('windline') +local cava_text = "OK" +local uv = vim.loop +if _G._cava_stop then + _G._cava_stop() +end + +vim.api.nvim_create_autocmd("VimLeave", { + pattern = "*", + callback = function() + vim.fn.system({ "pkill", '-9', "cava" }) + end +}) +local create_cava_colors = function(colors) + local HSL = require('wlanimation.utils') + local d_colors = { + "green", "blue", "yellow", "magenta", "red", "cyan" + } + local cava_colors = HSL.rgb_to_hsl(colors[d_colors[math.random(#d_colors)]]):tints(10, 8) + for i = 1, 8, 1 do + colors["cava" .. i] = cava_colors[i]:to_rgb() + end + return colors +end + +local bars = { "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" } +local cava_comp = { + name = "cava", + hl_colors = { + cava1 = { "cava1", "NormalBg" }, + cava2 = { "cava2", "NormalBg" }, + cava3 = { "cava3", "NormalBg" }, + cava4 = { "cava4", "NormalBg" }, + cava5 = { "cava5", "NormalBg" }, + cava6 = { "cava6", "NormalBg" }, + cava7 = { "cava7", "NormalBg" }, + cava8 = { "cava8", "NormalBg" }, + }, + text = function() + local result = {} + for i = 1, 60, 2 do + local c = tonumber(cava_text:sub(i, i)) + if c then + c = c + 1 + result[#result + 1] = { bars[c], "cava" .. c } + end + end + -- result[#result+1] = {"%="} + return result + end, + click = function() + vim.notify("change cava colors") + windline.change_colors(create_cava_colors(windline.get_colors())) + end +} + +local function run_cava() + local sourced_file = require('plenary.debug_utils').sourced_filepath() + local plugin_directory = vim.fn.fnamemodify(sourced_file, ':h:h:h:h') + + vim.fn.system({ "pkill", '-9', "cava" }) + local cava_path = vim.fn.expand(plugin_directory .. "/scripts/cava.sh") + local stdin = uv.new_pipe(false) + local stdout = uv.new_pipe(false) + local stderr = uv.new_pipe(false) + local handle = uv.spawn(cava_path, + { stdio = { stdin, stdout, stderr }, }, + function() _G._cava_stop() end + ) + + uv.read_start(stdout, vim.schedule_wrap(function(_, data) + if data then + cava_text = data + vim.cmd.redrawstatus() + end + end)) + _G._cava_stop = function() + stdin:read_stop() + stdin:close() + stdout:read_stop() + stdout:close() + stderr:read_stop() + stderr:close() + handle:close() + _G._cava_stop = nil + end +end + +local M = {} +M.is_stop = true + +M.toggle = function() + if M.is_stop then + run_cava() + windline.add_component(cava_comp, { + name = "cava", + position = "right", + auto_remove = true, + colors_name = create_cava_colors + }) + else + vim.fn.system({ "pkill", '-9', "cava" }) + if _G._cava_stop then + _G._cava_stop() + end + windline.remove_component(cava_comp) + end + M.is_stop = not M.is_stop +end +-- M.toggle() +return M diff --git a/lua/wlfloatline/init.lua b/lua/wlfloatline/init.lua deleted file mode 100644 index be0dc2b..0000000 --- a/lua/wlfloatline/init.lua +++ /dev/null @@ -1,609 +0,0 @@ ------------------------------------------------------------------------------ --- Copyright (c) 2020-2021 windwp --- License: MIT --- make a floating window statusline ------------------------------------------------------------------------------ -local M = {} -local utils = require('windline.utils') -local mode = utils.mode -local Comp = require('windline.component') -local windline = require('windline') -local Animation = require('wlanimation.animation') -local cache_utils = require('windline.cache_utils') -local api = vim.api -local namespace = api.nvim_create_namespace('WindLine.floatline_status') -local floor = math.floor -local max = math.max - -_G.WindLine = _G.WindLine or M -local state = WindLine.state - -local default_config = { - interval = 300, - is_nocmdheight = false, - ui = { - active_char = '▁', - active_color = 'blue', - active_hl = nil, - }, - close_on_cmdline = false, - skip_filetypes = { - 'fern', - 'NvimTree', - 'lir', - }, -} - -local close_float_win = function() - if - state.floatline - and state.floatline.winid - and api.nvim_win_is_valid(state.floatline.winid) - then - api.nvim_buf_clear_namespace(state.floatline.bufnr, namespace, 1, 2) - api.nvim_win_close(state.floatline.winid, true) - state.floatline.winid = nil - state.floatline.bufnr = nil - end -end - -local create_floating_win = function() - local cur_winid = api.nvim_get_current_win() - close_float_win() - local status_bufnr = api.nvim_create_buf(false, true) - local content_opts = { - relative = 'editor', - width = vim.o.columns, - height = 1, - col = 0, - row = vim.o.lines - vim.o.cmdheight - 1, - focusable = false, - style = 'minimal', - } - local status_winid = api.nvim_open_win(status_bufnr, true, content_opts) - api.nvim_buf_set_option(status_bufnr, 'ft', 'windline') - api.nvim_buf_set_option(status_bufnr, 'buftype', 'nofile') - api.nvim_win_set_option(status_winid, 'wrap', false) - api.nvim_win_set_option(status_winid, 'number', false) - api.nvim_win_set_option(status_winid, 'relativenumber', false) - api.nvim_win_set_option(status_winid, 'cursorline', false) - api.nvim_win_set_option(status_winid, 'winblend', 0) - api.nvim_win_set_option(status_winid, 'signcolumn', 'no') - api.nvim_win_set_option( - status_winid, - 'winhighlight', - 'NormalNC:Normal,Search:None' - ) - state.floatline.winid = status_winid - state.floatline.bufnr = status_bufnr - api.nvim_win_set_cursor(status_winid, { 1, 1 }) - api.nvim_set_current_win(cur_winid) -end - -local function render_comp(comp, bufnr, winid, width, th_id) - local hl_data = comp.hl_data or {} - local childs = comp.text(bufnr, winid, width, true) - if th_id ~= state.thread_id then - -- when text running too long and another loop change thread_id - return false - end - local result = {} - if type(childs) == 'table' then - for _, child in pairs(childs) do - local text, hl = child[1], child[2] - if type(text) == 'function' then - text = child[1](bufnr, winid, width, true) - end - if type(hl) == 'string' then - hl = hl_data[hl] or hl - end - if text and text ~= '' then - table.insert(result, { - text = text:gsub('%%%%', '%%'), - hl = comp:make_hl(hl, hl_data.default), - item = comp.name, - }) - end - end - return result - end - if childs and childs ~= '' then - table.insert(result, { - text = childs:gsub('%%%%', '%%'), - hl = comp:make_hl(comp.hl, hl_data.default), - name = comp.name, - }) - end - - return result -end - -local function render_float_status(bufnr, winid, items, th_id) - state.comp = {} - state.mode = mode() - Comp.reset() - local total_width = vim.o.columns - local status = '' - local cur_position = 0 - state.text_groups = {} - for _, comp in pairs(items) do - if comp.width == nil or comp.width < total_width then - local hl = render_comp(comp, bufnr, winid, total_width, th_id) - if hl then - for _, item in pairs(hl) do - table.insert(state.text_groups, item) - end - else - state.text_groups = {} - return false - end - end - end - local full_status_width = 0 - -- calculate first to get statusline width - for _, group in ipairs(state.text_groups) do - full_status_width = full_status_width - + vim.api.nvim_strwidth(group.text or '') - end - local last_group = nil - for _, group in ipairs(state.text_groups) do - local next_position = cur_position - -- replace by space - if group.text == '%=' then - local space_width = max(total_width - full_status_width + 2, 2) - status = status .. string.rep(' ', space_width) - next_position = cur_position + space_width - else - status = status .. group.text - next_position = cur_position + #group.text - end - - -- convert highlight to extmark point - if - last_group - and last_group.range - and (group.hl == '' or group.hl == last_group.hl) - then - last_group.range = { last_group.range[1], next_position } - elseif group.hl ~= '' then - group.range = { cur_position, next_position } - last_group = group - else - last_group = group - end - cur_position = next_position - end - - state.floatline.status = status - return true -end - -M.update_status = function(th_id) - if state.floatline.is_hide and not state.is_focus then - return - end - if - not state.floatline.bufnr or not api.nvim_win_is_valid(state.floatline.winid) - then - create_floating_win() - return - end - if vim.api.nvim_get_mode().mode == 'no' then - --that mode is textlock can't change buffer - return - end - local bufnr = api.nvim_get_current_buf() - local winid = api.nvim_get_current_win() - windline.check_autocmd_component(bufnr) - local ft = api.nvim_buf_get_option(bufnr, 'filetype') - local check_line = windline.get_statusline_ft(ft) or {} - if - utils.is_in_table(state.config.skip_filetypes, ft) - or ( - api.nvim_win_get_config(winid).relative ~= '' - and not check_line.floatline_show_float - ) - then - bufnr = state.last_bufnr or bufnr - winid = state.last_winid or winid - end - if not api.nvim_win_is_valid(winid) or not api.nvim_buf_is_valid(bufnr) then - return - end - - local line = windline.get_statusline(bufnr) or WindLine.default_line - state.text_groups = {} - if - state.thread_id == th_id - and render_float_status(bufnr, winid, line.active, th_id) - then - state.last_bufnr = bufnr - state.last_winid = winid - vim.api.nvim_buf_set_lines( - state.floatline.bufnr, - 0, - 1, - false, - { state.floatline.status } - ) - end -end - -M.floatline_show = function(bufnr, winid) - if not state.floatline then - return '' - end - bufnr = bufnr or api.nvim_get_current_buf() - local cur_win = api.nvim_get_current_win() - local line = windline.get_statusline(bufnr) or WindLine.default_line - if line.floatline_show_both then - return windline.show(bufnr, winid) - end - if vim.g.statusline_winid == cur_win then - return windline.render_status(bufnr, winid, state.floatline.active) - else - return windline.render_status(bufnr, winid, state.floatline.inactive) - end -end - -M.floatline_on_win_enter = function(bufnr, winid) - if not state.is_focus then - M.on_focus(true) - end - bufnr = bufnr or vim.api.nvim_get_current_buf() - winid = winid or vim.api.nvim_get_current_win() - if not vim.api.nvim_win_is_valid(winid) then - return false - end - vim.api.nvim_win_set_option( - winid, - 'statusline', - string.format( - '%%!v:lua.WindLine.floatline_show(%s,%s)', - bufnr or vim.api.nvim_win_get_buf(winid), - winid - ) - ) -end - -local function check_tab_have_floatline_window() - local tabnr = api.nvim_get_current_tabpage() - local windows = vim.api.nvim_tabpage_list_wins(tabnr) - local count = 0 - for _, winid in pairs(windows) do - if api.nvim_win_get_config(winid).relative == '' then - count = count + 1 - end - end - return count == 1 -end - -M.floatline_fix_command = function(cmd) - cmd = cmd or 'quit' - if api.nvim_win_get_config(0).relative ~= '' then - pcall(api.nvim_command, cmd) - return - end - if check_tab_have_floatline_window() and vim.fn.tabpagenr('$') > 1 then - close_float_win() - end - pcall(api.nvim_command, cmd) -end - -M.floatline_on_tabenter = function() - create_floating_win() - M.update_status() -end - -local function get_layout_height(tree_layout, height) - if tree_layout[1] == 'row' then - --if it is row we only get the first window - return get_layout_height(tree_layout[2][1], height) - elseif tree_layout[1] == 'col' then - --need to sum all window layout - for _, value in pairs(tree_layout[2]) do - -- +1 because the size for statusline - height = get_layout_height(value, height + 1) - end - return height - 1 - elseif tree_layout[1] == 'leaf' then - -- get window height - if api.nvim_win_is_valid(tree_layout[2]) then - return api.nvim_win_get_height(tree_layout[2]) + height - end - return height - end -end - -local function check_tree_node(node, winid) - if node[1] == 'col' then - -- only check last node - return check_tree_node(node[2][#node[2]], winid) - elseif node[1] == 'row' then - for _, v in ipairs(node[2]) do - if check_tree_node(v, winid) then - return true - end - end - return false - elseif node[1] == 'leaf' then - return node[2] == winid - end -end - -local check_is_bottom_win = function(winid) - winid = winid or api.nvim_get_current_win() - local layout = vim.fn.winlayout(api.nvim_get_current_tabpage()) - return check_tree_node(layout, winid) -end - -M.floatline_on_resize = function(sub_height) - if api.nvim_win_is_valid(state.floatline.winid) then - sub_height = sub_height or 0 - local layout = vim.fn.winlayout(api.nvim_get_current_tabpage()) - local tabline = vim.o.showtabline > 0 and 1 or 0 - if vim.o.showtabline == 1 then - tabline = #vim.api.nvim_list_tabpages() > 1 and 1 or 0 - end - local height = get_layout_height(layout, tabline) - or vim.o.lines - vim.o.cmdheight - 1 - api.nvim_win_set_config(state.floatline.winid, { - relative = 'editor', - width = vim.o.columns, - height = 1, - col = 0, - row = height - sub_height, - style = 'minimal', - }) - api.nvim_win_set_option(state.floatline.winid, 'winblend', 0) - else - create_floating_win() - end -end - -M.floatline_hide = function(close) - vim.g.statusline_winid = vim.api.nvim_get_current_win() - vim.wo.statusline = windline.show( - vim.api.nvim_get_current_buf(), - vim.api.nvim_get_current_win() - ) - if vim.o.cmdheight ~= 0 then - vim.cmd('redrawstatus') - end - - if close then - vim.api.nvim_win_close(state.floatline.winid, true) - else - api.nvim_win_set_config(state.floatline.winid, { - relative = 'editor', - width = 1, - height = 1, - col = 0, - row = 0, - style = 'minimal', - }) - api.nvim_win_set_option(state.floatline.winid, 'winblend', 100) - end -end - -M.floatline_on_cmd_leave = function() - if state.config.is_nocmdheight then - M.floatline_on_resize(-1) - return - end - if state.floatline.is_hide then - state.floatline.is_hide = false - if vim.v.event.cmdtype:match('[%:%-]') or vim.o.cmdheight == 0 then - M.floatline_on_win_enter() - M.floatline_on_resize() - end - end -end - -M.floatline_on_cmd_enter = function() - if state.config.is_nocmdheight then - state.floatline.is_hide = true - M.floatline_on_resize(0) - vim.cmd("redraw") - return - end - if vim.v.event.cmdtype:match('[%:%-]') then - state.floatline.is_hide = true - vim.defer_fn(function() - if state.floatline and state.floatline.is_hide then - M.floatline_hide(state.config.close_on_cmdline) - end - end, 500) - end -end - -M.on_focus = function(is_focus) - if not state.floatline.runner and state.is_focus ~= is_focus then - return - end - state.is_focus = is_focus - if is_focus then - state.floatline.runner:run() - else - state.floatline.runner:stop(true) - end -end - -M.setup = function(opts) - opts = opts or {} - opts = vim.tbl_deep_extend('force', default_config, opts) - opts.statuslines = nil - -- overide default WindLine event - WindLine.floatline_disable = M.disable - WindLine.floatline_show = M.floatline_show - WindLine.floatline_on_resize = M.floatline_on_resize - WindLine.floatline_fix_command = M.floatline_fix_command - if vim.o.cmdheight == 0 then - state.config.is_nocmdheight = true - end - - vim.cmd([[set statusline=%!v:lua.WindLine.floatline_show()]]) - - state.thread_id = 0 - api.nvim_exec( - [[augroup WindLine - au! - au BufWinEnter,WinEnter * lua require('wlfloatline').floatline_on_win_enter() - au TabEnter * lua require('wlfloatline').floatline_on_tabenter() - au CmdlineEnter * lua require('wlfloatline').floatline_on_cmd_enter() - au CmdlineLeave * lua require('wlfloatline').floatline_on_cmd_leave() - au FocusGained * lua require('wlfloatline').on_focus(true) - au FocusLost * lua require('wlfloatline').on_focus(false) - au VimResized * lua require('wlfloatline').floatline_on_resize() - au VimEnter * lua WindLine.on_vimenter() - au ColorScheme * lua WindLine.on_colorscheme() - augroup END]], - false - ) - - -- remove this when this issue is fixed - -- https://github.com/neovim/neovim/issues/11440 - api.nvim_exec( - 'command! -nargs=* Wquit call v:lua.WindLine.floatline_fix_command("quit")', - false - ) - api.nvim_exec( - 'command! -nargs=* Wbdelete call v:lua.WindLine.floatline_fix_command("bdelete")', - false - ) - vim.g.wl_quit_command = 'Wquit' - vim.g.wl_delete_command = 'Wbdelete' - -- - -- extend windline config with floatline config - state.config = vim.tbl_extend('force', opts, state.config) - state.floatline = state.floatline or {} - local floatline = windline.get_statusline_ft('floatline') - local check_bottom = cache_utils.cache_on_buffer( - 'BufEnter,WinEnter', - 'windline_dash', - check_is_bottom_win - ) - - local default_floatline = { - filetypes = { 'floatline' }, - active = { - { - hl_colors = { - line = { state.config.ui.active_color, 'NormalBg' }, - }, - text = function(_, winid, width) - if check_bottom(winid) then - return { { ' ', 'Normal' } } - end - return { - { - string.rep( - state.config.ui.active_char, - floor(width - 1), - '' - ), - 'line', - }, - { - state.config.ui.active_char, - 'line', - }, - } - end, - }, - }, - } - if floatline then - if not floatline.active then - floatline.active = default_floatline.active - end - windline.setup_hightlight() - else - windline.add_status(default_floatline) - end - - state.text_groups = {} - state.floatline.active = windline.get_statusline_ft('floatline').active - state.floatline.inactive = windline.get_statusline_ft('floatline').inactive - or windline.default_line.inactive - - create_floating_win() - M.start_runner() - if not WindLine.floatline_set_decoration then - -- only set it one time - vim.api.nvim_set_decoration_provider(namespace, { - on_start = function() - return state.floatline ~= nil - end, - on_win = function(_, winid) - return state.floatline and winid == state.floatline.winid - end, - on_line = function(_, winid, bufnr, row) - if row == 0 and winid == state.floatline.winid then - for _, group in pairs(state.text_groups) do - if group.range and group.hl ~= '' then - vim.api.nvim_buf_set_extmark( - bufnr, - namespace, - 0, - group.range[1], - { - end_line = 0, - end_col = group.range[2], - hl_group = group.hl, - hl_mode = 'combine', - ephemeral = true, - } - ) - end - end - end - end, - }) - WindLine.floatline_set_decoration = true - end -end - -M.start_runner = function() - M.stop_runner() - -- a wrapper of vim.loop - local runner = Animation.new({ - timeout = nil, - delay = 200, - type = 'blank', - interval = state.config.interval, - tick = function() - state.thread_id = state.thread_id < 1000 and state.thread_id + 1 or 1 - M.update_status(state.thread_id) - end, - manage = false, - }) - state.is_focus = true - runner:run() - state.floatline.runner = runner -end - -M.stop_runner = function() - if state.floatline and state.floatline.runner then - state.floatline.runner:stop() - state.floatline.runner = nil - end -end - -M.disable = function() - M.stop_runner() - close_float_win() - state.floatline = nil -end - --- toggle floatline -M.toggle = function() - if state.floatline then - M.disable() - windline.setup_event() - else - M.setup() - end -end - -return M diff --git a/lua/wlsample/airline_luffy.lua b/lua/wlsample/airline_luffy.lua index 56d6020..d0afef4 100644 --- a/lua/wlsample/airline_luffy.lua +++ b/lua/wlsample/airline_luffy.lua @@ -141,10 +141,11 @@ animation.stop_all() animation.basic_animation({ timeout = nil, delay = 200, - interval = 150, + interval = 200, effect = efffects.list_text(luffy), on_tick = function(value) luffy_text = value + vim.cmd.redrawstatus() end }) diff --git a/scripts/cava.sh b/scripts/cava.sh new file mode 100755 index 0000000..8242f81 --- /dev/null +++ b/scripts/cava.sh @@ -0,0 +1,28 @@ +#! /bin/bash + +pipe="/tmp/cava.fifo" +if [ -p $pipe ]; then + unlink $pipe +fi +mkfifo $pipe + +# write cava config +config_file="/tmp/polybar_cava_config" +echo " +[general] +bars = 30 +[output] +method = raw +raw_target = $pipe +data_format = ascii +ascii_max_range = 7 +" > $config_file + +# run cava in the background +cava -p $config_file & + +# reading data from fifo +while read -r cmd; do + echo $cmd +done < $pipe +pkill -9 cava