├── stylua.toml ├── custom ├── theme │ ├── icons │ │ ├── ac.png │ │ ├── cpu.png │ │ ├── fwd.png │ │ ├── hdd.png │ │ ├── mail.png │ │ ├── mem.png │ │ ├── net.png │ │ ├── next.png │ │ ├── note.png │ │ ├── pause.png │ │ ├── play.png │ │ ├── prev.png │ │ ├── rwd.png │ │ ├── stop.png │ │ ├── task.png │ │ ├── temp.png │ │ ├── tile.png │ │ ├── awesome.png │ │ ├── battery.png │ │ ├── mail_on.png │ │ ├── note_on.png │ │ ├── pacman.png │ │ ├── phones.png │ │ ├── submenu.png │ │ ├── tiletop.png │ │ ├── brightness.png │ │ ├── floating.png │ │ ├── scissors.png │ │ ├── square_sel.png │ │ ├── tilebottom.png │ │ ├── tileleft.png │ │ ├── battery_low.png │ │ ├── square_unsel.png │ │ ├── battery_empty.png │ │ └── titlebar │ │ │ ├── close_focus.png │ │ │ ├── close_normal.png │ │ │ ├── ontop_focus_active.png │ │ │ ├── floating_focus_active.png │ │ │ ├── ontop_focus_inactive.png │ │ │ ├── ontop_normal_active.png │ │ │ ├── ontop_normal_inactive.png │ │ │ ├── sticky_focus_active.png │ │ │ ├── sticky_focus_inactive.png │ │ │ ├── sticky_normal_active.png │ │ │ ├── floating_focus_inactive.png │ │ │ ├── floating_normal_active.png │ │ │ ├── maximized_focus_active.png │ │ │ ├── maximized_normal_active.png │ │ │ ├── sticky_normal_inactive.png │ │ │ ├── floating_normal_inactive.png │ │ │ ├── maximized_focus_inactive.png │ │ │ └── maximized_normal_inactive.png │ ├── constants.lua │ ├── bottombar.lua │ └── sidebar.lua ├── plugins │ └── yt-music.lua ├── file.lua ├── volume.lua ├── setup.lua ├── widgets │ ├── mem.lua │ └── menu.lua ├── obsidian.lua ├── tbl.lua ├── log.lua ├── types.lua ├── inspect.lua └── theme.lua └── rc.lua /stylua.toml: -------------------------------------------------------------------------------- 1 | indent_type = "Spaces" 2 | indent_width = 2 3 | no_call_parentheses = true 4 | -------------------------------------------------------------------------------- /custom/theme/icons/ac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/ac.png -------------------------------------------------------------------------------- /custom/theme/icons/cpu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/cpu.png -------------------------------------------------------------------------------- /custom/theme/icons/fwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/fwd.png -------------------------------------------------------------------------------- /custom/theme/icons/hdd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/hdd.png -------------------------------------------------------------------------------- /custom/theme/icons/mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/mail.png -------------------------------------------------------------------------------- /custom/theme/icons/mem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/mem.png -------------------------------------------------------------------------------- /custom/theme/icons/net.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/net.png -------------------------------------------------------------------------------- /custom/theme/icons/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/next.png -------------------------------------------------------------------------------- /custom/theme/icons/note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/note.png -------------------------------------------------------------------------------- /custom/theme/icons/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/pause.png -------------------------------------------------------------------------------- /custom/theme/icons/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/play.png -------------------------------------------------------------------------------- /custom/theme/icons/prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/prev.png -------------------------------------------------------------------------------- /custom/theme/icons/rwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/rwd.png -------------------------------------------------------------------------------- /custom/theme/icons/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/stop.png -------------------------------------------------------------------------------- /custom/theme/icons/task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/task.png -------------------------------------------------------------------------------- /custom/theme/icons/temp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/temp.png -------------------------------------------------------------------------------- /custom/theme/icons/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/tile.png -------------------------------------------------------------------------------- /custom/theme/icons/awesome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/awesome.png -------------------------------------------------------------------------------- /custom/theme/icons/battery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/battery.png -------------------------------------------------------------------------------- /custom/theme/icons/mail_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/mail_on.png -------------------------------------------------------------------------------- /custom/theme/icons/note_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/note_on.png -------------------------------------------------------------------------------- /custom/theme/icons/pacman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/pacman.png -------------------------------------------------------------------------------- /custom/theme/icons/phones.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/phones.png -------------------------------------------------------------------------------- /custom/theme/icons/submenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/submenu.png -------------------------------------------------------------------------------- /custom/theme/icons/tiletop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/tiletop.png -------------------------------------------------------------------------------- /custom/theme/icons/brightness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/brightness.png -------------------------------------------------------------------------------- /custom/theme/icons/floating.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/floating.png -------------------------------------------------------------------------------- /custom/theme/icons/scissors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/scissors.png -------------------------------------------------------------------------------- /custom/theme/icons/square_sel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/square_sel.png -------------------------------------------------------------------------------- /custom/theme/icons/tilebottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/tilebottom.png -------------------------------------------------------------------------------- /custom/theme/icons/tileleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/tileleft.png -------------------------------------------------------------------------------- /custom/theme/icons/battery_low.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/battery_low.png -------------------------------------------------------------------------------- /custom/theme/icons/square_unsel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/square_unsel.png -------------------------------------------------------------------------------- /custom/theme/icons/battery_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/battery_empty.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/close_focus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/close_focus.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/close_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/close_normal.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/ontop_focus_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/ontop_focus_active.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/floating_focus_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/floating_focus_active.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/ontop_focus_inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/ontop_focus_inactive.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/ontop_normal_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/ontop_normal_active.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/ontop_normal_inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/ontop_normal_inactive.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/sticky_focus_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/sticky_focus_active.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/sticky_focus_inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/sticky_focus_inactive.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/sticky_normal_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/sticky_normal_active.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/floating_focus_inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/floating_focus_inactive.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/floating_normal_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/floating_normal_active.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/maximized_focus_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/maximized_focus_active.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/maximized_normal_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/maximized_normal_active.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/sticky_normal_inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/sticky_normal_inactive.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/floating_normal_inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/floating_normal_inactive.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/maximized_focus_inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/maximized_focus_inactive.png -------------------------------------------------------------------------------- /custom/theme/icons/titlebar/maximized_normal_inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjdevries/config.awesomewm/HEAD/custom/theme/icons/titlebar/maximized_normal_inactive.png -------------------------------------------------------------------------------- /custom/plugins/yt-music.lua: -------------------------------------------------------------------------------- 1 | -- I have a YT Music thing installed from here, could be fun to create some shortcuts for this. 2 | -- https://github.com/th-ch/youtube-music/blob/master/src/plugins/api-server/backend/routes/control.ts 3 | -------------------------------------------------------------------------------- /custom/theme/constants.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | M.mouse = {} 4 | M.mouse.left = 1 5 | M.mouse.right = 3 6 | M.mouse.wheel = 2 7 | M.mouse.up = 4 8 | M.mouse.down = 5 9 | 10 | M.mods = {} 11 | M.mods.super = "Mod4" 12 | M.mods.shift = "Shift" 13 | M.mods.control = "Control" 14 | 15 | return M 16 | -------------------------------------------------------------------------------- /custom/file.lua: -------------------------------------------------------------------------------- 1 | local lfs = require "lfs" 2 | local file = {} 3 | 4 | file.write = function(name, contents) 5 | local f = assert(io.open(name, "w"), "File must be writable") 6 | f:write(contents) 7 | f:close() 8 | end 9 | 10 | file.exists = function(filepath) 11 | local attr = lfs.attributes(filepath) 12 | return attr ~= nil and attr.mode == "file" 13 | end 14 | 15 | return file 16 | -------------------------------------------------------------------------------- /custom/volume.lua: -------------------------------------------------------------------------------- 1 | local naughty = require "naughty" 2 | local spawn = require "awful.spawn" 3 | 4 | local GET_VOLUME_CMD = "amixer -D pulse sget Master" 5 | local INC_VOLUME_CMD = "amixer -D pulse sset Master 5%+" 6 | local DEC_VOLUME_CMD = "amixer -D pulse sset Master 5%-" 7 | 8 | local bind = function(text, cmd) 9 | return function() 10 | spawn.easy_async(cmd, function() 11 | spawn.easy_async(GET_VOLUME_CMD, function(stdout) 12 | local volume_level = string.match(stdout, "(%d?%d?%d)%%") -- (\d?\d?\d)\%) 13 | 14 | naughty.notify { 15 | text = string.format("%s: %s", text, volume_level), 16 | title = "Volume", 17 | timeout = 1, 18 | } 19 | end) 20 | end) 21 | end 22 | end 23 | 24 | return { 25 | inc = bind("Up", INC_VOLUME_CMD), 26 | dec = bind("Down", DEC_VOLUME_CMD), 27 | } 28 | -------------------------------------------------------------------------------- /custom/setup.lua: -------------------------------------------------------------------------------- 1 | local awesome = awesome or {} 2 | local naughty = require "naughty" 3 | 4 | -- Check if awesome encountered an error during startup and fell back to 5 | -- another config (This code will only ever execute for the fallback config) 6 | if awesome.startup_errors then 7 | naughty.notify { 8 | preset = naughty.config.presets.critical, 9 | title = "Oops, there were errors during startup!", 10 | text = awesome.startup_errors, 11 | } 12 | end 13 | 14 | do 15 | local in_error = false 16 | awesome.connect_signal("debug::error", function(err) 17 | if in_error then 18 | return 19 | end 20 | in_error = true 21 | 22 | naughty.notify { 23 | preset = naughty.config.presets.critical, 24 | title = "Oops, an error happened!", 25 | text = tostring(err), 26 | } 27 | in_error = false 28 | end) 29 | end 30 | -------------------------------------------------------------------------------- /custom/widgets/mem.lua: -------------------------------------------------------------------------------- 1 | local wibox = require "wibox" 2 | local gears = require "gears" 3 | local awful = require "awful" 4 | 5 | -- Create memory widget 6 | local mem_widget = wibox.widget { 7 | { 8 | id = "text", 9 | text = "Mem: 0", 10 | widget = wibox.widget.textbox, 11 | }, 12 | widget = wibox.container.margin, 13 | margins = 5, 14 | } 15 | 16 | -- Function to update memory usage 17 | local function update_mem_usage(widget) 18 | awful.spawn.easy_async_with_shell("free -m | awk '/Mem:/ {printf(\"%.1f%%\", $3/$2 * 100)}'", function(stdout) 19 | widget.text = "Mem: " .. stdout 20 | end) 21 | end 22 | 23 | -- Periodically update memory usage 24 | gears.timer { 25 | call_now = true, 26 | timeout = 5, -- Update every 5 seconds 27 | autostart = true, 28 | callback = function() 29 | update_mem_usage(mem_widget:get_children_by_id("text")[1]) 30 | end, 31 | } 32 | 33 | -- Return widget for inclusion in topbar 34 | return mem_widget 35 | -------------------------------------------------------------------------------- /custom/widgets/menu.lua: -------------------------------------------------------------------------------- 1 | local awful = require "awful" 2 | local beautiful = require "beautiful" 3 | local hotkeys_popup = require "awful.hotkeys_popup" 4 | 5 | -- Load Debian menu entries 6 | local debian = require "debian.menu" 7 | local has_fdo, freedesktop = pcall(require, "freedesktop") 8 | 9 | -- Create a launcher widget and a main menu 10 | local my_awesome_menu = { 11 | { 12 | "hotkeys", 13 | function() 14 | hotkeys_popup.show_help(nil, awful.screen.focused()) 15 | end, 16 | }, 17 | { "edit config", "nvim " .. awesome.conffile }, 18 | { "restart", awesome.restart }, 19 | { 20 | "quit", 21 | function() 22 | awesome.quit() 23 | end, 24 | }, 25 | } 26 | 27 | local menu_awesome = { "awesome", my_awesome_menu, beautiful.awesome_icon } 28 | 29 | local my_main_menu 30 | if has_fdo then 31 | my_main_menu = freedesktop.menu.build { before = { menu_awesome } } 32 | else 33 | my_main_menu = awful.menu { 34 | items = { 35 | menu_awesome, 36 | { "Debian", debian.menu.Debian_menu.Debian }, 37 | }, 38 | } 39 | end 40 | 41 | return my_main_menu 42 | -------------------------------------------------------------------------------- /custom/theme/bottombar.lua: -------------------------------------------------------------------------------- 1 | local awful = require "awful" 2 | local dpi = require("beautiful.xresources").apply_dpi 3 | local wibox = require "wibox" 4 | 5 | local M = {} 6 | 7 | --- On Screen Connect 8 | ---@param s Awesome.screen 9 | M.at_screen_connect = function(s) 10 | -- Create the bottom wibox 11 | local mybottomwibox = awful.wibar { 12 | position = "bottom", 13 | screen = s, 14 | border_width = dpi(0), 15 | height = dpi(32), 16 | } 17 | 18 | local borderwibox = 19 | awful.wibar { position = "bottom", screen = s, height = dpi(1), bg = theme.fg_focus, x = dpi(0), y = dpi(33) } 20 | 21 | -- Add widgets to the bottom wibox 22 | mybottomwibox:setup { 23 | layout = wibox.layout.align.horizontal, 24 | { -- Left widgets 25 | layout = wibox.layout.fixed.horizontal, 26 | mylauncher, 27 | }, 28 | s.mytasklist, -- Middle widget 29 | { -- Right widgets 30 | layout = wibox.layout.fixed.horizontal, 31 | spr_bottom_right, 32 | netdown_icon, 33 | networkwidget, 34 | netup_icon, 35 | bottom_bar, 36 | cpu_icon, 37 | cpuwidget, 38 | bottom_bar, 39 | calendar_icon, 40 | calendarwidget, 41 | bottom_bar, 42 | clock_icon, 43 | clockwidget, 44 | }, 45 | } 46 | end 47 | 48 | return M 49 | -------------------------------------------------------------------------------- /custom/obsidian.lua: -------------------------------------------------------------------------------- 1 | local awful = require "awful" 2 | 3 | local M = {} 4 | 5 | M.setup = function() 6 | client.connect_signal("property::name", function(c) 7 | if c.name == "edit-obisidian" then 8 | c.floating = true 9 | -- vertical monitor to the left, 0,0 10 | -- 3840 by 2160 11 | -- 2160 x 3840 12 | -- local screen_geometry = c.screen.geometry 13 | local width = 2500 14 | local height = 1800 15 | -- local x = 2160 + ((3840 - width) / 2) 16 | -- local y = (3840 - (2160 / 2)) + ((2160 - height) / 2) 17 | c:geometry { width = width, height = height } 18 | awful.placement.centered(c) 19 | end 20 | end) 21 | end 22 | 23 | --- Focus The Obsidian Client 24 | ---@param c Awesome.client 25 | M.focus = function(c) 26 | -- Move to the current screen/tag 27 | c:move_to_screen(awful.screen.focused()) 28 | c:move_to_tag(awful.screen.focused().selected_tag) 29 | 30 | -- Make floating/on top, not maximized/fullscreen 31 | c.floating = true 32 | c.ontop = true 33 | c.minimized = false 34 | c.maximized = false 35 | c.fullscreen = false 36 | 37 | -- Calculate 80% geometry and center it 38 | local s = awful.screen.focused() 39 | local wa = s.workarea 40 | local new_width = math.floor(wa.width * 0.8) 41 | local new_height = math.floor(wa.height * 0.8) 42 | local new_x = wa.x + (wa.width - new_width) / 2 43 | local new_y = wa.y + (wa.height - new_height) / 2 44 | 45 | c:geometry { 46 | x = new_x, 47 | y = new_y, 48 | width = new_width, 49 | height = new_height, 50 | } 51 | 52 | -- Raise and focus 53 | c:raise() 54 | client.focus = c 55 | end 56 | 57 | M.spawn = function(vault) 58 | awful.spawn(("obsidian %s"):format(vault)) 59 | end 60 | 61 | return M 62 | -------------------------------------------------------------------------------- /custom/tbl.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M.tbl_islist(t) 4 | if type(t) ~= "table" then 5 | return false 6 | end 7 | 8 | local count = 0 9 | 10 | for k, _ in pairs(t) do 11 | if type(k) == "number" then 12 | count = count + 1 13 | else 14 | return false 15 | end 16 | end 17 | 18 | if count > 0 then 19 | return true 20 | else 21 | return false 22 | end 23 | end 24 | 25 | local function tbl_extend(behavior, deep_extend, ...) 26 | if behavior ~= "error" and behavior ~= "keep" and behavior ~= "force" then 27 | error('invalid "behavior": ' .. tostring(behavior)) 28 | end 29 | 30 | if select("#", ...) < 2 then 31 | error("wrong number of arguments (given " .. tostring(1 + select("#", ...)) .. ", expected at least 3)") 32 | end 33 | 34 | local ret = {} 35 | for i = 1, select("#", ...) do 36 | local tbl = select(i, ...) 37 | if tbl then 38 | for k, v in pairs(tbl) do 39 | if type(v) == "table" and deep_extend and not M.tbl_islist(v) then 40 | ret[k] = tbl_extend(behavior, true, ret[k] or {}, v) 41 | elseif behavior ~= "force" and ret[k] ~= nil then 42 | if behavior == "error" then 43 | error("key found in more than one map: " .. k) 44 | end -- Else behavior is "keep". 45 | else 46 | ret[k] = v 47 | end 48 | end 49 | end 50 | end 51 | return ret 52 | end 53 | 54 | --- Merges two or more map-like tables. 55 | --- 56 | --@see |extend()| 57 | --- 58 | --@param behavior Decides what to do if a key is found in more than one map: 59 | --- - "error": raise an error 60 | --- - "keep": use value from the leftmost map 61 | --- - "force": use value from the rightmost map 62 | --@param ... Two or more map-like tables. 63 | function M.tbl_extend(behavior, ...) 64 | return tbl_extend(behavior, false, ...) 65 | end 66 | 67 | --- Merges recursively two or more map-like tables. 68 | --- 69 | --@see |tbl_extend()| 70 | --- 71 | --@param behavior Decides what to do if a key is found in more than one map: 72 | --- - "error": raise an error 73 | --- - "keep": use value from the leftmost map 74 | --- - "force": use value from the rightmost map 75 | --@param ... Two or more map-like tables. 76 | function M.tbl_deep_extend(behavior, ...) 77 | return tbl_extend(behavior, true, ...) 78 | end 79 | 80 | return M 81 | -------------------------------------------------------------------------------- /custom/theme/sidebar.lua: -------------------------------------------------------------------------------- 1 | local gears = require "gears" 2 | 3 | local xresources = require "beautiful.xresources" 4 | local dpi = xresources.apply_dpi 5 | 6 | local barcolor = gears.color { 7 | type = "linear", 8 | from = { 0, dpi(46) }, 9 | to = { dpi(46), dpi(46) }, 10 | stops = { { 0, theme.bg_focus }, { 0.9, theme.bg_focus2 } }, 11 | } 12 | 13 | local barcolor2 = gears.color { 14 | type = "linear", 15 | from = { 0, dpi(46) }, 16 | to = { dpi(46), dpi(46) }, 17 | stops = { { 0, "#323232" }, { 1, theme.bg_normal } }, 18 | } 19 | 20 | local dockshape = function(cr, width, height) 21 | gears.shape.partially_rounded_rect(cr, width, height, false, true, true, false, 6) 22 | end 23 | 24 | function theme.vertical_wibox(s) 25 | -- Create the vertical wibox 26 | s.dockheight = (35 * s.workarea.height) / 100 27 | 28 | s.myleftwibox = wibox { 29 | screen = s, 30 | x = 0, 31 | y = s.workarea.height / 2 - s.dockheight / 2, 32 | width = dpi(6), 33 | height = s.dockheight, 34 | fg = theme.fg_normal, 35 | bg = barcolor2, 36 | ontop = true, 37 | visible = true, 38 | type = "dock", 39 | } 40 | 41 | if s.index > 1 and s.myleftwibox.y == 0 then 42 | s.myleftwibox.y = screen[1].myleftwibox.y 43 | end 44 | 45 | -- Add widgets to the vertical wibox 46 | s.myleftwibox:setup { 47 | layout = wibox.layout.align.vertical, 48 | { 49 | layout = wibox.layout.fixed.vertical, 50 | lspace1, 51 | s.mytaglist, 52 | lspace2, 53 | s.layoutb, 54 | wibox.container.margin(mylauncher, dpi(5), dpi(8), dpi(13), dpi(0)), 55 | }, 56 | } 57 | 58 | -- Add toggling functionalities 59 | s.docktimer = gears.timer { timeout = 2 } 60 | s.docktimer:connect_signal("timeout", function() 61 | local s = awful.screen.focused() 62 | s.myleftwibox.width = dpi(9) 63 | s.layoutb.visible = false 64 | mylauncher.visible = false 65 | if s.docktimer.started then 66 | s.docktimer:stop() 67 | end 68 | end) 69 | tag.connect_signal("property::selected", function(t) 70 | local s = t.screen or awful.screen.focused() 71 | s.myleftwibox.width = dpi(38) 72 | s.layoutb.visible = true 73 | mylauncher.visible = true 74 | gears.surface.apply_shape_bounding(s.myleftwibox, dockshape) 75 | if not s.docktimer.started then 76 | s.docktimer:start() 77 | end 78 | end) 79 | 80 | s.myleftwibox:connect_signal("mouse::leave", function() 81 | local s = awful.screen.focused() 82 | s.myleftwibox.width = dpi(9) 83 | s.layoutb.visible = false 84 | mylauncher.visible = false 85 | end) 86 | 87 | s.myleftwibox:connect_signal("mouse::enter", function() 88 | local s = awful.screen.focused() 89 | s.myleftwibox.width = dpi(38) 90 | s.layoutb.visible = true 91 | mylauncher.visible = true 92 | gears.surface.apply_shape_bounding(s.myleftwibox, dockshape) 93 | end) 94 | end 95 | -------------------------------------------------------------------------------- /custom/log.lua: -------------------------------------------------------------------------------- 1 | -- log.lua 2 | -- 3 | -- Inspired by rxi/log.lua 4 | -- Modified by tjdevries and can be found at github.com/tjdevries/vlog.nvim 5 | -- 6 | -- Copied from plenary as well. 7 | -- 8 | -- This library is free software; you can redistribute it and/or modify it 9 | -- under the terms of the MIT license. See LICENSE for details. 10 | 11 | local tbl = require "custom.tbl" 12 | local inspect = require "custom.inspect" 13 | 14 | -- User configuration section 15 | local default_config = { 16 | -- Name of the plugin. Prepended to log messages 17 | plugin = "plenary", 18 | 19 | -- Should highlighting be used in console (using echohl) 20 | highlights = true, 21 | 22 | -- Should write to a file 23 | use_file = true, 24 | 25 | -- Any messages above this level will be logged. 26 | level = "info", 27 | 28 | -- Level configuration 29 | modes = { 30 | { name = "trace", hl = "Comment" }, 31 | { name = "debug", hl = "Comment" }, 32 | { name = "info", hl = "None" }, 33 | { name = "warn", hl = "WarningMsg" }, 34 | { name = "error", hl = "ErrorMsg" }, 35 | { name = "fatal", hl = "ErrorMsg" }, 36 | }, 37 | 38 | -- Can limit the number of decimals displayed for floats 39 | float_precision = 0.01, 40 | } 41 | 42 | -- {{{ NO NEED TO CHANGE 43 | local log = {} 44 | 45 | local unpack = unpack or table.unpack 46 | 47 | log.new = function(config, standalone) 48 | config = tbl.tbl_deep_extend("force", default_config, config) 49 | 50 | local outfile = "/home/tjdevries/.cache/awesome.log" 51 | 52 | local obj 53 | if standalone then 54 | obj = log 55 | else 56 | obj = config 57 | end 58 | 59 | local levels = {} 60 | for i, v in ipairs(config.modes) do 61 | levels[v.name] = i 62 | end 63 | 64 | local round = function(x, increment) 65 | increment = increment or 1 66 | x = x / increment 67 | return (x > 0 and math.floor(x + 0.5) or math.ceil(x - 0.5)) * increment 68 | end 69 | 70 | local make_string = function(...) 71 | local t = {} 72 | for i = 1, select("#", ...) do 73 | local x = select(i, ...) 74 | 75 | if type(x) == "number" and config.float_precision then 76 | x = tostring(round(x, config.float_precision)) 77 | elseif type(x) == "table" then 78 | x = inspect(x) 79 | else 80 | x = tostring(x) 81 | end 82 | 83 | t[#t + 1] = x 84 | end 85 | return table.concat(t, " ") 86 | end 87 | 88 | local log_at_level = function(level, level_config, message_maker, ...) 89 | -- Return early if we're below the config.level 90 | if level < levels[config.level] then 91 | return 92 | end 93 | local nameupper = level_config.name:upper() 94 | 95 | local msg = message_maker(...) 96 | local info = debug.getinfo(config.info_level or 2, "Sl") 97 | local lineinfo = info.short_src .. ":" .. info.currentline 98 | 99 | -- Output to log file 100 | if config.use_file then 101 | local fp = assert(io.open(outfile, "a")) 102 | local str = string.format("[%-6s%s] %s: %s\n", nameupper, os.date(), lineinfo, msg) 103 | fp:write(str) 104 | fp:close() 105 | end 106 | end 107 | 108 | for i, x in ipairs(config.modes) do 109 | -- log.info("these", "are", "separated") 110 | obj[x.name] = function(...) 111 | return log_at_level(i, x, make_string, ...) 112 | end 113 | 114 | -- log.fmt_info("These are %s strings", "formatted") 115 | obj[("fmt_%s"):format(x.name)] = function(...) 116 | return log_at_level(i, x, function(...) 117 | local passed = { ... } 118 | local fmt = table.remove(passed, 1) 119 | local inspected = {} 120 | for _, v in ipairs(passed) do 121 | table.insert(inspected, inspect(v)) 122 | end 123 | return string.format(fmt, unpack(inspected)) 124 | end, ...) 125 | end 126 | 127 | -- log.lazy_info(expensive_to_calculate) 128 | obj[("lazy_%s"):format(x.name)] = function() 129 | return log_at_level(i, x, function(f) 130 | return f() 131 | end) 132 | end 133 | end 134 | 135 | return obj 136 | end 137 | 138 | log.new(default_config, true) 139 | -- }}} 140 | 141 | return log 142 | -------------------------------------------------------------------------------- /custom/types.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | If it's obvious from the documentation that the table returns an array of a particular type, use the style of TYPE[], for example: 3 | 4 | ---@field clients fun(self: Awesome.tag): table 5 | 6 | Should instead be: 7 | 8 | ---@field clients fun(self: Awesome.tag): Awesome.client[] 9 | 10 | --]] 11 | 12 | ---@meta 13 | 14 | ---@class Awesome.screen 15 | ---@field geometry table: The screen coordinates. 16 | ---@field index integer: The internal screen number. 17 | ---@field outputs table: A list of outputs for this screen and their size in mm. 18 | ---@field workarea table: The screen workarea. 19 | ---@field padding table: The screen padding. 20 | ---@field clients Awesome.client[]: The list of visible clients for the screen. 21 | ---@field hidden_clients Awesome.client[]: The list of clients assigned to the screen but not currently visible. 22 | ---@field all_clients Awesome.client[]: All clients assigned to the screen. 23 | ---@field tiled_clients Awesome.client[]: Tiled clients for the screen. 24 | ---@field tags Awesome.tag[]: A list of all tags on the screen. 25 | ---@field selected_tags Awesome.tag[]: A list of all selected tags on the screen. 26 | ---@field selected_tag Awesome.tag: The first selected tag. 27 | ---@field dpi number: The number of pixels per inch of the screen. 28 | ---@field get_clients fun(self: Awesome.screen, stacked: boolean|nil): Awesome.client[] 29 | ---@field get_all_clients fun(self: Awesome.screen, stacked: boolean|nil): Awesome.client[] 30 | ---@field get_tiled_clients fun(self: Awesome.screen, stacked: boolean|nil): Awesome.client[] 31 | ---@field set_auto_dpi_enabled fun(self: Awesome.screen, enabled: boolean): nil 32 | ---@field connect_signal fun(self: Awesome.screen, name: string, func: fun()): nil 33 | ---@field disconnect_signal fun(self: Awesome.screen, name: string, func: fun()): nil 34 | ---@field emit_signal fun(self: Awesome.screen, name: string, ...): nil 35 | 36 | -- Signals (TODO) 37 | -- ["primary_changed"] fun(self: Awesome.screen): nil 38 | -- ["added"] fun(self: Awesome.screen): nil 39 | -- ["removed"] fun(self: Awesome.screen): nil 40 | -- ["list"] fun(self: Awesome.screen): nil 41 | -- ["swapped"] fun(self: Awesome.screen): nil 42 | -- ["tag::history::update"] fun(self: Awesome.screen): nil 43 | 44 | ---@class Awesome.tag 45 | ---@field name string: Tag name. 46 | ---@field selected boolean: True if the tag is selected to be viewed. 47 | ---@field activated boolean: True if the tag is active and can be used. 48 | ---@field index integer: The tag index. 49 | ---@field screen Awesome.screen: The tag screen. 50 | ---@field master_width_factor number: The tag master width factor. 51 | ---@field layout any: The tag client layout. 52 | ---@field layouts any[]: The list of available layouts for this tag. 53 | ---@field volatile boolean: Define if the tag must be deleted when the last client is untagged. 54 | ---@field gap number: The gap (spacing, also called useless_gap) between clients. 55 | ---@field gap_single_client boolean: Enable gaps for a single client. 56 | ---@field master_fill_policy string: Set size fill policy for the master client(s). 57 | ---@field master_count integer: Set the number of master windows. 58 | ---@field icon any: Set the tag icon. 59 | ---@field column_count integer: Set the number of columns. 60 | ---@field clients fun(self: Awesome.tag): Awesome.client[] 61 | ---@field delete fun(self: Awesome.tag, fallback_tag: Awesome.tag|nil, force: boolean|nil): nil 62 | ---@field find_by_name fun(screen: Awesome.screen, name: string): Awesome.tag|nil 63 | ---@field view_only fun(self: Awesome.tag): nil 64 | ---@field connect_signal fun(self: Awesome.tag, signal: string, func: fun()): nil 65 | ---@field disconnect_signal fun(self: Awesome.tag, signal: string, func: fun()): nil 66 | ---@field emit_signal fun(self: Awesome.tag, signal: string, ...): nil 67 | 68 | -- Signals (TODO) 69 | -- ["request::select"] fun(self: Awesome.tag): nil 70 | -- ["tagged"] fun(self: Awesome.tag, client: Awesome.client): nil 71 | -- ["untagged"] fun(self: Awesome.tag, client: Awesome.client): nil 72 | -- ["property::urgent"] fun(self: Awesome.tag): nil 73 | -- ["property::urgent_count"] fun(self: Awesome.tag): nil 74 | -- ["request::screen"] fun(self: Awesome.tag): nil 75 | -- ["removal-pending"] fun(self: Awesome.tag): nil 76 | 77 | ---@type Awesome.tag 78 | 79 | ---@class Awesome.client 80 | ---@field window integer: The X Window ID. (Read-only) 81 | ---@field name string: The client title. 82 | ---@field skip_taskbar boolean: True if the client does not want to be in the taskbar. 83 | ---@field type string: The window type. (Read-only) 84 | ---@field class string: The client class. (Read-only) 85 | ---@field instance string: The client instance. (Read-only) 86 | ---@field pid integer|nil: The client PID, if available. (Read-only) 87 | ---@field role string|nil: The window role, if available. (Read-only) 88 | ---@field machine string|nil: The machine client is running on. (Read-only) 89 | ---@field icon_name string|nil: The client name when iconified. (Read-only) 90 | ---@field icon any|nil: The client icon as a surface. (Read-only) 91 | ---@field icon_sizes table|nil: The available sizes of client icons. (Read-only) 92 | ---@field screen Awesome.screen: Client screen. 93 | ---@field hidden boolean: Define if the client must be hidden, i.e., minimized. 94 | ---@field minimized boolean: Define if the client must be iconified, i.e., minimized. 95 | ---@field size_hints_honor boolean: Honor size hints, e.g., 'aspect_ratio'. 96 | ---@field border_width integer: The client border width. 97 | ---@field border_color string: The client border color. 98 | ---@field urgent boolean: The client urgent state. 99 | ---@field content any: A cairo surface for the client window content. (Read-only) 100 | ---@field opacity number: The client opacity. 101 | ---@field ontop boolean: The client is on top of every other window. 102 | ---@field above boolean: The client is above normal windows. 103 | ---@field below boolean: The client is below normal windows. 104 | ---@field fullscreen boolean: The client is fullscreen or not. 105 | ---@field maximized boolean: The client is maximized (horizontally and vertically) or not. 106 | ---@field maximized_horizontal boolean: The client is maximized horizontally or not. 107 | ---@field maximized_vertical boolean: The client is maximized vertically or not. 108 | ---@field transient_for Awesome.client|nil: The client the window is transient for. 109 | ---@field group_window integer|nil: Window identification unique to a group of windows. (Read-only) 110 | ---@field leader_window integer|nil: Identification unique to windows spawned by the same command. (Read-only) 111 | ---@field size_hints table: A table with size hints of the client. (Read-only) 112 | ---@field motif_wm_hints table: The motif WM hints of the client. (Read-only) 113 | ---@field sticky boolean: Set the client sticky, i.e., visible on all tags. 114 | ---@field modal boolean: Indicate if the client is modal. 115 | ---@field focusable boolean: True if the client can receive the input focus. 116 | ---@field shape_bounding any: The client’s bounding shape as set by awesome as a (native) cairo surface. 117 | ---@field shape_clip any: The client’s clip shape as set by awesome as a (native) cairo surface. 118 | ---@field shape_input any: The client’s input shape as set by awesome as a (native) cairo surface. 119 | ---@field client_shape_bounding any: The client’s bounding shape as set by the program as a (native) cairo surface. (Read-only) 120 | ---@field client_shape_clip any: The client’s clip shape as set by the program as a (native) cairo surface. (Read-only) 121 | ---@field startup_id string|nil: The FreeDesktop StartId. (Read-only) 122 | ---@field valid boolean: If the client that this object refers to is still managed by awesome. (Read-only) 123 | ---@field first_tag Awesome.tag|nil: The first tag of the client. (Read-only) 124 | ---@field marked boolean: If a client is marked or not. 125 | ---@field is_fixed boolean: Return if a client has a fixed size or not. (Read-only) 126 | ---@field immobilized_horizontal boolean: Is the client immobilized horizontally? (Read-only) 127 | ---@field immobilized_vertical boolean: Is the client immobilized vertically? (Read-only) 128 | ---@field floating boolean: The client floating state. 129 | ---@field x integer: The x coordinate. 130 | ---@field y integer: The y coordinate. 131 | ---@field width integer: The width of the client. 132 | ---@field height integer: The height of the client. 133 | ---@field dockable boolean: If the client is dockable. (Read-only) 134 | ---@field requests_no_titlebar boolean: If the client requests not to be decorated with a titlebar. (Read-only) 135 | ---@field shape any: Set the client shape. 136 | ---@field struts fun(self: Awesome.client, struts: table|nil): table 137 | ---@field buttons fun(self: Awesome.client, buttons_table: table|nil): table 138 | ---@field instances fun(): integer 139 | ---@field get fun(screen: Awesome.screen|nil, stacked: boolean|nil): Awesome.client[] 140 | ---@field isvisible fun(self: Awesome.client): boolean 141 | ---@field kill fun(self: Awesome.client): nil 142 | ---@field swap fun(self: Awesome.client, c: Awesome.client): nil 143 | ---@field tags fun(self: Awesome.client, tags_table: Awesome.tag[]|nil): Awesome.tag[] 144 | ---@field raise fun(self: Awesome.client): nil 145 | ---@field lower fun(self: Awesome.client): nil 146 | ---@field unmanage fun(self: Awesome.client): nil 147 | ---@field geometry fun(self: Awesome.client, geo: table|nil): table 148 | ---@field apply_size_hints fun(self: Awesome.client, width: integer, height: integer): integer, integer 149 | ---@field get_icon fun(self: Awesome.client, index: integer): any 150 | ---@field jump_to fun(self: Awesome.client, merge: boolean|nil): nil 151 | ---@field append_keybinding fun(self: Awesome.client, key: any): nil 152 | ---@field remove_keybinding fun(self: Awesome.client, key: any): nil 153 | ---@field append_mousebinding fun(self: Awesome.client, button: any): nil 154 | ---@field remove_mousebinding fun(self: Awesome.client, button: any): nil 155 | ---@field to_primary_section fun(self: Awesome.client): nil 156 | ---@field to_secondary_section fun(self: Awesome.client): nil 157 | ---@field relative_move fun(self: Awesome.client, x: integer, y: integer, w: integer, h: integer): nil 158 | ---@field move_to_tag fun(self: Awesome.client, target: Awesome.tag): nil 159 | ---@field toggle_tag fun(self: Awesome.client, target: Awesome.tag): nil 160 | ---@field move_to_screen fun(self: Awesome.client, s: Awesome.screen|nil): nil 161 | ---@field to_selected_tags fun(self: Awesome.client): nil 162 | ---@field get_transient_for_matching fun(self: Awesome.client, matcher: fun(c: Awesome.client): boolean): Awesome.client|nil 163 | ---@field is_transient_for fun(self: Awesome.client, c2: Awesome.client): boolean 164 | ---@field activate fun(self: Awesome.client, args: table|nil): nil 165 | -------------------------------------------------------------------------------- /custom/inspect.lua: -------------------------------------------------------------------------------- 1 | local inspect = { 2 | _VERSION = "inspect.lua 3.1.0", 3 | _URL = "http://github.com/kikito/inspect.lua", 4 | _DESCRIPTION = "human-readable representations of tables", 5 | _LICENSE = [[ 6 | MIT LICENSE 7 | 8 | Copyright (c) 2013 Enrique García Cota 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a 11 | copy of this software and associated documentation files (the 12 | "Software"), to deal in the Software without restriction, including 13 | without limitation the rights to use, copy, modify, merge, publish, 14 | distribute, sublicense, and/or sell copies of the Software, and to 15 | permit persons to whom the Software is furnished to do so, subject to 16 | the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included 19 | in all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 22 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 25 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 26 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 27 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | ]], 29 | } 30 | 31 | local tostring = tostring 32 | 33 | inspect.KEY = setmetatable({}, { __tostring = function() 34 | return "inspect.KEY" 35 | end }) 36 | inspect.METATABLE = setmetatable({}, { __tostring = function() 37 | return "inspect.METATABLE" 38 | end }) 39 | 40 | local function rawpairs(t) 41 | return next, t, nil 42 | end 43 | 44 | -- Apostrophizes the string if it has quotes, but not aphostrophes 45 | -- Otherwise, it returns a regular quoted string 46 | local function smartQuote(str) 47 | if str:match '"' and not str:match "'" then 48 | return "'" .. str .. "'" 49 | end 50 | return '"' .. str:gsub('"', '\\"') .. '"' 51 | end 52 | 53 | -- \a => '\\a', \0 => '\\0', 31 => '\31' 54 | local shortControlCharEscapes = { 55 | ["\a"] = "\\a", 56 | ["\b"] = "\\b", 57 | ["\f"] = "\\f", 58 | ["\n"] = "\\n", 59 | ["\r"] = "\\r", 60 | ["\t"] = "\\t", 61 | ["\v"] = "\\v", 62 | } 63 | local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031 64 | for i = 0, 31 do 65 | local ch = string.char(i) 66 | if not shortControlCharEscapes[ch] then 67 | shortControlCharEscapes[ch] = "\\" .. i 68 | longControlCharEscapes[ch] = string.format("\\%03d", i) 69 | end 70 | end 71 | 72 | local function escape(str) 73 | return (str:gsub("\\", "\\\\"):gsub("(%c)%f[0-9]", longControlCharEscapes):gsub("%c", shortControlCharEscapes)) 74 | end 75 | 76 | local function isIdentifier(str) 77 | return type(str) == "string" and str:match "^[_%a][_%a%d]*$" 78 | end 79 | 80 | local function isSequenceKey(k, sequenceLength) 81 | return type(k) == "number" and 1 <= k and k <= sequenceLength and math.floor(k) == k 82 | end 83 | 84 | local defaultTypeOrders = { 85 | ["number"] = 1, 86 | ["boolean"] = 2, 87 | ["string"] = 3, 88 | ["table"] = 4, 89 | ["function"] = 5, 90 | ["userdata"] = 6, 91 | ["thread"] = 7, 92 | } 93 | 94 | local function sortKeys(a, b) 95 | local ta, tb = type(a), type(b) 96 | 97 | -- strings and numbers are sorted numerically/alphabetically 98 | if ta == tb and (ta == "string" or ta == "number") then 99 | return a < b 100 | end 101 | 102 | local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb] 103 | -- Two default types are compared according to the defaultTypeOrders table 104 | if dta and dtb then 105 | return defaultTypeOrders[ta] < defaultTypeOrders[tb] 106 | elseif dta then 107 | return true -- default types before custom ones 108 | elseif dtb then 109 | return false -- custom types after default ones 110 | end 111 | 112 | -- custom types are sorted out alphabetically 113 | return ta < tb 114 | end 115 | 116 | -- For implementation reasons, the behavior of rawlen & # is "undefined" when 117 | -- tables aren't pure sequences. So we implement our own # operator. 118 | local function getSequenceLength(t) 119 | local len = 1 120 | local v = rawget(t, len) 121 | while v ~= nil do 122 | len = len + 1 123 | v = rawget(t, len) 124 | end 125 | return len - 1 126 | end 127 | 128 | local function getNonSequentialKeys(t) 129 | local keys, keysLength = {}, 0 130 | local sequenceLength = getSequenceLength(t) 131 | for k, _ in rawpairs(t) do 132 | if not isSequenceKey(k, sequenceLength) then 133 | keysLength = keysLength + 1 134 | keys[keysLength] = k 135 | end 136 | end 137 | table.sort(keys, sortKeys) 138 | return keys, keysLength, sequenceLength 139 | end 140 | 141 | local function countTableAppearances(t, tableAppearances) 142 | tableAppearances = tableAppearances or {} 143 | 144 | if type(t) == "table" then 145 | if not tableAppearances[t] then 146 | tableAppearances[t] = 1 147 | for k, v in rawpairs(t) do 148 | countTableAppearances(k, tableAppearances) 149 | countTableAppearances(v, tableAppearances) 150 | end 151 | countTableAppearances(getmetatable(t), tableAppearances) 152 | else 153 | tableAppearances[t] = tableAppearances[t] + 1 154 | end 155 | end 156 | 157 | return tableAppearances 158 | end 159 | 160 | local copySequence = function(s) 161 | local copy, len = {}, #s 162 | for i = 1, len do 163 | copy[i] = s[i] 164 | end 165 | return copy, len 166 | end 167 | 168 | local function makePath(path, ...) 169 | local keys = { ... } 170 | local newPath, len = copySequence(path) 171 | for i = 1, #keys do 172 | newPath[len + i] = keys[i] 173 | end 174 | return newPath 175 | end 176 | 177 | local function processRecursive(process, item, path, visited) 178 | if item == nil then 179 | return nil 180 | end 181 | if visited[item] then 182 | return visited[item] 183 | end 184 | 185 | local processed = process(item, path) 186 | if type(processed) == "table" then 187 | local processedCopy = {} 188 | visited[item] = processedCopy 189 | local processedKey 190 | 191 | for k, v in rawpairs(processed) do 192 | processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) 193 | if processedKey ~= nil then 194 | processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) 195 | end 196 | end 197 | 198 | local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) 199 | if type(mt) ~= "table" then 200 | mt = nil 201 | end -- ignore not nil/table __metatable field 202 | setmetatable(processedCopy, mt) 203 | processed = processedCopy 204 | end 205 | return processed 206 | end 207 | 208 | ------------------------------------------------------------------- 209 | 210 | local Inspector = {} 211 | local Inspector_mt = { __index = Inspector } 212 | 213 | function Inspector:puts(...) 214 | local args = { ... } 215 | local buffer = self.buffer 216 | local len = #buffer 217 | for i = 1, #args do 218 | len = len + 1 219 | buffer[len] = args[i] 220 | end 221 | end 222 | 223 | function Inspector:down(f) 224 | self.level = self.level + 1 225 | f() 226 | self.level = self.level - 1 227 | end 228 | 229 | function Inspector:tabify() 230 | self:puts(self.newline, string.rep(self.indent, self.level)) 231 | end 232 | 233 | function Inspector:alreadyVisited(v) 234 | return self.ids[v] ~= nil 235 | end 236 | 237 | function Inspector:getId(v) 238 | local id = self.ids[v] 239 | if not id then 240 | local tv = type(v) 241 | id = (self.maxIds[tv] or 0) + 1 242 | self.maxIds[tv] = id 243 | self.ids[v] = id 244 | end 245 | return tostring(id) 246 | end 247 | 248 | function Inspector:putKey(k) 249 | if isIdentifier(k) then 250 | return self:puts(k) 251 | end 252 | self:puts "[" 253 | self:putValue(k) 254 | self:puts "]" 255 | end 256 | 257 | function Inspector:putTable(t) 258 | if t == inspect.KEY or t == inspect.METATABLE then 259 | self:puts(tostring(t)) 260 | elseif self:alreadyVisited(t) then 261 | self:puts("