diff --git a/.config/yazi/init.lua b/.config/yazi/init.lua index 81efd17..99e606a 100644 --- a/.config/yazi/init.lua +++ b/.config/yazi/init.lua @@ -45,76 +45,15 @@ Header:children_add(function() end return ui.Span(ya.user_name() .. "@" .. ya.host_name() .. ":"):fg("blue") end, 500, Header.LEFT) ---git -require("git"):setup() --- yatline dracula_theme -local dracula_theme = require("yatline-dracula"):setup() --- yatline -require("yatline"):setup({ - theme = dracula_theme, - section_separator = { open = "", close = "" }, - part_separator = { open = "", close = "" }, - inverse_separator = { open = "", close = "" }, - show_background = false, - tab_width = 0, - - header_line = { - left = { - section_a = { - { type = "coloreds", custom = false, name = "tab_path" }, - {type = "line", custom = false, name = "tabs", params = {"left"}}, - }, - section_b = { - {type = "coloreds", custom = false, name = "githead"}, - }, - section_c = { - } - }, - right = { - section_a = { - - - }, - section_b = { - - }, - section_c = { - {type = "coloreds", custom = false, name = "count"}, - } - } - }, - - status_line = { - left = { - section_a = { - {type = "string", custom = false, name = "tab_mode"}, - }, - section_b = { - {type = "string", custom = false, name = "hovered_size"}, - }, - section_c = { - - {type = "string", custom = false, name = "hovered_path"}, - - } - }, - right = { - section_a = { - {type = "string", custom = false, name = "cursor_position"}, - }, - section_b = { - {type = "string", custom = false, name = "cursor_percentage"}, - }, - section_c = { - - - {type = "coloreds", custom = false, name = "permissions"}, - } - } - }, +-- startship prompt +require("starship"):setup({ + -- Hide flags (such as filter, find and search). This is recommended for starship themes which + -- are intended to go across the entire width of the terminal. + hide_flags = false, -- Default: false + -- Whether to place flags after the starship prompt. False means the flags will be placed before the prompt. + flags_after_prompt = true, -- Default: true + -- Custom starship configuration file to use + config_file = "~/.config/starship_full.toml", -- Default: nil }) --- yatline-tab-path -require("yatline-tab-path"):setup() --- yatline-githead -require("yatline-githead"):setup() - +-- git for yazi +require("git"):setup() diff --git a/.config/yazi/plugins/starship.yazi/LICENSE b/.config/yazi/plugins/starship.yazi/LICENSE new file mode 100644 index 0000000..c03ce66 --- /dev/null +++ b/.config/yazi/plugins/starship.yazi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Rolv Apneseth + +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. diff --git a/.config/yazi/plugins/starship.yazi/README.md b/.config/yazi/plugins/starship.yazi/README.md new file mode 100644 index 0000000..b2b4dd8 --- /dev/null +++ b/.config/yazi/plugins/starship.yazi/README.md @@ -0,0 +1,108 @@ +# starship.yazi + +Starship prompt plugin for [Yazi](https://github.com/sxyazi/yazi) + + + +## Requirements + +- [Yazi](https://github.com/sxyazi/yazi) (v25.4.8+) +- [starship](https://github.com/starship/starship) + +## Installation + +```bash +ya pack -a Rolv-Apneseth/starship +``` + +### Manual + +```sh +# Linux / MacOS +git clone https://github.com/Rolv-Apneseth/starship.yazi.git ~/.config/yazi/plugins/starship.yazi +# Windows +git clone https://github.com/Rolv-Apneseth/starship.yazi.git %AppData%\yazi\config\plugins\starship.yazi +``` + +## Usage + +Add this to `~/.config/yazi/init.lua`: + +```lua +require("starship"):setup() +``` + +Make sure you have [starship](https://github.com/starship/starship) installed and in your `PATH`. + +## Config + +Here is an example with all available config options: + +```lua +require("starship"):setup({ + -- Hide flags (such as filter, find and search). This is recommended for starship themes which + -- are intended to go across the entire width of the terminal. + hide_flags = false, -- Default: false + -- Whether to place flags after the starship prompt. False means the flags will be placed before the prompt. + flags_after_prompt = true, -- Default: true + -- Custom starship configuration file to use + config_file = "~/.config/starship_full.toml", -- Default: nil +}) +``` + +## Extra + +If you use a `starship` theme with a background colour, it might look a bit to cramped on just the one line `Yazi` gives the header by default. To fix this, you can add this to your `init.lua`: + +
+Click to expand + +```lua +local old_build = Tab.build + +Tab.build = function(self, ...) + local bar = function(c, x, y) + if x <= 0 or x == self._area.w - 1 then + return ui.Bar(ui.Bar.TOP):area(ui.Rect.default) + end + + return ui.Bar(ui.Bar.TOP) + :area(ui.Rect({ + x = x, + y = math.max(0, y), + w = ya.clamp(0, self._area.w - x, 1), + h = math.min(1, self._area.h), + })) + :symbol(c) + end + + local c = self._chunks + self._chunks = { + c[1]:pad(ui.Pad.y(1)), + c[2]:pad(ui.Pad(1, c[3].w > 0 and 0 or 1, 1, c[1].w > 0 and 0 or 1)), + c[3]:pad(ui.Pad.y(1)), + } + + local style = th.mgr.border_style + self._base = ya.list_merge(self._base or {}, { + ui.Bar(ui.Bar.RIGHT):area(self._chunks[1]):style(style), + ui.Bar(ui.Bar.LEFT):area(self._chunks[1]):style(style), + + bar("┬", c[1].right - 1, c[1].y), + bar("┴", c[1].right - 1, c[1].bottom - 1), + bar("┬", c[2].right, c[2].y), + bar("┴", c[2].right, c[2].bottom - 1), + }) + + old_build(self, ...) +end +``` + +
+ +> [!NOTE] +> This works by overriding your `Tab.build` function so make sure this is the only place you're doing that in your config. For example, this would be incompatible with the [full-border plugin](https://github.com/yazi-rs/plugins/tree/main/full-border.yazi) + +## Thanks + +- [sxyazi](https://github.com/sxyazi) for providing the code for this plugin and the demo video [in this comment](https://github.com/sxyazi/yazi/issues/767#issuecomment-1977082834) diff --git a/.config/yazi/plugins/starship.yazi/main.lua b/.config/yazi/plugins/starship.yazi/main.lua new file mode 100644 index 0000000..c4d3943 --- /dev/null +++ b/.config/yazi/plugins/starship.yazi/main.lua @@ -0,0 +1,123 @@ +--- @since 25.4.8 + +-- For development +--[[ local function notify(message) ]] +--[[ ya.notify({ title = "Starship", content = message, timeout = 3 }) ]] +--[[ end ]] + +local save = ya.sync(function(st, _cwd, output) + st.output = output + ya.render() +end) + +-- Helper function for accessing the `config_file` state variable +---@return string +local get_config_file = ya.sync(function(st) + return st.config_file +end) + +return { + ---User arguments for setup method + ---@class SetupArgs + ---@field config_file string Absolute path to a starship config file + ---@field hide_flags boolean Whether to hide all flags (such as filter and search). Recommended for themes which are intended to take the full width of the terminal. + ---@field flags_after_prompt boolean Whether to place flags (such as filter and search) after the starship prompt. By default this is true. + + --- Setup plugin + --- @param st table State + --- @param args SetupArgs|nil + setup = function(st, args) + local hide_flags = false + local flags_after_prompt = true + + -- Check setup args + if args ~= nil then + if args.config_file ~= nil then + local url = Url(args.config_file) + if url.is_regular then + local config_file = args.config_file + + -- Manually replace '~' and '$HOME' at the start of the path with the OS environment variable + local home = os.getenv("HOME") + if home then + home = tostring(home) + config_file = config_file:gsub("^~", home):gsub("^$HOME", home) + end + + st.config_file = config_file + end + end + + if args.hide_flags ~= nil then + hide_flags = args.hide_flags + end + + if args.flags_after_prompt ~= nil then + flags_after_prompt = args.flags_after_prompt + end + end + + -- Replace default header widget + Header:children_remove(1, Header.LEFT) + Header:children_add(function(self) + local max = self._area.w - self._right_width + if max <= 0 then + return "" + end + + if hide_flags or not st.output then + return ui.Line.parse(st.output or "") + end + + -- Split `st.output` at the first line break (or keep as is if none was found) + local output = st.output:match("([^\n]*)\n?") or st.output + + local flags = self:flags() + if flags_after_prompt then + output = output .. " " .. flags + else + output = flags .. " " .. output + end + + return ui.Line.parse(output) + end, 1000, Header.LEFT) + + -- Pass current working directory and custom config path (if specified) to the plugin's entry point + ---Callback for subscribers to update the prompt + local callback = function() + local cwd = cx.active.current.cwd + if st.cwd ~= cwd then + st.cwd = cwd + + ya.manager_emit("plugin", { + st._id, + ya.quote(tostring(cwd), true), + }) + end + end + + -- Subscribe to events + ps.sub("cd", callback) + ps.sub("tab", callback) + end, + + entry = function(_, job) + local args = job.args + local command = Command("starship") + :arg("prompt") + :stdin(Command.INHERIT) + :cwd(args[1]) + :env("STARSHIP_SHELL", "") + + -- Point to custom starship config + local config_file = get_config_file() + if config_file then + command = command:env("STARSHIP_CONFIG", config_file) + end + + local output = command:output() + if output then + save(args[1], output.stdout:gsub("^%s+", "")) + end + end, +} diff --git a/.config/yazi/plugins/yatline-dracula.yazi/main.lua b/.config/yazi/plugins/yatline-dracula.yazi/main.lua deleted file mode 100644 index 3bc9c55..0000000 --- a/.config/yazi/plugins/yatline-dracula.yazi/main.lua +++ /dev/null @@ -1,62 +0,0 @@ ---==================-- --- Dracula Theme -- ---==================-- - -local dracula_palette = { - bg = "#282a36", -- Dracula background - bg_highlight = "#44475a", -- Dracula current line/selection - fg = "#f8f8f2", -- Dracula foreground - blue = "#6272a4", -- Dracula comment - cyan = "#8be9fd", -- Dracula cyan - green = "#50fa7b", -- Dracula green - orange = "#ffb86c", -- Dracula orange - pink = "#ff79c6", -- Dracula pink - purple = "#bd93f9", -- Dracula purple - red = "#ff5555", -- Dracula red - yellow = "#f1fa8c", -- Dracula yellow -} - ---- Gets the Dracula theme. ---- @return table theme Used in Yatline. -local function dracula_theme() - local palette = dracula_palette - - return { - section_separator = { open = "", close = "" }, - part_separator = { open = "", close = "" }, - inverse_separator = { open = "", close = "" }, - ---#=== yatline ===#--- - style_a = { - fg = palette.bg, - bg_mode = { - normal = palette.purple, -- Using purple as primary mode color - select = palette.pink, -- Using pink for select mode - un_set = palette.red, -- Keeping red for unset mode - }, - }, - style_b = { bg = palette.blue, fg = palette.fg }, - style_c = { bg = palette.bg_highlight, fg = palette.fg }, - - permissions_t_fg = palette.cyan, -- Using cyan for 't' permissions - permissions_r_fg = palette.yellow, -- Using yellow for 'r' permissions - permissions_w_fg = palette.red, -- Using red for 'w' permissions - permissions_x_fg = palette.green, -- Using green for 'x' permissions - permissions_s_fg = palette.fg, -- Using default fg for 's' permissions - - selected = { icon = "󰻭", fg = palette.pink }, -- Using pink for selected items - copied = { icon = "", fg = palette.green }, -- Using green for copied items - cut = { icon = "", fg = palette.red }, -- Using red for cut items - - total = { icon = "󰮍", fg = palette.yellow }, -- Using yellow for totals - succ = { icon = "", fg = palette.green }, -- Using green for success - fail = { icon = "", fg = palette.red }, -- Using red for failures - found = { icon = "󰮕", fg = palette.cyan }, -- Using cyan for found items - processed = { icon = "󰐍", fg = palette.orange }, -- Using orange for processed items - } -end - -return { - setup = function() - return dracula_theme() - end, -} diff --git a/.config/yazi/plugins/yatline-githead.yazi/main.lua b/.config/yazi/plugins/yatline-githead.yazi/main.lua deleted file mode 100644 index 73e2df0..0000000 --- a/.config/yazi/plugins/yatline-githead.yazi/main.lua +++ /dev/null @@ -1,314 +0,0 @@ ----@diagnostic disable: undefined-global - -local save = ya.sync(function(this, cwd, output) - if cx.active.current.cwd == Url(cwd) then - this.output = output - ya.render() - end -end) - -return { - setup = function(this, options) - options = options or {} - - local config = { - show_branch = options.show_branch == nil and true or options.show_branch, - branch_prefix = options.branch_prefix or "on", - branch_symbol = options.branch_symbol or "", - branch_borders = options.branch_borders or "()", - - commit_symbol = options.commit_symbol or "@", - - show_behind_ahead = options.behind_ahead == nil and true or options.behind_ahead, - behind_symbol = options.behind_symbol or "⇣", - ahead_symbol = options.ahead_symbol or "⇡", - - show_stashes = options.show_stashes == nil and true or options.show_stashes, - stashes_symbol = options.stashes_symbol or "$", - - show_state = options.show_state == nil and true or options.show_state, - show_state_prefix = options.show_state_prefix == nil and true or options.show_state_prefix, - state_symbol = options.state_symbol or "~", - - show_staged = options.show_staged == nil and true or options.show_staged, - staged_symbol = options.staged_symbol or "+", - - show_unstaged = options.show_unstaged == nil and true or options.show_unstaged, - unstaged_symbol = options.unstaged_symbol or "!", - - show_untracked = options.show_untracked == nil and true or options.show_untracked, - untracked_symbol = options.untracked_symbol or "?", - } - - if options.theme then - options = options.theme - end - - local theme = { - prefix_color = options.prefix_color or "white", - branch_color = options.branch_color or "blue", - commit_color = options.commit_color or "bright magenta", - behind_color = options.behind_color or "bright magenta", - ahead_color = options.ahead_color or "bright magenta", - stashes_color = options.stashes_color or "bright magenta", - state_color = options.state_color or "red", - staged_color = options.staged_color or "bright yellow", - unstaged_color = options.unstaged_color or "bright yellow", - untracked_color = options.untracked_color or "bright blue", - } - - local function get_branch(status) - local branch = status:match("On branch (%S+)") - - if branch == nil then - local commit = status:match("onto (%S+)") or status:match("detached at (%S+)") - - if commit == nil then - return "" - else - local branch_prefix = config.branch_prefix == "" and " " or " " .. config.branch_prefix .. " " - local commit_prefix = config.commit_symbol == "" and "" or config.commit_symbol - - return { "commit", branch_prefix .. commit_prefix, commit } - end - else - local left_border = config.branch_borders:sub(1, 1) - local right_border = config.branch_borders:sub(2, 2) - - local branch_string = "" - - if config.branch_symbol == "" then - branch_string = left_border .. branch .. right_border - else - branch_string = left_border .. config.branch_symbol .. " " .. branch .. right_border - end - - local branch_prefix = config.branch_prefix == "" and " " or " " .. config.branch_prefix .. " " - - return { "branch", branch_prefix, branch_string } - end - end - - local function get_behind_ahead(status) - local diverged_ahead, diverged_behind = status:match("have (%d+) and (%d+) different") - if diverged_ahead and diverged_behind then - return { " " .. config.behind_symbol .. diverged_behind, config.ahead_symbol .. diverged_ahead } - else - local behind = status:match("behind %S+ by (%d+) commit") - local ahead = status:match("ahead of %S+ by (%d+) commit") - if ahead then - return { "", " " .. config.ahead_symbol .. ahead } - elseif behind then - return { " " .. config.behind_symbol .. behind, "" } - else - return "" - end - end - end - - local function get_stashes(status) - local stashes = tonumber(status:match("Your stash currently has (%S+)")) - - return stashes ~= nil and " " .. config.stashes_symbol .. stashes or "" - end - - local function get_state(status) - local result = status:match("Unmerged paths:%s*(.-)%s*\n\n") - if result then - local filtered_result = result:gsub("^[%s]*%b()[%s]*", ""):gsub("^[%s]*%b()[%s]*", "") - - local unmerged = 0 - for line in filtered_result:gmatch("[^\r\n]+") do - if line:match("%S") then - unmerged = unmerged + 1 - end - end - - local state_name = "" - - if config.show_state_prefix then - if status:find("git merge") then - state_name = "merge " - elseif status:find("git cherry%-pick") then - state_name = "cherry " - elseif status:find("git rebase") then - state_name = "rebase " - - if status:find("done") then - local done = status:match("%((%d+) com.- done%)") or "" - state_name = state_name .. done .. "/" .. unmerged .. " " - end - else - state_name = "" - end - end - - return " " .. state_name .. config.state_symbol .. unmerged - else - return "" - end - end - - local function get_staged(status) - local result = status:match("Changes to be committed:%s*(.-)%s*\n\n") - if result then - local filtered_result = result:gsub("^[%s]*%b()[%s]*", "") - - local staged = 0 - for line in filtered_result:gmatch("[^\r\n]+") do - if line:match("%S") then - staged = staged + 1 - end - end - - return " " .. config.staged_symbol .. staged - else - return "" - end - end - - local function get_unstaged(status) - local result = status:match("Changes not staged for commit:%s*(.-)%s*\n\n") - if result then - local filtered_result = result:gsub("^[%s]*%b()[\r\n]*", ""):gsub("^[%s]*%b()[\r\n]*", "") - - local unstaged = 0 - for line in filtered_result:gmatch("[^\r\n]+") do - if line:match("%S") then - unstaged = unstaged + 1 - end - end - - return " " .. config.unstaged_symbol .. unstaged - else - return "" - end - end - - local function get_untracked(status) - local result = status:match("Untracked files:%s*(.-)%s*\n\n") - if result then - local filtered_result = result:gsub("^[%s]*%b()[\r\n]*", "") - - local untracked = 0 - for line in filtered_result:gmatch("[^\r\n]+") do - if line:match("%S") then - untracked = untracked + 1 - end - end - - return " " .. config.untracked_symbol .. untracked - else - return "" - end - end - - function Header:githead() - local status = this.output - - local branch_array = get_branch(status) - local prefix = ui.Span(config.show_branch and branch_array[2] or ""):fg(theme.prefix_color) - local branch = ui.Span(config.show_branch and branch_array[3] or "") - :fg(branch_array[1] == "commit" and theme.commit_color or theme.branch_color) - local behind_ahead = get_behind_ahead(status) - local behind = ui.Span(config.show_behind_ahead and behind_ahead[1] or ""):fg(theme.behind_color) - local ahead = ui.Span(config.show_behind_ahead and behind_ahead[2] or ""):fg(theme.ahead_color) - local stashes = ui.Span(config.show_stashes and get_stashes(status) or ""):fg(theme.stashes_color) - local state = ui.Span(config.show_state and get_state(status) or ""):fg(theme.state_color) - local staged = ui.Span(config.show_staged and get_staged(status) or ""):fg(theme.staged_color) - local unstaged = ui.Span(config.show_unstaged and get_unstaged(status) or ""):fg(theme.unstaged_color) - local untracked = ui.Span(config.show_untracked and get_untracked(status) or ""):fg(theme.untracked_color) - - return ui.Line({ prefix, branch, behind, ahead, stashes, state, staged, unstaged, untracked }) - end - - Header:children_add(Header.githead, 2000, Header.LEFT) - - local callback = function() - local cwd = cx.active.current.cwd - - if this.cwd ~= cwd then - this.cwd = cwd - ya.manager_emit("plugin", { - this._id, - ya.quote(tostring(cwd), true), - }) - end - end - - ps.sub("cd", callback) - ps.sub("tab", callback) - - if Yatline ~= nil then - function Yatline.coloreds.get:githead() - local status = this.output - local githead = {} - - local branch = config.show_branch and get_branch(status) or "" - if branch ~= nil and branch ~= "" then - table.insert(githead, { branch[2], theme.prefix_color }) - if branch[1] == "commit" then - table.insert(githead, { branch[3], theme.commit_color }) - else - table.insert(githead, { branch[3], theme.branch_color }) - end - end - - local behind_ahead = config.show_behind_ahead and get_behind_ahead(status) or "" - if behind_ahead ~= nil and behind_ahead ~= "" then - if behind_ahead[1] ~= nil and behind_ahead[1] ~= "" then - table.insert(githead, { behind_ahead[1], theme.behind_color }) - elseif behind_ahead[2] ~= nil and behind_ahead[2] ~= "" then - table.insert(githead, { behind_ahead[2], theme.ahead_color }) - end - end - - local stashes = config.show_stashes and get_stashes(status) or "" - if stashes ~= nil and stashes ~= "" then - table.insert(githead, { stashes, theme.stashes_color }) - end - - local state = config.show_state and get_state(status) or "" - if state ~= nil and state ~= "" then - table.insert(githead, { state, theme.state_color }) - end - - local staged = config.show_staged and get_staged(status) or "" - if staged ~= nil and staged ~= "" then - table.insert(githead, { staged, theme.staged_color }) - end - - local unstaged = config.show_unstaged and get_unstaged(status) or "" - if unstaged ~= nil and unstaged ~= "" then - table.insert(githead, { unstaged, theme.unstaged_color }) - end - - local untracked = config.show_untracked and get_untracked(status) or "" - if untracked ~= nil and untracked ~= "" then - table.insert(githead, { untracked, theme.untracked_color }) - end - - if #githead == 0 then - return "" - else - table.insert(githead, { " ", theme.prefix_color }) - return githead - end - end - end - end, - - entry = function(_, job) - local args = job.args or job - local command = Command("git") - :args({ "status", "--ignore-submodules=dirty", "--branch", "--show-stash", "--ahead-behind" }) - :cwd(args[1]) - :env("LANGUAGE", "en_US.UTF-8") - :stdout(Command.PIPED) - local output = command:output() - - if output then - save(args[1], output.stdout) - end - end, -} diff --git a/.config/yazi/plugins/yatline.yazi/main.lua b/.config/yazi/plugins/yatline.yazi/main.lua deleted file mode 100644 index 48591c5..0000000 --- a/.config/yazi/plugins/yatline.yazi/main.lua +++ /dev/null @@ -1,1362 +0,0 @@ ---- @diagnostic disable: undefined-global, undefined-field ---- @alias Mode Mode Comes from Yazi. ---- @alias Rect Rect Comes from Yazi. ---- @alias Paragraph Paragraph Comes from Yazi. ---- @alias Line Line Comes from Yazi. ---- @alias Span Span Comes from Yazi. ---- @alias Color Color Comes from Yazi. ---- @alias Config Config The config used for setup. ---- @alias Coloreds Coloreds The array returned by colorizer in {{string, Color}, {string, Color} ... } format ---- @alias Side # [ LEFT ... RIGHT ] ---- | `enums.LEFT` # The left side of either the header-line or status-line. [ LEFT ... ] ---- | `enums.RIGHT` # The right side of either the header-line or status-line. [ ... RIGHT] ---- @alias SeparatorType ---- | `enums.OUTER` # Separators on the outer side of sections. [ c o | c o | c o ... ] or [ ... o c | o c | o c ] ---- | `enums.INNER` # Separators on the inner side of sections. [ c i c | c i c | c i c ... ] or [ ... c i c | c i c | c i c ] ---- @alias ComponentType ---- | `enums.A` # Components on the first section. [ A | | ... ] or [ ... | | A ] ---- | `enums.B` # Components on the second section. [ | B | ... ] or [ ... | B | ] ---- | `enums.C` # Components on the third section. [ | | C ... ] or [ ... C | | ] - ---==================-- --- Type Declaration -- ---==================-- - -Yatline = {} - -local Side = { LEFT = 0, RIGHT = 1 } -local SeparatorType = { OUTER = 0, INNER = 1 } -local ComponentType = { A = 0, B = 1, C = 2 } - ---=========================-- --- Variable Initialization -- ---=========================-- - -local section_separator_open -local section_separator_close - -local inverse_separator_open -local inverse_separator_close - -local part_separator_open -local part_separator_close - -local separator_style = { bg = nil, fg = nil } - -local style_a -local style_b -local style_c - -local style_a_normal_bg -local style_a_select_bg -local style_a_un_set_bg - -local permissions_t_fg -local permissions_r_fg -local permissions_w_fg -local permissions_x_fg -local permissions_s_fg - -local tab_width - -local selected_icon -local copied_icon -local cut_icon -local files_icon -local filtereds_icon - -local selected_fg -local copied_fg -local cut_fg -local files_fg -local filtereds_fg - -local task_total_icon -local task_succ_icon -local task_fail_icon -local task_found_icon -local task_processed_icon - -local task_total_fg -local task_succ_fg -local task_fail_fg -local task_found_fg -local task_processed_fg - -local show_background - -local section_order = { "section_a", "section_b", "section_c" } - ---=================-- --- Component Setup -- ---=================-- - ---- Sets the background of style_a according to the tab's mode. ---- @param mode Mode The mode of the active tab. ---- @see cx.active.mode To get the active tab's mode. -local function set_mode_style(mode) - if mode.is_select then - style_a.bg = style_a_select_bg - elseif mode.is_unset then - style_a.bg = style_a_un_set_bg - else - style_a.bg = style_a_normal_bg - end -end - ---- Sets the style of the component according to the its type. ---- @param component Span Component that will be styled. ---- @param component_type ComponentType Which section component will be in [ a | b | c ]. ---- @see Style To see how to style, in Yazi's documentation. -local function set_component_style(component, component_type) - if component_type == ComponentType.A then - component:style(style_a):bold() - elseif component_type == ComponentType.B then - component:style(style_b) - else - component:style(style_c) - end -end - ---- Connects component to a separator. ---- @param component Span Component that will be connected to separator. ---- @param side Side Left or right side of the either header-line or status-line. ---- @param separator_type SeparatorType Where will there be a separator in the section. ---- @return Line line A Line which has component and separator. -local function connect_separator(component, side, separator_type) - local open, close - if - separator_type == SeparatorType.OUTER and not (separator_style.bg == "reset" and separator_style.fg == "reset") - then - open = ui.Span(section_separator_open) - close = ui.Span(section_separator_close) - - if separator_style.fg == "reset" then - if separator_style.bg ~= "reset" and separator_style.bg ~= nil then - open = ui.Span(inverse_separator_open) - close = ui.Span(inverse_separator_close) - - separator_style.fg, separator_style.bg = separator_style.bg, separator_style.fg - else - return ui.Line({ component }) - end - end - else - open = ui.Span(part_separator_open) - close = ui.Span(part_separator_close) - end - - open:style(separator_style) - close:style(separator_style) - - if side == Side.LEFT then - return ui.Line({ component, close }) - else - return ui.Line({ open, component }) - end -end - ---==================-- --- Helper Functions -- ---==================-- - ---- Gets the file name from given file extension. ---- @param file_name string The name of a file whose extension will be taken. ---- @return string file_extension Extension of a file. -local function get_file_extension(file_name) - local extension = file_name:match("^.+%.(.+)$") - - if extension == nil or extension == "" then - return "null" - else - return extension - end -end - ---- Reverse the order of given array ---- @param array Line Array which wants to be reversed. ---- @return table reversed Reversed ordered given array. -local function reverse_order(array) - local reversed = {} - for i = #array, 1, -1 do - table.insert(reversed, array[i]) - end - - return reversed -end - ---- the number of characters in a UTF-8 string ---- @param s string The string to process. ---- @return integer The number of characters in the string. -local function utf8len(s) - -- count the number of non-continuing bytes - return select(2, s:gsub("[^\128-\193]", "")) -end - ---- like string.sub() but i, j are utf8 strings ---- a utf8-safe string.sub() ---- @param s string The string to process. ---- @param i integer The start position. ---- @param j integer The end position. ---- @return string The substring. -local function utf8sub(s, i, j) - -- pattern for matching UTF-8 characters - local pattern = "[%z\1-\127\194-\244][\128-\191]*" - - -- helper function for position calculation - --- @param pos integer The position of the character. - --- @param len integer The length of the string. - --- @return integer The relative position of the character. - local function posrelat(pos, len) - if pos < 0 then - pos = len + pos + 1 - end - return pos - end - - -- helper function to iterate over UTF-8 chars - local function chars(_s, no_subs) - local function map(f) - local _i = 0 - if no_subs then - for b, e in _s:gmatch("()" .. pattern .. "()") do - _i = _i + 1 - local c = e - b - f(_i, c, b) - end - else - for b, c in _s:gmatch("()(" .. pattern .. ")") do - _i = _i + 1 - f(_i, c, b) - end - end - end - return coroutine.wrap(function() - return map(coroutine.yield) - end) - end - - local l = utf8len(s) - - i = posrelat(i, l) - j = j and posrelat(j, l) or l - - if i < 1 then - i = 1 - end - if j > l then - j = l - end - - if i > j then - return "" - end - - local diff = j - i - local iter = chars(s, true) - - -- advance up to i - for _ = 1, i - 1 do - iter() - end - - local c, b = select(2, iter()) - - -- becareful with the edge case of empty string - if not b then - return "" - end - - -- i and j are the same, single-character sub - if diff == 0 then - return string.sub(s, b, b + c - 1) - end - - i = b - - -- advance up to j - for _ = 1, diff - 1 do - iter() - end - - c, b = select(2, iter()) - - return string.sub(s, i, b + c - 1) -end - ---- Trims the filename if it is longer than the max_length. ---- @param filename string The name of a file which will be trimmed. ---- @param max_length integer Maximum length of the filename. ---- @param trim_length integer Length of the trimmed filename. ---- @return string trimmed_filename Trimmed filename. -local function trim_filename(filename, max_length, trim_length) - if not max_length or not trim_length then - return filename - end - - -- Count UTF-8 characters - local len = utf8len(filename) - - if len <= max_length then - return filename - end - - if len <= trim_length * 2 then - return filename - end - - return utf8sub(filename, 1, trim_length) .. "..." .. utf8sub(filename, len - trim_length + 1, len) -end - ---========================-- --- Component String Group -- ---========================-- - -Yatline.string = {} -Yatline.string.get = {} -Yatline.string.has_separator = true - ---- Creates a component from given string according to other parameters. ---- @param string string The text which will be shown inside of the component. ---- @param component_type ComponentType Which section component will be in [ a | b | c ]. ---- @return Line line Customized Line which follows desired style of the parameters. ---- @see set_mode_style To know how mode style selected. ---- @see set_component_style To know how component style applied. -function Yatline.string.create(string, component_type) - local span = ui.Span(" " .. string .. " ") - set_mode_style(cx.active.mode) - set_component_style(span, component_type) - - return ui.Line({ span }) -end - ---- Configuration for getting hovered file's name ---- @class HoveredNameConfig ---- @field trimed? boolean Whether to trim the filename if it's too long (default: false) ---- @field max_length? integer Maximum length of the filename (default: 24) ---- @field trim_length? integer Length of each end when trimming (default: 10) ---- @field show_symlink? boolean Whether to show symlink target (default: false) ---- Gets the hovered file's name of the current active tab. ---- @param config? HoveredNameConfig Configuration for getting hovered file's name ---- @return string name Current active tab's hovered file's name -function Yatline.string.get:hovered_name(config) - local hovered = cx.active.current.hovered - if not hovered then - return "" - end - - if not config then - return hovered.name - end - - local trimed = config.trimed or false - local max_length = config.max_length or 24 - local trim_length = config.trim_length or 10 - local show_symlink = config.show_symlink or false - - local link_delimiter = " -> " - local linked = (show_symlink and hovered.link_to ~= nil) and (link_delimiter .. tostring(hovered.link_to)) or "" - - if trimed then - local trimmed_name = trim_filename(hovered.name, max_length, trim_length) - local trimmed_linked = #linked ~= 0 - and link_delimiter .. trim_filename( - string.sub(linked, #link_delimiter + 1, -1), - max_length, - trim_length - ) - or "" - return trimmed_name .. trimmed_linked - else - return hovered.name .. linked - end -end - ---- Configuration for getting hovered file's path ---- @class HoveredPathConfig ---- @field trimed? boolean Whether to trim the file path if it's too long (default: false) ---- @field max_length? integer Maximum length of the file path (default: 24) ---- @field trim_length? integer Length of each end when trimming (default: 10) ---- Gets the hovered file's path of the current active tab. ---- @param config? HoveredPathConfig Configuration for getting hovered file's path ---- @return string path Current active tab's hovered file's path. -function Yatline.string.get:hovered_path(config) - local hovered = cx.active.current.hovered - if not hovered then - return "" - end - - if not config then - return ya.readable_path(tostring(hovered.url)) - end - - local trimed = config.trimed or false - local max_length = config.max_length or 24 - local trim_length = config.trim_length or 10 - - if trimed then - return trim_filename(ya.readable_path(tostring(hovered.url)), max_length, trim_length) - else - return ya.readable_path(tostring(hovered.url)) - end -end - ---- Gets the hovered file's size of the current active tab. ---- @return string size Current active tab's hovered file's size. -function Yatline.string.get:hovered_size() - local hovered = cx.active.current.hovered - if hovered then - return ya.readable_size(hovered:size() or hovered.cha.len) - else - return "" - end -end - ---- Gets the hovered file's path of the current active tab. ---- @return string mime Current active tab's hovered file's mime. -function Yatline.string.get:hovered_mime() - local hovered = cx.active.current.hovered - if hovered then - return hovered:mime() - else - return "" - end -end - ---- Gets the hovered file's user and group ownership of the current active tab. ---- @return string ownership Current active tab's hovered file's user and group ownership. -function Yatline.string.get:hovered_ownership() - local hovered = cx.active.current.hovered - - if hovered then - return ya.user_name(hovered.cha.uid) .. ":" .. ya.group_name(hovered.cha.gid) - else - return "" - end -end - ---- Gets the hovered file's extension of the current active tab. ---- @param show_icon boolean Whether or not an icon will be shown. ---- @return string file_extension Current active tab's hovered file's extension. -function Yatline.string.get:hovered_file_extension(show_icon) - local hovered = cx.active.current.hovered - - if hovered then - local cha = hovered.cha - - local name - if cha.is_dir then - name = "dir" - else - name = get_file_extension(hovered.url.name) - end - - if show_icon then - local icon = hovered:icon().text - return icon .. " " .. name - else - return name - end - else - return "" - end -end - ---- Configuration for getting curent active tab's path ---- @class TabPathConfig ---- @field trimed? boolean Whether to trim the current active tab's path if it's too long (default: false) ---- @field max_length? integer Maximum length of the current active tab's path (default: 24) ---- @field trim_length? integer Length of each end when trimming (default: 10) ---- Gets the path of the current active tab. ---- @param config? TabPathConfig Configuration for getting current active tab's path ---- @return string path Current active tab's path. -function Yatline.string.get:tab_path(config) - local cwd = cx.active.current.cwd - local filter = cx.active.current.files.filter - - local search = cwd.is_search and string.format(" (search: %s", cwd.frag) or "" - - local suffix - if not filter then - suffix = search == "" and search or search .. ")" - elseif search == "" then - suffix = string.format(" (filter: %s)", tostring(filter)) - else - suffix = string.format("%s, filter: %s)", search, tostring(filter)) - end - - if not config then - return ya.readable_path(tostring(cwd)) .. suffix - end - - local trimed = config.trimed or false - local max_length = config.max_length or 24 - local trim_length = config.trim_length or 10 - - if trimed then - return trim_filename(ya.readable_path(tostring(cwd)), max_length, trim_length) .. suffix - else - return ya.readable_path(tostring(cwd)) .. suffix - end -end - ---- Gets the mode of active tab. ---- @return string mode Active tab's mode. -function Yatline.string.get:tab_mode() - local mode = tostring(cx.active.mode):upper() - if mode == "UNSET" then - mode = "UN-SET" - end - - return mode -end - ---- Gets the number of files in the current active tab. ---- @return string num_files Number of files in the current active tab. -function Yatline.string.get:tab_num_files() - return tostring(#cx.active.current.files) -end - ---- Gets the cursor position in the current active tab. ---- @return string cursor_position Current active tab's cursor position. -function Yatline.string.get:cursor_position() - local cursor = cx.active.current.cursor - local length = #cx.active.current.files - - if length ~= 0 then - return string.format(" %2d/%-2d", cursor + 1, length) - else - return "0" - end -end - ---- Gets the cursor position as percentage which is according to the number of files inside of current active tab. ---- @return string percentage Percentage of current active tab's cursor position and number of percentages. -function Yatline.string.get:cursor_percentage() - local percentage = 0 - local cursor = cx.active.current.cursor - local length = #cx.active.current.files - if cursor ~= 0 and length ~= 0 then - percentage = math.floor((cursor + 1) * 100 / length) - end - - if percentage == 0 then - return " Top " - elseif percentage == 100 then - return " Bot " - else - return string.format("%3d%% ", percentage) - end -end - ---- Gets the local date or time values. ---- @param format string Format for giving desired date or time values. ---- @return string date Date or time values. ---- @see os.date To see how format works. -function Yatline.string.get:date(format) - return tostring(os.date(format)) -end - ---======================-- --- Component Line Group -- ---======================-- - -Yatline.line = {} -Yatline.line.get = {} -Yatline.line.has_separator = false - ---- To follow component group naming and functions, returns the given line without any changes. ---- @param line Line The line already pre-defined. ---- @param component_type ComponentType Which section component will be in [ a | b | c ]. Will not be used. ---- @return Line line The given line as an input. -function Yatline.line.create(line, component_type) - return line -end - ---- Creates and returns line component for tabs. ---- @param side Side Left or right side of the either header-line or status-line. ---- @return Line line Customized Line which contains tabs. ---- @see set_mode_style To know how mode style selected. ---- @see set_component_style To know how component style applied. ---- @see connect_separator To know how component and separator connected. -function Yatline.line.get:tabs(side) - local tabs = #cx.tabs - local lines = {} - - local in_side - if side == "left" then - in_side = Side.LEFT - else - in_side = Side.RIGHT - end - - for i = 1, tabs do - local text = i - if tab_width > 2 then - text = ya.truncate(text .. " " .. cx.tabs[i].name, { max = tab_width }) - end - - separator_style = { bg = nil, fg = nil } - if i == cx.tabs.idx then - local span = ui.Span(" " .. text .. " ") - set_mode_style(cx.tabs[i].mode) - set_component_style(span, ComponentType.A) - - if style_a.bg ~= "reset" or show_background then - separator_style.fg = style_a.bg - if show_background then - separator_style.bg = style_c.bg - end - - lines[#lines + 1] = connect_separator(span, in_side, SeparatorType.OUTER) - else - separator_style.fg = style_a.fg - - lines[#lines + 1] = connect_separator(span, in_side, SeparatorType.INNER) - end - else - local span = ui.Span(" " .. text .. " ") - if show_background then - set_component_style(span, ComponentType.C) - else - span:style({ fg = style_c.fg }) - end - - if i == cx.tabs.idx - 1 then - set_mode_style(cx.tabs[i + 1].mode) - - local open, close - if style_a.bg ~= "reset" or (show_background and style_c.bg ~= "reset") then - if not show_background or (show_background and style_c.bg == "reset") then - separator_style.fg = style_a.bg - if show_background then - separator_style.bg = style_c.bg - end - - open = ui.Span(inverse_separator_open) - close = ui.Span(inverse_separator_close) - else - separator_style.bg = style_a.bg - if show_background then - separator_style.fg = style_c.bg - end - - open = ui.Span(section_separator_open) - close = ui.Span(section_separator_close) - end - else - separator_style.fg = style_c.fg - - open = ui.Span(part_separator_open) - close = ui.Span(part_separator_close) - end - - open:style(separator_style) - close:style(separator_style) - - if in_side == Side.LEFT then - lines[#lines + 1] = ui.Line({ span, close }) - else - lines[#lines + 1] = ui.Line({ open, span }) - end - else - separator_style.fg = style_c.fg - if show_background then - separator_style.bg = style_c.bg - end - - lines[#lines + 1] = connect_separator(span, in_side, SeparatorType.INNER) - end - end - end - - if in_side == Side.RIGHT then - local lines_in_right = {} - for i = #lines, 1, -1 do - lines_in_right[#lines_in_right + 1] = lines[i] - end - - return ui.Line(lines_in_right) - else - return ui.Line(lines) - end -end - ---==========================-- --- Component Coloreds Group -- ---==========================-- - -Yatline.coloreds = {} -Yatline.coloreds.get = {} -Yatline.coloreds.has_separator = true - ---- Creates a component from given Coloreds according to other parameters. ---- The component it created, can contain multiple strings with different foreground color. ---- @param coloreds Coloreds The array which contains an array which contains text which will be shown inside of the component and its foreground color. ---- @param component_type ComponentType Which section component will be in [ a | b | c ]. ---- @return Line line Customized Line which follows desired style of the parameters. ---- @see set_mode_style To know how mode style selected. ---- @see set_component_style To know how component style applied. -function Yatline.coloreds.create(coloreds, component_type) - set_mode_style(cx.active.mode) - - local spans = {} - for i, colored in ipairs(coloreds) do - local span = ui.Span(colored[1]) - set_component_style(span, component_type) - span:fg(colored[2]) - - spans[i] = span - end - - return ui.Line(spans) -end - ---- Gets the hovered file's permissions of the current active tab. ---- @return Coloreds coloreds Current active tab's hovered file's permissions -function Yatline.coloreds.get:permissions() - local hovered = cx.active.current.hovered - - if hovered then - local perm = hovered.cha:perm() - - if perm then - local coloreds = {} - coloreds[1] = { " ", "black" } - - for i = 1, #perm do - local c = perm:sub(i, i) - - local fg = permissions_t_fg - if c == "-" then - fg = permissions_s_fg - elseif c == "r" then - fg = permissions_r_fg - elseif c == "w" then - fg = permissions_w_fg - elseif c == "x" or c == "s" or c == "S" or c == "t" or c == "T" then - fg = permissions_x_fg - end - - coloreds[i + 1] = { c, fg } - end - - coloreds[#perm + 2] = { " ", "black" } - - return coloreds - else - return "" - end - else - return "" - end -end - ---- Gets the number of selected and yanked files and also number of files or filtered files of the active tab. ---- @param filter? boolean Whether or not number of files (or filtered files) will be shown. ---- @return Coloreds coloreds Active tab's number of selected and yanked files and also number of files or filtered files -function Yatline.coloreds.get:count(filter) - local num_yanked = #cx.yanked - local num_selected = #cx.active.selected - local num_files = #cx.active.current.files - - local yanked_fg, yanked_icon - if cx.yanked.is_cut then - yanked_fg = cut_fg - yanked_icon = cut_icon - else - yanked_fg = copied_fg - yanked_icon = copied_icon - end - - local files_count_fg, files_count_icon - if cx.active.current.files.filter or cx.active.current.cwd.is_search then - files_count_icon = filtereds_icon - files_count_fg = filtereds_fg - else - files_count_icon = files_icon - files_count_fg = files_fg - end - - local coloreds - if filter then - coloreds = { - { string.format(" %s %d ", files_count_icon, num_files), files_count_fg }, - { string.format(" %s %d ", selected_icon, num_selected), selected_fg }, - { string.format(" %s %d ", yanked_icon, num_yanked), yanked_fg }, - } - else - coloreds = { - { string.format(" %s %d ", selected_icon, num_selected), selected_fg }, - { string.format(" %s %d ", yanked_icon, num_yanked), yanked_fg }, - } - end - - return coloreds -end - ---- Gets the number of task states. ---- @return Coloreds coloreds Number of task states. -function Yatline.coloreds.get:task_states() - local tasks = cx.tasks.progress - - local coloreds = { - { string.format(" %s %d ", task_total_icon, tasks.total), task_total_fg }, - { string.format(" %s %d ", task_succ_icon, tasks.succ), task_succ_fg }, - { string.format(" %s %d ", task_fail_icon, tasks.fail), task_fail_fg }, - } - - return coloreds -end - ---- Gets the number of task workloads. ---- @return Coloreds coloreds Number of task workloads. -function Yatline.coloreds.get:task_workload() - local tasks = cx.tasks.progress - - local coloreds = { - { string.format(" %s %d ", task_found_icon, tasks.found), task_found_fg }, - { string.format(" %s %d ", task_processed_icon, tasks.processed), task_processed_fg }, - } - - return coloreds -end - ---- Gets colored which contains string based component's string and desired foreground color. ---- @param component_name string String based component's name. ---- @param fg Color Desired foreground color. ---- @param params? table Array of parameters of string based component. It is optional. ---- @return Coloreds coloreds Array of solely array of string based component's string and desired foreground color. -function Yatline.coloreds.get:string_based_component(component_name, fg, params) - local getter = Yatline.string.get[component_name] - - if getter then - local output - if params then - output = getter(Yatline.string.get, table.unpack(params)) - else - output = getter() - end - - if output ~= nil and output ~= "" then - return { { " " .. output .. " ", fg } } - else - return "" - end - else - return "" - end -end - ---===============-- --- Configuration -- ---===============-- - ---- Configure separators if it is need to be added to the components. ---- Connects them with each component. ---- @param section_components table Array of components in one of the sections. ---- @param component_type ComponentType Which section component will be in [ a | b | c ]. ---- @param in_side Side Left or right side of the either header-line or status-line. ---- @param num_section_b_components integer Number of components in section-b. ---- @param num_section_c_components integer Number of components in section-c. ---- @return table section_line_components Array of line components whether or not connected with separators. ---- @see connect_separator To know how component and separator connected. -local function config_components_separators( - section_components, - component_type, - in_side, - num_section_b_components, - num_section_c_components -) - local num_section_components = #section_components - local section_line_components = {} - for i, component in ipairs(section_components) do - if component[2] == true then - separator_style = { bg = nil, fg = nil } - - local separator_type - if i ~= num_section_components then - if component_type == ComponentType.A then - separator_style = style_a - elseif component_type == ComponentType.B then - separator_style = style_b - else - separator_style = style_c - end - - separator_type = SeparatorType.INNER - else - if component_type == ComponentType.A then - separator_style.fg = style_a.bg - elseif component_type == ComponentType.B then - separator_style.fg = style_b.bg - else - separator_style.fg = style_c.bg - end - - if component_type == ComponentType.A and num_section_b_components ~= 0 then - separator_style.bg = style_b.bg - else - if num_section_c_components == 0 or component_type == ComponentType.C then - if show_background then - separator_style.bg = style_c.bg - end - else - separator_style.bg = style_c.bg - end - end - - separator_type = SeparatorType.OUTER - end - - section_line_components[i] = connect_separator(component[1], in_side, separator_type) - else - if in_side == Side.LEFT then - section_line_components[i] = component[1] - else - section_line_components[i] = component[1] - end - end - end - - return section_line_components -end - ---- Leads the given parameters to the other functions. ---- @param section_a_components table Components array whose components are in section-a of either side. ---- @param section_b_components table Components array whose components are in section-b of either side. ---- @param section_c_components table Components array whose components are in section-c of either side. ---- @param in_side Side Left or right side of the either header-line or status-line. ---- @return table section_a_line_components Array of components whose components are connected to separator and are in section-a of either side. ---- @return table section_b_line_components Array of components whose components are connected to separator and are in section-b of either side. ---- @return table section_c_line_components Array of components whose components are connected to separator and are in section-c of either side. ---- @see config_components_separators To know how separators are configured. -local function config_components(section_a_components, section_b_components, section_c_components, in_side) - local num_section_b_components = #section_b_components - local num_section_c_components = #section_c_components - - local section_a_line_components = config_components_separators( - section_a_components, - ComponentType.A, - in_side, - num_section_b_components, - num_section_c_components - ) - local section_b_line_components = config_components_separators( - section_b_components, - ComponentType.B, - in_side, - num_section_b_components, - num_section_c_components - ) - local section_c_line_components = config_components_separators( - section_c_components, - ComponentType.C, - in_side, - num_section_b_components, - num_section_c_components - ) - - return section_a_line_components, section_b_line_components, section_c_line_components -end - ---- Automatically creates and configures either left or right side according to their config. ---- @param side Config Configuration of either left or right side. ---- @return table section_a_components Components array whose components are in section-a of either side. ---- @return table section_b_components Components array whose components are in section-b of either side. ---- @return table section_c_components Components array whose components are in section-c of either side. -local function config_side(side) - local section_a_components = {} - local section_b_components = {} - local section_c_components = {} - - for _, section in ipairs(section_order) do - local components = side[section] - - local in_section, section_components - if section == "section_a" then - in_section = ComponentType.A - section_components = section_a_components - elseif section == "section_b" then - in_section = ComponentType.B - section_components = section_b_components - else - in_section = ComponentType.C - section_components = section_c_components - end - - for _, component in ipairs(components) do - local component_group = Yatline[component.type] - - if component_group then - if component.custom then - section_components[#section_components + 1] = - { component_group.create(component.name, in_section), component_group.has_separator } - else - local getter = component_group.get[component.name] - - if getter then - local output - if component.params then - output = getter(component_group.get, table.unpack(component.params)) - else - output = getter() - end - - if output ~= nil and output ~= "" then - section_components[#section_components + 1] = - { component_group.create(output, in_section), component_group.has_separator } - end - end - end - end - end - end - - return section_a_components, section_b_components, section_c_components -end - ---- Automatically creates and configures either header-line or status-line. ---- @param side Config Configuration of either left or right side. ---- @return table left_components Components array whose components are in left side of the line. ---- @return table right_components Components array whose components are in right side of the line. ---- @see config_side To know how components are gotten from side's config. ---- @see config_components To know how components are configured. -local function config_line(side, in_side) - local section_a_components, section_b_components, section_c_components = config_side(side) - - local section_a_line_components, section_b_line_components, section_c_line_components = - config_components(section_a_components, section_b_components, section_c_components, in_side) - - if in_side == Side.RIGHT then - section_a_line_components = reverse_order(section_a_line_components) - section_b_line_components = reverse_order(section_b_line_components) - section_c_line_components = reverse_order(section_c_line_components) - end - - local section_a_line = ui.Line(section_a_line_components) - local section_b_line = ui.Line(section_b_line_components) - local section_c_line = ui.Line(section_c_line_components) - - if in_side == Side.LEFT then - return ui.Line({ section_a_line, section_b_line, section_c_line }) - else - return ui.Line({ section_c_line, section_b_line, section_a_line }) - end -end - ---- Checks if either header-line or status-line contains components. ---- @param line Config Configuration of either header-line or status-line. ---- @return boolean show_line Returns yes if it contains components, otherwise returns no. -local function show_line(line) - local total_components = 0 - - for _, side in pairs(line) do - for _, section in pairs(side) do - total_components = total_components + #section - end - end - - return total_components ~= 0 -end - ---- Creates and configures paragraph which is used as left or right of either ---- header-line or status-line. ---- @param area Rect The area where paragraph will be placed in. ---- @param line? Line The line which used in paragraph. It is optional. ---- @return Paragraph paragraph Configured parapgraph. -local function config_paragraph(area, line) - local line_array = { line } or {} - if show_background then - return ui.Text(line_array):area(area):style(style_c) - else - return ui.Text(line_array):area(area) - end -end - -return { - setup = function(_, config) - config = config or {} - - tab_width = config.tab_width or 20 - - local component_positions = config.component_positions or { "header", "tab", "status" } - - show_background = config.show_background or false - - local display_header_line = config.display_header_line - if display_header_line == nil then - display_header_line = true - end - - local display_status_line = config.display_status_line - if display_status_line == nil then - display_status_line = true - end - - local header_line = config.header_line - or { - left = { section_a = {}, section_b = {}, section_c = {} }, - right = { section_a = {}, section_b = {}, section_c = {} }, - } - local status_line = config.status_line - or { - left = { section_a = {}, section_b = {}, section_c = {} }, - right = { section_a = {}, section_b = {}, section_c = {} }, - } - - config.theme = (not rt.term.light and config.theme_dark) - or (rt.term.light and config.theme_light) - or config.theme - - if config.theme then - for key, value in pairs(config.theme) do - if not config[key] then - config[key] = value - end - end - end - - if config.section_separator then - section_separator_open = config.section_separator.open - section_separator_close = config.section_separator.close - else - section_separator_open = "" - section_separator_close = "" - end - - if config.inverse_separator then - inverse_separator_open = config.inverse_separator.open - inverse_separator_close = config.inverse_separator.close - else - inverse_separator_open = "" - inverse_separator_close = "" - end - - if config.part_separator then - part_separator_open = config.part_separator.open - part_separator_close = config.part_separator.close - else - part_separator_open = "" - part_separator_close = "" - end - - if config.style_a then - style_a = { bg = config.style_a.bg_mode.normal, fg = config.style_a.fg } - - style_a_normal_bg = config.style_a.bg_mode.normal - style_a_select_bg = config.style_a.bg_mode.select - style_a_un_set_bg = config.style_a.bg_mode.un_set - else - style_a = { bg = "white", fg = "black" } - - style_a_normal_bg = "white" - style_a_select_bg = "brightyellow" - style_a_un_set_bg = "brightred" - end - - style_b = config.style_b or { bg = "brightblack", fg = "brightwhite" } - style_c = config.style_c or { bg = "black", fg = "brightwhite" } - - permissions_t_fg = config.permissions_t_fg or "green" - permissions_r_fg = config.permissions_r_fg or "yellow" - permissions_w_fg = config.permissions_w_fg or "red" - permissions_x_fg = config.permissions_x_fg or "cyan" - permissions_s_fg = config.permissions_s_fg or "white" - - if config.selected then - selected_fg = config.selected.fg - selected_icon = config.selected.icon - else - selected_fg = "yellow" - selected_icon = "󰻭" - end - - if config.copied then - copied_fg = config.copied.fg - copied_icon = config.copied.icon - else - copied_fg = "green" - copied_icon = "" - end - - if config.cut then - cut_icon = config.cut.icon - cut_fg = config.cut.fg - else - cut_icon = "" - cut_fg = "red" - end - - if config.files then - files_icon = config.files.icon - files_fg = config.files.fg - else - files_icon = "" - files_fg = "blue" - end - - if config.filtereds then - filtereds_icon = config.filtereds.icon - filtereds_fg = config.filtereds.fg - else - filtereds_icon = "" - filtereds_fg = "magenta" - end - - if config.total then - task_total_icon = config.total.icon - task_total_fg = config.total.fg - else - task_total_icon = "󰮍" - task_total_fg = "yellow" - end - - if config.succ then - task_succ_icon = config.succ.icon - task_succ_fg = config.succ.fg - else - task_succ_icon = "" - task_succ_fg = "green" - end - - if config.fail then - task_fail_icon = config.fail.icon - task_fail_fg = config.fail.fg - else - task_fail_icon = "" - task_fail_fg = "red" - end - - if config.found then - task_found_icon = config.found.icon - task_found_fg = config.found.fg - else - task_found_icon = "󰮕" - task_found_fg = "blue" - end - - if config.processed then - task_processed_icon = config.processed.icon - task_processed_fg = config.processed.fg - else - task_processed_icon = "󰐍" - task_processed_fg = "green" - end - - config = nil - - Progress.partial_render = function(self) - local progress = cx.tasks.progress - if progress.total == 0 then - return { config_paragraph(self._area) } - end - - local gauge = ui.Gauge():area(self._area) - if progress.fail == 0 then - gauge = gauge:gauge_style(th.status.progress_normal) - else - gauge = gauge:gauge_style(th.status.progress_error) - end - - local percent = 99 - if progress.found ~= 0 then - percent = math.min(99, ya.round(progress.processed * 100 / progress.found)) - end - - local left = progress.total - progress.succ - return { - gauge - :percent(percent) - :label(ui.Span(string.format("%3d%%, %d left", percent, left)):style(th.status.progress_label)), - } - end - - if display_header_line then - if show_line(header_line) then - Header.redraw = function(self) - local left_line = config_line(header_line.left, Side.LEFT) - local right_line = config_line(header_line.right, Side.RIGHT) - - return { - config_paragraph(self._area, left_line), - ui.Text(right_line):area(self._area):align(ui.Text.RIGHT), - } - end - - Header.children_add = function() - return {} - end - Header.children_remove = function() - return {} - end - end - else - Header.redraw = function() - return {} - end - end - - if display_status_line then - if show_line(status_line) then - Status.redraw = function(self) - local left_line = config_line(status_line.left, Side.LEFT) - local right_line = config_line(status_line.right, Side.RIGHT) - local right_width = right_line:width() - - return { - config_paragraph(self._area, left_line), - ui.Text(right_line):area(self._area):align(ui.Text.RIGHT), - table.unpack(Progress:new(self._area, right_width):redraw()), - } - end - - Status.children_add = function() - return {} - end - Status.children_remove = function() - return {} - end - end - else - Status.redraw = function() - return {} - end - end - - Root.layout = function(self) - local constraints = {} - for _, component in ipairs(component_positions) do - if - (component == "header" and display_header_line) or (component == "status" and display_status_line) - then - table.insert(constraints, ui.Constraint.Length(1)) - elseif component == "tab" then - table.insert(constraints, ui.Constraint.Fill(1)) - end - end - - self._chunks = ui.Layout():direction(ui.Layout.VERTICAL):constraints(constraints):split(self._area) - end - - Root.build = function(self) - local childrens = {} - - local i = 1 - for _, component in ipairs(component_positions) do - if component == "header" and display_header_line then - table.insert(childrens, Header:new(self._chunks[i], cx.active)) - i = i + 1 - elseif component == "tab" then - table.insert(childrens, Tab:new(self._chunks[i], cx.active)) - i = i + 1 - elseif component == "status" and display_status_line then - table.insert(childrens, Status:new(self._chunks[i], cx.active)) - i = i + 1 - end - end - - self._children = childrens - end - end, -} diff --git a/.config/yazi/yazi.toml b/.config/yazi/yazi.toml index 6cbf958..49c88e1 100644 --- a/.config/yazi/yazi.toml +++ b/.config/yazi/yazi.toml @@ -1,6 +1,6 @@ [manager] show_hidden = true -linemode = "owner" +linemode = "size_and_mtime" [plugin] prepend_previewers = [