From a7c79cb5a04562be10347856642a80f0c4be89fc Mon Sep 17 00:00:00 2001 From: delta Date: Fri, 17 Apr 2026 08:10:30 +0200 Subject: sync --- .config/awesome/misc/autostart.lua | 2 +- .config/awesome/misc/cfg.lua | 1 - .config/awesome/misc/init.lua | 2 +- .config/awesome/misc/keys.lua | 30 ++- .config/awesome/misc/rules.lua | 12 ++ .config/awesome/prismite.lua | 4 + .config/awesome/quarrel/native/Cargo.toml | 2 +- .../quarrel/native/src/lenses/application.rs | 14 +- .../quarrel/native/src/lenses/calculator.rs | 27 ++- .config/awesome/quarrel/native/src/lenses/mod.rs | 90 +++++---- .config/awesome/quarrel/native/src/net/mod.rs | 4 +- .config/awesome/quarrel/native/src/net/wireless.rs | 4 +- .config/awesome/quarrel/ui/init.lua | 87 ++++++++ .config/awesome/services/mpris/consts.lua | 13 ++ .config/awesome/services/mpris/init.lua | 188 ++++++++++------- .config/awesome/services/osd.lua | 18 ++ .config/awesome/services/wifi.lua | 0 .config/awesome/ui/fresnel/init.lua | 19 +- .config/awesome/ui/fresnel/text_input.lua | 4 +- .config/awesome/ui/osd/init.lua | 136 ++++++++++--- .config/awesome/ui/statusbar/init.lua | 31 ++- .../awesome/ui/statusbar/panel/widgets/mpris.lua | 158 +++++++-------- .config/fish/.gitignore | 1 + .config/fish/colors.fish | 8 +- .config/fish/conf.d/fish_frozen_key_bindings.fish | 14 ++ .config/fish/conf.d/fish_frozen_theme.fish | 37 ++++ .config/fish/config.fish | 3 +- .config/fish/fish_variables | 30 +-- .config/nvim/ftplugin/markdown.lua | 2 + .config/nvim/ftplugin/text.lua | 2 + .config/nvim/lazy-lock.json | 40 ++-- .config/nvim/lua/diagnostics.lua | 12 ++ .config/nvim/lua/plugins/alpha.lua | 7 + .config/nvim/lua/plugins/better-escape.lua | 13 ++ .config/nvim/lua/plugins/blink.lua | 43 ++++ .config/nvim/lua/plugins/cokeline.lua | 220 ++++++++++++++++++++ .config/nvim/lua/plugins/color-converter.lua | 11 + .config/nvim/lua/plugins/d2.lua | 9 + .config/nvim/lua/plugins/git-blame.lua | 7 + .config/nvim/lua/plugins/graphviz.lua | 5 + .config/nvim/lua/plugins/hardtime.lua | 5 + .config/nvim/lua/plugins/indentmini.lua | 7 + .config/nvim/lua/plugins/leap.lua | 14 ++ .config/nvim/lua/plugins/lsp.lua | 1 + .config/nvim/lua/plugins/lualine.lua | 223 +++++++++++++++++++++ .config/nvim/lua/plugins/mini/colors.lua | 1 + .config/nvim/lua/plugins/mini/comment.lua | 1 + .config/nvim/lua/plugins/mini/completions.lua | 1 + .config/nvim/lua/plugins/mini/surround.lua | 15 ++ .config/nvim/lua/plugins/noice.lua | 90 +++++++++ .config/nvim/lua/plugins/nui.lua | 3 + .config/nvim/lua/plugins/nvim-cmp.lua | 64 ++++++ .config/nvim/lua/plugins/nvim-colorizer.lua | 12 ++ .config/nvim/lua/plugins/nvim-tree.lua | 131 ++++++++++++ .config/nvim/lua/plugins/nvim-treesitter.lua | 122 +++++++++++ .config/nvim/lua/plugins/snacks.lua | 9 + .config/nvim/lua/plugins/statuscol.lua | 20 ++ .config/nvim/lua/plugins/telescope.lua | 65 ++++++ .config/nvim/lua/plugins/trouble.lua | 109 ++++++++++ .config/nvim/lua/state.lua | 2 + .config/nvim/lua/utils/delegate.lua | 22 ++ 61 files changed, 1892 insertions(+), 335 deletions(-) create mode 100644 .config/awesome/services/mpris/consts.lua create mode 100644 .config/awesome/services/wifi.lua create mode 100644 .config/fish/.gitignore create mode 100644 .config/fish/conf.d/fish_frozen_key_bindings.fish create mode 100644 .config/fish/conf.d/fish_frozen_theme.fish create mode 100644 .config/nvim/ftplugin/markdown.lua create mode 100644 .config/nvim/ftplugin/text.lua create mode 100644 .config/nvim/lua/diagnostics.lua create mode 100644 .config/nvim/lua/plugins/alpha.lua create mode 100644 .config/nvim/lua/plugins/better-escape.lua create mode 100644 .config/nvim/lua/plugins/blink.lua create mode 100644 .config/nvim/lua/plugins/cokeline.lua create mode 100644 .config/nvim/lua/plugins/color-converter.lua create mode 100644 .config/nvim/lua/plugins/d2.lua create mode 100644 .config/nvim/lua/plugins/git-blame.lua create mode 100644 .config/nvim/lua/plugins/graphviz.lua create mode 100644 .config/nvim/lua/plugins/hardtime.lua create mode 100644 .config/nvim/lua/plugins/indentmini.lua create mode 100644 .config/nvim/lua/plugins/leap.lua create mode 100644 .config/nvim/lua/plugins/lualine.lua create mode 100644 .config/nvim/lua/plugins/mini/colors.lua create mode 100644 .config/nvim/lua/plugins/mini/comment.lua create mode 100644 .config/nvim/lua/plugins/mini/completions.lua create mode 100644 .config/nvim/lua/plugins/mini/surround.lua create mode 100644 .config/nvim/lua/plugins/noice.lua create mode 100644 .config/nvim/lua/plugins/nui.lua create mode 100644 .config/nvim/lua/plugins/nvim-cmp.lua create mode 100644 .config/nvim/lua/plugins/nvim-colorizer.lua create mode 100644 .config/nvim/lua/plugins/nvim-tree.lua create mode 100644 .config/nvim/lua/plugins/nvim-treesitter.lua create mode 100644 .config/nvim/lua/plugins/snacks.lua create mode 100644 .config/nvim/lua/plugins/statuscol.lua create mode 100644 .config/nvim/lua/plugins/telescope.lua create mode 100644 .config/nvim/lua/plugins/trouble.lua create mode 100644 .config/nvim/lua/state.lua create mode 100644 .config/nvim/lua/utils/delegate.lua diff --git a/.config/awesome/misc/autostart.lua b/.config/awesome/misc/autostart.lua index de79c65..85b91c0 100644 --- a/.config/awesome/misc/autostart.lua +++ b/.config/awesome/misc/autostart.lua @@ -1,7 +1,7 @@ local awful = require "awful" local qpersistent = require "quarrel.persistent" -if qpersistent.is_restart() then +if qpersistent.is_restart() or awesome.release == "somewm" then return end diff --git a/.config/awesome/misc/cfg.lua b/.config/awesome/misc/cfg.lua index f62612f..a05f911 100644 --- a/.config/awesome/misc/cfg.lua +++ b/.config/awesome/misc/cfg.lua @@ -1,7 +1,6 @@ local awful = require "awful" local cfg = { - terminal = "wezterm", tags = { -- home { diff --git a/.config/awesome/misc/init.lua b/.config/awesome/misc/init.lua index 069046a..d3c36b2 100644 --- a/.config/awesome/misc/init.lua +++ b/.config/awesome/misc/init.lua @@ -1,3 +1,3 @@ require "misc.keys" require "misc.rules" --- require "misc.autostart" +require "misc.autostart" diff --git a/.config/awesome/misc/keys.lua b/.config/awesome/misc/keys.lua index b7dc4c4..9a5e481 100644 --- a/.config/awesome/misc/keys.lua +++ b/.config/awesome/misc/keys.lua @@ -2,7 +2,10 @@ local awful = require "awful" local backlight = require "services.backlight" local beautiful = require "beautiful" local cfg = require "misc.cfg" -local fresnel = require "ui.fresnel" +local fresnel = { show = function() end} +if awesome.release ~= "somewm" then + fresnel = require "ui.fresnel" +end local gtable = require "gears.table" local gtimer = require "gears.timer" local insightful = require "ui.insightful" @@ -13,6 +16,7 @@ local playerctl = require "services.playerctl" local powermenu = require "ui.powermenu" local qbind = require "quarrel.bind" local dnd = require "services.dnd" +-- local osd = require "ui.osd" local recording = { false, "" } @@ -28,6 +32,17 @@ client.connect_signal("request::default_mousebindings", function() group = "client", desc = "raise client", }, + qbind { + mods = qbind.mods.M, + triggers = qbind.btns.middle, + press = function(c) + c:activate { + context = "mouse_click", + } + end, + group = "client", + desc = "raise client", + }, qbind { mods = qbind.mods.M, triggers = qbind.btns.left, @@ -115,6 +130,15 @@ awful.keyboard.append_global_keybindings { group = "awesome", desc = "toggle insightful", }, + -- qbind { + -- mods = qbind.mods.M, + -- triggers = "u", + -- press = function() + -- osd:toggle() + -- end, + -- group = "awesome", + -- desc = "toggle osd", + -- }, qbind { mods = qbind.mods.M, triggers = "space", @@ -229,7 +253,7 @@ awful.keyboard.append_global_keybindings { mods = qbind.mods.M, triggers = "Return", press = function() - awful.spawn(cfg.terminal) + awful.spawn("xdg-terminal-exec") end, group = "launcher", desc = "launch terminal", @@ -293,7 +317,7 @@ awful.keyboard.append_global_keybindings { else recording[1] = true recording[2] = os.getenv "HOME" .. "/Videos/" .. os.date "%Y%m%d_%H%M%S" .. ".mp4" - awful.spawn("giph --format mp4 --framerate 30 " .. recording[2]) + awful.spawn("giph -a --format mp4 --framerate 30 " .. recording[2]) naughty.notification { app_name = "Giph", title = "Recording started", diff --git a/.config/awesome/misc/rules.lua b/.config/awesome/misc/rules.lua index 18b11f1..3f59929 100644 --- a/.config/awesome/misc/rules.lua +++ b/.config/awesome/misc/rules.lua @@ -53,6 +53,18 @@ ruled.client.connect_signal("request::rules", function() }, }, + { + id = "dragon-drop", + rule = { + class = "Dragon-drop" + }, + properties = { + ontop = true, + sticky = true, + floating = true + } + }, + { id = "browser_tag", rule_any = { diff --git a/.config/awesome/prismite.lua b/.config/awesome/prismite.lua index b9423e8..610ab9b 100644 --- a/.config/awesome/prismite.lua +++ b/.config/awesome/prismite.lua @@ -25,6 +25,10 @@ theme.border_normal = qcolor.palette.border() theme.border_focus = qcolor.palette.border() theme.border_marked = qcolor.palette.border() +theme.snap_bg = qcolor.palette.yellow() +theme.snap_border_width = qui.BORDER_WIDTH * 2 +theme.snap_shape = qui.shape + theme.notification_icon_size = dpi(32) theme.notification_border_width = qui.BORDER_WIDTH theme.notification_border_color = theme.border_normal diff --git a/.config/awesome/quarrel/native/Cargo.toml b/.config/awesome/quarrel/native/Cargo.toml index 1b98681..8d1bd80 100644 --- a/.config/awesome/quarrel/native/Cargo.toml +++ b/.config/awesome/quarrel/native/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "qnative" version = "0.1.0" -edition = "2021" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/.config/awesome/quarrel/native/src/lenses/application.rs b/.config/awesome/quarrel/native/src/lenses/application.rs index 38a7762..cc12e82 100644 --- a/.config/awesome/quarrel/native/src/lenses/application.rs +++ b/.config/awesome/quarrel/native/src/lenses/application.rs @@ -3,13 +3,13 @@ use std::{ fs::read_dir, path::PathBuf, sync::{ + Arc, + OnceLock, + RwLock, atomic::{ AtomicBool, Ordering, }, - Arc, - OnceLock, - RwLock, }, }; @@ -20,7 +20,10 @@ use rayon::prelude::*; use url::Url; use crate::lenses::{ - Cache, Entries, Entry, Lense + Cache, + Entries, + Entry, + Lense, }; static APPS_DIR: &'static str = "/usr/share/applications"; @@ -34,6 +37,7 @@ pub struct Application { impl Lense for Application { const NAME: &str = "Application"; + const PREFIX: Option<&'static str> = None; fn init() -> Arc { let this = Arc::new(Application::default()); @@ -94,7 +98,7 @@ impl Lense for Application { // self.should_interrupt.load(Ordering::Relaxed) } - fn entries(&self, _: &Lua, _: String) -> Result { + fn entries(&self, _: &Lua, _: &str) -> Result { let entries = read_dir(APPS_DIR)? .map(|result| result.map(|e| e.path())) .collect::, std::io::Error>>()?; diff --git a/.config/awesome/quarrel/native/src/lenses/calculator.rs b/.config/awesome/quarrel/native/src/lenses/calculator.rs index 36f9805..a483d10 100644 --- a/.config/awesome/quarrel/native/src/lenses/calculator.rs +++ b/.config/awesome/quarrel/native/src/lenses/calculator.rs @@ -1,22 +1,25 @@ use std::sync::{ + Arc, + LazyLock, + Mutex, atomic::{ AtomicBool, Ordering, }, - Arc, - LazyLock, - Mutex, }; use fend_core::{ - evaluate_with_interrupt, Context, Interrupt, + evaluate_with_interrupt, }; use mlua::prelude::*; use crate::lenses::{ - Cache, Entries, Entry, Lense + Cache, + Entries, + Entry, + Lense, }; static CTX: LazyLock> = LazyLock::new(|| { @@ -32,6 +35,7 @@ pub struct Calculator { impl Lense for Calculator { const NAME: &str = "Calculator"; + const PREFIX: Option<&'static str> = Some("#"); fn init() -> std::sync::Arc { Arc::new(Calculator::default()) @@ -55,18 +59,14 @@ impl Lense for Calculator { self.should_interrupt.load(Ordering::Relaxed) } - fn entries(&self, _: &Lua, input: String) -> Result { + fn entries(&self, _: &Lua, input: &str) -> Result { let result = match evaluate_with_interrupt( input.trim(), &mut CTX.lock().expect("Failed to acquire Fend context lock"), self, ) { - Ok(result) => { - result.get_main_result().to_string() - } - Err(err) => { - err - } + Ok(result) => result.get_main_result().to_string(), + Err(err) => err, }; Ok(if result.is_empty() { @@ -77,11 +77,10 @@ impl Lense for Calculator { exec: None, }) }) - } #[inline] - fn filter(&self, entries: Entries, _: String) -> Entries { + fn filter(&self, entries: Entries, _: &str) -> Entries { entries } } diff --git a/.config/awesome/quarrel/native/src/lenses/mod.rs b/.config/awesome/quarrel/native/src/lenses/mod.rs index bb7f727..0b7864e 100644 --- a/.config/awesome/quarrel/native/src/lenses/mod.rs +++ b/.config/awesome/quarrel/native/src/lenses/mod.rs @@ -5,8 +5,8 @@ use std::sync::Arc; use itertools::Itertools; use mlua::{ - prelude::*, LuaSerdeExt, + prelude::*, }; use rayon::iter::FromParallelIterator; use serde::{ @@ -18,7 +18,7 @@ use serde::{ pub enum Entries { Multiple(Vec), Single(Entry), - None + None, } impl FromIterator for Entries { @@ -32,7 +32,7 @@ impl FromIterator for Entries { let mut vec = Vec::from([first, second]); vec.extend(iter); Self::Multiple(vec) - }, + } } } } @@ -44,8 +44,8 @@ impl From> for Entries { 1 => { let entry = entries.into_iter().exactly_one().unwrap(); Self::Single(entry) - }, - _ => Self::Multiple(entries) + } + _ => Self::Multiple(entries), } } } @@ -55,7 +55,7 @@ impl IntoLua for Entries { match self { Entries::Multiple(entries) => entries.into_lua(lua), Entries::Single(entry) => entry.into_lua(lua), - Entries::None => Ok(LuaValue::Nil) + Entries::None => Ok(LuaValue::Nil), } } } @@ -89,6 +89,7 @@ pub struct _Lense(pub Arc); pub trait Lense { const NAME: &'static str; + const PREFIX: Option<&'static str>; fn init() -> Arc; @@ -98,51 +99,70 @@ pub trait Lense { fn set_interrupt(&self, interrupt: bool); fn get_interrupt(&self) -> bool; - fn entries(&self, lua: &Lua, input: String) -> Result; - fn filter(&self, entries: Entries, input: String) -> Entries { - let entry_contains = |entry: &Entry| { - entry.message.to_lowercase().contains(&input.to_lowercase()) - }; + fn entries(&self, lua: &Lua, input: &str) -> Result; + fn filter(&self, entries: Entries, input: &str) -> Entries { + let entry_contains = + |entry: &Entry| entry.message.to_lowercase().contains(&input.to_lowercase()); match entries { - Entries::Multiple(entries) => entries.into_iter() - .filter(entry_contains) - .collect(), - Entries::Single(entry) => if entry_contains(&entry) { Entries::Single(entry) } else { Entries::None } - Entries::None => Entries::None + Entries::Multiple(entries) => entries.into_iter().filter(entry_contains).collect(), + Entries::Single(entry) => { + if entry_contains(&entry) { + Entries::Single(entry) + } else { + Entries::None + } + } + Entries::None => Entries::None, } } } impl LuaUserData for _Lense { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("stale", |_, this| { - Ok(matches!(this.0.get_cache(), Cache::Stale)) + fields.add_field_method_get("stale", |_, Self(this)| { + Ok(matches!(this.get_cache(), Cache::Stale)) }); fields.add_field("name", T::NAME); } fn add_methods>(methods: &mut M) { - methods.add_method_mut("query", |lua, this, input: String| { - this.0.set_interrupt(false); // reset interrupt so that we can use the lense again - return Ok(match this.0.get_cache() { - Cache::Valid(entries) => this.0.filter(entries.clone(), input), - Cache::Stale => { - let entries = this.0.entries(lua, input.clone()).map_err(LuaError::external)?; - let mut entries = this.0.filter(entries, input); - match entries { - Entries::Multiple(ref mut entries) => entries.sort_by(|a, b| a.message.cmp(&b.message)), - _ => {} - } - this.0.set_cache(Cache::Valid(entries.clone())); - entries + methods.add_method_mut("query", |lua, Self(this), input: String| { + if T::PREFIX.is_some_and(|prefix| input.starts_with(prefix)) || T::PREFIX.is_none() { + let input = if let Some(prefix) = T::PREFIX { + input.trim_start_matches(prefix) + } else { + &input } - }); + .trim(); + + this.set_interrupt(false); // reset interrupt so that we can use the lense again + + return Ok(match this.get_cache() { + Cache::Valid(entries) => this.filter(entries.clone(), input), + Cache::Stale => { + let entries = this.entries(lua, input).map_err(LuaError::external)?; + let mut entries = this.filter(entries, input); + match entries { + Entries::Multiple(ref mut entries) => { + entries.sort_by(|a, b| a.message.cmp(&b.message)) + } + _ => {} + } + this.set_cache(Cache::Valid(entries.clone())); + entries + } + }); + } else { + return Ok(Entries::None); + } }); - methods.add_method_mut("mark_stale", |_, this, _: ()| { - Ok(this.0.set_cache(Cache::Stale)) + methods.add_method_mut("mark_stale", |_, Self(this), _: ()| { + Ok(this.set_cache(Cache::Stale)) + }); + methods.add_method_mut("interrupt", |_, Self(this), _: ()| { + Ok(this.set_interrupt(true)) }); - methods.add_method_mut("interrupt", |_, this, _: ()| Ok(this.0.set_interrupt(true))); } } diff --git a/.config/awesome/quarrel/native/src/net/mod.rs b/.config/awesome/quarrel/native/src/net/mod.rs index 383e800..728e6ec 100644 --- a/.config/awesome/quarrel/native/src/net/mod.rs +++ b/.config/awesome/quarrel/native/src/net/mod.rs @@ -20,21 +20,21 @@ use nix::{ ioctl_read_bad, libc::IF_NAMESIZE, sys::socket::{ - socket as open_socket, AddressFamily, SockFlag, SockType, + socket as open_socket, }, unistd::close, }; use wireless::{ + IW_ESSID_MAX_SIZE, IfConf, IfConfData, IwPoint, IwReq, IwReqData, IwReqName, - IW_ESSID_MAX_SIZE, SIOCGIFCONF, SIOCGIWESSID, }; diff --git a/.config/awesome/quarrel/native/src/net/wireless.rs b/.config/awesome/quarrel/native/src/net/wireless.rs index d3999db..3500751 100644 --- a/.config/awesome/quarrel/native/src/net/wireless.rs +++ b/.config/awesome/quarrel/native/src/net/wireless.rs @@ -7,11 +7,11 @@ use std::ffi::{ use nix::libc::{ __s16, __s32, - __u16, __u8, + __u16, + IF_NAMESIZE, ifreq as IfReq, sockaddr as SockAddr, - IF_NAMESIZE, }; pub static SIOCGIFCONF: c_int = 0x8912; diff --git a/.config/awesome/quarrel/ui/init.lua b/.config/awesome/quarrel/ui/init.lua index 5e68b1d..7ba6a58 100644 --- a/.config/awesome/quarrel/ui/init.lua +++ b/.config/awesome/quarrel/ui/init.lua @@ -4,7 +4,9 @@ local gshape = require "gears.shape" local gtable = require "gears.table" local qbind = require "quarrel.bind" local qcolor = require "quarrel.color" +local qdelegate = require "quarrel.delegate" local wibox = require "wibox" +local cairo = require("lgi").cairo --- Clip Cairo context ---@param cr cairo_surface Cairo surface @@ -199,4 +201,89 @@ function M.hoverable(widget, cursor) return widget end +---@param wibox wibox +function M.animateable_shape(wibox) + local old_apply_shape = wibox._apply_shape + + wibox:disconnect_signal("property::geometry", old_apply_shape) + wibox:disconnect_signal("property::border_width", old_apply_shape) + + function wibox:set_shape_width(value) + self._shape_width = value + self:emit_signal("property::shape_width") + end + + function wibox:get_shape_width(value) + return self._shape_width + end + + function wibox:set_shape_height(value) + self._shape_height = value + self:emit_signal("property::shape_height") + end + + function wibox:get_shape_height(value) + return self._shape_height + end + + -- override to allow us to override shape + wibox._apply_shape = qdelegate(function(env, self) + local shape = self._shape + + if not shape then + self.shape_bounding = nil + self.shape_clip = nil + return + end + + local geo = self:geometry() + local bw = self.border_width + + if self.shape_width then + geo.width = self.shape_width + end + + if self.shape_height then + geo.height = self.shape_height + end + + -- First handle the bounding shape (things including the border) + local img = cairo.ImageSurface(cairo.Format.A1, geo.width + 2*bw, geo.height + 2*bw) + local cr = cairo.Context(img) + + -- We just draw the shape in its full size + shape(cr, geo.width + 2*bw, geo.height + 2*bw) + cr:set_operator(cairo.Operator.SOURCE) + cr:fill() + self.shape_bounding = img._native + img:finish() + + -- Now handle the clip shape (things excluding the border) + img = cairo.ImageSurface(cairo.Format.A1, geo.width, geo.height) + cr = cairo.Context(img) + + -- We give the shape the same arguments as for the bounding shape and draw + -- it in its full size (the translate is to compensate for the smaller + -- surface) + cr:translate(-bw, -bw) + shape(cr, geo.width + 2*bw, geo.height + 2*bw) + cr:set_operator(cairo.Operator.SOURCE) + cr:fill_preserve() + -- Now we remove an area of width 'bw' again around the shape (We use 2*bw + -- since half of that is on the outside and only half on the inside) + cr:set_source_rgba(0, 0, 0, 0) + cr:set_line_width(2*bw) + cr:stroke() + self.shape_clip = img._native + img:finish() + end, old_apply_shape) + + wibox:connect_signal("property::geometry", wibox._apply_shape) + wibox:connect_signal("property::border_width", wibox._apply_shape) + wibox:connect_signal("property::shape_width", wibox._apply_shape) + wibox:connect_signal("property::shape_height", wibox._apply_shape) + + return wibox +end + return M diff --git a/.config/awesome/services/mpris/consts.lua b/.config/awesome/services/mpris/consts.lua new file mode 100644 index 0000000..e86e50e --- /dev/null +++ b/.config/awesome/services/mpris/consts.lua @@ -0,0 +1,13 @@ +local M = {} + +M.EXCLUDED_PLAYERS = {} + +M.TRACKED_METADATA = { + length = "mpris:length", + album = "xesam:album", + title = "xesam:title", + artist = "xesam:artist", + art = "mpris:artUrl", +} + +return M diff --git a/.config/awesome/services/mpris/init.lua b/.config/awesome/services/mpris/init.lua index 3659a3a..1c907e7 100644 --- a/.config/awesome/services/mpris/init.lua +++ b/.config/awesome/services/mpris/init.lua @@ -1,100 +1,132 @@ local gobject = require "gears.object" local playerctl = require "services.mpris.playerctl" local qpersistent = require "quarrel.persistent" +local playerctl = require("lgi").Playerctl ---@class ServiceMpris : gears.object ----@field inner Playerctl ---@field index number the index of the currently active player -local M = {} +---@field manager lgi.Playerctl.PlayerManager +local M = { object = {} } ----@param self ServiceMpris ----@param player Playerctl.data -local function update_player(self, player) - self:emit_signal("player::metadata", player) - self:emit_signal("player::position", player) - -- handle_position(nil, player) +---@class WrappedPlayer : gears.object +---@field inner lgi.Playerctl.Player +---@field _position_timer gears.timer + +---@param self WrappedPlayer +function M.object:_update_position(by_user) + self:emit_signal("property::position", by_user) end ----@param diff_player Playerctl.data -local function recalculate_active_player(diff_player, vanished) - if type(diff_player) ~= "table" then - return - end - -- if #layout.children == 0 then - -- M.active_player_index = 1 - -- update_player() - -- return - -- end - - local active_player = players[M.active_player_index] - if not active_player then -- we're recovering from a state with no players - update_player(diff_player) - return +---@param self WrappedPlayer +function M.object:_update_position_timer() + if self.inner.playback_status == "PLAYING" then + self._position_timer:again() + else + self._position_timer:stop() end +end - if diff_player.instance == active_player.instance and vanished then -- active player vanished; fall back to previous player - M.previous_player() - else -- non-active player appeared/vanished; try to find active player - for i, p in ipairs(playerctl:list()) do - if p.instance == active_player.instance then - M.active_player_index = i - update_player(p) - return - end - end - - gdebug.print_warning( - "failed to find active player:\n " .. gdebug.dump_return(active_player, nil, 2):gsub("\n", "\n ") - ) - M.active_player_index = 1 - update_player(playerctl:list()[M.active_player_index]) - end +---@param self WrappedPlayer +---@param value boolean +function M.object:set_shuffle(value) + self.inner:set_shuffle(value) +end + +---@param self WrappedPlayer +---@param value number +function M.object:set_volume(value) + self.inner:set_volume(value) +end + +---@param self WrappedPlayer +---@param value integer +function M.object:set_position(value) + self.inner:set_position(value) +end + +---@param self WrappedPlayer +---@param value lgi.Playerctl.LoopStatus +function M.object:set_loop_status(value) + self.inner:set_loop_status(value) +end + +---@param self WrappedPlayer +function M.object:play_pause() + self.inner:play_pause() +end + +---@param self WrappedPlayer +function M.object:play() + self.inner:play() +end + +---@param self WrappedPlayer +function M.object:pause() + self.inner:pause() +end + +---@param self WrappedPlayer +function M.object:stop() + self.inner:stop() end -function M:next_player() - local players = self.inner:list() +---@param self WrappedPlayer +function M.object:previous() + self.inner:previous() +end - if #players == 0 then - return - elseif self.index + 1 > #players then - self.index = 1 +---@param self WrappedPlayer +function M.object:next() + self.inner:next() +end + +---@param self Playerctl +---@param player_a lgi.Playerctl.Player +---@param player_b lgi.Playerctl.Player +---@return sign +local function compare_players(self, player_a, player_b) + if player_a.player_name < player_b.player_name then + return -1 + elseif player_a.player_name > player_b.player_name then + return 1 else - self.index = self.index + 1 + return 0 end +end - -- update_player(playerctl:list()[M.active_player_index]) - local player = players[self.index] - self:emit_signal("player::metadata", player) - self:emit_signal("player::position", player) +local function manage_player(self, full_name) + local player = playerctl.Player.new_from_name(full_name) + + function player.on_metadata(p, metadata) + + end end -function M:previous_player() - local players = self.inner:list() - if #players == 0 then - return - elseif self.index - 1 < #players then - self.index = #players - else - self.index = self.index - 1 +local function init_manager(self) + self.manager = playerctl.PlayerManager() + self.manager:set_sort_func(function(a, b) + local player_a = playerctl.Player(a) + local player_b = playerctl.Player(b) + return compare_players(self, player_a, player_b) + end) + + function self.manager.on_name_appeared(_, full_name) + end - local player = players[self.index] - self:emit_signal("player::metadata", player) - self:emit_signal("player::position", player) -end - -M.inner = playerctl.new { - players = {}, - metadata = { - album = "xesam:album", - title = "xesam:title", - artist = "xesam:artist", - art = "mpris:artUrl", - }, -} - -local instance = gobject { class = M } -instance:connect_signal("property::index", function(self, index) - qpersistent.store("active_player_index", self.index) -end) + function self.manager.on_player_appeared() + + end +end + +local function new() + local self = gobject {} + + init_manager(self) + + return self +end + +local instance = new() + return instance diff --git a/.config/awesome/services/osd.lua b/.config/awesome/services/osd.lua index c4fa3e7..1851016 100644 --- a/.config/awesome/services/osd.lua +++ b/.config/awesome/services/osd.lua @@ -1,7 +1,25 @@ +local gobject = require "gears.object" local battery = require "services.battery" local osd = require "ui.osd" local phosphor = require "assets.phosphor" +local inner = false + +local M = gobject { + class = { + set_dnd = function(self, value) + inner = value + self:emit_signal("dnd", inner) + end, + get_dnd = function() + return inner + end + }, + enable_properties = true, +} + +return M + -- battery:connect_signal("icon", function (self, icon, _) -- osd.notify(icon, self.level, 100) -- end) diff --git a/.config/awesome/services/wifi.lua b/.config/awesome/services/wifi.lua new file mode 100644 index 0000000..e69de29 diff --git a/.config/awesome/ui/fresnel/init.lua b/.config/awesome/ui/fresnel/init.lua index dc8e333..613bc9c 100644 --- a/.config/awesome/ui/fresnel/init.lua +++ b/.config/awesome/ui/fresnel/init.lua @@ -1,7 +1,5 @@ local text_input = require "ui.fresnel.text_input" local awful = require "awful" -local qdebug = require "quarrel.debug" -local cfg = require "misc.cfg" local gshape = require "gears.shape" local gtable = require "gears.table" local qanim = require "quarrel.animation" @@ -152,7 +150,7 @@ function fresnel:_exec_entry(entry) local exec = entry.exec if type(exec) ~= "userdata" and type(exec) ~= "nil" then if exec[2] then - awful.spawn(cfg.terminal .. " -e /bin/sh -c " .. exec[1] .. " 1>/dev/null 2>&1") + awful.spawn.with_shell("xdg-terminal-exec /bin/sh -c '" .. exec[1]:gsub("'", [[\']]) .. "' 1>/dev/null 2>&1") else awful.spawn.with_shell(exec[1] .. " 1>/dev/null 2>&1") end @@ -318,17 +316,17 @@ fresnel._w_status = wibox.widget { widget = wibox.widget.textbox, text = "0/0", } -fresnel._w_popup = qui.popup { +fresnel._w_popup = qui.animateable_shape(qui.popup { -- visible = false, ontop = true, placement = false, - shape = function(cr, w) - gshape.partially_rounded_rect(cr, w, 0, false, false, true, true, qui.BORDER_RADIUS) + shape = function(cr, w, h) + gshape.partially_rounded_rect(cr, w, h, false, false, true, true, qui.BORDER_RADIUS) end, x = width / 2, + y = -qui.BORDER_WIDTH, minimum_width = width, maximum_width = width, - -- maximum_height = max_height, widget = { qui.styled { widget = wibox.container.background, @@ -373,7 +371,7 @@ fresnel._w_popup = qui.popup { }, layout = wibox.layout.align.vertical, }, -} +}) function fresnel:show() self._prev_scroll_amount = DEFAULT_SCROLL_AMOUNT @@ -406,13 +404,10 @@ fresnel._t_height = qanim:new { pos = 0, easing = qvars.easing, subscribed = function(pos) - fresnel._w_popup.shape = function(cr, w) - gshape.partially_rounded_rect(cr, w, pos, false, false, true, true, qui.BORDER_RADIUS) - end + fresnel._w_popup.shape_height = pos end, } --- TODO: optimize the search algo to be more efficient and not require making fresnel invisible fresnel._t_opacity = rubato.timed { duration = qvars.anim_duration, pos = 0, diff --git a/.config/awesome/ui/fresnel/text_input.lua b/.config/awesome/ui/fresnel/text_input.lua index 2dc7368..5b6525b 100644 --- a/.config/awesome/ui/fresnel/text_input.lua +++ b/.config/awesome/ui/fresnel/text_input.lua @@ -3,8 +3,8 @@ -- @copyright 2021-2025 Kasper24 ------------------------------------------- local lgi = require('lgi') -local Gtk = lgi.require('Gtk', '3.0') -local Gdk = lgi.require('Gdk', '3.0') +local Gtk = lgi.require('Gtk', '3.0')-- somewm:ignore +local Gdk = lgi.require('Gdk', '3.0')-- somewm:ignore local Pango = lgi.Pango local awful = require("awful") local gtable = require("gears.table") diff --git a/.config/awesome/ui/osd/init.lua b/.config/awesome/ui/osd/init.lua index 4310f17..85cb1d7 100644 --- a/.config/awesome/ui/osd/init.lua +++ b/.config/awesome/ui/osd/init.lua @@ -4,24 +4,49 @@ local gtimer = require "gears.timer" local phosphor = require "assets.phosphor" local qmath = require "quarrel.math" local qui = require "quarrel.ui" +local qdebug = require "quarrel.debug" +local gshape = require "gears.shape" +local qanim = require "quarrel.animation" local qvars = require "quarrel.vars" local rubato = require "lib.rubato" local wibox = require "wibox" -- local mpris_widget = require "ui.osd.mpris" -local osd = {} +local MAX_HEIGHT = qui.BIG_PADDING * 2 + qui.CHAR_HEIGHT -local widget = awful.popup(qui.styled { +local M = { + _toggled = false +} + +local geo = awful.screen.focused().geometry + +M._w_popup = qui.animateable_shape(qui.popup { ontop = true, visible = true, - placement = function(d) - awful.placement.top(d, { - margins = { - top = beautiful.useless_gap * 2, - }, + shape = function(cr, w, h) + gshape.partially_rounded_rect(cr, w, h, true, true, false, false, qui.BORDER_RADIUS) + end, + placement = function (d) + return (awful.placement.bottom + awful.placement.center_horizontal)(d, { + offset = { + y = qui.BORDER_WIDTH + 1 -- Needs one more pixel to fully occlude the bottom border for some reason + } }) end, - minimum_height = qui.CHAR_HEIGHT * 3, + -- placement = function(d) + -- awful.placement.bottom(d, { + -- margins = { + -- top = beautiful.useless_gap * 2, + -- }, + -- }) + -- end, + -- x = geo.width / 2, + -- y = geo.height - 10, + minimum_height = qui.CHAR_HEIGHT, + widget = { + widget = wibox.widget.textbox, + text = "blah" + } -- widget = mpris_widget -- minimum_width = awful.screen.focused().geometry.width / 2, -- widget = { @@ -65,38 +90,85 @@ local widget = awful.popup(qui.styled { -- }, }) -local timer +M._t_height = qanim:new { + duration = qvars.anim_duration, + pos = 0, + easing = qvars.easing, + subscribed = function(pos) + M._w_popup.shape_height = pos + end, +} -local anim = rubato.timed { +-- TODO: optimize the search algo to be more efficient and not require making fresnel invisible +M._t_opacity = rubato.timed { duration = qvars.anim_duration, - intro = qvars.anim_intro, - pos = 1, + pos = 0, subscribed = function(pos) - widget.opacity = pos + M._w_popup.opacity = pos + if pos == 0 then - widget.visible = false - elseif not widget.visible then - widget.visible = true - elseif pos == 1 then - -- timer:start() + M._w_popup.visible = false + else + M._w_popup.visible = true end end, } -timer = gtimer { - timeout = 1, - callback = function() - -- anim.target = 0 - end, - single_shot = true, -} +M._t_height:set(qui.BIG_PADDING * 2 + qui.CHAR_HEIGHT) +M._t_opacity.target = 1 + +function M:show() + self._toggled = true + self._t_opacity.target = 1 + self._t_height:set(MAX_HEIGHT) +end -function osd.notify(icon, value, max) - anim.target = 1 - widget:get_children_by_id("icon")[1].image = icon - widget:get_children_by_id("progress")[1].value = value - widget:get_children_by_id("progress")[1].max_value = max - widget:get_children_by_id("percentage")[1].text = tostring(qmath.percentage(value, max)) .. "%" +function M:hide() + self._toggled = false + self._t_opacity.target = 0 + self._t_height:set(0) end -return osd +function M:toggle() + if self._toggled then + self:hide() + else + self:show() + end +end + +-- local timer +-- +-- local anim = rubato.timed { +-- duration = qvars.anim_duration, +-- intro = qvars.anim_intro, +-- pos = 1, +-- subscribed = function(pos) +-- widget.opacity = pos +-- if pos == 0 then +-- widget.visible = false +-- elseif not widget.visible then +-- widget.visible = true +-- elseif pos == 1 then +-- -- timer:start() +-- end +-- end, +-- } +-- +-- timer = gtimer { +-- timeout = 1, +-- callback = function() +-- -- anim.target = 0 +-- end, +-- single_shot = true, +-- } + +-- function M.notify(icon, value, max) +-- anim.target = 1 +-- widget:get_children_by_id("icon")[1].image = icon +-- widget:get_children_by_id("progress")[1].value = value +-- widget:get_children_by_id("progress")[1].max_value = max +-- widget:get_children_by_id("percentage")[1].text = tostring(qmath.percentage(value, max)) .. "%" +-- end + +return M diff --git a/.config/awesome/ui/statusbar/init.lua b/.config/awesome/ui/statusbar/init.lua index 0a1198a..baaf2fa 100644 --- a/.config/awesome/ui/statusbar/init.lua +++ b/.config/awesome/ui/statusbar/init.lua @@ -15,7 +15,7 @@ local keyboardlayout = require "ui.statusbar.widgets.keyboardlayout" local taglist = require "ui.statusbar.widgets.taglist_new" screen.connect_signal("request::desktop_decoration", function(s) - local bar = qui.popup { + local bar = qui.animateable_shape(qui.popup { placement = function(d) return awful.placement.left(d, { margins = beautiful.useless_gap * 2, @@ -59,13 +59,14 @@ screen.connect_signal("request::desktop_decoration", function(s) }, toggled = false, - } + }) - local bar_width = bar.width + qui.BORDER_WIDTH * 2 + local bar_width = bar.width - bar.shape = function(cr, _, h) - qui.shape(cr, bar_width, h) - end + bar.shape_width = bar_width + -- bar.shape = function(cr, _, h) + -- qui.shape(cr, bar_width, h) + -- end bar:struts { left = bar_width + beautiful.useless_gap * 4, @@ -73,20 +74,30 @@ screen.connect_signal("request::desktop_decoration", function(s) bar.widget.widget.third = panel + local idle = true + local timed = qanim:new { duration = qvars.anim_duration, pos = bar_width, easing = qvars.easing, subscribed = function(pos) - if pos ~= bar_width and bar.toggled then + if idle and pos ~= bar_width and bar.toggled then bar.ontop = true + -- panel.visible = true + idle = false elseif pos == bar_width and not bar.toggled then bar.ontop = false + -- panel.visible = false + idle = true + elseif pos == bar_width + M.EXPANDED_BAR_SIZE then + idle = true end - bar.shape = function(cr, _, h) - qui.shape(cr, pos, h) - end + bar.shape_width = pos + + -- bar.shape = function(cr, _, h) + -- + -- end end, } diff --git a/.config/awesome/ui/statusbar/panel/widgets/mpris.lua b/.config/awesome/ui/statusbar/panel/widgets/mpris.lua index 687bbb1..f462ea3 100644 --- a/.config/awesome/ui/statusbar/panel/widgets/mpris.lua +++ b/.config/awesome/ui/statusbar/panel/widgets/mpris.lua @@ -99,106 +99,98 @@ M.widget = wibox.widget(qui.styled { { widget = client_background, { - nil, - qui.padded_big { + nil, + qui.padded_big { + { + { + widget = wibox.widget.textbox, + text = DEFAULTS.progresstext.position .. " / " .. DEFAULTS.progresstext.length, -- position / length + id = "progresstext", + }, { + widget = wibox.container.place, { - widget = wibox.container.background, - bg = qcolor.palette.bg(), + widget = wibox.widget.progressbar, + value = DEFAULTS.position, + max_value = DEFAULTS.length, + background_color = qcolor.palette.bg.lowest, + color = qcolor.palette.yellow(), + forced_height = qui.BORDER_RADIUS, + border_width = 0, shape = qui.shape, - { - widget = wibox.widget.imagebox, - image = DEFAULTS.art, - forced_height = qui.CHAR_HEIGHT * 5, - forced_width = qui.CHAR_HEIGHT * 5, - valign = "center", - halign = "center", - stylesheet = qui.recolor(qcolor.palette.bg.highest), - id = "cover", - }, + -- - forced_width = qui.CHAR_HEIGHT, + id = "progressbar", }, + }, + layout = wibox.layout.fixed.horizontal, + spacing = qui.BIG_PADDING, + }, + -- { + -- widget = wibox.container.background, + -- bg = qcolor.palette.bg(), + -- shape = qui.shape, + { + widget = wibox.widget.imagebox, + image = DEFAULTS.art, + forced_height = qui.CHAR_HEIGHT * 8, + forced_width = qui.CHAR_HEIGHT * 8, + valign = "center", + halign = "center", + stylesheet = qui.recolor(qcolor.palette.bg.highest), + clip_shape = qui.shape, + id = "cover", + }, + -- }, + { + { + widget = wibox.container.constraint, + height = qui.CHAR_HEIGHT * 2.5, + strategy = "max", { - widget = wibox.container.margin, - left = qui.BIG_PADDING, - { - { - widget = wibox.container.constraint, - height = qui.CHAR_HEIGHT * 2.5, - strategy = "max", - { - widget = wibox.widget.textbox, - text = DEFAULTS.title, -- Song - id = "song", - valign = "top", - }, - }, - { - widget = wibox.container.constraint, - height = qui.CHAR_HEIGHT * 2.5, - strategy = "max", - { - widget = wibox.container.background, - fg = qcolor.palette.fg.low, - { - widget = wibox.widget.textbox, - text = DEFAULTS.artist_album, -- Artist - Album Name - id = "artist_album", - valign = "top", - }, - }, - }, - layout = wibox.layout.fixed.vertical, - }, + widget = wibox.widget.textbox, + text = DEFAULTS.title, -- Song + id = "song", + valign = "top", }, - layout = wibox.layout.fixed.horizontal, }, - nil, { - widget = wibox.container.margin, - top = qui.BIG_PADDING, + widget = wibox.container.constraint, + height = qui.CHAR_HEIGHT * 2.5, + strategy = "max", { + widget = wibox.container.background, + fg = qcolor.palette.fg.low, { widget = wibox.widget.textbox, - text = DEFAULTS.progresstext.position .. " / " .. DEFAULTS.progresstext.length, -- position / length - id = "progresstext", - }, - { - widget = wibox.container.place, - { - widget = wibox.widget.progressbar, - forced_height = qui.PADDING, - color = qcolor.palette.yellow(), - value = DEFAULTS.position, - max_value = DEFAULTS.length, - background_color = qcolor.palette.bg.lowest, - bar_shape = qui.shape, - shape = qui.shape, - id = "progressbar", - }, + text = DEFAULTS.artist_album, -- Artist - Album Name + id = "artist_album", + valign = "top", }, - layout = wibox.layout.fixed.horizontal, - spacing = qui.BIG_PADDING, }, }, - layout = wibox.layout.align.vertical, + layout = wibox.layout.fixed.vertical, + spacing = qui.PADDING + }, + layout = wibox.layout.fixed.vertical, + spacing = qui.BIG_PADDING, + }, + { + qui.separator { + size = qui.BORDER_WIDTH, + vertical = true }, { - qui.separator { - size = qui.BORDER_WIDTH, - vertical = true - }, - { - widget = wibox.container.background, - bg = qcolor.palette.bg.high, - qui.padded_big { - layout = wibox.layout.flex.vertical, - spacing = qui.BIG_PADDING, - id = "client_list", - } - }, - layout = wibox.layout.fixed.horizontal, + widget = wibox.container.background, + bg = qcolor.palette.bg.high, + qui.padded_big { + layout = wibox.layout.flex.vertical, + spacing = qui.BIG_PADDING, + id = "client_list", + } }, - layout = wibox.layout.align.horizontal, + layout = wibox.layout.fixed.horizontal, + }, + layout = wibox.layout.align.horizontal, }, }, }) diff --git a/.config/fish/.gitignore b/.config/fish/.gitignore new file mode 100644 index 0000000..c989a98 --- /dev/null +++ b/.config/fish/.gitignore @@ -0,0 +1 @@ +secret.fish diff --git a/.config/fish/colors.fish b/.config/fish/colors.fish index fdc8987..05a70cb 100644 --- a/.config/fish/colors.fish +++ b/.config/fish/colors.fish @@ -9,15 +9,15 @@ set fish_color_error white set fish_color_param white set fish_color_valid_path white set fish_color_option white -set fish_color_comment "#a8afb7" +set fish_color_comment "#323b46" set fish_color_operator white set fish_color_escape yellow -set fish_color_autosuggestion "#a8afb7" +set fish_color_autosuggestion "#323b46" set fish_color_search_match --background=black # pager set fish_pager_color_progress "#a8afb7" set fish_pager_color_prefix white -set fish_pager_color_completion "#a8afb7" +set fish_pager_color_completion white --bold set fish_pager_color_description "#a8afb7" -set fish_pager_color_selected_background --background=black +set fish_pager_color_selected_background --background="#171c22" diff --git a/.config/fish/conf.d/fish_frozen_key_bindings.fish b/.config/fish/conf.d/fish_frozen_key_bindings.fish new file mode 100644 index 0000000..495aee9 --- /dev/null +++ b/.config/fish/conf.d/fish_frozen_key_bindings.fish @@ -0,0 +1,14 @@ +# This file was created by fish when upgrading to version 4.3, to migrate +# the 'fish_key_bindings' variable from its old default scope (universal) +# to its new default scope (global). We recommend you delete this file +# and configure key bindings in ~/.config/fish/config.fish if needed. + +# set --global fish_key_bindings fish_default_key_bindings + +# Prior to version 4.3, fish shipped an event handler that runs +# `set --universal fish_key_bindings fish_default_key_bindings` +# whenever the fish_key_bindings variable is erased. +# This means that as long as any fish < 4.3 is still running on this system, +# we cannot complete the migration. +# As a workaround, erase the universal variable at every shell startup. +set --erase --universal fish_key_bindings diff --git a/.config/fish/conf.d/fish_frozen_theme.fish b/.config/fish/conf.d/fish_frozen_theme.fish new file mode 100644 index 0000000..bffe9ad --- /dev/null +++ b/.config/fish/conf.d/fish_frozen_theme.fish @@ -0,0 +1,37 @@ +# This file was created by fish when upgrading to version 4.3, to migrate +# theme variables from universal to global scope. +# Don't edit this file, as it will be written by the web-config tool (`fish_config`). +# To customize your theme, delete this file and see +# help interactive#syntax-highlighting +# or +# man fish-interactive | less +/^SYNTAX.HIGHLIGHTING +# for appropriate commands to add to ~/.config/fish/config.fish instead. +# See also the release notes for fish 4.3.0 (run `help relnotes`). + +set --global fish_color_autosuggestion '#323b46' +set --global fish_color_cancel -r +set --global fish_color_command white +set --global fish_color_comment '#323b46' +set --global fish_color_cwd green +set --global fish_color_cwd_root red +set --global fish_color_end pink +set --global fish_color_error white +set --global fish_color_escape yellow +set --global fish_color_history_current --bold +set --global fish_color_host normal +set --global fish_color_host_remote yellow +set --global fish_color_normal white +set --global fish_color_operator white +set --global fish_color_param white +set --global fish_color_quote yellow +set --global fish_color_redirection yellow +set --global fish_color_search_match --background=black +set --global fish_color_selection white --bold --background=brblack +set --global fish_color_status red +set --global fish_color_user brgreen +set --global fish_color_valid_path white +set --global fish_pager_color_completion white --bold +set --global fish_pager_color_description '#a8afb7' +set --global fish_pager_color_prefix white +set --global fish_pager_color_progress '#a8afb7' +set --global fish_pager_color_selected_background '--background=#171c22' diff --git a/.config/fish/config.fish b/.config/fish/config.fish index 97e55eb..47dca4e 100644 --- a/.config/fish/config.fish +++ b/.config/fish/config.fish @@ -112,7 +112,8 @@ direnv hook fish | source function dl yt-dlp -v --download-archive "archive.txt" \ - --write-info-json --write-thumbnail --embed-subs --embed-thumbnail --embed-metadata \ + --write-info-json --write-thumbnail --embed-thumbnail --embed-metadata \ + --write-subs --sub-langs all --sub-format=srv3 \ --merge-output-format "mkv" -f "bestvideo[height<=1080][width<=1920]+bestaudio" \ --abort-on-error -o "files/%(extractor_key)s%(id)s/item.%(ext)s" \ --extractor-args "youtube:player-client=default,mweb" $argv diff --git a/.config/fish/fish_variables b/.config/fish/fish_variables index 2ac8f35..f8d1652 100644 --- a/.config/fish/fish_variables +++ b/.config/fish/fish_variables @@ -1,34 +1,6 @@ # This file contains fish universal variable definitions. # VERSION: 3.0 SETUVAR __done_exclude:lite\x2dxl -SETUVAR __fish_initialized:3800 -SETUVAR fish_color_autosuggestion:\x2377828c -SETUVAR fish_color_cancel:\x2dr -SETUVAR fish_color_command:white -SETUVAR fish_color_comment:\x2377828c -SETUVAR fish_color_cwd:green -SETUVAR fish_color_cwd_root:red -SETUVAR fish_color_end:pink -SETUVAR fish_color_error:white -SETUVAR fish_color_escape:yellow -SETUVAR fish_color_history_current:\x2d\x2dbold -SETUVAR fish_color_host:normal -SETUVAR fish_color_host_remote:yellow -SETUVAR fish_color_normal:white -SETUVAR fish_color_operator:white -SETUVAR fish_color_param:white -SETUVAR fish_color_quote:yellow -SETUVAR fish_color_redirection:yellow -SETUVAR fish_color_search_match:\x2d\x2dbackground\x3dblack -SETUVAR fish_color_selection:white\x1e\x2d\x2dbold\x1e\x2d\x2dbackground\x3dbrblack -SETUVAR fish_color_status:red -SETUVAR fish_color_user:brgreen -SETUVAR fish_color_valid_path:white +SETUVAR __fish_initialized:4300 SETUVAR fish_greeting:\x1d -SETUVAR fish_key_bindings:fish_default_key_bindings -SETUVAR fish_pager_color_completion:\x2377828c -SETUVAR fish_pager_color_description:\x2377828c -SETUVAR fish_pager_color_prefix:white -SETUVAR fish_pager_color_progress:\x2377828c -SETUVAR fish_pager_color_selected_background:\x2d\x2dbackground\x3dblack SETUVAR fish_user_paths:/home/delta/\x2elocal/share/pnpm\x1e/home/delta/\x2elocal/bin\x1e/home/delta/\x2elocal/share/cargo/bin\x1e/home/delta/\x2elocal/share/gem/ruby/3\x2e0\x2e0/bin\x1e/home/delta/\x2elocal/share/go/bin\x1e/home/delta/\x2edetaspace/bin\x1e/usr/local/texlive/2023/bin/x86_64\x2dlinux diff --git a/.config/nvim/ftplugin/markdown.lua b/.config/nvim/ftplugin/markdown.lua new file mode 100644 index 0000000..7757adb --- /dev/null +++ b/.config/nvim/ftplugin/markdown.lua @@ -0,0 +1,2 @@ +local winid = vim.api.nvim_get_current_win() +vim.wo[winid][0].wrap = true diff --git a/.config/nvim/ftplugin/text.lua b/.config/nvim/ftplugin/text.lua new file mode 100644 index 0000000..7757adb --- /dev/null +++ b/.config/nvim/ftplugin/text.lua @@ -0,0 +1,2 @@ +local winid = vim.api.nvim_get_current_win() +vim.wo[winid][0].wrap = true diff --git a/.config/nvim/lazy-lock.json b/.config/nvim/lazy-lock.json index 9daf3a7..b0132b2 100644 --- a/.config/nvim/lazy-lock.json +++ b/.config/nvim/lazy-lock.json @@ -1,30 +1,32 @@ { - "alpha-nvim": { "branch": "main", "commit": "2b3cbcdd980cae1e022409289245053f62fb50f6" }, + "alpha-nvim": { "branch": "main", "commit": "7563da4a861ee6b3ed674d0ee5c5c0bd19383a38" }, "better-escape.nvim": { "branch": "master", "commit": "19a38aab94961016430905ebec30d272a01e9742" }, - "blink.cmp": { "branch": "main", "commit": "a4ab037eefdb9949896ea8553f918bd6250d62bd" }, + "blink.cmp": { "branch": "main", "commit": "c573a15a62bd0bfd4006ee0849b24f5404395500" }, + "d2-vim": { "branch": "master", "commit": "cb3eb7fcb1a2d45c4304bf2e91077d787b724a39" }, "git-blame.nvim": { "branch": "master", "commit": "54da04264ec5313d602aebea7c5dc90141696ad7" }, + "graphviz.nvim": { "branch": "main", "commit": "d97b81c163be5226a7e6895b22252ba86111bfca" }, "hardtime.nvim": { "branch": "main", "commit": "6d7664d5bdfaea44c5f50b29f5239fab7b00c273" }, - "indentmini.nvim": { "branch": "main", "commit": "e0f1e381a3949ea6757365fa33f8f1722d3eae90" }, - "lazy.nvim": { "branch": "main", "commit": "1ea3c4085785f460fb0e46d2fe1ee895f5f9e7c1" }, - "leap.nvim": { "branch": "main", "commit": "aa204264ffaf83650ee676ef68f52e327688b048" }, - "lualine.nvim": { "branch": "master", "commit": "3946f0122255bc377d14a59b27b609fb3ab25768" }, - "mason-lspconfig.nvim": { "branch": "main", "commit": "2304ff65ecc8cb2afc2484de3e2ed9a407edf0b9" }, - "mason.nvim": { "branch": "main", "commit": "ad7146aa61dcaeb54fa900144d768f040090bff0" }, - "mini.colors": { "branch": "main", "commit": "ef76867adda63d6010acdc8732a816c8527d276b" }, - "noice.nvim": { "branch": "main", "commit": "38c702be0d8fea81527ee6a73e1e834e72481193" }, + "indentmini.nvim": { "branch": "main", "commit": "38572ce5a7a064a5deb89d6d861b7c40fc929ab1" }, + "lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" }, + "leap.nvim": { "branch": "main", "commit": "b960d5038c5c505c52e56a54490f9bbb1f0e6ef6" }, + "lualine.nvim": { "branch": "master", "commit": "a905eeebc4e63fdc48b5135d3bf8aea5618fb21c" }, + "mason-lspconfig.nvim": { "branch": "main", "commit": "0a3b42c3e503df87aef6d6513e13148381495c3a" }, + "mason.nvim": { "branch": "main", "commit": "b03fb0f20bc1d43daf558cda981a2be22e73ac42" }, + "mini.colors": { "branch": "main", "commit": "f43cab15ee0458acb13ac33a478b7e14fd4616eb" }, + "noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" }, "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" }, "nvim-cmp": { "branch": "main", "commit": "b5311ab3ed9c846b585c0c15b7559be131ec4be9" }, "nvim-cokeline": { "branch": "main", "commit": "9fbed130683b7b6f73198c09e35ba4b33f547c08" }, - "nvim-colorizer.lua": { "branch": "master", "commit": "51cf7c995ed1eb6642aecf19067ee634fa1b6ba2" }, - "nvim-lspconfig": { "branch": "master", "commit": "ac98db2f9f06a56498ec890a96928774eae412c3" }, + "nvim-colorizer.lua": { "branch": "master", "commit": "5cfe7fffbd01e17b3c1e14af85d5febdef88bd8c" }, + "nvim-lspconfig": { "branch": "master", "commit": "4b7fbaa239c5db6b36f424a4521ca9f1a401be33" }, "nvim-notify": { "branch": "master", "commit": "8701bece920b38ea289b457f902e2ad184131a5d" }, - "nvim-tree.lua": { "branch": "master", "commit": "e397756d2a79d74314ea4cd3efc41300e91c0ff0" }, - "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, - "nvim-web-devicons": { "branch": "master", "commit": "b8221e42cf7287c4dcde81f232f58d7b947c210d" }, - "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" }, - "snacks.nvim": { "branch": "main", "commit": "b59ef39194d00584445ca9f32a90182b383d32de" }, + "nvim-tree.lua": { "branch": "master", "commit": "d277467fc0d1d0e2bca88165a1de6b526f9f6fe8" }, + "nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" }, + "nvim-web-devicons": { "branch": "master", "commit": "c72328a5494b4502947a022fe69c0c47e53b6aa6" }, + "plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" }, + "snacks.nvim": { "branch": "main", "commit": "ad9ede6a9cddf16cedbd31b8932d6dcdee9b716e" }, "statuscol.nvim": { "branch": "main", "commit": "c46172d0911aa5d49ba5f39f4351d1bb7aa289cc" }, - "telescope.nvim": { "branch": "master", "commit": "b4da76be54691e854d3e0e02c36b0245f945c2c7" }, - "trouble.nvim": { "branch": "main", "commit": "c098362fe603d3922095e7db595961e020bdf2d0" }, + "telescope.nvim": { "branch": "master", "commit": "471eebb1037899fd942cc0f52c012f8773505da1" }, + "trouble.nvim": { "branch": "main", "commit": "bd67efe408d4816e25e8491cc5ad4088e708a69a" }, "vim-repeat": { "branch": "master", "commit": "65846025c15494983dafe5e3b46c8f88ab2e9635" } } diff --git a/.config/nvim/lua/diagnostics.lua b/.config/nvim/lua/diagnostics.lua new file mode 100644 index 0000000..db2a556 --- /dev/null +++ b/.config/nvim/lua/diagnostics.lua @@ -0,0 +1,12 @@ +local icons = require "icons" + +vim.diagnostic.config { + signs = { + text = { + [vim.diagnostic.severity.ERROR] = icons.diagnostics.Error, + [vim.diagnostic.severity.WARN] = icons.diagnostics.Warn, + [vim.diagnostic.severity.HINT] = icons.diagnostics.Hint, + [vim.diagnostic.severity.INFO] = icons.diagnostics.Info, + }, + }, +} diff --git a/.config/nvim/lua/plugins/alpha.lua b/.config/nvim/lua/plugins/alpha.lua new file mode 100644 index 0000000..79180eb --- /dev/null +++ b/.config/nvim/lua/plugins/alpha.lua @@ -0,0 +1,7 @@ +return { + "goolord/alpha-nvim", + dependencies = { "nvim-tree/nvim-web-devicons" }, + config = function() + require("alpha").setup(require "dash") + end, +} diff --git a/.config/nvim/lua/plugins/better-escape.lua b/.config/nvim/lua/plugins/better-escape.lua new file mode 100644 index 0000000..493408a --- /dev/null +++ b/.config/nvim/lua/plugins/better-escape.lua @@ -0,0 +1,13 @@ +return { + "max397574/better-escape.nvim", + opts = { + default_mappings = false, + mappings = { + i = { + j = { + j = "", + }, + }, + }, + }, +} diff --git a/.config/nvim/lua/plugins/blink.lua b/.config/nvim/lua/plugins/blink.lua new file mode 100644 index 0000000..a978add --- /dev/null +++ b/.config/nvim/lua/plugins/blink.lua @@ -0,0 +1,43 @@ +return { + "saghen/blink.cmp", + opts = { + keymap = { + preset = "enter", + [""] = false, + [""] = { "accept", "fallback" }, + [""] = { "select_prev" }, + [""] = { "select_next" }, + [""] = { "scroll_documentation_up" }, + [""] = { "scroll_documentation_down" }, + }, + sources = { + default = { "lsp", "path" }, + }, + completion = { + documentation = { + auto_show = true, + auto_show_delay_ms = 100, + window = { + border = "rounded", + scrollbar = false, + }, + }, + -- ghost_text = { + -- enabled = true, + -- }, + menu = { + border = "rounded", + scrollbar = false, + draw = { + columns = { { "label", "label_description", gap = 1 }, { "kind_icon", "kind", gap = 1 } }, + }, + }, + }, + cmdline = { + enabled = false, + }, + }, + opts_extend = { "sources.default" }, + build = "cargo build --release", + -- enabled = false +} diff --git a/.config/nvim/lua/plugins/cokeline.lua b/.config/nvim/lua/plugins/cokeline.lua new file mode 100644 index 0000000..2b70c53 --- /dev/null +++ b/.config/nvim/lua/plugins/cokeline.lua @@ -0,0 +1,220 @@ +local state = require "state" + +return { + "willothy/nvim-cokeline", + lazy = false, + config = function() + local hl = require "cokeline.hlgroups" + local is_picking_focus = require("cokeline.mappings").is_picking_focus + local is_picking_close = require("cokeline.mappings").is_picking_close + + local function has_diagnostics(buffer) + return buffer.diagnostics.errors > 0 + or buffer.diagnostics.warnings > 0 + or buffer.diagnostics.infos > 0 + or buffer.diagnostics.hints > 0 + end + + require("cokeline").setup { + show_if_buffers_are_at_least = 0, + components = { + { + text = function(buffer) + return buffer.is_first and (require("cokeline.sidebar").get_width "left" == 0 and "" or "│") + or "▎" + end, + fg = function() + return hl.get_hl_attr("WinSeparator", "fg") + end, + bg = function() + return hl.get_hl_attr("Normal", "bg") + end, + }, + { + text = function(buffer) + return (is_picking_focus() or is_picking_close()) and " " .. buffer.pick_letter or "" + end, + fg = function() + return hl.get_hl_attr("PrismiteYellow", "fg") + end, + }, + { + text = function(buffer) + return " " .. buffer.devicon.icon + end, + fg = function(buffer) + return buffer.devicon.color + end, + }, + { + text = function(buffer) + return buffer.unique_prefix + end, + fg = function() + return hl.get_hl_attr("Comment", "fg") + end, + italic = true, + }, + { + text = function(buffer) + return buffer.filename + end, + }, + { + text = function(buffer) + return buffer.is_modified and " " or "" + end, + }, + { + text = function(buffer) + return has_diagnostics(buffer) and " [" or "" + end, + fg = function() + return hl.get_hl_attr("WinSeparator", "fg") + end, + }, + { + text = function(buffer) + local errors = buffer.diagnostics.errors + return errors > 0 and errors or "" + end, + fg = function() + return hl.get_hl_attr("DiagnosticError", "fg") + end, + }, + { + text = function(buffer) + local bd = buffer.diagnostics + return (bd.errors > 0 and (bd.warnings > 0 or bd.infos > 0 or bd.hints > 0)) and "|" or "" + end, + fg = function() + return hl.get_hl_attr("WinSeparator", "fg") + end, + }, + { + text = function(buffer) + local warnings = buffer.diagnostics.warnings + return warnings > 0 and warnings or "" + end, + fg = function() + return hl.get_hl_attr("DiagnosticWarn", "fg") + end, + }, + { + text = function(buffer) + local bd = buffer.diagnostics + return (bd.warnings > 0 and (bd.infos > 0 or bd.hints > 0)) and "|" or "" + end, + fg = function() + return hl.get_hl_attr("WinSeparator", "fg") + end, + }, + { + text = function(buffer) + local infos = buffer.diagnostics.infos + return infos > 0 and infos or "" + end, + fg = function() + return hl.get_hl_attr("DiagnosticInfo", "fg") + end, + }, + { + text = function(buffer) + local bd = buffer.diagnostics + return (bd.infos > 0 and bd.hints > 0) and "|" or "" + end, + fg = function() + return hl.get_hl_attr("WinSeparator", "fg") + end, + }, + { + text = function(buffer) + local hints = buffer.diagnostics.hints + return hints > 0 and hints or "" + end, + fg = function() + return hl.get_hl_attr("DiagnosticHint", "fg") + end, + }, + { + text = function(buffer) + return has_diagnostics(buffer) and "]" or "" + end, + fg = function() + return hl.get_hl_attr("WinSeparator", "fg") + end, + }, + { + text = " ", + }, + }, + buffers = { + delete_on_right_click = false, + }, + mappings = { + disable_mouse = true, + }, + sidebar = { + components = { + { + text = function() + local width = require("cokeline.sidebar").get_width "left" + if #state.nvim_tree_root > width then + return state.nvim_tree_root:sub(1, width - 2) .. "…" + else + return state.nvim_tree_root + end + end, + bg = function() + return hl.get_hl_attr("TabLineSel", "bg") + end, + }, + { + text = function() + return string.rep( + " ", + math.max(0, require("cokeline.sidebar").get_width "left" - #state.nvim_tree_root) + ) + end, + bg = function() + return hl.get_hl_attr("TabLineSel", "bg") + end, + }, + }, + }, + } + end, + dependencies = { "nvim-tree/nvim-web-devicons", "nvim-lua/plenary.nvim" }, + keys = { + { + "bp", + function() + require("cokeline.mappings").pick "focus" + end, + desc = "Pick a buffer to focus", + }, + { + "bd", + function() + require("cokeline.mappings").pick "close" + end, + desc = "Pick a buffer to close", + }, + { + "p", + function() + require("cokeline.mappings").by_step("switch", -1) + end, + desc = "Move current buffer left", + silent = true, + }, + { + "n", + function() + require("cokeline.mappings").by_step("switch", 1) + end, + desc = "Move current buffer right", + silent = true, + }, + }, +} diff --git a/.config/nvim/lua/plugins/color-converter.lua b/.config/nvim/lua/plugins/color-converter.lua new file mode 100644 index 0000000..dac0cb9 --- /dev/null +++ b/.config/nvim/lua/plugins/color-converter.lua @@ -0,0 +1,11 @@ +return { + "color_converter", + dev = true, + dependencies = { + { "echasnovski/mini.colors", version = "*" }, + "MunifTanjim/nui.nvim", + }, + -- lazy = false, + cmd = "ColorConverter", + config = true, +} diff --git a/.config/nvim/lua/plugins/d2.lua b/.config/nvim/lua/plugins/d2.lua new file mode 100644 index 0000000..179a432 --- /dev/null +++ b/.config/nvim/lua/plugins/d2.lua @@ -0,0 +1,9 @@ +return { + "terrastruct/d2-vim", + ft = { "d2" }, + config = function() + vim.keymap.del("v", "d2") + vim.keymap.del("v", "rd2") + vim.keymap.del("n", "yd2") + end +} diff --git a/.config/nvim/lua/plugins/git-blame.lua b/.config/nvim/lua/plugins/git-blame.lua new file mode 100644 index 0000000..22f9aed --- /dev/null +++ b/.config/nvim/lua/plugins/git-blame.lua @@ -0,0 +1,7 @@ +return { + "f-person/git-blame.nvim", + event = "VeryLazy", + opts = { + date_format = "%r", + }, +} diff --git a/.config/nvim/lua/plugins/graphviz.lua b/.config/nvim/lua/plugins/graphviz.lua new file mode 100644 index 0000000..f0402bc --- /dev/null +++ b/.config/nvim/lua/plugins/graphviz.lua @@ -0,0 +1,5 @@ +return { + "izocha/graphviz.nvim", + ft = { "dot" }, + opts = {} +} diff --git a/.config/nvim/lua/plugins/hardtime.lua b/.config/nvim/lua/plugins/hardtime.lua new file mode 100644 index 0000000..f47549e --- /dev/null +++ b/.config/nvim/lua/plugins/hardtime.lua @@ -0,0 +1,5 @@ +return { + "m4xshen/hardtime.nvim", + config = true, + enabled = false, +} diff --git a/.config/nvim/lua/plugins/indentmini.lua b/.config/nvim/lua/plugins/indentmini.lua new file mode 100644 index 0000000..793f5ea --- /dev/null +++ b/.config/nvim/lua/plugins/indentmini.lua @@ -0,0 +1,7 @@ +return { + "nvimdev/indentmini.nvim", + config = true, + opts = { + char = "▏", + }, +} diff --git a/.config/nvim/lua/plugins/leap.lua b/.config/nvim/lua/plugins/leap.lua new file mode 100644 index 0000000..891d99f --- /dev/null +++ b/.config/nvim/lua/plugins/leap.lua @@ -0,0 +1,14 @@ +return { + "https://codeberg.org/andyg/leap.nvim", + config = function() + local leap = require "leap" + -- leap.add_default_mappings() + vim.keymap.set({'n', 'x', 'o'}, 's', '(leap-forward)') + vim.keymap.set({'n', 'x', 'o'}, 'S', '(leap-backward)') + -- vim.keymap.set('n', 'gs', '(leap-from-window)') + leap.opts.highlight_unlabeled_phase_one_targets = true + end, + dependencies = { + "tpope/vim-repeat", + }, +} diff --git a/.config/nvim/lua/plugins/lsp.lua b/.config/nvim/lua/plugins/lsp.lua index a438f3a..05d55c5 100644 --- a/.config/nvim/lua/plugins/lsp.lua +++ b/.config/nvim/lua/plugins/lsp.lua @@ -37,6 +37,7 @@ return { }, rust_analyzer = { settings = { completion = { fullFunctionSignatures = true } } }, ts_ls = {}, + denols = {} }, setup = {}, inlay_hints = { diff --git a/.config/nvim/lua/plugins/lualine.lua b/.config/nvim/lua/plugins/lualine.lua new file mode 100644 index 0000000..085593c --- /dev/null +++ b/.config/nvim/lua/plugins/lualine.lua @@ -0,0 +1,223 @@ +local delegate = require "utils.delegate" + +local leap_active = false +vim.api.nvim_create_autocmd("User", { + pattern = "LeapEnter", + callback = function() + leap_active = true + end, +}) + +vim.api.nvim_create_autocmd("User", { + pattern = "LeapLeave", + callback = function() + leap_active = false + end, +}) + +return { + "nvim-lualine/lualine.nvim", + event = "VeryLazy", + init = function() + local diagnostics = require "lualine.components.diagnostics" + + diagnostics.init = delegate(function(env, self, options) + -- Run super() + env.M.super.init(self, options) + -- Apply default options + env.modules.default_config.apply_default_colors(self.options) + self.options = vim.tbl_deep_extend("keep", self.options or {}, env.modules.default_config.options) + -- Apply default symbols + self.symbols = vim.tbl_extend( + "keep", + self.options.symbols or {}, + self.options.icons_enabled ~= false and env.modules.default_config.symbols.icons + or env.modules.default_config.symbols.no_icons + ) + -- Initialize highlight groups + if self.options.colored then + self.highlight_groups = { + error = self:create_hl(self.options.diagnostics_color.error, "error"), + warn = self:create_hl(self.options.diagnostics_color.warn, "warn"), + info = self:create_hl(self.options.diagnostics_color.info, "info"), + hint = self:create_hl(self.options.diagnostics_color.hint, "hint"), + } + self.spacing_hl = self:create_hl("WinSeparator", "spacing") + end + + -- Initialize variable to store last update so we can use it in insert + -- mode for no update_in_insert + self.last_diagnostics_count = {} + + -- Error out no source + if #self.options.sources < 1 then + env.modules.utils_notices.add_notice "### diagnostics.sources\n\nno sources for diagnostics configured.\nPlease specify which diagnostics source you want lualine to use with `sources` option.\n" + end + end, diagnostics.init) + + diagnostics.update_status = delegate(function(env, self) + local bufnr = vim.api.nvim_get_current_buf() + local diagnostics_count + local result = {} + if self.options.update_in_insert or vim.api.nvim_get_mode().mode:sub(1, 1) ~= "i" then + local error_count, warning_count, info_count, hint_count = 0, 0, 0, 0 + local diagnostic_data = env.modules.sources.get_diagnostics(self.options.sources) + -- sum all the counts + for _, data in pairs(diagnostic_data) do + error_count = error_count + data.error + warning_count = warning_count + data.warn + info_count = info_count + data.info + hint_count = hint_count + data.hint + end + diagnostics_count = { + error = error_count, + warn = warning_count, + info = info_count, + hint = hint_count, + } + -- Save count for insert mode + self.last_diagnostics_count[bufnr] = diagnostics_count + else -- Use cached count in insert mode with update_in_insert disabled + diagnostics_count = self.last_diagnostics_count[bufnr] or { error = 0, warn = 0, info = 0, hint = 0 } + end + + local always_visible = false + if type(self.options.always_visible) == "boolean" then + always_visible = self.options.always_visible + elseif type(self.options.always_visible) == "function" then + always_visible = self.options.always_visible() + end + + -- format the counts with symbols and highlights + if self.options.colored then + local colors, bgs = {}, {} + for name, hl in pairs(self.highlight_groups) do + colors[name] = self:format_hl(hl) + bgs[name] = env.modules.utils.extract_highlight_colors(colors[name]:match "%%#(.-)#", "bg") + end + local previous_section, padding + for _, section in ipairs(self.options.sections) do + if diagnostics_count[section] ~= nil and (always_visible or diagnostics_count[section] > 0) then + padding = previous_section and (bgs[previous_section] ~= bgs[section]) and " " or "" + previous_section = section + table.insert( + result, + colors[section] .. padding .. self.symbols[section] .. diagnostics_count[section] + ) + end + end + else + for _, section in ipairs(self.options.sections) do + if diagnostics_count[section] ~= nil and (always_visible or diagnostics_count[section] > 0) then + table.insert(result, self.symbols[section] .. diagnostics_count[section]) + end + end + end + return table.concat(result, self:format_hl(self.spacing_hl) .. "|") + end, diagnostics.update_status) + end, + opts = { + options = { + section_separators = { left = "", right = "" }, + component_separators = { left = "", right = "" }, + fmt = function(str) + return vim.trim(str) + end, + globalstatus = true, + disabled_filetypes = { "NvimTree" }, + }, + sections = { + lualine_a = { + "mode", + { + function() + return "[L]" + end, + cond = function() + return leap_active + end, + }, + { + function() + local reg = vim.fn.reg_recording() + if reg == "" then + return "" + end -- not recording + return "[@" .. reg .. "]" + end, + }, + }, + lualine_b = { "filename" }, + lualine_c = { + "branch", + { + function() + return "[" + end, + cond = function() + local count = vim.diagnostic.count(0) + return type( + count[vim.diagnostic.severity.ERROR] + or count[vim.diagnostic.severity.WARN] + or count[vim.diagnostic.severity.INFO] + or count[vim.diagnostic.severity.HINT] + ) ~= "nil" + end, + padding = { left = 1, right = 0 }, + color = "WinSeparator", + }, + { + "diagnostics", + symbols = { + error = "", + warn = "", + info = "", + hint = "", + }, + padding = 0, + }, + { + function() + return "]" + end, + cond = function() + local count = vim.diagnostic.count(0) + return type( + count[vim.diagnostic.severity.ERROR] + or count[vim.diagnostic.severity.WARN] + or count[vim.diagnostic.severity.INFO] + or count[vim.diagnostic.severity.HINT] + ) ~= "nil" + end, + padding = { left = 0, right = 1 }, + color = "WinSeparator", + }, + }, + lualine_x = { + { + function() + local noice_loaded, noice = pcall(require, "noice") + if noice_loaded then + return noice.api.status.command.get() + else + return "" + end + end, + cond = function() + local noice_loaded, noice = pcall(require, "noice") + if noice_loaded then + return noice.api.status.command.has() + else + return false + end + end, + }, + }, + lualine_y = { "filetype" }, + lualine_z = { "searchcount", "progress", "location" }, + }, + }, + dependencies = { + "nvim-tree/nvim-web-devicons", + }, +} diff --git a/.config/nvim/lua/plugins/mini/colors.lua b/.config/nvim/lua/plugins/mini/colors.lua new file mode 100644 index 0000000..ac6400a --- /dev/null +++ b/.config/nvim/lua/plugins/mini/colors.lua @@ -0,0 +1 @@ +return { "echasnovski/mini.colors", version = "*" } diff --git a/.config/nvim/lua/plugins/mini/comment.lua b/.config/nvim/lua/plugins/mini/comment.lua new file mode 100644 index 0000000..d0c7f98 --- /dev/null +++ b/.config/nvim/lua/plugins/mini/comment.lua @@ -0,0 +1 @@ +return { "echasnovski/mini.comment", config = true } diff --git a/.config/nvim/lua/plugins/mini/completions.lua b/.config/nvim/lua/plugins/mini/completions.lua new file mode 100644 index 0000000..a564707 --- /dev/null +++ b/.config/nvim/lua/plugins/mini/completions.lua @@ -0,0 +1 @@ +return {} diff --git a/.config/nvim/lua/plugins/mini/surround.lua b/.config/nvim/lua/plugins/mini/surround.lua new file mode 100644 index 0000000..7a05e44 --- /dev/null +++ b/.config/nvim/lua/plugins/mini/surround.lua @@ -0,0 +1,15 @@ +return { + "echasnovski/mini.surround", + version = false, + opts = { + mappings = { + add = "\\a", -- Add surrounding in Normal and Visual modes + delete = "\\d", -- Delete surrounding + find = "\\f", -- Find surrounding (to the right) + find_left = "\\F", -- Find surrounding (to the left) + highlight = "\\h", -- Highlight surrounding + replace = "\\r", -- Replace surrounding + update_n_lines = "\\n", -- Update `n_lines` + }, + }, +} diff --git a/.config/nvim/lua/plugins/noice.lua b/.config/nvim/lua/plugins/noice.lua new file mode 100644 index 0000000..3c909a9 --- /dev/null +++ b/.config/nvim/lua/plugins/noice.lua @@ -0,0 +1,90 @@ +return { + "folke/noice.nvim", + event = "VeryLazy", + opts = { + cmdline = { + format = { + cmdline = { icon = ":" }, + }, + }, + lsp = { + override = { + ["vim.lsp.util.convert_input_to_markdown_lines"] = true, + ["vim.lsp.util.stylize_markdown"] = true, + ["cmp.entry.get_documentation"] = true, + }, + }, + presets = { + command_palette = true, + long_message_to_split = true, + inc_rename = true, + lsp_doc_border = true, + }, + messages = { + view_search = false, + }, + views = { + confirm_warn = { + backend = "popup", + relative = "editor", + focusable = false, + align = "center", + enter = false, + zindex = 210, + format = { "{confirm}" }, + position = { + row = 3, + col = "50%", + }, + size = "auto", + border = { + style = "rounded", + padding = { 0, 1 }, + text = { + top = " Confirm ", + }, + }, + win_options = { + winhighlight = { + Normal = "NoiceConfirm", + FloatBorder = "PrismiteOrange", + }, + winbar = "", + foldenable = false, + }, + }, + }, + routes = { + { + filter = { + event = "msg_show", + kind = "confirm", + }, + view = "confirm_warn", + }, + { + filter = { + event = "msg_show", + any = { + { find = "Already at newest change" }, + { find = "Already at oldest change" }, + { find = "E486: Pattern not found: .+" }, -- failed search + { find = "%d+ .+ line.?; before #%d+" }, -- undo + { find = "%d+ change; after #%d+" }, -- redo + { find = "%[nvim-treesitter" }, -- treesitter messages + { find = "No treesitter parser found for filetype: " }, -- Treesitter parser not installed + { find = "%d+ lines yanked" }, -- y + { find = "%d+ more lines" }, -- p + { find = "%d+ fewer lines" }, -- d + }, + -- kind = "", + }, + view = "mini", + }, + }, + }, + dependencies = { + "MunifTanjim/nui.nvim", + "rcarriga/nvim-notify", + }, +} diff --git a/.config/nvim/lua/plugins/nui.lua b/.config/nvim/lua/plugins/nui.lua new file mode 100644 index 0000000..f85bdd3 --- /dev/null +++ b/.config/nvim/lua/plugins/nui.lua @@ -0,0 +1,3 @@ +return { + "MunifTanjim/nui.nvim", +} diff --git a/.config/nvim/lua/plugins/nvim-cmp.lua b/.config/nvim/lua/plugins/nvim-cmp.lua new file mode 100644 index 0000000..d95d0bc --- /dev/null +++ b/.config/nvim/lua/plugins/nvim-cmp.lua @@ -0,0 +1,64 @@ +return { + "hrsh7th/nvim-cmp", + opts = function() + vim.api.nvim_set_hl(0, "CmpGhostText", { link = "Comment", default = true }) + local cmp = require "cmp" + local defaults = require "cmp.config.default"() + return { + enabled = function() + local context = require "cmp.config.context" + if vim.api.nvim_get_mode().mode == "c" then + return true + else + return not context.in_treesitter_capture "comment" and not context.in_syntax_group "Comment" + end + end, + + completion = { + completeopt = "menu,menuone,noinsert", + }, + snippet = { + expand = function(args) + require("luasnip").lsp_expand(args.body) + end, + }, + window = { + completion = cmp.config.window.bordered(), + documentation = cmp.config.window.bordered(), + }, + mapping = cmp.mapping.preset.insert { + [""] = cmp.mapping.select_next_item { behavior = cmp.SelectBehavior.Insert }, + [""] = cmp.mapping.select_prev_item { behavior = cmp.SelectBehavior.Insert }, + [""] = cmp.mapping.scroll_docs(-4), + [""] = cmp.mapping.scroll_docs(4), + [""] = cmp.mapping.abort(), + [""] = cmp.mapping.confirm { select = false }, + }, + sources = cmp.config.sources { + { name = "nvim_lsp" }, + { name = "path" }, + }, + formatting = { + format = function(_, item) + local icons = require("icons").kinds + if icons[item.kind] then + item.kind = icons[item.kind] .. item.kind + end + return item + end, + }, + experimental = { + ghost_text = { + hl_group = "CmpGhostText", + }, + }, + sorting = defaults.sorting, + } + end, + dependencies = { + { "L3MON4D3/LuaSnip", build = "make install_jsregexp" }, + "hrsh7th/cmp-path", + "saadparwaiz1/cmp_luasnip", + }, + enabled = false, +} diff --git a/.config/nvim/lua/plugins/nvim-colorizer.lua b/.config/nvim/lua/plugins/nvim-colorizer.lua new file mode 100644 index 0000000..e026ae5 --- /dev/null +++ b/.config/nvim/lua/plugins/nvim-colorizer.lua @@ -0,0 +1,12 @@ +return { + "catgoose/nvim-colorizer.lua", + event = "BufReadPre", + opts = { + user_default_options = { + names = false, + RRGGBBAA = true, + css_fn = true, + xterm = true, + }, + }, +} diff --git a/.config/nvim/lua/plugins/nvim-tree.lua b/.config/nvim/lua/plugins/nvim-tree.lua new file mode 100644 index 0000000..8afa105 --- /dev/null +++ b/.config/nvim/lua/plugins/nvim-tree.lua @@ -0,0 +1,131 @@ +local state = require "state" + +local function natural_cmp(_left, _right) + local left = _left.name:lower() + local right = _right.name:lower() + + if left == right then + return false + end + + local is_l_dir = _left.type ~= "directory" + local is_r_dir = _right.type ~= "directory" + if ((is_l_dir or is_r_dir) and not (is_l_dir and is_r_dir)) then + return is_r_dir + end + + for i = 1, math.max(string.len(left), string.len(right)), 1 do + local l = string.sub(left, i, -1) + local r = string.sub(right, i, -1) + + if type(tonumber(string.sub(l, 1, 1))) == "number" and type(tonumber(string.sub(r, 1, 1))) == "number" then + local l_number = tonumber(string.match(l, "^[0-9]+")) + local r_number = tonumber(string.match(r, "^[0-9]+")) + + if l_number ~= r_number then + return l_number < r_number + end + elseif string.sub(l, 1, 1) ~= string.sub(r, 1, 1) then + return l < r + end + end +end + +return { + "nvim-tree/nvim-tree.lua", + opts = { + sort_by = function (nodes) + table.sort(nodes, natural_cmp) + end, + view = { + width = 30, + }, + renderer = { + root_folder_label = function() + return ".." + end, + indent_markers = { + enable = true, + }, + icons = { + show = { + folder_arrow = false, + }, + glyphs = { + git = { + unstaged = "-", + staged = "+", + unmerged = "", + renamed = "󰏪", + untracked = "", + deleted = "󰆴", + ignored = "", + }, + }, + }, + }, + filters = { + git_ignored = false, + }, + }, + config = function(_, opts) + local api = require "nvim-tree.api" + local Event = api.events.Event + + api.events.subscribe(Event.Ready, function() + state.nvim_tree_root = api.tree.get_nodes().absolute_path + end) + + api.events.subscribe(Event.TreeRendered, function() + state.nvim_tree_root = api.tree.get_nodes().absolute_path + end) + + require("nvim-tree").setup(vim.tbl_extend("force", opts, { + -- on_attach = function () + -- local function opts(desc) + -- return { desc = "nvim-tree: " .. desc, buffer = bufnr, noremap = true, silent = true, nowait = true } + -- end + -- + -- local function edit_or_open() + -- local node = api.tree.get_node_under_cursor() + -- + -- if node.nodes ~= nil then + -- -- expand or collapse folder + -- api.node.open.edit() + -- else + -- -- open file + -- api.node.open.edit() + -- -- Close the tree if file was opened + -- api.tree.close() + -- end + -- end + -- + -- -- open as vsplit on current node + -- local function vsplit_preview() + -- local node = api.tree.get_node_under_cursor() + -- + -- if node.nodes ~= nil then + -- -- expand or collapse folder + -- api.node.open.edit() + -- else + -- -- open file as vsplit + -- api.node.open.vertical() + -- end + -- + -- -- Finally refocus on tree if it was lost + -- api.tree.focus() + -- end + -- + -- vim.keymap.set("n", "l", edit_or_open, opts("Edit Or Open")) + -- vim.keymap.set("n", "h", api.tree.close, opts("Close")) + -- end + -- + })) + end, + dependencies = { + "nvim-tree/nvim-web-devicons", + }, + keys = { + { "e", "NvimTreeToggle", desc = "Toggle nvim-tree" }, + }, +} diff --git a/.config/nvim/lua/plugins/nvim-treesitter.lua b/.config/nvim/lua/plugins/nvim-treesitter.lua new file mode 100644 index 0000000..2f8e3cb --- /dev/null +++ b/.config/nvim/lua/plugins/nvim-treesitter.lua @@ -0,0 +1,122 @@ +return { + "nvim-treesitter/nvim-treesitter", + branch = "main", + build = ":TSUpdate", + init = function() + vim.o.foldmethod = "expr" + vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()" + + end, + config = function(_, opts) + vim.api.nvim_create_autocmd('User', { + pattern = 'TSUpdate', + callback = function() + vim.tbl_extend("force", require('nvim-treesitter.parsers'), opts.custom_parsers or {}) + end + }) + + local ts = require('nvim-treesitter') + + local already_installed = ts.get_installed() + local parsers_to_install = vim.iter(opts.parsers or {}) + :filter(function(parser) return not vim.tbl_contains(already_installed, parser) end) + :totable() + + ts.install(parsers_to_install) + + -- Auto-install and start parsers for any buffer + vim.api.nvim_create_autocmd({ "BufRead", "FileType" }, { + desc = "Enable Treesitter", + callback = function(event) + local bufnr = event.buf + local filetype = vim.api.nvim_get_option_value("filetype", { buf = bufnr }) + + -- Skip if no filetype + if filetype == "" then + return + end + + -- Get parser name based on filetype + local parser_name = vim.treesitter.language.get_lang(filetype) + if not parser_name then + vim.notify(vim.inspect("No treesitter parser found for filetype: " .. filetype), vim.log.levels.WARN) + return + end + + -- Try to get existing parser + local parser_configs = require("nvim-treesitter.parsers") + if not parser_configs[parser_name] then + return -- Parser not available, skip silently + end + + local parser_exists = pcall(vim.treesitter.get_parser, bufnr, parser_name) + + local function init_treesitter() + -- vim.notify(vim.inspect("Starting treesitter parser " .. parser_name .. " for filetype: " .. filetype), vim.log.levels.WARN) + vim.treesitter.start(bufnr, parser_name) + -- Use regex based syntax-highlighting as fallback as some plugins might need it + -- vim.bo[bufnr].syntax = "ON" + -- vim.wo.foldtext = "v:lua.vim.treesitter.foldtext()" + -- Use treesitter for indentation + vim.bo[bufnr].indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()" + end + + if not parser_exists then + -- check if parser is already installed + if vim.tbl_contains(already_installed, parser_name) then + vim.notify("Parser for " .. parser_name .. " already installed.", vim.log.levels.INFO) + init_treesitter() + -- else + -- -- If not installed, install parser synchronously + -- vim.notify("Installing parser for " .. parser_name, vim.log.levels.INFO) + -- treesitter.install({ parser_name }):await(init_treesitter) + end + else + init_treesitter() + end + + end, + }) + end, + opts = { + parsers = { + "lua", + "c", + "vim", + "vimdoc", + "query", + "rust", + "fish", + "json", + "javascript", + "latex", + "markdown", + "markdown_inline", + "zig", + "typescript", + "toml", + "svelte", + "comment", + "html", + "typst", + "ron", + "smali", + "java", + "kotlin", + "styled", + "nix", + "gitignore", + "meson" + -- "d2" + }, + custom_parsers = { + d2 = { + install_info = { + url = 'https://github.com/ravsii/tree-sitter-d2', + queries = 'queries', + }, + } + } + }, + lazy = false +} diff --git a/.config/nvim/lua/plugins/snacks.lua b/.config/nvim/lua/plugins/snacks.lua new file mode 100644 index 0000000..7cd44c8 --- /dev/null +++ b/.config/nvim/lua/plugins/snacks.lua @@ -0,0 +1,9 @@ +return { + "folke/snacks.nvim", + priority = 1000, + lazy = false, + ---@type snacks.Config + opts = { + input = { enabled = true }, + }, +} diff --git a/.config/nvim/lua/plugins/statuscol.lua b/.config/nvim/lua/plugins/statuscol.lua new file mode 100644 index 0000000..70a0947 --- /dev/null +++ b/.config/nvim/lua/plugins/statuscol.lua @@ -0,0 +1,20 @@ +return { + "luukvbaal/statuscol.nvim", + opts = function() + local builtin = require "statuscol.builtin" + return { + setopt = true, + -- override the default list of segments with: + -- number-less fold indicator, then signs, then line number & separator + segments = { + { text = { builtin.foldfunc }, click = "v:lua.ScFa" }, + { text = { "%s" }, click = "v:lua.ScSa" }, + { + text = { builtin.lnumfunc, " " }, + condition = { true, builtin.not_empty }, + click = "v:lua.ScLa", + }, + }, + } + end, +} diff --git a/.config/nvim/lua/plugins/telescope.lua b/.config/nvim/lua/plugins/telescope.lua new file mode 100644 index 0000000..7633996 --- /dev/null +++ b/.config/nvim/lua/plugins/telescope.lua @@ -0,0 +1,65 @@ +local state = require "state" + +return { + "nvim-telescope/telescope.nvim", + opts = function(_, opts) + local actions = require "telescope.actions" + local actions_mt = require "telescope.actions.mt" + + local open_quickfix = actions_mt.transform("open_quickfix", actions_mt.create(), nil, { + action = function() + require("trouble").open "qflist" + end, + }) + + return vim.tbl_deep_extend("force", opts, { + defaults = { + mappings = { + n = { + [""] = actions.send_to_qflist + open_quickfix, + [""] = "preview_scrolling_left", + [""] = "preview_scrolling_up", + [""] = "preview_scrolling_down", + [""] = "preview_scrolling_right", + }, + i = { + [""] = actions.send_to_qflist + open_quickfix, + [""] = "preview_scrolling_left", + [""] = "preview_scrolling_up", + [""] = "preview_scrolling_down", + [""] = "preview_scrolling_right", + }, + }, + }, + }) + end, + dependencies = { + "nvim-lua/plenary.nvim", + }, + keys = function() + local builtin = require "telescope.builtin" + return { + { "ff", builtin.find_files, desc = "Telescope: find files" }, + { + "fF", + function() + builtin.find_files(state.nvim_tree_root and { + cwd = state.nvim_tree_root, + }) + end, + desc = "Telescope: find files in nvim-tree root", + }, + { "fg", builtin.live_grep, desc = "Telescope: live grep" }, + { + "fG", + function() + builtin.live_grep(state.nvim_tree_root and { + cwd = state.nvim_tree_root, + }) + end, + desc = "Telescope: live grep in nvim-tree root", + }, + } + end, + cmd = { "Telescope" }, +} diff --git a/.config/nvim/lua/plugins/trouble.lua b/.config/nvim/lua/plugins/trouble.lua new file mode 100644 index 0000000..cc40587 --- /dev/null +++ b/.config/nvim/lua/plugins/trouble.lua @@ -0,0 +1,109 @@ +local icons = require "icons" + +return { + "folke/trouble.nvim", + opts = function() + local Util = require "trouble.util" + return { + modes = { + symbols = { + win = { + position = "right", + size = 0.4, + }, + format = "{symbol_kind} {symbol_pos:Comment} {symbol.name} {text:Comment}", + preview = { + type = "float", + relative = "editor", + border = "rounded", + title = "Preview", + title_pos = "center", + position = { 1, 0 }, + size = { width = 0.3, height = 0.3 }, + zindex = 200, + }, + }, + qflist = { + win = { + position = "right", + size = 0.4, + }, + format = "{severity_icon|item.type:DiagnosticSignWarn} {text:ts} {pos:Comment}", + preview = { + type = "float", + relative = "editor", + border = "rounded", + title = "Preview", + title_pos = "center", + position = { 1, 0 }, + size = { width = 0.3, height = 0.3 }, + zindex = 200, + }, + }, + diagnostics = { + win = { + position = "down", + size = 0.4, + }, + preview = { + type = "split", + relative = "win", + position = "right", + size = 0.5, + }, + }, + }, + icons = { + kinds = icons.kinds, + }, + formatters = { + symbol_kind = function(ctx) + return { text = "[" .. ctx.item.kind .. "]", hl = "Type" } + end, + symbol_pos = function(ctx) + return "@ " .. ctx.item.pos[1] .. ":" .. (ctx.item.pos[2] + 1) + end, + severity_icon = function(ctx) + local severity = ctx.item.severity or vim.diagnostic.severity.ERROR + if not vim.diagnostic.severity[severity] then + return + end + if type(severity) == "string" then + severity = vim.diagnostic.severity[severity:upper()] or vim.diagnostic.severity.ERROR + end + local name = Util.camel(vim.diagnostic.severity[severity]:lower()) + local sign = vim.fn.sign_getdefined("DiagnosticSign" .. name)[1] + local config = vim.diagnostic.config() or {} + if config.signs == nil or type(config.signs) == "boolean" then + return { + text = sign and "[" .. sign.text .. "]" or name:sub(1, 1), + hl = "DiagnosticSign" .. name, + } + end + local signs = config.signs or {} + if type(signs) == "function" then + signs = signs(0, 0) --[[@as vim.diagnostic.Opts.Signs]] + end + return { + text = type(signs) == "table" and signs.text and "[" .. signs.text[severity] .. "]" + or sign and sign.text + or name:sub(1, 1), + hl = "DiagnosticSign" .. name, + } + end, + }, + signs = true, + } + end, + dependencies = { "nvim-tree/nvim-web-devicons" }, + keys = { + { + "pe", + "Trouble diagnostics toggle win.position=bottom", + desc = "Toggle diagnstostics panel", + }, + { "ps", "Trouble symbols toggle", desc = "Toggle symbols panel" }, + { "pq", "Trouble qflist toggle", desc = "Toggle quickfix panel" }, + }, + cmd = { "Trouble" }, +} diff --git a/.config/nvim/lua/state.lua b/.config/nvim/lua/state.lua new file mode 100644 index 0000000..6a723b7 --- /dev/null +++ b/.config/nvim/lua/state.lua @@ -0,0 +1,2 @@ +-- this is basically just a glorified global table to pass values between plugins (but not global) +return {} diff --git a/.config/nvim/lua/utils/delegate.lua b/.config/nvim/lua/utils/delegate.lua new file mode 100644 index 0000000..3ea6d75 --- /dev/null +++ b/.config/nvim/lua/utils/delegate.lua @@ -0,0 +1,22 @@ +local M = {} + +--- Capture `fn`'s upvalues and pass to `delegate` +---@param delegate fun(env: table, _: ...): ... +---@param fn function +---@return fun(...): ... +function M.new(delegate, fn) + local upvalues = {} + for i = 1, debug.getinfo(fn, "u").nups do + local name, value = debug.getupvalue(fn, i) + upvalues[name] = value + end + return function(...) + return delegate(upvalues, ...) + end +end + +return setmetatable(M, { + __call = function(_, ...) + return M.new(...) + end, +}) -- cgit v1.2.3