diff --git a/multimedia/.config/mpv/scripts/autoload.lua b/multimedia/.config/mpv/scripts/autoload.lua index 7150abb..a245a7a 100644 --- a/multimedia/.config/mpv/scripts/autoload.lua +++ b/multimedia/.config/mpv/scripts/autoload.lua @@ -21,72 +21,110 @@ audio=yes MAXENTRIES = 5000 -local msg = require 'mp.msg' -local options = require 'mp.options' -local utils = require 'mp.utils' +local msg = require("mp.msg") +local options = require("mp.options") +local utils = require("mp.utils") o = { - disabled = false, - images = true, - videos = true, - audio = true + disabled = false, + images = true, + videos = true, + audio = true, } options.read_options(o) -function Set (t) - local set = {} - for _, v in pairs(t) do set[v] = true end - return set +function Set(t) + local set = {} + for _, v in pairs(t) do + set[v] = true + end + return set end -function SetUnion (a,b) - local res = {} - for k in pairs(a) do res[k] = true end - for k in pairs(b) do res[k] = true end - return res +function SetUnion(a, b) + local res = {} + for k in pairs(a) do + res[k] = true + end + for k in pairs(b) do + res[k] = true + end + return res end -EXTENSIONS_VIDEO = Set { - 'mkv', 'avi', 'mp4', 'ogv', 'webm', 'rmvb', 'flv', 'wmv', 'mpeg', 'mpg', 'm4v', '3gp' -} +EXTENSIONS_VIDEO = Set({ + "mkv", + "avi", + "mp4", + "ogv", + "webm", + "rmvb", + "flv", + "wmv", + "mpeg", + "mpg", + "m4v", + "3gp", +}) -EXTENSIONS_AUDIO = Set { - 'mp3', 'wav', 'ogm', 'flac', 'm4a', 'wma', 'ogg', 'opus' -} +EXTENSIONS_AUDIO = Set({ + "mp3", + "wav", + "ogm", + "flac", + "m4a", + "wma", + "ogg", + "opus", +}) -EXTENSIONS_IMAGES = Set { - 'jpg', 'jpeg', 'png', 'tif', 'tiff', 'gif', 'webp', 'svg', 'bmp' -} +EXTENSIONS_IMAGES = Set({ + "jpg", + "jpeg", + "png", + "tif", + "tiff", + "gif", + "webp", + "svg", + "bmp", +}) -EXTENSIONS = Set {} -if o.videos then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_VIDEO) end -if o.audio then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_AUDIO) end -if o.images then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_IMAGES) end +EXTENSIONS = Set({}) +if o.videos then + EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_VIDEO) +end +if o.audio then + EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_AUDIO) +end +if o.images then + EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_IMAGES) +end function add_files_at(index, files) - index = index - 1 - local oldcount = mp.get_property_number("playlist-count", 1) - for i = 1, #files do - mp.commandv("loadfile", files[i], "append") - mp.commandv("playlist-move", oldcount + i - 1, index + i - 1) - end + index = index - 1 + local oldcount = mp.get_property_number("playlist-count", 1) + for i = 1, #files do + mp.commandv("loadfile", files[i], "append") + mp.commandv("playlist-move", oldcount + i - 1, index + i - 1) + end end function get_extension(path) - match = string.match(path, "%.([^%.]+)$" ) - if match == nil then - return "nomatch" - else - return match - end + match = string.match(path, "%.([^%.]+)$") + if match == nil then + return "nomatch" + else + return match + end end table.filter = function(t, iter) - for i = #t, 1, -1 do - if not iter(t[i]) then - table.remove(t, i) - end - end + for i = #t, 1, -1 do + if not iter(t[i]) then + table.remove(t, i) + end + end end -- splitbynum and alnumcomp from alphanum.lua (C) Andre Bogus @@ -95,126 +133,136 @@ end -- split a string into a table of number and string values function splitbynum(s) - local result = {} - for x, y in (s or ""):gmatch("(%d*)(%D*)") do - if x ~= "" then table.insert(result, tonumber(x)) end - if y ~= "" then table.insert(result, y) end - end - return result + local result = {} + for x, y in (s or ""):gmatch("(%d*)(%D*)") do + if x ~= "" then + table.insert(result, tonumber(x)) + end + if y ~= "" then + table.insert(result, y) + end + end + return result end function clean_key(k) - k = (' '..k..' '):gsub("%s+", " "):sub(2, -2):lower() - return splitbynum(k) + k = (" " .. k .. " "):gsub("%s+", " "):sub(2, -2):lower() + return splitbynum(k) end -- compare two strings function alnumcomp(x, y) - local xt, yt = clean_key(x), clean_key(y) - for i = 1, math.min(#xt, #yt) do - local xe, ye = xt[i], yt[i] - if type(xe) == "string" then ye = tostring(ye) - elseif type(ye) == "string" then xe = tostring(xe) end - if xe ~= ye then return xe < ye end - end - return #xt < #yt + local xt, yt = clean_key(x), clean_key(y) + for i = 1, math.min(#xt, #yt) do + local xe, ye = xt[i], yt[i] + if type(xe) == "string" then + ye = tostring(ye) + elseif type(ye) == "string" then + xe = tostring(xe) + end + if xe ~= ye then + return xe < ye + end + end + return #xt < #yt end local autoloaded = nil function find_and_add_entries() - local path = mp.get_property("path", "") - local dir, filename = utils.split_path(path) - msg.trace(("dir: %s, filename: %s"):format(dir, filename)) - if o.disabled then - msg.verbose("stopping: autoload disabled") - return - elseif #dir == 0 then - msg.verbose("stopping: not a local path") - return - end + local path = mp.get_property("path", "") + local dir, filename = utils.split_path(path) + msg.trace(("dir: %s, filename: %s"):format(dir, filename)) + if o.disabled then + msg.verbose("stopping: autoload disabled") + return + elseif #dir == 0 then + msg.verbose("stopping: not a local path") + return + end - local pl_count = mp.get_property_number("playlist-count", 1) - -- check if this is a manually made playlist - if (pl_count > 1 and autoloaded == nil) or - (pl_count == 1 and EXTENSIONS[string.lower(get_extension(filename))] == nil) then - msg.verbose("stopping: manually made playlist") - return - else - autoloaded = true - end + local pl_count = mp.get_property_number("playlist-count", 1) + -- check if this is a manually made playlist + if + (pl_count > 1 and autoloaded == nil) + or (pl_count == 1 and EXTENSIONS[string.lower(get_extension(filename))] == nil) + then + msg.verbose("stopping: manually made playlist") + return + else + autoloaded = true + end - local pl = mp.get_property_native("playlist", {}) - local pl_current = mp.get_property_number("playlist-pos-1", 1) - msg.trace(("playlist-pos-1: %s, playlist: %s"):format(pl_current, - utils.to_string(pl))) + local pl = mp.get_property_native("playlist", {}) + local pl_current = mp.get_property_number("playlist-pos-1", 1) + msg.trace(("playlist-pos-1: %s, playlist: %s"):format(pl_current, utils.to_string(pl))) - local files = utils.readdir(dir, "files") - if files == nil then - msg.verbose("no other files in directory") - return - end - table.filter(files, function (v, k) - if string.match(v, "^%.") then - return false - end - local ext = get_extension(v) - if ext == nil then - return false - end - return EXTENSIONS[string.lower(ext)] - end) - table.sort(files, alnumcomp) + local files = utils.readdir(dir, "files") + if files == nil then + msg.verbose("no other files in directory") + return + end + table.filter(files, function(v, k) + if string.match(v, "^%.") then + return false + end + local ext = get_extension(v) + if ext == nil then + return false + end + return EXTENSIONS[string.lower(ext)] + end) + table.sort(files, alnumcomp) - if dir == "." then - dir = "" - end + if dir == "." then + dir = "" + end - -- Find the current pl entry (dir+"/"+filename) in the sorted dir list - local current - for i = 1, #files do - if files[i] == filename then - current = i - break - end - end - if current == nil then - return - end - msg.trace("current file position in files: "..current) + -- Find the current pl entry (dir+"/"+filename) in the sorted dir list + local current + for i = 1, #files do + if files[i] == filename then + current = i + break + end + end + if current == nil then + return + end + msg.trace("current file position in files: " .. current) - local append = {[-1] = {}, [1] = {}} - for direction = -1, 1, 2 do -- 2 iterations, with direction = -1 and +1 - for i = 1, MAXENTRIES do - local file = files[current + i * direction] - local pl_e = pl[pl_current + i * direction] - if file == nil or file[1] == "." then - break - end + local append = { [-1] = {}, [1] = {} } + for direction = -1, 1, 2 do -- 2 iterations, with direction = -1 and +1 + for i = 1, MAXENTRIES do + local file = files[current + i * direction] + local pl_e = pl[pl_current + i * direction] + if file == nil or file[1] == "." then + break + end - local filepath = dir .. file - if pl_e then - -- If there's a playlist entry, and it's the same file, stop. - msg.trace(pl_e.filename.." == "..filepath.." ?") - if pl_e.filename == filepath then - break - end - end + local filepath = dir .. file + if pl_e then + -- If there's a playlist entry, and it's the same file, stop. + msg.trace(pl_e.filename .. " == " .. filepath .. " ?") + if pl_e.filename == filepath then + break + end + end - if direction == -1 then - if pl_current == 1 then -- never add additional entries in the middle - msg.info("Prepending " .. file) - table.insert(append[-1], 1, filepath) - end - else - msg.info("Adding " .. file) - table.insert(append[1], filepath) - end - end - end + if direction == -1 then + if pl_current == 1 then -- never add additional entries in the middle + msg.info("Prepending " .. file) + table.insert(append[-1], 1, filepath) + end + else + msg.info("Adding " .. file) + table.insert(append[1], filepath) + end + end + end - add_files_at(pl_current + 1, append[1]) - add_files_at(pl_current, append[-1]) + add_files_at(pl_current + 1, append[1]) + add_files_at(pl_current, append[-1]) end mp.register_event("start-file", find_and_add_entries) diff --git a/multimedia/.config/mpv/scripts/battery.lua b/multimedia/.config/mpv/scripts/battery.lua index 0477821..4a303c8 100644 --- a/multimedia/.config/mpv/scripts/battery.lua +++ b/multimedia/.config/mpv/scripts/battery.lua @@ -1,5 +1,5 @@ -- If the laptop is on battery, the profile 'lq' will be loaded; otherwise 'hq' is used -local mp = require 'mp' +local mp = require("mp") local SHOULD_ADJUST = false @@ -7,27 +7,31 @@ local lqprofile = "lowquality" local hqprofile = "highquality" local function powerstate() - local f = io.open("/sys/class/power_supply/AC/online") - if f == nil then return end - local t = f:read("*n") - f:close() - return t + local f = io.open("/sys/class/power_supply/AC/online") + if f == nil then + return + end + local t = f:read("*n") + f:close() + return t end local function adjust() - if not SHOULD_ADJUST then return end + if not SHOULD_ADJUST then + return + end - local state = powerstate() - -- this actually overrides automatically applied profiles - -- like 'protocol.http' - if state == 0 then - mp.set_property("profile", lqprofile) - mp.msg.info("[quality] running battery, setting low-quality options.") - mp.osd_message("[quality] LQ") - else - mp.set_property("profile", hqprofile) - mp.msg.info("[quality] running ac, setting high-quality options.") - mp.osd_message("[quality] HQ") - end + local state = powerstate() + -- this actually overrides automatically applied profiles + -- like 'protocol.http' + if state == 0 then + mp.set_property("profile", lqprofile) + mp.msg.info("[quality] running battery, setting low-quality options.") + mp.osd_message("[quality] LQ") + else + mp.set_property("profile", hqprofile) + mp.msg.info("[quality] running ac, setting high-quality options.") + mp.osd_message("[quality] HQ") + end end mp.add_hook("on_load", 1, adjust) diff --git a/multimedia/.config/mpv/scripts/copy_videotime.lua b/multimedia/.config/mpv/scripts/copy_videotime.lua index 9331a56..7aabd75 100644 --- a/multimedia/.config/mpv/scripts/copy_videotime.lua +++ b/multimedia/.config/mpv/scripts/copy_videotime.lua @@ -1,5 +1,5 @@ -local mp = require 'mp' -require 'mp.msg' +local mp = require("mp") +require("mp.msg") -- Copy the current time of the video to clipboard. @@ -8,73 +8,76 @@ UNIX = 3 KEY_BIND = "y" local function platform_type() - local utils = require 'mp.utils' - local workdir = utils.to_string(mp.get_property_native("working-directory")) - if string.find(workdir, "\\") then - return WINDOWS - else - return UNIX - end + local utils = require("mp.utils") + local workdir = utils.to_string(mp.get_property_native("working-directory")) + if string.find(workdir, "\\") then + return WINDOWS + else + return UNIX + end end local function command_exists(cmd) - local pipe = io.popen("type " .. cmd .. " > /dev/null 2> /dev/null; printf \"$?\"", "r") - if not pipe then return end - local exists = pipe:read() == "0" - pipe:close() - return exists + local pipe = io.popen("type " .. cmd .. ' > /dev/null 2> /dev/null; printf "$?"', "r") + if not pipe then + return + end + local exists = pipe:read() == "0" + pipe:close() + return exists end local function get_clipboard_cmd() - if command_exists("xclip") then - return "xclip -silent -in -selection clipboard" - elseif command_exists("wl-copy") then - return "wl-copy" - elseif command_exists("pbcopy") then - return "pbcopy" - else - mp.msg.error("No supported clipboard command found") - return false - end + if command_exists("xclip") then + return "xclip -silent -in -selection clipboard" + elseif command_exists("wl-copy") then + return "wl-copy" + elseif command_exists("pbcopy") then + return "pbcopy" + else + mp.msg.error("No supported clipboard command found") + return false + end end local function divmod(a, b) - return a / b, a % b + return a / b, a % b end local function set_clipboard(text) - if platform == WINDOWS then - mp.commandv("run", "powershell", "set-clipboard", text) - return true - elseif (platform == UNIX and clipboard_cmd) then - local pipe = io.popen(clipboard_cmd, "w") - if not pipe then return end - pipe:write(text) - pipe:close() - return true - else - mp.msg.error("Set_clipboard error") - return false - end + if platform == WINDOWS then + mp.commandv("run", "powershell", "set-clipboard", text) + return true + elseif platform == UNIX and clipboard_cmd then + local pipe = io.popen(clipboard_cmd, "w") + if not pipe then + return + end + pipe:write(text) + pipe:close() + return true + else + mp.msg.error("Set_clipboard error") + return false + end end local function copyTime() - local time_pos = mp.get_property_number("time-pos") - local minutes, remainder = divmod(time_pos, 60) - local hours, minutes = divmod(minutes, 60) - local seconds = math.floor(remainder) - local milliseconds = math.floor((remainder - seconds) * 1000) - local time = string.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds) - if set_clipboard(time) then - mp.osd_message(string.format("[copytime] %s", time)) - else - mp.osd_message("[copytime] failed") - end + local time_pos = mp.get_property_number("time-pos") + local minutes, remainder = divmod(time_pos, 60) + local hours, minutes = divmod(minutes, 60) + local seconds = math.floor(remainder) + local milliseconds = math.floor((remainder - seconds) * 1000) + local time = string.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds) + if set_clipboard(time) then + mp.osd_message(string.format("[copytime] %s", time)) + else + mp.osd_message("[copytime] failed") + end end - platform = platform_type() if platform == UNIX then - clipboard_cmd = get_clipboard_cmd() + clipboard_cmd = get_clipboard_cmd() end mp.add_key_binding(KEY_BIND, "copyTime", copyTime) diff --git a/multimedia/.config/mpv/scripts/gallery-dl.lua b/multimedia/.config/mpv/scripts/gallery-dl.lua index 3cc21db..118a33f 100644 --- a/multimedia/.config/mpv/scripts/gallery-dl.lua +++ b/multimedia/.config/mpv/scripts/gallery-dl.lua @@ -7,27 +7,27 @@ -- e.g. -- `mpv gallery-dl://https://imgur.com/....` -local mp = require 'mp' -local utils = require 'mp.utils' -local msg = require 'mp.msg' +local mp = require("mp") +local utils = require("mp.utils") +local msg = require("mp.msg") local function exec(args) - local ret = utils.subprocess({ args = args }) - return ret.status, ret.stdout, ret + local ret = utils.subprocess({ args = args }) + return ret.status, ret.stdout, ret end mp.add_hook("on_load", 15, function() - local fn = mp.get_property("stream-open-filename", "") - if (fn:find("gdl://") ~= 1) then - msg.debug("not a gdl:// url: " .. fn) - return - end - local url = string.gsub(url, "gdl://", "") + local fn = mp.get_property("stream-open-filename", "") + if fn:find("gdl://") ~= 1 then + msg.debug("not a gdl:// url: " .. fn) + return + end + local url = string.gsub(url, "gdl://", "") - local es, urls, result = exec({ "gallery-dl", "-g", url }) - if (es < 0) or (urls == nil) or (urls == "") then - msg.error("failed to get album list.") - end + local es, urls, result = exec({ "gallery-dl", "-g", url }) + if (es < 0) or (urls == nil) or (urls == "") then + msg.error("failed to get album list.") + end - mp.commandv("loadlist", "memory://" .. urls) + mp.commandv("loadlist", "memory://" .. urls) end) diff --git a/multimedia/.config/mpv/scripts/playlistmanager.lua b/multimedia/.config/mpv/scripts/playlistmanager.lua index 7a7a6d5..e94dc24 100644 --- a/multimedia/.config/mpv/scripts/playlistmanager.lua +++ b/multimedia/.config/mpv/scripts/playlistmanager.lua @@ -1,30 +1,30 @@ local settings = { - -- #### FUNCTIONALITY SETTINGS + -- #### FUNCTIONALITY SETTINGS - -- navigation keybindings force override only while playlist is visible - -- if "no" then you can display the playlist by any of the navigation keys - dynamic_binds = true, + -- navigation keybindings force override only while playlist is visible + -- if "no" then you can display the playlist by any of the navigation keys + dynamic_binds = true, - -- to bind multiple keys separate them by a space - key_moveup = "UP", - key_movedown = "DOWN", - key_selectfile = "RIGHT LEFT", - key_unselectfile = "", - key_playfile = "ENTER", - key_removefile = "BS", - key_closeplaylist = "ESC", + -- to bind multiple keys separate them by a space + key_moveup = "UP", + key_movedown = "DOWN", + key_selectfile = "RIGHT LEFT", + key_unselectfile = "", + key_playfile = "ENTER", + key_removefile = "BS", + key_closeplaylist = "ESC", - -- replaces matches on filenames based on extension, put as empty string to not replace anything - -- replace rules are executed in provided order - -- replace rule key is the pattern and value is the replace value - -- uses :gsub('pattern', 'replace'), read more http://lua-users.org/wiki/StringLibraryTutorial - -- 'all' will match any extension or protocol if it has one - -- uses json and parses it into a lua table to be able to support .conf file + -- replaces matches on filenames based on extension, put as empty string to not replace anything + -- replace rules are executed in provided order + -- replace rule key is the pattern and value is the replace value + -- uses :gsub('pattern', 'replace'), read more http://lua-users.org/wiki/StringLibraryTutorial + -- 'all' will match any extension or protocol if it has one + -- uses json and parses it into a lua table to be able to support .conf file - filename_replace = "", + filename_replace = "", - --[=====[ START OF SAMPLE REPLACE, to use remove start and end line + --[=====[ START OF SAMPLE REPLACE, to use remove start and end line --Sample replace: replaces underscore to space on all files --for mp4 and webm; remove extension, remove brackets and surrounding whitespace, change dot between alphanumeric to space filename_replace = [[ @@ -51,8 +51,8 @@ local settings = { ]], --END OF SAMPLE REPLACE ]=====] - -- json array of filetypes to search from directory - loadfiles_filetypes = [[ + -- json array of filetypes to search from directory + loadfiles_filetypes = [[ [ "jpg", "jpeg", "png", "tif", "tiff", "gif", "webp", "svg", "bmp", "mp3", "wav", "ogm", "flac", "m4a", "wma", "ogg", "opus", @@ -60,106 +60,106 @@ local settings = { ] ]], - -- loadfiles at startup if there is 0 or 1 items in playlist, if 0 uses worḱing dir for files - loadfiles_on_start = false, + -- loadfiles at startup if there is 0 or 1 items in playlist, if 0 uses worḱing dir for files + loadfiles_on_start = false, - -- sort playlist on mpv start - sortplaylist_on_start = false, + -- sort playlist on mpv start + sortplaylist_on_start = false, - -- sort playlist when files are added to playlist - sortplaylist_on_file_add = false, + -- sort playlist when files are added to playlist + sortplaylist_on_file_add = false, - -- use alphanumerical sort - alphanumsort = true, + -- use alphanumerical sort + alphanumsort = true, - -- "linux | windows | auto" - system = "auto", + -- "linux | windows | auto" + system = "auto", - -- Use ~ for home directory. Leave as empty to use mpv/playlists - playlist_savepath = "", + -- Use ~ for home directory. Leave as empty to use mpv/playlists + playlist_savepath = "", - -- show playlist or filename every time a new file is loaded - -- 2 shows playlist, 1 shows current file(filename strip applied) as osd text, 0 shows nothing - -- instead of using this you can also call script-message playlistmanager show playlist/filename - -- ex. KEY playlist-next ; script-message playlistmanager show playlist - show_playlist_on_fileload = 0, + -- show playlist or filename every time a new file is loaded + -- 2 shows playlist, 1 shows current file(filename strip applied) as osd text, 0 shows nothing + -- instead of using this you can also call script-message playlistmanager show playlist/filename + -- ex. KEY playlist-next ; script-message playlistmanager show playlist + show_playlist_on_fileload = 0, - -- sync cursor when file is loaded from outside reasons(file-ending, playlist-next shortcut etc.) - -- has the sideeffect of moving cursor if file happens to change when navigating - -- good side is cursor always following current file when going back and forth files with playlist-next/prev - sync_cursor_on_load = true, + -- sync cursor when file is loaded from outside reasons(file-ending, playlist-next shortcut etc.) + -- has the sideeffect of moving cursor if file happens to change when navigating + -- good side is cursor always following current file when going back and forth files with playlist-next/prev + sync_cursor_on_load = true, - -- playlist open key will toggle visibility instead of refresh, best used with long timeout - open_toggles = true, + -- playlist open key will toggle visibility instead of refresh, best used with long timeout + open_toggles = true, - -- allow the playlist cursor to loop from end to start and vice versa - loop_cursor = true, + -- allow the playlist cursor to loop from end to start and vice versa + loop_cursor = true, - -- #### VISUAL SETTINGS + -- #### VISUAL SETTINGS - -- prefer to display titles for following files: "all", "url", "none". Sorting still uses filename. - prefer_titles = "url", + -- prefer to display titles for following files: "all", "url", "none". Sorting still uses filename. + prefer_titles = "url", - -- call youtube-dl to resolve the titles of urls in the playlist - resolve_titles = false, + -- call youtube-dl to resolve the titles of urls in the playlist + resolve_titles = false, - -- osd timeout on inactivity, with high value on this open_toggles is good to be true - playlist_display_timeout = 5, + -- osd timeout on inactivity, with high value on this open_toggles is good to be true + playlist_display_timeout = 5, - -- amount of entries to show before slicing. Optimal value depends on font/video size etc. - showamount = 16, + -- amount of entries to show before slicing. Optimal value depends on font/video size etc. + showamount = 16, - -- font size scales by window, if false requires larger font and padding sizes - scale_playlist_by_window = true, - -- playlist ass style overrides inside curly brackets, \keyvalue is one field, extra \ for escape in lua - -- example {\\fnUbuntu\\fs10\\b0\\bord1} equals: font=Ubuntu, size=10, bold=no, border=1 - -- read http://docs.aegisub.org/3.2/ASS_Tags/ for reference of tags - -- undeclared tags will use default osd settings - -- these styles will be used for the whole playlist - style_ass_tags = "{}", - -- paddings from top left corner - text_padding_x = 10, - text_padding_y = 30, + -- font size scales by window, if false requires larger font and padding sizes + scale_playlist_by_window = true, + -- playlist ass style overrides inside curly brackets, \keyvalue is one field, extra \ for escape in lua + -- example {\\fnUbuntu\\fs10\\b0\\bord1} equals: font=Ubuntu, size=10, bold=no, border=1 + -- read http://docs.aegisub.org/3.2/ASS_Tags/ for reference of tags + -- undeclared tags will use default osd settings + -- these styles will be used for the whole playlist + style_ass_tags = "{}", + -- paddings from top left corner + text_padding_x = 10, + text_padding_y = 30, - -- set title of window with stripped name - set_title_stripped = false, - title_prefix = "", - title_suffix = " - mpv", + -- set title of window with stripped name + set_title_stripped = false, + title_prefix = "", + title_suffix = " - mpv", - -- slice long filenames, and how many chars to show - slice_longfilenames = false, - slice_longfilenames_amount = 70, + -- slice long filenames, and how many chars to show + slice_longfilenames = false, + slice_longfilenames_amount = 70, - -- Playlist header template - -- %mediatitle or %filename = title or name of playing file - -- %pos = position of playing file - -- %cursor = position of navigation - -- %plen = playlist length - -- %N = newline - playlist_header = "[%cursor/%plen]", + -- Playlist header template + -- %mediatitle or %filename = title or name of playing file + -- %pos = position of playing file + -- %cursor = position of navigation + -- %plen = playlist length + -- %N = newline + playlist_header = "[%cursor/%plen]", - -- Playlist file templates - -- %pos = position of file with leading zeros - -- %name = title or name of file - -- %N = newline - -- you can also use the ass tags mentioned above. For example: - -- selected_file="{\\c&HFF00FF&}➔ %name" | to add a color for selected file. However, if you - -- use ass tags you need to reset them for every line (see https://github.com/jonniek/mpv-playlistmanager/issues/20) - normal_file = "○ %name", - hovered_file = "● %name", - selected_file = "➔ %name", - playing_file = "▷ %name", - playing_hovered_file = "▶ %name", - playing_selected_file = "➤ %name", - - -- what to show when playlist is truncated - playlist_sliced_prefix = "...", - playlist_sliced_suffix = "..." + -- Playlist file templates + -- %pos = position of file with leading zeros + -- %name = title or name of file + -- %N = newline + -- you can also use the ass tags mentioned above. For example: + -- selected_file="{\\c&HFF00FF&}➔ %name" | to add a color for selected file. However, if you + -- use ass tags you need to reset them for every line (see https://github.com/jonniek/mpv-playlistmanager/issues/20) + normal_file = "○ %name", + hovered_file = "● %name", + selected_file = "➔ %name", + playing_file = "▷ %name", + playing_hovered_file = "▶ %name", + playing_selected_file = "➤ %name", + -- what to show when playlist is truncated + playlist_sliced_prefix = "...", + playlist_sliced_suffix = "...", } local opts = require("mp.options") -opts.read_options(settings, "playlistmanager", - function(list) update_opts(list) end) +opts.read_options(settings, "playlistmanager", function(list) + update_opts(list) +end) local utils = require("mp.utils") local msg = require("mp.msg") @@ -167,12 +167,12 @@ local assdraw = require("mp.assdraw") -- check os if settings.system == "auto" then - local o = {} - if mp.get_property_native('options/vo-mmcss-profile', o) ~= o then - settings.system = "windows" - else - settings.system = "linux" - end + local o = {} + if mp.get_property_native("options/vo-mmcss-profile", o) ~= o then + settings.system = "windows" + else + settings.system = "linux" + end end -- global variables @@ -194,387 +194,428 @@ local sort_watching = false local filetype_lookup = {} function update_opts(changelog) - msg.verbose('updating options') + msg.verbose("updating options") - -- parse filename json - if changelog.filename_replace then - if (settings.filename_replace ~= "") then - settings.filename_replace = utils.parse_json( - settings.filename_replace) - else - settings.filename_replace = false - end - end + -- parse filename json + if changelog.filename_replace then + if settings.filename_replace ~= "" then + settings.filename_replace = utils.parse_json(settings.filename_replace) + else + settings.filename_replace = false + end + end - -- parse loadfiles json - if changelog.loadfiles_filetypes then - settings.loadfiles_filetypes = utils.parse_json( - settings.loadfiles_filetypes) + -- parse loadfiles json + if changelog.loadfiles_filetypes then + settings.loadfiles_filetypes = utils.parse_json(settings.loadfiles_filetypes) - filetype_lookup = {} - -- create loadfiles set - for _, ext in ipairs(settings.loadfiles_filetypes) do - filetype_lookup[ext] = true - end - end + filetype_lookup = {} + -- create loadfiles set + for _, ext in ipairs(settings.loadfiles_filetypes) do + filetype_lookup[ext] = true + end + end - if changelog.resolve_titles then resolve_titles() end + if changelog.resolve_titles then + resolve_titles() + end - if changelog.playlist_display_timeout then - keybindstimer = mp.add_periodic_timer(settings.playlist_display_timeout, - remove_keybinds) - keybindstimer:kill() - end + if changelog.playlist_display_timeout then + keybindstimer = mp.add_periodic_timer(settings.playlist_display_timeout, remove_keybinds) + keybindstimer:kill() + end - if playlist_visible then showplaylist() end + if playlist_visible then + showplaylist() + end end -update_opts({filename_replace = true, loadfiles_filetypes = true}) +update_opts({ filename_replace = true, loadfiles_filetypes = true }) function on_loaded() - filename = mp.get_property("filename") - path = mp.get_property('path') - -- if not a url then join path with working directory - if not path:match("^%a%a+:%/%/") then - path = utils.join_path(mp.get_property('working-directory'), path) - directory = utils.split_path(path) - else - directory = nil - end + filename = mp.get_property("filename") + path = mp.get_property("path") + -- if not a url then join path with working directory + if not path:match("^%a%a+:%/%/") then + path = utils.join_path(mp.get_property("working-directory"), path) + directory = utils.split_path(path) + else + directory = nil + end - refresh_globals() - if settings.sync_cursor_on_load then - cursor = pos - -- refresh playlist if cursor moved - if playlist_visible then draw_playlist() end - end + refresh_globals() + if settings.sync_cursor_on_load then + cursor = pos + -- refresh playlist if cursor moved + if playlist_visible then + draw_playlist() + end + end - local media_title = mp.get_property("media-title") - if path:match('^https?://') and not url_table[path] and path ~= media_title then - url_table[path] = media_title - end + local media_title = mp.get_property("media-title") + if path:match("^https?://") and not url_table[path] and path ~= media_title then + url_table[path] = media_title + end - strippedname = stripfilename(mp.get_property('media-title')) - if settings.show_playlist_on_fileload == 2 then - showplaylist() - elseif settings.show_playlist_on_fileload == 1 then - mp.commandv('show-text', strippedname) - end - if settings.set_title_stripped then - mp.set_property("title", settings.title_prefix .. strippedname .. - settings.title_suffix) - end + strippedname = stripfilename(mp.get_property("media-title")) + if settings.show_playlist_on_fileload == 2 then + showplaylist() + elseif settings.show_playlist_on_fileload == 1 then + mp.commandv("show-text", strippedname) + end + if settings.set_title_stripped then + mp.set_property("title", settings.title_prefix .. strippedname .. settings.title_suffix) + end - local didload = false - if settings.loadfiles_on_start and plen == 1 then - didload = true -- save reference for sorting - msg.info("Loading files from playing files directory") - playlist() - end + local didload = false + if settings.loadfiles_on_start and plen == 1 then + didload = true -- save reference for sorting + msg.info("Loading files from playing files directory") + playlist() + end - -- if we promised to sort files on launch do it - if promised_sort then - promised_sort = false - msg.info("Your playlist is sorted before starting playback") - if didload then - sortplaylist() - else - sortplaylist(true) - end - end + -- if we promised to sort files on launch do it + if promised_sort then + promised_sort = false + msg.info("Your playlist is sorted before starting playback") + if didload then + sortplaylist() + else + sortplaylist(true) + end + end - -- if we promised to listen and sort on playlist size increase do it - if promised_sort_watch then - promised_sort_watch = false - sort_watching = true - msg.info("Added files will be automatically sorted") - mp.observe_property('playlist-count', "number", autosort) - end + -- if we promised to listen and sort on playlist size increase do it + if promised_sort_watch then + promised_sort_watch = false + sort_watching = true + msg.info("Added files will be automatically sorted") + mp.observe_property("playlist-count", "number", autosort) + end end function on_closed() - strippedname = nil - path = nil - directory = nil - filename = nil - if playlist_visible then showplaylist() end + strippedname = nil + path = nil + directory = nil + filename = nil + if playlist_visible then + showplaylist() + end end function refresh_globals() - pos = mp.get_property_number('playlist-pos', 0) - plen = mp.get_property_number('playlist-count', 0) + pos = mp.get_property_number("playlist-pos", 0) + plen = mp.get_property_number("playlist-count", 0) end function escapepath(dir, escapechar) - return string.gsub(dir, escapechar, '\\' .. escapechar) + return string.gsub(dir, escapechar, "\\" .. escapechar) end -- strip a filename based on its extension or protocol according to rules in settings function stripfilename(pathfile, media_title) - if pathfile == nil then return '' end - local ext = pathfile:match("^.+%.(.+)$") - local protocol = pathfile:match("^(%a%a+)://") - if not ext then ext = "" end - local tmp = pathfile - if settings.filename_replace and not media_title then - for k, v in ipairs(settings.filename_replace) do - if (v['ext'] and - (v['ext'][ext] or (ext and not protocol and v['ext']['all']))) or - (v['protocol'] and - (v['protocol'][protocol] or - (protocol and not ext and v['protocol']['all']))) then - for ruleindex, indexrules in ipairs(v['rules']) do - for rule, override in pairs(indexrules) do - tmp = tmp:gsub(rule, override) - end - end - end - end - end - if settings.slice_longfilenames and tmp:len() > - settings.slice_longfilenames_amount + 5 then - tmp = tmp:sub(1, settings.slice_longfilenames_amount) .. " ..." - end - return tmp + if pathfile == nil then + return "" + end + local ext = pathfile:match("^.+%.(.+)$") + local protocol = pathfile:match("^(%a%a+)://") + if not ext then + ext = "" + end + local tmp = pathfile + if settings.filename_replace and not media_title then + for k, v in ipairs(settings.filename_replace) do + if + (v["ext"] and (v["ext"][ext] or (ext and not protocol and v["ext"]["all"]))) + or (v["protocol"] and (v["protocol"][protocol] or (protocol and not ext and v["protocol"]["all"]))) + then + for ruleindex, indexrules in ipairs(v["rules"]) do + for rule, override in pairs(indexrules) do + tmp = tmp:gsub(rule, override) + end + end + end + end + end + if settings.slice_longfilenames and tmp:len() > settings.slice_longfilenames_amount + 5 then + tmp = tmp:sub(1, settings.slice_longfilenames_amount) .. " ..." + end + return tmp end -- gets a nicename of playlist entry at 0-based position i function get_name_from_index(i, notitle) - refresh_globals() - if plen <= i then - msg.error("no index in playlist", i, "length", plen); - return nil - end - local _, name = nil - local title = mp.get_property('playlist/' .. i .. '/title') - local name = mp.get_property('playlist/' .. i .. '/filename') + refresh_globals() + if plen <= i then + msg.error("no index in playlist", i, "length", plen) + return nil + end + local _, name = nil + local title = mp.get_property("playlist/" .. i .. "/title") + local name = mp.get_property("playlist/" .. i .. "/filename") - local should_use_title = settings.prefer_titles == 'all' or - name:match('^https?://') and - settings.prefer_titles == 'url' - -- check if file has a media title stored or as property - if not title and should_use_title then - local mtitle = mp.get_property('media-title') - if i == pos and mp.get_property('filename') ~= mtitle then - if not url_table[name] then url_table[name] = mtitle end - title = mtitle - elseif url_table[name] then - title = url_table[name] - end - end + local should_use_title = settings.prefer_titles == "all" + or name:match("^https?://") and settings.prefer_titles == "url" + -- check if file has a media title stored or as property + if not title and should_use_title then + local mtitle = mp.get_property("media-title") + if i == pos and mp.get_property("filename") ~= mtitle then + if not url_table[name] then + url_table[name] = mtitle + end + title = mtitle + elseif url_table[name] then + title = url_table[name] + end + end - -- if we have media title use a more conservative strip - if title and not notitle and should_use_title then - return stripfilename(title, true) - end + -- if we have media title use a more conservative strip + if title and not notitle and should_use_title then + return stripfilename(title, true) + end - -- remove paths if they exist, keeping protocols for stripping - if string.sub(name, 1, 1) == '/' or name:match("^%a:[/\\]") then - _, name = utils.split_path(name) - end - return stripfilename(name) + -- remove paths if they exist, keeping protocols for stripping + if string.sub(name, 1, 1) == "/" or name:match("^%a:[/\\]") then + _, name = utils.split_path(name) + end + return stripfilename(name) end function parse_header(string) - local esc_title = stripfilename(mp.get_property("media-title"), true):gsub( - "%%", "%%%%") - local esc_file = stripfilename(mp.get_property("filename")):gsub("%%", - "%%%%") - return string:gsub("%%N", "\\N"):gsub("%%pos", mp.get_property_number( - "playlist-pos", 0) + 1):gsub( - "%%plen", mp.get_property("playlist-count")):gsub("%%cursor", - cursor + 1) - :gsub("%%mediatitle", esc_title):gsub("%%filename", esc_file) -- undo name escape - :gsub("%%%%", "%%") + local esc_title = stripfilename(mp.get_property("media-title"), true):gsub("%%", "%%%%") + local esc_file = stripfilename(mp.get_property("filename")):gsub("%%", "%%%%") + return string + :gsub("%%N", "\\N") + :gsub("%%pos", mp.get_property_number("playlist-pos", 0) + 1) + :gsub("%%plen", mp.get_property("playlist-count")) + :gsub("%%cursor", cursor + 1) + :gsub("%%mediatitle", esc_title) + :gsub("%%filename", esc_file) -- undo name escape + :gsub("%%%%", "%%") end function parse_filename(string, name, index) - local base = tostring(plen):len() - local esc_name = stripfilename(name):gsub("%%", "%%%%") - return string:gsub("%%N", "\\N"):gsub("%%pos", string.format( - "%0" .. base .. "d", index + 1)) - :gsub("%%name", esc_name) -- undo name escape - :gsub("%%%%", "%%") + local base = tostring(plen):len() + local esc_name = stripfilename(name):gsub("%%", "%%%%") + return string + :gsub("%%N", "\\N") + :gsub("%%pos", string.format("%0" .. base .. "d", index + 1)) + :gsub("%%name", esc_name) -- undo name escape + :gsub("%%%%", "%%") end function parse_filename_by_index(index) - local template = settings.normal_file + local template = settings.normal_file - local is_idle = mp.get_property_native('idle-active') - local position = is_idle and -1 or pos + local is_idle = mp.get_property_native("idle-active") + local position = is_idle and -1 or pos - if index == position then - if index == cursor then - if selection then - template = settings.playing_selected_file - else - template = settings.playing_hovered_file - end - else - template = settings.playing_file - end - elseif index == cursor then - if selection then - template = settings.selected_file - else - template = settings.hovered_file - end - end + if index == position then + if index == cursor then + if selection then + template = settings.playing_selected_file + else + template = settings.playing_hovered_file + end + else + template = settings.playing_file + end + elseif index == cursor then + if selection then + template = settings.selected_file + else + template = settings.hovered_file + end + end - return parse_filename(template, get_name_from_index(index), index) + return parse_filename(template, get_name_from_index(index), index) end function draw_playlist() - refresh_globals() - local ass = assdraw.ass_new() - ass:new_event() - ass:pos(settings.text_padding_x, settings.text_padding_y) - ass:append(settings.style_ass_tags) + refresh_globals() + local ass = assdraw.ass_new() + ass:new_event() + ass:pos(settings.text_padding_x, settings.text_padding_y) + ass:append(settings.style_ass_tags) - if settings.playlist_header ~= "" then - ass:append(parse_header(settings.playlist_header) .. "\\N") - end - local start = cursor - math.floor(settings.showamount / 2) - local showall = false - local showrest = false - if start < 0 then start = 0 end - if plen <= settings.showamount then - start = 0 - showall = true - end - if start > math.max(plen - settings.showamount - 1, 0) then - start = plen - settings.showamount - showrest = true - end - if start > 0 and not showall then - ass:append(settings.playlist_sliced_prefix .. "\\N") - end - for index = start, start + settings.showamount - 1, 1 do - if index == plen then break end + if settings.playlist_header ~= "" then + ass:append(parse_header(settings.playlist_header) .. "\\N") + end + local start = cursor - math.floor(settings.showamount / 2) + local showall = false + local showrest = false + if start < 0 then + start = 0 + end + if plen <= settings.showamount then + start = 0 + showall = true + end + if start > math.max(plen - settings.showamount - 1, 0) then + start = plen - settings.showamount + showrest = true + end + if start > 0 and not showall then + ass:append(settings.playlist_sliced_prefix .. "\\N") + end + for index = start, start + settings.showamount - 1, 1 do + if index == plen then + break + end - ass:append(parse_filename_by_index(index) .. "\\N") - if index == start + settings.showamount - 1 and not showall and - not showrest then ass:append(settings.playlist_sliced_suffix) end - end - local w, h = mp.get_osd_size() - if settings.scale_playlist_by_window then w, h = 0, 0 end - mp.set_osd_ass(w, h, ass.text) + ass:append(parse_filename_by_index(index) .. "\\N") + if index == start + settings.showamount - 1 and not showall and not showrest then + ass:append(settings.playlist_sliced_suffix) + end + end + local w, h = mp.get_osd_size() + if settings.scale_playlist_by_window then + w, h = 0, 0 + end + mp.set_osd_ass(w, h, ass.text) end function toggle_playlist() - if settings.open_toggles then - if playlist_visible then - remove_keybinds() - return - end - end - showplaylist() + if settings.open_toggles then + if playlist_visible then + remove_keybinds() + return + end + end + showplaylist() end function showplaylist(duration) - refresh_globals() - if plen == 0 then return end - playlist_visible = true - add_keybinds() + refresh_globals() + if plen == 0 then + return + end + playlist_visible = true + add_keybinds() - draw_playlist() - keybindstimer:kill() - if duration then - keybindstimer = mp.add_periodic_timer(duration, remove_keybinds) - else - keybindstimer:resume() - end + draw_playlist() + keybindstimer:kill() + if duration then + keybindstimer = mp.add_periodic_timer(duration, remove_keybinds) + else + keybindstimer:resume() + end end selection = nil function selectfile() - refresh_globals() - if plen == 0 then return end - if not selection then - selection = cursor - else - selection = nil - end - showplaylist() + refresh_globals() + if plen == 0 then + return + end + if not selection then + selection = cursor + else + selection = nil + end + showplaylist() end function unselectfile() - selection = nil - showplaylist() + selection = nil + showplaylist() end function removefile() - refresh_globals() - if plen == 0 then return end - selection = nil - if cursor == pos then - mp.command( - "script-message unseenplaylist mark true \"playlistmanager avoid conflict when removing file\"") - end - mp.commandv("playlist-remove", cursor) - if cursor == plen - 1 then cursor = cursor - 1 end - showplaylist() + refresh_globals() + if plen == 0 then + return + end + selection = nil + if cursor == pos then + mp.command('script-message unseenplaylist mark true "playlistmanager avoid conflict when removing file"') + end + mp.commandv("playlist-remove", cursor) + if cursor == plen - 1 then + cursor = cursor - 1 + end + showplaylist() end function moveup() - refresh_globals() - if plen == 0 then return end - if cursor ~= 0 then - if selection then - mp.commandv("playlist-move", cursor, cursor - 1) - end - cursor = cursor - 1 - elseif settings.loop_cursor then - if selection then mp.commandv("playlist-move", cursor, plen) end - cursor = plen - 1 - end - showplaylist() + refresh_globals() + if plen == 0 then + return + end + if cursor ~= 0 then + if selection then + mp.commandv("playlist-move", cursor, cursor - 1) + end + cursor = cursor - 1 + elseif settings.loop_cursor then + if selection then + mp.commandv("playlist-move", cursor, plen) + end + cursor = plen - 1 + end + showplaylist() end function movedown() - refresh_globals() - if plen == 0 then return end - if cursor ~= plen - 1 then - if selection then - mp.commandv("playlist-move", cursor, cursor + 2) - end - cursor = cursor + 1 - elseif settings.loop_cursor then - if selection then mp.commandv("playlist-move", cursor, 0) end - cursor = 0 - end - showplaylist() + refresh_globals() + if plen == 0 then + return + end + if cursor ~= plen - 1 then + if selection then + mp.commandv("playlist-move", cursor, cursor + 2) + end + cursor = cursor + 1 + elseif settings.loop_cursor then + if selection then + mp.commandv("playlist-move", cursor, 0) + end + cursor = 0 + end + showplaylist() end function Watch_later() - if mp.get_property_bool("save-position-on-quit") then - mp.command("write-watch-later-config") - end + if mp.get_property_bool("save-position-on-quit") then + mp.command("write-watch-later-config") + end end function playfile() - refresh_globals() - if plen == 0 then return end - selection = nil - local is_idle = mp.get_property_native('idle-active') - if cursor ~= pos or is_idle then - mp.set_property("playlist-pos", cursor) - else - if cursor ~= plen - 1 then cursor = cursor + 1 end - Watch_later() - mp.commandv("playlist-next", "weak") - end - if settings.show_playlist_on_fileload ~= 2 then remove_keybinds() end + refresh_globals() + if plen == 0 then + return + end + selection = nil + local is_idle = mp.get_property_native("idle-active") + if cursor ~= pos or is_idle then + mp.set_property("playlist-pos", cursor) + else + if cursor ~= plen - 1 then + cursor = cursor + 1 + end + Watch_later() + mp.commandv("playlist-next", "weak") + end + if settings.show_playlist_on_fileload ~= 2 then + remove_keybinds() + end end function get_files_windows(dir) - local args = { - 'powershell', '-NoProfile', '-Command', [[& { + local args = { + "powershell", + "-NoProfile", + "-Command", + [[& { Trap { Write-Error -ErrorRecord $_ Exit 1 } - $path = "]] .. dir .. [[" + $path = "]] + .. dir + .. [[" $escapedPath = [WildcardPattern]::Escape($path) cd $escapedPath @@ -582,420 +623,469 @@ function get_files_windows(dir) $string = ($list -join "/") $u8list = [System.Text.Encoding]::UTF8.GetBytes($string) [Console]::OpenStandardOutput().Write($u8list, 0, $u8list.Length) - }]] - } - local process = utils.subprocess({args = args, cancellable = false}) - return parse_files(process, '%/') + }]], + } + local process = utils.subprocess({ args = args, cancellable = false }) + return parse_files(process, "%/") end function get_files_linux(dir) - local args = {'ls', '-1pv', dir} - local process = utils.subprocess({args = args, cancellable = false}) - return parse_files(process, '\n') + local args = { "ls", "-1pv", dir } + local process = utils.subprocess({ args = args, cancellable = false }) + return parse_files(process, "\n") end function parse_files(res, delimiter) - if not res.error and res.status == 0 then - local valid_files = {} - for line in res.stdout:gmatch("[^" .. delimiter .. "]+") do - local ext = line:match("^.+%.(.+)$") - if ext and filetype_lookup[ext:lower()] then - table.insert(valid_files, line) - end - end - return valid_files, nil - else - return nil, res.error - end + if not res.error and res.status == 0 then + local valid_files = {} + for line in res.stdout:gmatch("[^" .. delimiter .. "]+") do + local ext = line:match("^.+%.(.+)$") + if ext and filetype_lookup[ext:lower()] then + table.insert(valid_files, line) + end + end + return valid_files, nil + else + return nil, res.error + end end -- Creates a playlist of all files in directory, will keep the order and position -- For exaple, Folder has 12 files, you open the 5th file and run this, the remaining 7 are added behind the 5th file and prior 4 files before it function playlist(force_dir) - refresh_globals() - if not directory and plen > 0 then return end - local hasfile = true - if plen == 0 then - hasfile = false - dir = mp.get_property('working-directory') - else - dir = directory - end - if force_dir then dir = force_dir end + refresh_globals() + if not directory and plen > 0 then + return + end + local hasfile = true + if plen == 0 then + hasfile = false + dir = mp.get_property("working-directory") + else + dir = directory + end + if force_dir then + dir = force_dir + end - local files, error - if settings.system == "linux" then - files, error = get_files_linux(dir) - else - files, error = get_files_windows(dir) - end + local files, error + if settings.system == "linux" then + files, error = get_files_linux(dir) + else + files, error = get_files_windows(dir) + end - local c, c2 = 0, 0 - if files then - local cur = false - local filename = mp.get_property("filename") - for _, file in ipairs(files) do - local appendstr = "append" - if not hasfile then - cur = true - appendstr = "append-play" - hasfile = true - end - if cur == true then - mp.commandv("loadfile", utils.join_path(dir, file), appendstr) - msg.info("Appended to playlist: " .. file) - c2 = c2 + 1 - elseif file ~= filename then - mp.commandv("loadfile", utils.join_path(dir, file), appendstr) - msg.info("Prepended to playlist: " .. file) - mp.commandv("playlist-move", - mp.get_property_number("playlist-count", 1) - 1, c) - c = c + 1 - else - cur = true - end - end - if c2 > 0 or c > 0 then - mp.osd_message("Added " .. c + c2 .. " files to playlist") - else - mp.osd_message("No additional files found") - end - cursor = mp.get_property_number('playlist-pos', 1) - else - msg.error("Could not scan for files: " .. (error or "")) - end - if sort_watching then - msg.info("Ignoring directory structure and using playlist sort") - sortplaylist() - end - refresh_globals() - if playlist_visible then showplaylist() end - return c + c2 + local c, c2 = 0, 0 + if files then + local cur = false + local filename = mp.get_property("filename") + for _, file in ipairs(files) do + local appendstr = "append" + if not hasfile then + cur = true + appendstr = "append-play" + hasfile = true + end + if cur == true then + mp.commandv("loadfile", utils.join_path(dir, file), appendstr) + msg.info("Appended to playlist: " .. file) + c2 = c2 + 1 + elseif file ~= filename then + mp.commandv("loadfile", utils.join_path(dir, file), appendstr) + msg.info("Prepended to playlist: " .. file) + mp.commandv("playlist-move", mp.get_property_number("playlist-count", 1) - 1, c) + c = c + 1 + else + cur = true + end + end + if c2 > 0 or c > 0 then + mp.osd_message("Added " .. c + c2 .. " files to playlist") + else + mp.osd_message("No additional files found") + end + cursor = mp.get_property_number("playlist-pos", 1) + else + msg.error("Could not scan for files: " .. (error or "")) + end + if sort_watching then + msg.info("Ignoring directory structure and using playlist sort") + sortplaylist() + end + refresh_globals() + if playlist_visible then + showplaylist() + end + return c + c2 end function parse_home(path) - if not path:find("^~") then return path end - local home_dir = os.getenv("HOME") or os.getenv("USERPROFILE") - if not home_dir then - local drive = os.getenv("HOMEDRIVE") - local path = os.getenv("HOMEPATH") - if drive and path then - home_dir = utils.join_path(drive, path) - else - msg.error("Couldn't find home dir.") - return nil - end - end - local result = path:gsub("^~", home_dir) - return result + if not path:find("^~") then + return path + end + local home_dir = os.getenv("HOME") or os.getenv("USERPROFILE") + if not home_dir then + local drive = os.getenv("HOMEDRIVE") + local path = os.getenv("HOMEPATH") + if drive and path then + home_dir = utils.join_path(drive, path) + else + msg.error("Couldn't find home dir.") + return nil + end + end + local result = path:gsub("^~", home_dir) + return result end -- saves the current playlist into a m3u file function save_playlist() - local length = mp.get_property_number('playlist-count', 0) - if length == 0 then return end + local length = mp.get_property_number("playlist-count", 0) + if length == 0 then + return + end - -- get playlist save path - local savepath - if settings.playlist_savepath == nil or settings.playlist_savepath == "" then - savepath = mp.command_native({"expand-path", "~~home/"}) .. "/playlists" - else - savepath = parse_home(settings.playlist_savepath) - if savepath == nil then return end - end + -- get playlist save path + local savepath + if settings.playlist_savepath == nil or settings.playlist_savepath == "" then + savepath = mp.command_native({ "expand-path", "~~home/" }) .. "/playlists" + else + savepath = parse_home(settings.playlist_savepath) + if savepath == nil then + return + end + end - -- create savepath if it doesn't exist - if utils.readdir(savepath) == nil then - local windows_args = { - 'powershell', '-NoProfile', '-Command', 'mkdir', savepath - } - local unix_args = {'mkdir', savepath} - local args = settings.system == 'windows' and windows_args or unix_args - local res = utils.subprocess({args = args, cancellable = false}) - if res.status ~= 0 then - msg.error("Failed to create playlist save directory " .. savepath .. - ". Error: " .. (res.error or "unknown")) - return - end - end + -- create savepath if it doesn't exist + if utils.readdir(savepath) == nil then + local windows_args = { + "powershell", + "-NoProfile", + "-Command", + "mkdir", + savepath, + } + local unix_args = { "mkdir", savepath } + local args = settings.system == "windows" and windows_args or unix_args + local res = utils.subprocess({ args = args, cancellable = false }) + if res.status ~= 0 then + msg.error( + "Failed to create playlist save directory " .. savepath .. ". Error: " .. (res.error or "unknown") + ) + return + end + end - local date = os.date("*t") - local datestring = ("%02d-%02d-%02d_%02d-%02d-%02d"):format(date.year, - date.month, - date.day, - date.hour, - date.min, - date.sec) + local date = os.date("*t") + local datestring = ("%02d-%02d-%02d_%02d-%02d-%02d"):format( + date.year, + date.month, + date.day, + date.hour, + date.min, + date.sec + ) - local savepath = utils.join_path(savepath, - datestring .. "_playlist-size_" .. length .. - ".m3u") - local file, err = io.open(savepath, "w") - if not file then - msg.error( - "Error in creating playlist file, check permissions. Error: " .. - (err or "unknown")) - else - local i = 0 - while i < length do - local pwd = mp.get_property("working-directory") - local filename = mp.get_property('playlist/' .. i .. '/filename') - local fullpath = filename - if not filename:match("^%a%a+:%/%/") then - fullpath = utils.join_path(pwd, filename) - end - local title = mp.get_property('playlist/' .. i .. '/title') - if title then file:write("#EXTINF:," .. title .. "\n") end - file:write(fullpath, "\n") - i = i + 1 - end - msg.info("Playlist written to: " .. savepath) - file:close() - end + local savepath = utils.join_path(savepath, datestring .. "_playlist-size_" .. length .. ".m3u") + local file, err = io.open(savepath, "w") + if not file then + msg.error("Error in creating playlist file, check permissions. Error: " .. (err or "unknown")) + else + local i = 0 + while i < length do + local pwd = mp.get_property("working-directory") + local filename = mp.get_property("playlist/" .. i .. "/filename") + local fullpath = filename + if not filename:match("^%a%a+:%/%/") then + fullpath = utils.join_path(pwd, filename) + end + local title = mp.get_property("playlist/" .. i .. "/title") + if title then + file:write("#EXTINF:," .. title .. "\n") + end + file:write(fullpath, "\n") + i = i + 1 + end + msg.info("Playlist written to: " .. savepath) + file:close() + end end function alphanumsort(a, b) - local function padnum(d) - local dec, n = string.match(d, "(%.?)0*(.+)") - return #dec > 0 and ("%.12f"):format(d) or - ("%s%03d%s"):format(dec, #n, n) - end - return tostring(a):lower():gsub("%.?%d+", padnum) .. ("%3d"):format(#b) < - tostring(b):lower():gsub("%.?%d+", padnum) .. ("%3d"):format(#a) + local function padnum(d) + local dec, n = string.match(d, "(%.?)0*(.+)") + return #dec > 0 and ("%.12f"):format(d) or ("%s%03d%s"):format(dec, #n, n) + end + return tostring(a):lower():gsub("%.?%d+", padnum) .. ("%3d"):format(#b) + < tostring(b):lower():gsub("%.?%d+", padnum) .. ("%3d"):format(#a) end function dosort(a, b) - if settings.alphanumsort then - return alphanumsort(a, b) - else - return a < b - end + if settings.alphanumsort then + return alphanumsort(a, b) + else + return a < b + end end function sortplaylist(startover) - local length = mp.get_property_number('playlist-count', 0) - if length < 2 then return end - -- use insertion sort on playlist to make it easy to order files with playlist-move - for outer = 1, length - 1, 1 do - local outerfile = get_name_from_index(outer, true) - local inner = outer - 1 - while inner >= 0 and dosort(outerfile, get_name_from_index(inner, true)) do - inner = inner - 1 - end - inner = inner + 1 - if outer ~= inner then mp.commandv('playlist-move', outer, inner) end - end - cursor = mp.get_property_number('playlist-pos', 0) - if startover then mp.set_property('playlist-pos', 0) end - if playlist_visible then showplaylist() end + local length = mp.get_property_number("playlist-count", 0) + if length < 2 then + return + end + -- use insertion sort on playlist to make it easy to order files with playlist-move + for outer = 1, length - 1, 1 do + local outerfile = get_name_from_index(outer, true) + local inner = outer - 1 + while inner >= 0 and dosort(outerfile, get_name_from_index(inner, true)) do + inner = inner - 1 + end + inner = inner + 1 + if outer ~= inner then + mp.commandv("playlist-move", outer, inner) + end + end + cursor = mp.get_property_number("playlist-pos", 0) + if startover then + mp.set_property("playlist-pos", 0) + end + if playlist_visible then + showplaylist() + end end function autosort(name, param) - if param == 0 then return end - if plen < param then - msg.info("Playlistmanager autosorting playlist") - refresh_globals() - sortplaylist() - end + if param == 0 then + return + end + if plen < param then + msg.info("Playlistmanager autosorting playlist") + refresh_globals() + sortplaylist() + end end function reverseplaylist() - local length = mp.get_property_number('playlist-count', 0) - if length < 2 then return end - for outer = 1, length - 1, 1 do mp.commandv('playlist-move', outer, 0) end - if playlist_visible then showplaylist() end + local length = mp.get_property_number("playlist-count", 0) + if length < 2 then + return + end + for outer = 1, length - 1, 1 do + mp.commandv("playlist-move", outer, 0) + end + if playlist_visible then + showplaylist() + end end function shuffleplaylist() - refresh_globals() - if plen < 2 then return end - mp.command("playlist-shuffle") - math.randomseed(os.time()) - mp.commandv("playlist-move", pos, math.random(0, plen - 1)) - mp.set_property('playlist-pos', 0) - refresh_globals() - if playlist_visible then showplaylist() end + refresh_globals() + if plen < 2 then + return + end + mp.command("playlist-shuffle") + math.randomseed(os.time()) + mp.commandv("playlist-move", pos, math.random(0, plen - 1)) + mp.set_property("playlist-pos", 0) + refresh_globals() + if playlist_visible then + showplaylist() + end end function bind_keys(keys, name, func, opts) - if not keys then - mp.add_forced_key_binding(keys, name, func, opts) - return - end - local i = 1 - for key in keys:gmatch("[^%s]+") do - local prefix = i == 1 and '' or i - mp.add_forced_key_binding(key, name .. prefix, func, opts) - i = i + 1 - end + if not keys then + mp.add_forced_key_binding(keys, name, func, opts) + return + end + local i = 1 + for key in keys:gmatch("[^%s]+") do + local prefix = i == 1 and "" or i + mp.add_forced_key_binding(key, name .. prefix, func, opts) + i = i + 1 + end end function unbind_keys(keys, name) - if not keys then - mp.remove_key_binding(name) - return - end - local i = 1 - for key in keys:gmatch("[^%s]+") do - local prefix = i == 1 and '' or i - mp.remove_key_binding(name .. prefix) - i = i + 1 - end + if not keys then + mp.remove_key_binding(name) + return + end + local i = 1 + for key in keys:gmatch("[^%s]+") do + local prefix = i == 1 and "" or i + mp.remove_key_binding(name .. prefix) + i = i + 1 + end end function add_keybinds() - bind_keys(settings.key_moveup, 'moveup', moveup, "repeatable") - bind_keys(settings.key_movedown, 'movedown', movedown, "repeatable") - bind_keys(settings.key_selectfile, 'selectfile', selectfile) - bind_keys(settings.key_unselectfile, 'unselectfile', unselectfile) - bind_keys(settings.key_playfile, 'playfile', playfile) - bind_keys(settings.key_removefile, 'removefile', removefile, "repeatable") - bind_keys(settings.key_closeplaylist, 'closeplaylist', remove_keybinds) + bind_keys(settings.key_moveup, "moveup", moveup, "repeatable") + bind_keys(settings.key_movedown, "movedown", movedown, "repeatable") + bind_keys(settings.key_selectfile, "selectfile", selectfile) + bind_keys(settings.key_unselectfile, "unselectfile", unselectfile) + bind_keys(settings.key_playfile, "playfile", playfile) + bind_keys(settings.key_removefile, "removefile", removefile, "repeatable") + bind_keys(settings.key_closeplaylist, "closeplaylist", remove_keybinds) end function remove_keybinds() - keybindstimer:kill() - keybindstimer = mp.add_periodic_timer(settings.playlist_display_timeout, - remove_keybinds) - keybindstimer:kill() - mp.set_osd_ass(0, 0, "") - playlist_visible = false - if settings.dynamic_binds then - unbind_keys(settings.key_moveup, 'moveup') - unbind_keys(settings.key_movedown, 'movedown') - unbind_keys(settings.key_selectfile, 'selectfile') - unbind_keys(settings.key_unselectfile, 'unselectfile') - unbind_keys(settings.key_playfile, 'playfile') - unbind_keys(settings.key_removefile, 'removefile') - unbind_keys(settings.key_closeplaylist, 'closeplaylist') - end + keybindstimer:kill() + keybindstimer = mp.add_periodic_timer(settings.playlist_display_timeout, remove_keybinds) + keybindstimer:kill() + mp.set_osd_ass(0, 0, "") + playlist_visible = false + if settings.dynamic_binds then + unbind_keys(settings.key_moveup, "moveup") + unbind_keys(settings.key_movedown, "movedown") + unbind_keys(settings.key_selectfile, "selectfile") + unbind_keys(settings.key_unselectfile, "unselectfile") + unbind_keys(settings.key_playfile, "playfile") + unbind_keys(settings.key_removefile, "removefile") + unbind_keys(settings.key_closeplaylist, "closeplaylist") + end end -keybindstimer = mp.add_periodic_timer(settings.playlist_display_timeout, - remove_keybinds) +keybindstimer = mp.add_periodic_timer(settings.playlist_display_timeout, remove_keybinds) keybindstimer:kill() -if not settings.dynamic_binds then add_keybinds() end +if not settings.dynamic_binds then + add_keybinds() +end -if settings.loadfiles_on_start and mp.get_property_number('playlist-count', 0) == - 0 then playlist() end +if settings.loadfiles_on_start and mp.get_property_number("playlist-count", 0) == 0 then + playlist() +end promised_sort_watch = false -if settings.sortplaylist_on_file_add then promised_sort_watch = true end +if settings.sortplaylist_on_file_add then + promised_sort_watch = true +end promised_sort = false -if settings.sortplaylist_on_start then promised_sort = true end +if settings.sortplaylist_on_start then + promised_sort = true +end -mp.observe_property('playlist-count', "number", function() - if playlist_visible then showplaylist() end - if settings.prefer_titles == 'none' then return end - -- resolve titles - resolve_titles() +mp.observe_property("playlist-count", "number", function() + if playlist_visible then + showplaylist() + end + if settings.prefer_titles == "none" then + return + end + -- resolve titles + resolve_titles() end) -- resolves url titles by calling youtube-dl function resolve_titles() - if not settings.resolve_titles then return end - local length = mp.get_property_number('playlist-count', 0) - if length < 2 then return end - local i = 0 - -- loop all items in playlist because we can't predict how it has changed - while i < length do - local filename = mp.get_property('playlist/' .. i .. '/filename') - local title = mp.get_property('playlist/' .. i .. '/title') - if i ~= pos and filename and filename:match('^https?://') and not title and - not url_table[filename] and not requested_urls[filename] then - requested_urls[filename] = true + if not settings.resolve_titles then + return + end + local length = mp.get_property_number("playlist-count", 0) + if length < 2 then + return + end + local i = 0 + -- loop all items in playlist because we can't predict how it has changed + while i < length do + local filename = mp.get_property("playlist/" .. i .. "/filename") + local title = mp.get_property("playlist/" .. i .. "/title") + if + i ~= pos + and filename + and filename:match("^https?://") + and not title + and not url_table[filename] + and not requested_urls[filename] + then + requested_urls[filename] = true - local args = { - 'youtube-dl', '--no-playlist', '--flat-playlist', '-sJ', - filename - } - local req = mp.command_native_async( - { - name = "subprocess", - args = args, - playback_only = false, - capture_stdout = true - }, function(success, res) - if res.killed_by_us then - msg.verbose( - 'Request to resolve url title ' .. filename .. - ' timed out') - return - end - if res.status == 0 then - local json, err = utils.parse_json(res.stdout) - if not err then - local is_playlist = - json['_type'] and json['_type'] == 'playlist' - local title = - (is_playlist and '[playlist]: ' or '') .. - json['title'] - msg.verbose(filename .. " resolved to '" .. title .. - "'") - url_table[filename] = title - refresh_globals() - if playlist_visible then - showplaylist() - end - return - else - msg.error("Failed parsing json, reason: " .. - (err or "unknown")) - end - else - msg.error("Failed to resolve url title " .. filename .. - " Error: " .. (res.error or "unknown")) - end - end) + local args = { + "youtube-dl", + "--no-playlist", + "--flat-playlist", + "-sJ", + filename, + } + local req = mp.command_native_async({ + name = "subprocess", + args = args, + playback_only = false, + capture_stdout = true, + }, function(success, res) + if res.killed_by_us then + msg.verbose("Request to resolve url title " .. filename .. " timed out") + return + end + if res.status == 0 then + local json, err = utils.parse_json(res.stdout) + if not err then + local is_playlist = json["_type"] and json["_type"] == "playlist" + local title = (is_playlist and "[playlist]: " or "") .. json["title"] + msg.verbose(filename .. " resolved to '" .. title .. "'") + url_table[filename] = title + refresh_globals() + if playlist_visible then + showplaylist() + end + return + else + msg.error("Failed parsing json, reason: " .. (err or "unknown")) + end + else + msg.error("Failed to resolve url title " .. filename .. " Error: " .. (res.error or "unknown")) + end + end) - mp.add_timeout(5, function() mp.abort_async_command(req) end) - - end - i = i + 1 - end + mp.add_timeout(5, function() + mp.abort_async_command(req) + end) + end + i = i + 1 + end end -- script message handler function handlemessage(msg, value, value2) - if msg == "show" and value == "playlist" then - if value2 ~= "toggle" then - showplaylist(value2) - return - else - toggle_playlist() - return - end - end - if msg == "show" and value == "filename" and strippedname and value2 then - mp.commandv('show-text', strippedname, tonumber(value2) * 1000); - return - end - if msg == "show" and value == "filename" and strippedname then - mp.commandv('show-text', strippedname); - return - end - if msg == "sort" then - sortplaylist(value); - return - end - if msg == "shuffle" then - shuffleplaylist(); - return - end - if msg == "reverse" then - reverseplaylist(); - return - end - if msg == "loadfiles" then - playlist(value); - return - end - if msg == "save" then - save_playlist(); - return - end + if msg == "show" and value == "playlist" then + if value2 ~= "toggle" then + showplaylist(value2) + return + else + toggle_playlist() + return + end + end + if msg == "show" and value == "filename" and strippedname and value2 then + mp.commandv("show-text", strippedname, tonumber(value2) * 1000) + return + end + if msg == "show" and value == "filename" and strippedname then + mp.commandv("show-text", strippedname) + return + end + if msg == "sort" then + sortplaylist(value) + return + end + if msg == "shuffle" then + shuffleplaylist() + return + end + if msg == "reverse" then + reverseplaylist() + return + end + if msg == "loadfiles" then + playlist(value) + return + end + if msg == "save" then + save_playlist() + return + end end mp.register_script_message("playlistmanager", handlemessage) diff --git a/multimedia/.config/mpv/scripts/sponsorblock_minimal.lua b/multimedia/.config/mpv/scripts/sponsorblock_minimal.lua index 907a191..b29c1cf 100644 --- a/multimedia/.config/mpv/scripts/sponsorblock_minimal.lua +++ b/multimedia/.config/mpv/scripts/sponsorblock_minimal.lua @@ -5,90 +5,93 @@ -- -- original from https://codeberg.org/jouni/mpv_sponsorblock_minimal -- adapted for local playback skipping and some refactoring by me -local mp = require 'mp' +local mp = require("mp") local options = { - API = "https://sponsor.ajay.app/api/skipSegments", + API = "https://sponsor.ajay.app/api/skipSegments", - -- Categories to fetch and skip - categories = '"sponsor","intro","outro","interaction","selfpromo"' + -- Categories to fetch and skip + categories = '"sponsor","intro","outro","interaction","selfpromo"', } local function getranges() - local args = { - "curl", "-s", "-d", "videoID=" .. Youtube_id, "-d", - "categories=[" .. options.categories .. "]", "-G", options.API - } - local sponsors = mp.command_native({ - name = "subprocess", - capture_stdout = true, - playback_only = false, - args = args - }) + local args = { + "curl", + "-s", + "-d", + "videoID=" .. Youtube_id, + "-d", + "categories=[" .. options.categories .. "]", + "-G", + options.API, + } + local sponsors = mp.command_native({ + name = "subprocess", + capture_stdout = true, + playback_only = false, + args = args, + }) - if string.match(sponsors.stdout, "%[(.-)%]") then - Ranges = {} - for i in string.gmatch(string.sub(sponsors.stdout, 2, -2), "%[(.-)%]") do - local k, v = string.match(i, "(%d+.?%d*),(%d+.?%d*)") - Ranges[k] = v - end - end + if string.match(sponsors.stdout, "%[(.-)%]") then + Ranges = {} + for i in string.gmatch(string.sub(sponsors.stdout, 2, -2), "%[(.-)%]") do + local k, v = string.match(i, "(%d+.?%d*),(%d+.?%d*)") + Ranges[k] = v + end + end end local function skip_ads(_, pos) - if pos ~= nil then - for k, v in pairs(Ranges) do - if tonumber(k) <= pos and tonumber(v) > pos then - -- this message may sometimes be wrong - -- it only seems to be a visual thing though - mp.osd_message("[sponsorblock] skipping forward " .. - math.floor( - tonumber(v) - mp.get_property("time-pos")) .. - "s") - -- need to do the +0.01 otherwise mpv will start spamming skip sometimes - -- example: https://www.youtube.com/watch?v=4ypMJzeNooo - mp.set_property("time-pos", tonumber(v) + 0.01) - return - end - end - end + if pos ~= nil then + for k, v in pairs(Ranges) do + if tonumber(k) <= pos and tonumber(v) > pos then + -- this message may sometimes be wrong + -- it only seems to be a visual thing though + mp.osd_message( + "[sponsorblock] skipping forward " .. math.floor(tonumber(v) - mp.get_property("time-pos")) .. "s" + ) + -- need to do the +0.01 otherwise mpv will start spamming skip sometimes + -- example: https://www.youtube.com/watch?v=4ypMJzeNooo + mp.set_property("time-pos", tonumber(v) + 0.01) + return + end + end + end end local function file_loaded() - local video_path = mp.get_property("path") - local youtube_id1 = string.match(video_path, - "https?://youtu%.be/([%w-_]+).*") - local youtube_id2 = string.match(video_path, - "https?://w?w?w?%.?youtube%.com/v/([%w-_]+).*") - local youtube_id3 = string.match(video_path, "/watch.*[?&]v=([%w-_]+).*") - local youtube_id4 = string.match(video_path, "/embed/([%w-_]+).*") - local localytfile = string.match(video_path, - "-([%a%d%-_]+)%.[mw][kpe][v4b][m]?$") - Youtube_id = youtube_id1 or youtube_id2 or youtube_id3 or youtube_id4 or - localytfile - if not Youtube_id or string.len(Youtube_id) < 11 then return end - Youtube_id = string.sub(Youtube_id, 1, 11) + local video_path = mp.get_property("path") + local youtube_id1 = string.match(video_path, "https?://youtu%.be/([%w-_]+).*") + local youtube_id2 = string.match(video_path, "https?://w?w?w?%.?youtube%.com/v/([%w-_]+).*") + local youtube_id3 = string.match(video_path, "/watch.*[?&]v=([%w-_]+).*") + local youtube_id4 = string.match(video_path, "/embed/([%w-_]+).*") + local localytfile = string.match(video_path, "-([%a%d%-_]+)%.[mw][kpe][v4b][m]?$") + Youtube_id = youtube_id1 or youtube_id2 or youtube_id3 or youtube_id4 or localytfile + if not Youtube_id or string.len(Youtube_id) < 11 then + return + end + Youtube_id = string.sub(Youtube_id, 1, 11) - getranges() - if Ranges then - ON = true - mp.add_key_binding("b", "sponsorblock", toggle) - mp.observe_property("time-pos", "native", skip_ads) - end - return + getranges() + if Ranges then + ON = true + mp.add_key_binding("b", "sponsorblock", toggle) + mp.observe_property("time-pos", "native", skip_ads) + end + return end local function toggle() - if ON then - mp.unobserve_property(skip_ads) - mp.osd_message("[sponsorblock] off") - ON = false - return - end - mp.observe_property("time-pos", "native", skip_ads) - mp.osd_message("[sponsorblock] on") - ON = true - return + if ON then + mp.unobserve_property(skip_ads) + mp.osd_message("[sponsorblock] off") + ON = false + return + end + mp.observe_property("time-pos", "native", skip_ads) + mp.osd_message("[sponsorblock] on") + ON = true + return end mp.register_event("file-loaded", file_loaded) diff --git a/multimedia/.config/mpv/scripts/thumbfast.lua b/multimedia/.config/mpv/scripts/thumbfast.lua index e01ef4f..23707d3 100644 --- a/multimedia/.config/mpv/scripts/thumbfast.lua +++ b/multimedia/.config/mpv/scripts/thumbfast.lua @@ -5,48 +5,48 @@ -- Built for easy integration in third-party UIs. local options = { - -- Socket path (leave empty for auto) - socket = "", + -- Socket path (leave empty for auto) + socket = "", - -- Thumbnail path (leave empty for auto) - thumbnail = "", + -- Thumbnail path (leave empty for auto) + thumbnail = "", - -- Maximum thumbnail size in pixels (scaled down to fit) - -- Values are scaled when hidpi is enabled - max_height = 200, - max_width = 200, + -- Maximum thumbnail size in pixels (scaled down to fit) + -- Values are scaled when hidpi is enabled + max_height = 200, + max_width = 200, - -- Apply tone-mapping, no to disable - tone_mapping = "auto", + -- Apply tone-mapping, no to disable + tone_mapping = "auto", - -- Overlay id - overlay_id = 42, + -- Overlay id + overlay_id = 42, - -- Spawn thumbnailer on file load for faster initial thumbnails - spawn_first = false, + -- Spawn thumbnailer on file load for faster initial thumbnails + spawn_first = false, - -- Close thumbnailer process after an inactivity period in seconds, 0 to disable - quit_after_inactivity = 0, + -- Close thumbnailer process after an inactivity period in seconds, 0 to disable + quit_after_inactivity = 0, - -- Enable on network playback - network = false, + -- Enable on network playback + network = false, - -- Enable on audio playback - audio = false, + -- Enable on audio playback + audio = false, - -- Enable hardware decoding - hwdec = false, + -- Enable hardware decoding + hwdec = false, - -- Windows only: use native Windows API to write to pipe (requires LuaJIT) - direct_io = false, + -- Windows only: use native Windows API to write to pipe (requires LuaJIT) + direct_io = false, - -- Custom path to the mpv executable - mpv_path = "mpv" + -- Custom path to the mpv executable + mpv_path = "mpv", } -local mp = require "mp" -mp.utils = require "mp.utils" -mp.options = require "mp.options" +local mp = require("mp") +mp.utils = require("mp.utils") +mp.options = require("mp.options") mp.options.read_options(options, "thumbfast") local properties = {} @@ -54,73 +54,72 @@ local pre_0_30_0 = mp.command_native_async == nil local pre_0_33_0 = true function subprocess(args, async, callback) - callback = callback or function() - end + callback = callback or function() end - if not pre_0_30_0 then - if async then - return mp.command_native_async({ name = "subprocess", playback_only = true, args = args }, callback) - else - return mp.command_native({ name = "subprocess", playback_only = false, capture_stdout = true, args = args }) - end - else - if async then - return mp.utils.subprocess_detached({ args = args }, callback) - else - return mp.utils.subprocess({ args = args }) - end - end + if not pre_0_30_0 then + if async then + return mp.command_native_async({ name = "subprocess", playback_only = true, args = args }, callback) + else + return mp.command_native({ name = "subprocess", playback_only = false, capture_stdout = true, args = args }) + end + else + if async then + return mp.utils.subprocess_detached({ args = args }, callback) + else + return mp.utils.subprocess({ args = args }) + end + end end local winapi = {} if options.direct_io then - local ffi_loaded, ffi = pcall(require, "ffi") - if ffi_loaded then - winapi = { - ffi = ffi, - C = ffi.C, - bit = require("bit"), - socket_wc = "", + local ffi_loaded, ffi = pcall(require, "ffi") + if ffi_loaded then + winapi = { + ffi = ffi, + C = ffi.C, + bit = require("bit"), + socket_wc = "", - -- WinAPI constants - CP_UTF8 = 65001, - GENERIC_WRITE = 0x40000000, - OPEN_EXISTING = 3, - FILE_FLAG_WRITE_THROUGH = 0x80000000, - FILE_FLAG_NO_BUFFERING = 0x20000000, - PIPE_NOWAIT = ffi.new("unsigned long[1]", 0x00000001), + -- WinAPI constants + CP_UTF8 = 65001, + GENERIC_WRITE = 0x40000000, + OPEN_EXISTING = 3, + FILE_FLAG_WRITE_THROUGH = 0x80000000, + FILE_FLAG_NO_BUFFERING = 0x20000000, + PIPE_NOWAIT = ffi.new("unsigned long[1]", 0x00000001), - INVALID_HANDLE_VALUE = ffi.cast("void*", -1), + INVALID_HANDLE_VALUE = ffi.cast("void*", -1), - -- don't care about how many bytes WriteFile wrote, so allocate something to store the result once - _lpNumberOfBytesWritten = ffi.new("unsigned long[1]"), - } - -- cache flags used in run() to avoid bor() call - winapi._createfile_pipe_flags = winapi.bit.bor(winapi.FILE_FLAG_WRITE_THROUGH, winapi.FILE_FLAG_NO_BUFFERING) + -- don't care about how many bytes WriteFile wrote, so allocate something to store the result once + _lpNumberOfBytesWritten = ffi.new("unsigned long[1]"), + } + -- cache flags used in run() to avoid bor() call + winapi._createfile_pipe_flags = winapi.bit.bor(winapi.FILE_FLAG_WRITE_THROUGH, winapi.FILE_FLAG_NO_BUFFERING) - ffi.cdef [[ + ffi.cdef([[ void* __stdcall CreateFileW(const wchar_t *lpFileName, unsigned long dwDesiredAccess, unsigned long dwShareMode, void *lpSecurityAttributes, unsigned long dwCreationDisposition, unsigned long dwFlagsAndAttributes, void *hTemplateFile); bool __stdcall WriteFile(void *hFile, const void *lpBuffer, unsigned long nNumberOfBytesToWrite, unsigned long *lpNumberOfBytesWritten, void *lpOverlapped); bool __stdcall CloseHandle(void *hObject); bool __stdcall SetNamedPipeHandleState(void *hNamedPipe, unsigned long *lpMode, unsigned long *lpMaxCollectionCount, unsigned long *lpCollectDataTimeout); int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar); - ]] + ]]) - winapi.MultiByteToWideChar = function(MultiByteStr) - if MultiByteStr then - local utf16_len = winapi.C.MultiByteToWideChar(winapi.CP_UTF8, 0, MultiByteStr, -1, nil, 0) - if utf16_len > 0 then - local utf16_str = winapi.ffi.new("wchar_t[?]", utf16_len) - if winapi.C.MultiByteToWideChar(winapi.CP_UTF8, 0, MultiByteStr, -1, utf16_str, utf16_len) > 0 then - return utf16_str - end - end - end - return "" - end - else - options.direct_io = false - end + winapi.MultiByteToWideChar = function(MultiByteStr) + if MultiByteStr then + local utf16_len = winapi.C.MultiByteToWideChar(winapi.CP_UTF8, 0, MultiByteStr, -1, nil, 0) + if utf16_len > 0 then + local utf16_str = winapi.ffi.new("wchar_t[?]", utf16_len) + if winapi.C.MultiByteToWideChar(winapi.CP_UTF8, 0, MultiByteStr, -1, utf16_str, utf16_len) > 0 then + return utf16_str + end + end + end + return "" + end + else + options.direct_io = false + end end local file = nil @@ -157,13 +156,13 @@ local filters_runtime = { ["hflip"] = true, ["vflip"] = true } local filters_all = { ["hflip"] = true, ["vflip"] = true, ["lavfi-crop"] = true, ["crop"] = true } local tone_mappings = { - ["none"] = true, - ["clip"] = true, - ["linear"] = true, - ["gamma"] = true, - ["reinhard"] = true, - ["hable"] = true, - ["mobius"] = true + ["none"] = true, + ["clip"] = true, + ["linear"] = true, + ["gamma"] = true, + ["reinhard"] = true, + ["hable"] = true, + ["mobius"] = true, } local last_tone_mapping = nil @@ -192,50 +191,50 @@ if echo "print-text thumbfast" >&"$MPV_IPC_FD"; then echo -n > "$MPV_IPC_PATH"; ]=] local function get_os() - local raw_os_name = "" + local raw_os_name = "" - if jit and jit.os and jit.arch then - raw_os_name = jit.os - else - if package.config:sub(1, 1) == "\\" then - -- Windows - local env_OS = os.getenv("OS") - if env_OS then - raw_os_name = env_OS - end - else - raw_os_name = subprocess({ "uname", "-s" }).stdout - end - end + if jit and jit.os and jit.arch then + raw_os_name = jit.os + else + if package.config:sub(1, 1) == "\\" then + -- Windows + local env_OS = os.getenv("OS") + if env_OS then + raw_os_name = env_OS + end + else + raw_os_name = subprocess({ "uname", "-s" }).stdout + end + end - raw_os_name = (raw_os_name):lower() + raw_os_name = (raw_os_name):lower() - local os_patterns = { - ["windows"] = "windows", - ["linux"] = "linux", + local os_patterns = { + ["windows"] = "windows", + ["linux"] = "linux", - ["osx"] = "darwin", - ["mac"] = "darwin", - ["darwin"] = "darwin", + ["osx"] = "darwin", + ["mac"] = "darwin", + ["darwin"] = "darwin", - ["^mingw"] = "windows", - ["^cygwin"] = "windows", + ["^mingw"] = "windows", + ["^cygwin"] = "windows", - ["bsd$"] = "darwin", - ["sunos"] = "darwin" - } + ["bsd$"] = "darwin", + ["sunos"] = "darwin", + } - -- Default to linux - local str_os_name = "linux" + -- Default to linux + local str_os_name = "linux" - for pattern, name in pairs(os_patterns) do - if raw_os_name:match(pattern) then - str_os_name = name - break - end - end + for pattern, name in pairs(os_patterns) do + if raw_os_name:match(pattern) then + str_os_name = name + break + end + end - return str_os_name + return str_os_name end local os_name = mp.get_property("platform") or get_os() @@ -243,19 +242,19 @@ local os_name = mp.get_property("platform") or get_os() local path_separator = os_name == "windows" and "\\" or "/" if options.socket == "" then - if os_name == "windows" then - options.socket = "thumbfast" - else - options.socket = "/tmp/thumbfast" - end + if os_name == "windows" then + options.socket = "thumbfast" + else + options.socket = "/tmp/thumbfast" + end end if options.thumbnail == "" then - if os_name == "windows" then - options.thumbnail = os.getenv("TEMP") .. "\\thumbfast.out" - else - options.thumbnail = "/tmp/thumbfast.out" - end + if os_name == "windows" then + options.thumbnail = os.getenv("TEMP") .. "\\thumbfast.out" + else + options.thumbnail = "/tmp/thumbfast.out" + end end local unique = mp.utils.getpid() @@ -264,690 +263,811 @@ options.socket = options.socket .. unique options.thumbnail = options.thumbnail .. unique if options.direct_io then - if os_name == "windows" then - winapi.socket_wc = winapi.MultiByteToWideChar("\\\\.\\pipe\\" .. options.socket) - end + if os_name == "windows" then + winapi.socket_wc = winapi.MultiByteToWideChar("\\\\.\\pipe\\" .. options.socket) + end - if winapi.socket_wc == "" then - options.direct_io = false - end + if winapi.socket_wc == "" then + options.direct_io = false + end end local mpv_path = options.mpv_path if mpv_path == "mpv" and os_name == "darwin" and unique then - -- TODO: look into ~~osxbundle/ - mpv_path = string.gsub(subprocess({ "ps", "-o", "comm=", "-p", tostring(unique) }).stdout, "[\n\r]", "") - if mpv_path ~= "mpv" then - mpv_path = string.gsub(mpv_path, "/mpv%-bundle$", "/mpv") - local mpv_bin = mp.utils.file_info("/usr/local/mpv") - if mpv_bin and mpv_bin.is_file then - mpv_path = "/usr/local/mpv" - else - local mpv_app = mp.utils.file_info("/Applications/mpv.app/Contents/MacOS/mpv") - if mpv_app and mpv_app.is_file then - mp.msg.warn( - "symlink mpv to fix Dock icons: `sudo ln -s /Applications/mpv.app/Contents/MacOS/mpv /usr/local/mpv`") - else - mp.msg.warn( - "drag to your Applications folder and symlink mpv to fix Dock icons: `sudo ln -s /Applications/mpv.app/Contents/MacOS/mpv /usr/local/mpv`") - end - end - end + -- TODO: look into ~~osxbundle/ + mpv_path = string.gsub(subprocess({ "ps", "-o", "comm=", "-p", tostring(unique) }).stdout, "[\n\r]", "") + if mpv_path ~= "mpv" then + mpv_path = string.gsub(mpv_path, "/mpv%-bundle$", "/mpv") + local mpv_bin = mp.utils.file_info("/usr/local/mpv") + if mpv_bin and mpv_bin.is_file then + mpv_path = "/usr/local/mpv" + else + local mpv_app = mp.utils.file_info("/Applications/mpv.app/Contents/MacOS/mpv") + if mpv_app and mpv_app.is_file then + mp.msg.warn( + "symlink mpv to fix Dock icons: `sudo ln -s /Applications/mpv.app/Contents/MacOS/mpv /usr/local/mpv`" + ) + else + mp.msg.warn( + "drag to your Applications folder and symlink mpv to fix Dock icons: `sudo ln -s /Applications/mpv.app/Contents/MacOS/mpv /usr/local/mpv`" + ) + end + end + end end local function vo_tone_mapping() - local passes = mp.get_property_native("vo-passes") - if passes and passes["fresh"] then - for _, v in pairs(passes["fresh"]) do - for k2, v2 in pairs(v) do - if k2 == "desc" and v2 then - local tone_mapping = string.match(v2, "([0-9a-z.-]+) tone map") - if tone_mapping then - return tone_mapping - end - end - end - end - end + local passes = mp.get_property_native("vo-passes") + if passes and passes["fresh"] then + for _, v in pairs(passes["fresh"]) do + for k2, v2 in pairs(v) do + if k2 == "desc" and v2 then + local tone_mapping = string.match(v2, "([0-9a-z.-]+) tone map") + if tone_mapping then + return tone_mapping + end + end + end + end + end end local function vf_string(filters, full) - local vf = "" - local vf_table = properties["vf"] + local vf = "" + local vf_table = properties["vf"] - if vf_table and #vf_table > 0 then - for i = #vf_table, 1, -1 do - if filters[vf_table[i].name] then - local args = "" - for key, value in pairs(vf_table[i].params) do - if args ~= "" then - args = args .. ":" - end - args = args .. key .. "=" .. value - end - vf = vf .. vf_table[i].name .. "=" .. args .. "," - end - end - end + if vf_table and #vf_table > 0 then + for i = #vf_table, 1, -1 do + if filters[vf_table[i].name] then + local args = "" + for key, value in pairs(vf_table[i].params) do + if args ~= "" then + args = args .. ":" + end + args = args .. key .. "=" .. value + end + vf = vf .. vf_table[i].name .. "=" .. args .. "," + end + end + end - if (full and options.tone_mapping ~= "no") or options.tone_mapping == "auto" then - if properties["video-params"] and properties["video-params"]["primaries"] == "bt.2020" then - local tone_mapping = options.tone_mapping - if tone_mapping == "auto" then - tone_mapping = last_tone_mapping or properties["tone-mapping"] - if tone_mapping == "auto" and properties["current-vo"] == "gpu-next" then - tone_mapping = vo_tone_mapping() - end - end - if not tone_mappings[tone_mapping] then - tone_mapping = "hable" - end - last_tone_mapping = tone_mapping - vf = vf .. "zscale=transfer=linear,format=gbrpf32le,tonemap=" .. tone_mapping .. ",zscale=transfer=bt709," - end - end + if (full and options.tone_mapping ~= "no") or options.tone_mapping == "auto" then + if properties["video-params"] and properties["video-params"]["primaries"] == "bt.2020" then + local tone_mapping = options.tone_mapping + if tone_mapping == "auto" then + tone_mapping = last_tone_mapping or properties["tone-mapping"] + if tone_mapping == "auto" and properties["current-vo"] == "gpu-next" then + tone_mapping = vo_tone_mapping() + end + end + if not tone_mappings[tone_mapping] then + tone_mapping = "hable" + end + last_tone_mapping = tone_mapping + vf = vf .. "zscale=transfer=linear,format=gbrpf32le,tonemap=" .. tone_mapping .. ",zscale=transfer=bt709," + end + end - if full then - vf = vf .. - "scale=w=" .. - effective_w .. ":h=" .. effective_h .. par .. - ",pad=w=" .. effective_w .. ":h=" .. effective_h .. ":x=-1:y=-1,format=bgra" - end + if full then + vf = vf + .. "scale=w=" + .. effective_w + .. ":h=" + .. effective_h + .. par + .. ",pad=w=" + .. effective_w + .. ":h=" + .. effective_h + .. ":x=-1:y=-1,format=bgra" + end - return vf + return vf end local function calc_dimensions() - local width = properties["video-out-params"] and properties["video-out-params"]["dw"] - local height = properties["video-out-params"] and properties["video-out-params"]["dh"] - if not width or not height then return end + local width = properties["video-out-params"] and properties["video-out-params"]["dw"] + local height = properties["video-out-params"] and properties["video-out-params"]["dh"] + if not width or not height then + return + end - local scale = properties["display-hidpi-scale"] or 1 + local scale = properties["display-hidpi-scale"] or 1 - if width / height > options.max_width / options.max_height then - effective_w = math.floor(options.max_width * scale + 0.5) - effective_h = math.floor(height / width * effective_w + 0.5) - else - effective_h = math.floor(options.max_height * scale + 0.5) - effective_w = math.floor(width / height * effective_h + 0.5) - end + if width / height > options.max_width / options.max_height then + effective_w = math.floor(options.max_width * scale + 0.5) + effective_h = math.floor(height / width * effective_w + 0.5) + else + effective_h = math.floor(options.max_height * scale + 0.5) + effective_w = math.floor(width / height * effective_h + 0.5) + end - local v_par = properties["video-out-params"] and properties["video-out-params"]["par"] or 1 - if v_par == 1 then - par = ":force_original_aspect_ratio=decrease" - else - par = "" - end + local v_par = properties["video-out-params"] and properties["video-out-params"]["par"] or 1 + if v_par == 1 then + par = ":force_original_aspect_ratio=decrease" + else + par = "" + end end local info_timer = nil local function info(w, h) - local rotate = properties["video-params"] and properties["video-params"]["rotate"] - local image = properties["current-tracks"] and properties["current-tracks"]["video"] and - properties["current-tracks"]["video"]["image"] - local albumart = image and properties["current-tracks"]["video"]["albumart"] + local rotate = properties["video-params"] and properties["video-params"]["rotate"] + local image = properties["current-tracks"] + and properties["current-tracks"]["video"] + and properties["current-tracks"]["video"]["image"] + local albumart = image and properties["current-tracks"]["video"]["albumart"] - disabled = (w or 0) == 0 or (h or 0) == 0 or - has_vid == 0 or - (properties["demuxer-via-network"] and not options.network) or - (albumart and not options.audio) or - (image and not albumart) or - force_disabled + disabled = (w or 0) == 0 + or (h or 0) == 0 + or has_vid == 0 + or (properties["demuxer-via-network"] and not options.network) + or (albumart and not options.audio) + or (image and not albumart) + or force_disabled - if info_timer then - info_timer:kill() - info_timer = nil - elseif has_vid == 0 or (rotate == nil and not disabled) then - info_timer = mp.add_timeout(0.05, function() info(w, h) end) - end + if info_timer then + info_timer:kill() + info_timer = nil + elseif has_vid == 0 or (rotate == nil and not disabled) then + info_timer = mp.add_timeout(0.05, function() + info(w, h) + end) + end - local json, _ = mp.utils.format_json({ - width = w, - height = h, - disabled = disabled, - available = true, - socket = options.socket, - thumbnail = options.thumbnail, - overlay_id = options.overlay_id - }) - if pre_0_30_0 then - mp.command_native({ "script-message", "thumbfast-info", json }) - else - mp.command_native_async({ "script-message", "thumbfast-info", json }, function() - end) - end + local json, _ = mp.utils.format_json({ + width = w, + height = h, + disabled = disabled, + available = true, + socket = options.socket, + thumbnail = options.thumbnail, + overlay_id = options.overlay_id, + }) + if pre_0_30_0 then + mp.command_native({ "script-message", "thumbfast-info", json }) + else + mp.command_native_async({ "script-message", "thumbfast-info", json }, function() end) + end end local function remove_thumbnail_files() - if file then - file:close() - file = nil - file_bytes = 0 - end - os.remove(options.thumbnail) - os.remove(options.thumbnail .. ".bgra") + if file then + file:close() + file = nil + file_bytes = 0 + end + os.remove(options.thumbnail) + os.remove(options.thumbnail .. ".bgra") end local activity_timer local function spawn(time) - if disabled then return end + if disabled then + return + end - local path = properties["path"] - if path == nil then return end + local path = properties["path"] + if path == nil then + return + end - if options.quit_after_inactivity > 0 then - if show_thumbnail or activity_timer:is_enabled() then - activity_timer:kill() - end - activity_timer:resume() - end + if options.quit_after_inactivity > 0 then + if show_thumbnail or activity_timer:is_enabled() then + activity_timer:kill() + end + activity_timer:resume() + end - local open_filename = properties["stream-open-filename"] - local ytdl = open_filename and properties["demuxer-via-network"] and path ~= open_filename - if ytdl then - path = open_filename - end + local open_filename = properties["stream-open-filename"] + local ytdl = open_filename and properties["demuxer-via-network"] and path ~= open_filename + if ytdl then + path = open_filename + end - remove_thumbnail_files() + remove_thumbnail_files() - local vid = properties["vid"] - has_vid = vid or 0 + local vid = properties["vid"] + has_vid = vid or 0 - local args = { - mpv_path, "--no-config", "--msg-level=all=no", "--idle", "--pause", "--keep-open=always", "--really-quiet", - "--no-terminal", - "--load-scripts=no", "--osc=no", "--ytdl=no", "--load-stats-overlay=no", "--load-osd-console=no", - "--load-auto-profiles=no", - "--edition=" .. (properties["edition"] or "auto"), "--vid=" .. (vid or "auto"), "--no-sub", "--no-audio", - "--start=" .. time, allow_fast_seek and "--hr-seek=no" or "--hr-seek=yes", - "--ytdl-format=worst", "--demuxer-readahead-secs=0", "--demuxer-max-bytes=128KiB", - "--vd-lavc-skiploopfilter=all", "--vd-lavc-software-fallback=1", "--vd-lavc-fast", "--vd-lavc-threads=2", - "--hwdec=" .. (options.hwdec and "auto" or "no"), - "--vf=" .. vf_string(filters_all, true), - "--sws-scaler=fast-bilinear", - "--video-rotate=" .. last_rotate, - "--ovc=rawvideo", "--of=image2", "--ofopts=update=1", "--o=" .. options.thumbnail - } + local args = { + mpv_path, + "--no-config", + "--msg-level=all=no", + "--idle", + "--pause", + "--keep-open=always", + "--really-quiet", + "--no-terminal", + "--load-scripts=no", + "--osc=no", + "--ytdl=no", + "--load-stats-overlay=no", + "--load-osd-console=no", + "--load-auto-profiles=no", + "--edition=" .. (properties["edition"] or "auto"), + "--vid=" .. (vid or "auto"), + "--no-sub", + "--no-audio", + "--start=" .. time, + allow_fast_seek and "--hr-seek=no" or "--hr-seek=yes", + "--ytdl-format=worst", + "--demuxer-readahead-secs=0", + "--demuxer-max-bytes=128KiB", + "--vd-lavc-skiploopfilter=all", + "--vd-lavc-software-fallback=1", + "--vd-lavc-fast", + "--vd-lavc-threads=2", + "--hwdec=" .. (options.hwdec and "auto" or "no"), + "--vf=" .. vf_string(filters_all, true), + "--sws-scaler=fast-bilinear", + "--video-rotate=" .. last_rotate, + "--ovc=rawvideo", + "--of=image2", + "--ofopts=update=1", + "--o=" .. options.thumbnail, + } - if not pre_0_30_0 then - table.insert(args, "--sws-allow-zimg=no") - end + if not pre_0_30_0 then + table.insert(args, "--sws-allow-zimg=no") + end - if os_name == "darwin" and properties["macos-app-activation-policy"] then - table.insert(args, "--macos-app-activation-policy=accessory") - end + if os_name == "darwin" and properties["macos-app-activation-policy"] then + table.insert(args, "--macos-app-activation-policy=accessory") + end - if os_name == "windows" or pre_0_33_0 then - table.insert(args, "--input-ipc-server=" .. options.socket) - elseif not script_written then - local client_script_path = options.socket .. ".run" - local script = io.open(client_script_path, "w+") - if script == nil then - mp.msg.error("client script write failed") - return - else - script_written = true - script:write(string.format(client_script, options.socket)) - script:close() - subprocess({ "chmod", "+x", client_script_path }, true) - table.insert(args, "--scripts=" .. client_script_path) - end - else - local client_script_path = options.socket .. ".run" - table.insert(args, "--scripts=" .. client_script_path) - end + if os_name == "windows" or pre_0_33_0 then + table.insert(args, "--input-ipc-server=" .. options.socket) + elseif not script_written then + local client_script_path = options.socket .. ".run" + local script = io.open(client_script_path, "w+") + if script == nil then + mp.msg.error("client script write failed") + return + else + script_written = true + script:write(string.format(client_script, options.socket)) + script:close() + subprocess({ "chmod", "+x", client_script_path }, true) + table.insert(args, "--scripts=" .. client_script_path) + end + else + local client_script_path = options.socket .. ".run" + table.insert(args, "--scripts=" .. client_script_path) + end - table.insert(args, "--") - table.insert(args, path) + table.insert(args, "--") + table.insert(args, path) - spawned = true - spawn_waiting = true + spawned = true + spawn_waiting = true - subprocess(args, true, - function(success, result) - if spawn_waiting and (success == false or (result.status ~= 0 and result.status ~= -2)) then - spawned = false - spawn_waiting = false - options.tone_mapping = "no" - mp.msg.error("mpv subprocess create failed") - if not spawn_working then -- notify users of required configuration - if options.mpv_path == "mpv" then - if properties["current-vo"] == "libmpv" then - if options.mpv_path == mpv_path then -- attempt to locate ImPlay - mpv_path = "ImPlay" - spawn(time) - else -- ImPlay not in path - if os_name ~= "darwin" then - force_disabled = true - info(real_w or effective_w, real_h or effective_h) - end - mp.commandv("show-text", "thumbfast: ERROR! cannot create mpv subprocess", 5000) - mp.commandv("script-message-to", "implay", "show-message", "thumbfast initial setup", - "Set mpv_path=PATH_TO_ImPlay in thumbfast config:\n" .. - string.gsub(mp.command_native({ "expand-path", "~~/script-opts/thumbfast.conf" }), - "[/\\]", path_separator) .. "\nand restart ImPlay") - end - else - mp.commandv("show-text", "thumbfast: ERROR! cannot create mpv subprocess", 5000) - if os_name == "windows" then - mp.commandv("script-message-to", "mpvnet", "show-text", - "thumbfast: ERROR! install standalone mpv, see README", 5000, 20) - mp.commandv("script-message", "mpv.net", "show-text", - "thumbfast: ERROR! install standalone mpv, see README", 5000, 20) - end - end - else - mp.commandv("show-text", "thumbfast: ERROR! cannot create mpv subprocess", 5000) - -- found ImPlay but not defined in config - mp.commandv("script-message-to", "implay", "show-message", "thumbfast", - "Set mpv_path=PATH_TO_ImPlay in thumbfast config:\n" .. - string.gsub(mp.command_native({ "expand-path", "~~/script-opts/thumbfast.conf" }), "[/\\]", - path_separator) .. "\nand restart ImPlay") - end - end - elseif success == true and (result.status == 0 or result.status == -2) then - if not spawn_working and properties["current-vo"] == "libmpv" and options.mpv_path ~= mpv_path then - mp.commandv("script-message-to", "implay", "show-message", "thumbfast initial setup", - "Set mpv_path=ImPlay in thumbfast config:\n" .. - string.gsub(mp.command_native({ "expand-path", "~~/script-opts/thumbfast.conf" }), "[/\\]", - path_separator) .. "\nand restart ImPlay") - end - spawn_working = true - spawn_waiting = false - end - end - ) + subprocess(args, true, function(success, result) + if spawn_waiting and (success == false or (result.status ~= 0 and result.status ~= -2)) then + spawned = false + spawn_waiting = false + options.tone_mapping = "no" + mp.msg.error("mpv subprocess create failed") + if not spawn_working then -- notify users of required configuration + if options.mpv_path == "mpv" then + if properties["current-vo"] == "libmpv" then + if options.mpv_path == mpv_path then -- attempt to locate ImPlay + mpv_path = "ImPlay" + spawn(time) + else -- ImPlay not in path + if os_name ~= "darwin" then + force_disabled = true + info(real_w or effective_w, real_h or effective_h) + end + mp.commandv("show-text", "thumbfast: ERROR! cannot create mpv subprocess", 5000) + mp.commandv( + "script-message-to", + "implay", + "show-message", + "thumbfast initial setup", + "Set mpv_path=PATH_TO_ImPlay in thumbfast config:\n" + .. string.gsub( + mp.command_native({ "expand-path", "~~/script-opts/thumbfast.conf" }), + "[/\\]", + path_separator + ) + .. "\nand restart ImPlay" + ) + end + else + mp.commandv("show-text", "thumbfast: ERROR! cannot create mpv subprocess", 5000) + if os_name == "windows" then + mp.commandv( + "script-message-to", + "mpvnet", + "show-text", + "thumbfast: ERROR! install standalone mpv, see README", + 5000, + 20 + ) + mp.commandv( + "script-message", + "mpv.net", + "show-text", + "thumbfast: ERROR! install standalone mpv, see README", + 5000, + 20 + ) + end + end + else + mp.commandv("show-text", "thumbfast: ERROR! cannot create mpv subprocess", 5000) + -- found ImPlay but not defined in config + mp.commandv( + "script-message-to", + "implay", + "show-message", + "thumbfast", + "Set mpv_path=PATH_TO_ImPlay in thumbfast config:\n" + .. string.gsub( + mp.command_native({ "expand-path", "~~/script-opts/thumbfast.conf" }), + "[/\\]", + path_separator + ) + .. "\nand restart ImPlay" + ) + end + end + elseif success == true and (result.status == 0 or result.status == -2) then + if not spawn_working and properties["current-vo"] == "libmpv" and options.mpv_path ~= mpv_path then + mp.commandv( + "script-message-to", + "implay", + "show-message", + "thumbfast initial setup", + "Set mpv_path=ImPlay in thumbfast config:\n" + .. string.gsub( + mp.command_native({ "expand-path", "~~/script-opts/thumbfast.conf" }), + "[/\\]", + path_separator + ) + .. "\nand restart ImPlay" + ) + end + spawn_working = true + spawn_waiting = false + end + end) end local function run(command) - if not spawned then return end + if not spawned then + return + end - if options.direct_io then - local hPipe = winapi.C.CreateFileW(winapi.socket_wc, winapi.GENERIC_WRITE, 0, nil, winapi.OPEN_EXISTING, - winapi._createfile_pipe_flags, nil) - if hPipe ~= winapi.INVALID_HANDLE_VALUE then - local buf = command .. "\n" - winapi.C.SetNamedPipeHandleState(hPipe, winapi.PIPE_NOWAIT, nil, nil) - winapi.C.WriteFile(hPipe, buf, #buf + 1, winapi._lpNumberOfBytesWritten, nil) - winapi.C.CloseHandle(hPipe) - end + if options.direct_io then + local hPipe = winapi.C.CreateFileW( + winapi.socket_wc, + winapi.GENERIC_WRITE, + 0, + nil, + winapi.OPEN_EXISTING, + winapi._createfile_pipe_flags, + nil + ) + if hPipe ~= winapi.INVALID_HANDLE_VALUE then + local buf = command .. "\n" + winapi.C.SetNamedPipeHandleState(hPipe, winapi.PIPE_NOWAIT, nil, nil) + winapi.C.WriteFile(hPipe, buf, #buf + 1, winapi._lpNumberOfBytesWritten, nil) + winapi.C.CloseHandle(hPipe) + end - return - end + return + end - local command_n = command .. "\n" + local command_n = command .. "\n" - if os_name == "windows" then - if file and file_bytes + #command_n >= 4096 then - file:close() - file = nil - file_bytes = 0 - end - if not file then - file = io.open("\\\\.\\pipe\\" .. options.socket, "r+b") - end - elseif pre_0_33_0 then - subprocess({ "/usr/bin/env", "sh", "-c", "echo '" .. command .. "' | socat - " .. options.socket }) - return - elseif not file then - file = io.open(options.socket, "r+") - end - if file then - file_bytes = file:seek("end") - file:write(command_n) - file:flush() - end + if os_name == "windows" then + if file and file_bytes + #command_n >= 4096 then + file:close() + file = nil + file_bytes = 0 + end + if not file then + file = io.open("\\\\.\\pipe\\" .. options.socket, "r+b") + end + elseif pre_0_33_0 then + subprocess({ "/usr/bin/env", "sh", "-c", "echo '" .. command .. "' | socat - " .. options.socket }) + return + elseif not file then + file = io.open(options.socket, "r+") + end + if file then + file_bytes = file:seek("end") + file:write(command_n) + file:flush() + end end local function draw(w, h, script) - if not w or not show_thumbnail then return end - if x ~= nil then - if pre_0_30_0 then - mp.command_native({ "overlay-add", options.overlay_id, x, y, options.thumbnail .. ".bgra", 0, "bgra", w, h, - (4 * w) }) - else - mp.command_native_async( - { "overlay-add", options.overlay_id, x, y, options.thumbnail .. ".bgra", 0, "bgra", w, h, (4 * w) }, - function() - end) - end - elseif script then - local json, _ = mp.utils.format_json({ - width = w, - height = h, - x = x, - y = y, - socket = options.socket, - thumbnail = options.thumbnail, - overlay_id = options.overlay_id - }) - mp.commandv("script-message-to", script, "thumbfast-render", json) - end + if not w or not show_thumbnail then + return + end + if x ~= nil then + if pre_0_30_0 then + mp.command_native({ + "overlay-add", + options.overlay_id, + x, + y, + options.thumbnail .. ".bgra", + 0, + "bgra", + w, + h, + (4 * w), + }) + else + mp.command_native_async( + { "overlay-add", options.overlay_id, x, y, options.thumbnail .. ".bgra", 0, "bgra", w, h, (4 * w) }, + function() end + ) + end + elseif script then + local json, _ = mp.utils.format_json({ + width = w, + height = h, + x = x, + y = y, + socket = options.socket, + thumbnail = options.thumbnail, + overlay_id = options.overlay_id, + }) + mp.commandv("script-message-to", script, "thumbfast-render", json) + end end local function real_res(req_w, req_h, filesize) - local count = filesize / 4 - local diff = (req_w * req_h) - count + local count = filesize / 4 + local diff = (req_w * req_h) - count - if (properties["video-params"] and properties["video-params"]["rotate"] or 0) % 180 == 90 then - req_w, req_h = req_h, req_w - end + if (properties["video-params"] and properties["video-params"]["rotate"] or 0) % 180 == 90 then + req_w, req_h = req_h, req_w + end - if diff == 0 then - return req_w, req_h - else - local threshold = 5 -- throw out results that change too much - local long_side, short_side = req_w, req_h - if req_h > req_w then - long_side, short_side = req_h, req_w - end - for a = short_side, short_side - threshold, -1 do - if count % a == 0 then - local b = count / a - if long_side - b < threshold then - if req_h < req_w then return b, a else return a, b end - end - end - end - return nil - end + if diff == 0 then + return req_w, req_h + else + local threshold = 5 -- throw out results that change too much + local long_side, short_side = req_w, req_h + if req_h > req_w then + long_side, short_side = req_h, req_w + end + for a = short_side, short_side - threshold, -1 do + if count % a == 0 then + local b = count / a + if long_side - b < threshold then + if req_h < req_w then + return b, a + else + return a, b + end + end + end + end + return nil + end end local function move_file(from, to) - if os_name == "windows" then - os.remove(to) - end - -- move the file because it can get overwritten while overlay-add is reading it, and crash the player - os.rename(from, to) + if os_name == "windows" then + os.remove(to) + end + -- move the file because it can get overwritten while overlay-add is reading it, and crash the player + os.rename(from, to) end local function seek(fast) - if last_seek_time then - run("async seek " .. last_seek_time .. (fast and " absolute+keyframes" or " absolute+exact")) - end + if last_seek_time then + run("async seek " .. last_seek_time .. (fast and " absolute+keyframes" or " absolute+exact")) + end end local seek_period = 3 / 60 local seek_period_counter = 0 local seek_timer seek_timer = mp.add_periodic_timer(seek_period, function() - if seek_period_counter == 0 then - seek(allow_fast_seek) - seek_period_counter = 1 - else - if seek_period_counter == 2 then - if allow_fast_seek then - seek_timer:kill() - seek() - end - else - seek_period_counter = seek_period_counter + 1 - end - end + if seek_period_counter == 0 then + seek(allow_fast_seek) + seek_period_counter = 1 + else + if seek_period_counter == 2 then + if allow_fast_seek then + seek_timer:kill() + seek() + end + else + seek_period_counter = seek_period_counter + 1 + end + end end) seek_timer:kill() local function request_seek() - if seek_timer:is_enabled() then - seek_period_counter = 0 - else - seek_timer:resume() - seek(allow_fast_seek) - seek_period_counter = 1 - end + if seek_timer:is_enabled() then + seek_period_counter = 0 + else + seek_timer:resume() + seek(allow_fast_seek) + seek_period_counter = 1 + end end local function check_new_thumb() - -- the slave might start writing to the file after checking existance and - -- validity but before actually moving the file, so move to a temporary - -- location before validity check to make sure everything stays consistant - -- and valid thumbnails don't get overwritten by invalid ones - local tmp = options.thumbnail .. ".tmp" - move_file(options.thumbnail, tmp) - local finfo = mp.utils.file_info(tmp) - if not finfo then return false end - spawn_waiting = false - local w, h = real_res(effective_w, effective_h, finfo.size) - if w then -- only accept valid thumbnails - move_file(tmp, options.thumbnail .. ".bgra") + -- the slave might start writing to the file after checking existance and + -- validity but before actually moving the file, so move to a temporary + -- location before validity check to make sure everything stays consistant + -- and valid thumbnails don't get overwritten by invalid ones + local tmp = options.thumbnail .. ".tmp" + move_file(options.thumbnail, tmp) + local finfo = mp.utils.file_info(tmp) + if not finfo then + return false + end + spawn_waiting = false + local w, h = real_res(effective_w, effective_h, finfo.size) + if w then -- only accept valid thumbnails + move_file(tmp, options.thumbnail .. ".bgra") - real_w, real_h = w, h - if real_w and (real_w ~= last_real_w or real_h ~= last_real_h) then - last_real_w, last_real_h = real_w, real_h - info(real_w, real_h) - end - if not show_thumbnail then - file_timer:kill() - end - return true - end + real_w, real_h = w, h + if real_w and (real_w ~= last_real_w or real_h ~= last_real_h) then + last_real_w, last_real_h = real_w, real_h + info(real_w, real_h) + end + if not show_thumbnail then + file_timer:kill() + end + return true + end - return false + return false end file_timer = mp.add_periodic_timer(file_check_period, function() - if check_new_thumb() then - draw(real_w, real_h, script_name) - end + if check_new_thumb() then + draw(real_w, real_h, script_name) + end end) file_timer:kill() local function clear() - file_timer:kill() - seek_timer:kill() - if options.quit_after_inactivity > 0 then - if show_thumbnail or activity_timer:is_enabled() then - activity_timer:kill() - end - activity_timer:resume() - end - last_seek_time = nil - show_thumbnail = false - last_x = nil - last_y = nil - if script_name then return end - if pre_0_30_0 then - mp.command_native({ "overlay-remove", options.overlay_id }) - else - mp.command_native_async({ "overlay-remove", options.overlay_id }, function() - end) - end + file_timer:kill() + seek_timer:kill() + if options.quit_after_inactivity > 0 then + if show_thumbnail or activity_timer:is_enabled() then + activity_timer:kill() + end + activity_timer:resume() + end + last_seek_time = nil + show_thumbnail = false + last_x = nil + last_y = nil + if script_name then + return + end + if pre_0_30_0 then + mp.command_native({ "overlay-remove", options.overlay_id }) + else + mp.command_native_async({ "overlay-remove", options.overlay_id }, function() end) + end end local function quit() - activity_timer:kill() - if show_thumbnail then - activity_timer:resume() - return - end - run("quit") - spawned = false - real_w, real_h = nil, nil - clear() + activity_timer:kill() + if show_thumbnail then + activity_timer:resume() + return + end + run("quit") + spawned = false + real_w, real_h = nil, nil + clear() end activity_timer = mp.add_timeout(options.quit_after_inactivity, quit) activity_timer:kill() local function thumb(time, r_x, r_y, script) - if disabled then return end + if disabled then + return + end - time = tonumber(time) - if time == nil then return end + time = tonumber(time) + if time == nil then + return + end - if r_x == "" or r_y == "" then - x, y = nil, nil - else - x, y = math.floor(r_x + 0.5), math.floor(r_y + 0.5) - end + if r_x == "" or r_y == "" then + x, y = nil, nil + else + x, y = math.floor(r_x + 0.5), math.floor(r_y + 0.5) + end - script_name = script - if last_x ~= x or last_y ~= y or not show_thumbnail then - show_thumbnail = true - last_x = x - last_y = y - draw(real_w, real_h, script) - end + script_name = script + if last_x ~= x or last_y ~= y or not show_thumbnail then + show_thumbnail = true + last_x = x + last_y = y + draw(real_w, real_h, script) + end - if options.quit_after_inactivity > 0 then - if show_thumbnail or activity_timer:is_enabled() then - activity_timer:kill() - end - activity_timer:resume() - end + if options.quit_after_inactivity > 0 then + if show_thumbnail or activity_timer:is_enabled() then + activity_timer:kill() + end + activity_timer:resume() + end - if time == last_seek_time then return end - last_seek_time = time - if not spawned then spawn(time) end - request_seek() - if not file_timer:is_enabled() then file_timer:resume() end + if time == last_seek_time then + return + end + last_seek_time = time + if not spawned then + spawn(time) + end + request_seek() + if not file_timer:is_enabled() then + file_timer:resume() + end end local function watch_changes() - if not dirty or not properties["video-out-params"] then return end - dirty = false + if not dirty or not properties["video-out-params"] then + return + end + dirty = false - local old_w = effective_w - local old_h = effective_h + local old_w = effective_w + local old_h = effective_h - calc_dimensions() + calc_dimensions() - local vf_reset = vf_string(filters_reset) - local rotate = properties["video-rotate"] or 0 + local vf_reset = vf_string(filters_reset) + local rotate = properties["video-rotate"] or 0 - local resized = old_w ~= effective_w or - old_h ~= effective_h or - last_vf_reset ~= vf_reset or - (last_rotate % 180) ~= (rotate % 180) or - par ~= last_par + local resized = old_w ~= effective_w + or old_h ~= effective_h + or last_vf_reset ~= vf_reset + or (last_rotate % 180) ~= (rotate % 180) + or par ~= last_par - if resized then - last_rotate = rotate - info(effective_w, effective_h) - elseif last_has_vid ~= has_vid and has_vid ~= 0 then - info(effective_w, effective_h) - end + if resized then + last_rotate = rotate + info(effective_w, effective_h) + elseif last_has_vid ~= has_vid and has_vid ~= 0 then + info(effective_w, effective_h) + end - if spawned then - if resized then - -- mpv doesn't allow us to change output size - local seek_time = last_seek_time - run("quit") - clear() - spawned = false - spawn(seek_time or mp.get_property_number("time-pos", 0)) - file_timer:resume() - else - if rotate ~= last_rotate then - run("set video-rotate " .. rotate) - end - local vf_runtime = vf_string(filters_runtime) - if vf_runtime ~= last_vf_runtime then - run("vf set " .. vf_string(filters_all, true)) - last_vf_runtime = vf_runtime - end - end - else - last_vf_runtime = vf_string(filters_runtime) - end + if spawned then + if resized then + -- mpv doesn't allow us to change output size + local seek_time = last_seek_time + run("quit") + clear() + spawned = false + spawn(seek_time or mp.get_property_number("time-pos", 0)) + file_timer:resume() + else + if rotate ~= last_rotate then + run("set video-rotate " .. rotate) + end + local vf_runtime = vf_string(filters_runtime) + if vf_runtime ~= last_vf_runtime then + run("vf set " .. vf_string(filters_all, true)) + last_vf_runtime = vf_runtime + end + end + else + last_vf_runtime = vf_string(filters_runtime) + end - last_vf_reset = vf_reset - last_rotate = rotate - last_par = par - last_has_vid = has_vid + last_vf_reset = vf_reset + last_rotate = rotate + last_par = par + last_has_vid = has_vid - if not spawned and not disabled and options.spawn_first and resized then - spawn(mp.get_property_number("time-pos", 0)) - file_timer:resume() - end + if not spawned and not disabled and options.spawn_first and resized then + spawn(mp.get_property_number("time-pos", 0)) + file_timer:resume() + end end local function update_property(name, value) - properties[name] = value + properties[name] = value end local function update_property_dirty(name, value) - properties[name] = value - dirty = true - if name == "tone-mapping" then - last_tone_mapping = nil - end + properties[name] = value + dirty = true + if name == "tone-mapping" then + last_tone_mapping = nil + end end local function update_tracklist(_, value) - -- current-tracks shim - for _, track in ipairs(value) do - if track.type == "video" and track.selected then - properties["current-tracks/video/image"] = track.image - properties["current-tracks/video/albumart"] = track.albumart - return - end - end + -- current-tracks shim + for _, track in ipairs(value) do + if track.type == "video" and track.selected then + properties["current-tracks/video/image"] = track.image + properties["current-tracks/video/albumart"] = track.albumart + return + end + end end local function sync_changes(prop, val) - update_property(prop, val) - if val == nil then return end + update_property(prop, val) + if val == nil then + return + end - if type(val) == "boolean" then - if prop == "vid" then - has_vid = 0 - last_has_vid = 0 - info(effective_w, effective_h) - clear() - return - end - val = val and "yes" or "no" - end + if type(val) == "boolean" then + if prop == "vid" then + has_vid = 0 + last_has_vid = 0 + info(effective_w, effective_h) + clear() + return + end + val = val and "yes" or "no" + end - if prop == "vid" then - has_vid = 1 - end + if prop == "vid" then + has_vid = 1 + end - if not spawned then return end + if not spawned then + return + end - run("set " .. prop .. " " .. val) - dirty = true + run("set " .. prop .. " " .. val) + dirty = true end local function file_load() - clear() - spawned = false - real_w, real_h = nil, nil - last_real_w, last_real_h = nil, nil - last_tone_mapping = nil - last_seek_time = nil - if info_timer then - info_timer:kill() - info_timer = nil - end + clear() + spawned = false + real_w, real_h = nil, nil + last_real_w, last_real_h = nil, nil + last_tone_mapping = nil + last_seek_time = nil + if info_timer then + info_timer:kill() + info_timer = nil + end - calc_dimensions() - info(effective_w, effective_h) + calc_dimensions() + info(effective_w, effective_h) end local function shutdown() - run("quit") - remove_thumbnail_files() - if os_name ~= "windows" then - os.remove(options.socket) - os.remove(options.socket .. ".run") - end + run("quit") + remove_thumbnail_files() + if os_name ~= "windows" then + os.remove(options.socket) + os.remove(options.socket .. ".run") + end end local function on_duration(_, val) - allow_fast_seek = (val or 30) >= 30 + allow_fast_seek = (val or 30) >= 30 end mp.observe_property("current-tracks", "native", function(name, value) - if pre_0_33_0 then - mp.unobserve_property(update_tracklist) - pre_0_33_0 = false - end - update_property(name, value) + if pre_0_33_0 then + mp.unobserve_property(update_tracklist) + pre_0_33_0 = false + end + update_property(name, value) end) mp.observe_property("track-list", "native", update_tracklist) diff --git a/multimedia/.config/mpv/scripts/uosc.lua b/multimedia/.config/mpv/scripts/uosc.lua index f8eeba9..7b19d86 100644 --- a/multimedia/.config/mpv/scripts/uosc.lua +++ b/multimedia/.config/mpv/scripts/uosc.lua @@ -1,23 +1,23 @@ --[[ uosc 4.7.0 - 2023-Apr-15 | https://github.com/tomasklaen/uosc ]] -local uosc_version = '4.7.0' +local uosc_version = "4.7.0" -assdraw = require('mp.assdraw') -opt = require('mp.options') -utils = require('mp.utils') -msg = require('mp.msg') -osd = mp.create_osd_overlay('ass-events') +assdraw = require("mp.assdraw") +opt = require("mp.options") +utils = require("mp.utils") +msg = require("mp.msg") +osd = mp.create_osd_overlay("ass-events") INFINITY = 1e309 QUARTER_PI_SIN = math.sin(math.pi / 4) -- Enables relative requires from `scripts` directory -package.path = package.path .. ';' .. mp.find_config_file('scripts') .. '/?.lua' +package.path = package.path .. ";" .. mp.find_config_file("scripts") .. "/?.lua" -require('uosc_shared/lib/std') +require("uosc_shared/lib/std") --[[ OPTIONS ]] defaults = { - timeline_style = 'line', + timeline_style = "line", timeline_line_width = 2, timeline_line_width_fullscreen = 3, timeline_line_width_minimized_scale = 10, @@ -26,29 +26,29 @@ defaults = { timeline_size_min_fullscreen = 0, timeline_size_max_fullscreen = 60, timeline_start_hidden = false, - timeline_persistency = 'paused', + timeline_persistency = "paused", timeline_opacity = 0.9, timeline_border = 1, timeline_step = 5, timeline_chapters_opacity = 0.8, timeline_cache = true, - controls = 'menu,gap,subtitles,audio,video,editions,stream-quality,gap,space,speed,space,shuffle,loop-playlist,loop-file,gap,prev,items,next,gap,fullscreen', + controls = "menu,gap,subtitles,audio,video,editions,stream-quality,gap,space,speed,space,shuffle,loop-playlist,loop-file,gap,prev,items,next,gap,fullscreen", controls_size = 32, controls_size_fullscreen = 40, controls_margin = 8, controls_spacing = 2, - controls_persistency = '', + controls_persistency = "", - volume = 'right', + volume = "right", volume_size = 40, volume_size_fullscreen = 52, - volume_persistency = '', + volume_persistency = "", volume_opacity = 0.9, volume_border = 1, volume_step = 1, - speed_persistency = '', + speed_persistency = "", speed_opacity = 0.6, speed_step = 0.1, speed_step_is_factor = false, @@ -60,21 +60,21 @@ defaults = { menu_opacity = 1, menu_parent_opacity = 0.4, - top_bar = 'no-border', + top_bar = "no-border", top_bar_size = 40, top_bar_size_fullscreen = 46, - top_bar_persistency = '', + top_bar_persistency = "", top_bar_controls = true, - top_bar_title = 'yes', - top_bar_alt_title = '', - top_bar_alt_title_place = 'below', + top_bar_title = "yes", + top_bar_alt_title = "", + top_bar_alt_title_place = "below", top_bar_title_opacity = 0.8, window_border_size = 1, window_border_opacity = 0.8, autoload = false, - autoload_types = 'video,audio,image', + autoload_types = "video,audio,image", shuffle = false, ui_scale = 1, @@ -83,50 +83,54 @@ defaults = { text_width_estimation = true, pause_on_click_shorter_than = 0, -- deprecated by below click_threshold = 0, - click_command = 'cycle pause; script-binding uosc/flash-pause-indicator', + click_command = "cycle pause; script-binding uosc/flash-pause-indicator", flash_duration = 1000, proximity_in = 40, proximity_out = 120, - foreground = 'ffffff', - foreground_text = '000000', - background = '000000', - background_text = 'ffffff', + foreground = "ffffff", + foreground_text = "000000", + background = "000000", + background_text = "ffffff", total_time = false, -- deprecated by below - destination_time = 'playtime-remaining', + destination_time = "playtime-remaining", time_precision = 0, font_bold = false, autohide = false, buffered_time_threshold = 60, - pause_indicator = 'flash', + pause_indicator = "flash", curtain_opacity = 0.5, - stream_quality_options = '4320,2160,1440,1080,720,480,360,240,144', - video_types= '3g2,3gp,asf,avi,f4v,flv,h264,h265,m2ts,m4v,mkv,mov,mp4,mp4v,mpeg,mpg,ogm,ogv,rm,rmvb,ts,vob,webm,wmv,y4m', - audio_types= 'aac,ac3,aiff,ape,au,dsf,dts,flac,m4a,mid,midi,mka,mp3,mp4a,oga,ogg,opus,spx,tak,tta,wav,weba,wma,wv', - image_types= 'apng,avif,bmp,gif,j2k,jp2,jfif,jpeg,jpg,jxl,mj2,png,svg,tga,tif,tiff,webp', - subtitle_types = 'aqt,ass,gsub,idx,jss,lrc,mks,pgs,pjs,psb,rt,slt,smi,sub,sup,srt,ssa,ssf,ttxt,txt,usf,vt,vtt', - default_directory = '~/', + stream_quality_options = "4320,2160,1440,1080,720,480,360,240,144", + video_types = "3g2,3gp,asf,avi,f4v,flv,h264,h265,m2ts,m4v,mkv,mov,mp4,mp4v,mpeg,mpg,ogm,ogv,rm,rmvb,ts,vob,webm,wmv,y4m", + audio_types = "aac,ac3,aiff,ape,au,dsf,dts,flac,m4a,mid,midi,mka,mp3,mp4a,oga,ogg,opus,spx,tak,tta,wav,weba,wma,wv", + image_types = "apng,avif,bmp,gif,j2k,jp2,jfif,jpeg,jpg,jxl,mj2,png,svg,tga,tif,tiff,webp", + subtitle_types = "aqt,ass,gsub,idx,jss,lrc,mks,pgs,pjs,psb,rt,slt,smi,sub,sup,srt,ssa,ssf,ttxt,txt,usf,vt,vtt", + default_directory = "~/", use_trash = false, adjust_osd_margins = true, - chapter_ranges = 'openings:30abf964,endings:30abf964,ads:c54e4e80', - chapter_range_patterns = 'openings:オープニング;endings:エンディング', + chapter_ranges = "openings:30abf964,endings:30abf964,ads:c54e4e80", + chapter_range_patterns = "openings:オープニング;endings:エンディング", } options = table_shallow_copy(defaults) -opt.read_options(options, 'uosc') +opt.read_options(options, "uosc") -- Normalize values options.proximity_out = math.max(options.proximity_out, options.proximity_in + 1) -if options.chapter_ranges:sub(1, 4) == '^op|' then options.chapter_ranges = defaults.chapter_ranges end +if options.chapter_ranges:sub(1, 4) == "^op|" then + options.chapter_ranges = defaults.chapter_ranges +end if options.pause_on_click_shorter_than > 0 and options.click_threshold == 0 then - msg.warn('`pause_on_click_shorter_than` is deprecated. Use `click_threshold` and `click_command` instead.') + msg.warn("`pause_on_click_shorter_than` is deprecated. Use `click_threshold` and `click_command` instead.") options.click_threshold = options.pause_on_click_shorter_than end -if options.total_time and options.destination_time == 'playtime-remaining' then - msg.warn('`total_time` is deprecated. Use `destination_time` instead.') - options.destination_time = 'total' -elseif not itable_index_of({'total', 'playtime-remaining', 'time-remaining'}, options.destination_time) then - options.destination_time = 'playtime-remaining' +if options.total_time and options.destination_time == "playtime-remaining" then + msg.warn("`total_time` is deprecated. Use `destination_time` instead.") + options.destination_time = "total" +elseif not itable_index_of({ "total", "playtime-remaining", "time-remaining" }, options.destination_time) then + options.destination_time = "playtime-remaining" end -- Ensure required environment configuration -if options.autoload then mp.commandv('set', 'keep-open-pause', 'no') end +if options.autoload then + mp.commandv("set", "keep-open-pause", "no") +end -- Color shorthands fg, bg = serialize_rgba(options.foreground).color, serialize_rgba(options.background).color fgt, bgt = serialize_rgba(options.foreground_text).color, serialize_rgba(options.background_text).color @@ -135,33 +139,42 @@ fgt, bgt = serialize_rgba(options.foreground_text).color, serialize_rgba(options function create_default_menu() return { - {title = 'Subtitles', value = 'script-binding uosc/subtitles'}, - {title = 'Audio tracks', value = 'script-binding uosc/audio'}, - {title = 'Stream quality', value = 'script-binding uosc/stream-quality'}, - {title = 'Playlist', value = 'script-binding uosc/items'}, - {title = 'Chapters', value = 'script-binding uosc/chapters'}, - {title = 'Navigation', items = { - {title = 'Next', hint = 'playlist or file', value = 'script-binding uosc/next'}, - {title = 'Prev', hint = 'playlist or file', value = 'script-binding uosc/prev'}, - {title = 'Delete file & Next', value = 'script-binding uosc/delete-file-next'}, - {title = 'Delete file & Prev', value = 'script-binding uosc/delete-file-prev'}, - {title = 'Delete file & Quit', value = 'script-binding uosc/delete-file-quit'}, - {title = 'Open file', value = 'script-binding uosc/open-file'}, - },}, - {title = 'Utils', items = { - {title = 'Aspect ratio', items = { - {title = 'Default', value = 'set video-aspect-override "-1"'}, - {title = '16:9', value = 'set video-aspect-override "16:9"'}, - {title = '4:3', value = 'set video-aspect-override "4:3"'}, - {title = '2.35:1', value = 'set video-aspect-override "2.35:1"'}, - },}, - {title = 'Audio devices', value = 'script-binding uosc/audio-device'}, - {title = 'Editions', value = 'script-binding uosc/editions'}, - {title = 'Screenshot', value = 'async screenshot'}, - {title = 'Show in directory', value = 'script-binding uosc/show-in-directory'}, - {title = 'Open config folder', value = 'script-binding uosc/open-config-directory'}, - },}, - {title = 'Quit', value = 'quit'}, + { title = "Subtitles", value = "script-binding uosc/subtitles" }, + { title = "Audio tracks", value = "script-binding uosc/audio" }, + { title = "Stream quality", value = "script-binding uosc/stream-quality" }, + { title = "Playlist", value = "script-binding uosc/items" }, + { title = "Chapters", value = "script-binding uosc/chapters" }, + { + title = "Navigation", + items = { + { title = "Next", hint = "playlist or file", value = "script-binding uosc/next" }, + { title = "Prev", hint = "playlist or file", value = "script-binding uosc/prev" }, + { title = "Delete file & Next", value = "script-binding uosc/delete-file-next" }, + { title = "Delete file & Prev", value = "script-binding uosc/delete-file-prev" }, + { title = "Delete file & Quit", value = "script-binding uosc/delete-file-quit" }, + { title = "Open file", value = "script-binding uosc/open-file" }, + }, + }, + { + title = "Utils", + items = { + { + title = "Aspect ratio", + items = { + { title = "Default", value = 'set video-aspect-override "-1"' }, + { title = "16:9", value = 'set video-aspect-override "16:9"' }, + { title = "4:3", value = 'set video-aspect-override "4:3"' }, + { title = "2.35:1", value = 'set video-aspect-override "2.35:1"' }, + }, + }, + { title = "Audio devices", value = "script-binding uosc/audio-device" }, + { title = "Editions", value = "script-binding uosc/editions" }, + { title = "Screenshot", value = "async screenshot" }, + { title = "Show in directory", value = "script-binding uosc/show-in-directory" }, + { title = "Open config folder", value = "script-binding uosc/open-config-directory" }, + }, + }, + { title = "Quit", value = "quit" }, } end @@ -170,74 +183,83 @@ config = { -- sets max rendering frequency in case the -- native rendering frequency could not be detected render_delay = 1 / 60, - font = mp.get_property('options/osd-font'), - osd_margin_x = mp.get_property('osd-margin-x'), - osd_margin_y = mp.get_property('osd-margin-y'), - osd_alignment_x = mp.get_property('osd-align-x'), - osd_alignment_y = mp.get_property('osd-align-y'), + font = mp.get_property("options/osd-font"), + osd_margin_x = mp.get_property("osd-margin-x"), + osd_margin_y = mp.get_property("osd-margin-y"), + osd_alignment_x = mp.get_property("osd-align-x"), + osd_alignment_y = mp.get_property("osd-align-y"), types = { - video = split(options.video_types, ' *, *'), - audio = split(options.audio_types, ' *, *'), - image = split(options.image_types, ' *, *'), - subtitle = split(options.subtitle_types, ' *, *'), - media = split(options.video_types .. ',' .. options.audio_types .. ',' .. options.image_types, ' *, *'), + video = split(options.video_types, " *, *"), + audio = split(options.audio_types, " *, *"), + image = split(options.image_types, " *, *"), + subtitle = split(options.subtitle_types, " *, *"), + media = split(options.video_types .. "," .. options.audio_types .. "," .. options.image_types, " *, *"), autoload = (function() ---@type string[] local option_values = {} - for _, name in ipairs(split(options.autoload_types, ' *, *')) do - local value = options[name .. '_types'] - if type(value) == 'string' then option_values[#option_values + 1] = value end + for _, name in ipairs(split(options.autoload_types, " *, *")) do + local value = options[name .. "_types"] + if type(value) == "string" then + option_values[#option_values + 1] = value + end end - return split(table.concat(option_values, ','), ' *, *') + return split(table.concat(option_values, ","), " *, *") end)(), }, - stream_quality_options = split(options.stream_quality_options, ' *, *'), + stream_quality_options = split(options.stream_quality_options, " *, *"), menu_items = (function() - local input_conf_property = mp.get_property_native('input-conf') + local input_conf_property = mp.get_property_native("input-conf") local input_conf_path = mp.command_native({ - 'expand-path', input_conf_property == '' and '~~/input.conf' or input_conf_property, + "expand-path", + input_conf_property == "" and "~~/input.conf" or input_conf_property, }) local input_conf_meta, meta_error = utils.file_info(input_conf_path) -- File doesn't exist - if not input_conf_meta or not input_conf_meta.is_file then return create_default_menu() end + if not input_conf_meta or not input_conf_meta.is_file then + return create_default_menu() + end - local main_menu = {items = {}, items_by_command = {}} + local main_menu = { items = {}, items_by_command = {} } local by_id = {} for line in io.lines(input_conf_path) do - local key, command, comment = string.match(line, '%s*([%S]+)%s+(.-)%s+#%s*(.-)%s*$') - local title = '' + local key, command, comment = string.match(line, "%s*([%S]+)%s+(.-)%s+#%s*(.-)%s*$") + local title = "" if comment then - local comments = split(comment, '#') - local titles = itable_filter(comments, function(v, i) return v:match('^!') or v:match('^menu:') end) + local comments = split(comment, "#") + local titles = itable_filter(comments, function(v, i) + return v:match("^!") or v:match("^menu:") + end) if titles and #titles > 0 then - title = titles[1]:match('^!%s*(.*)%s*') or titles[1]:match('^menu:%s*(.*)%s*') + title = titles[1]:match("^!%s*(.*)%s*") or titles[1]:match("^menu:%s*(.*)%s*") end end - if title ~= '' then - local is_dummy = key:sub(1, 1) == '#' - local submenu_id = '' + if title ~= "" then + local is_dummy = key:sub(1, 1) == "#" + local submenu_id = "" local target_menu = main_menu - local title_parts = split(title or '', ' *> *') + local title_parts = split(title or "", " *> *") - for index, title_part in ipairs(#title_parts > 0 and title_parts or {''}) do + for index, title_part in ipairs(#title_parts > 0 and title_parts or { "" }) do if index < #title_parts then submenu_id = submenu_id .. title_part if not by_id[submenu_id] then local items = {} - by_id[submenu_id] = {items = items, items_by_command = {}} - target_menu.items[#target_menu.items + 1] = {title = title_part, items = items} + by_id[submenu_id] = { items = items, items_by_command = {} } + target_menu.items[#target_menu.items + 1] = { title = title_part, items = items } end target_menu = by_id[submenu_id] else - if command == 'ignore' then break end + if command == "ignore" then + break + end -- If command is already in menu, just append the key to it if target_menu.items_by_command[command] then local hint = target_menu.items_by_command[command].hint - target_menu.items_by_command[command].hint = hint and hint .. ', ' .. key or key + target_menu.items_by_command[command].hint = hint and hint .. ", " .. key or key else local item = { title = title_part, @@ -262,23 +284,29 @@ config = { chapter_ranges = (function() ---@type table Alternative patterns. local alt_patterns = {} - if options.chapter_range_patterns and options.chapter_range_patterns ~= '' then - for _, definition in ipairs(split(options.chapter_range_patterns, ';+ *')) do - local name_patterns = split(definition, ' *:') + if options.chapter_range_patterns and options.chapter_range_patterns ~= "" then + for _, definition in ipairs(split(options.chapter_range_patterns, ";+ *")) do + local name_patterns = split(definition, " *:") local name, patterns = name_patterns[1], name_patterns[2] - if name and patterns then alt_patterns[name] = split(patterns, ',') end + if name and patterns then + alt_patterns[name] = split(patterns, ",") + end end end ---@type table local ranges = {} - if options.chapter_ranges and options.chapter_ranges ~= '' then - for _, definition in ipairs(split(options.chapter_ranges, ' *,+ *')) do - local name_color = split(definition, ' *:+ *') + if options.chapter_ranges and options.chapter_ranges ~= "" then + for _, definition in ipairs(split(options.chapter_ranges, " *,+ *")) do + local name_color = split(definition, " *:+ *") local name, color = name_color[1], name_color[2] - if name and color - and name:match('^[a-zA-Z0-9_]+$') and color:match('^[a-fA-F0-9]+$') - and (#color == 6 or #color == 8) then + if + name + and color + and name:match("^[a-zA-Z0-9_]+$") + and color:match("^[a-fA-F0-9]+$") + and (#color == 6 or #color == 8) + then local range = serialize_rgba(name_color[2]) range.patterns = alt_patterns[name] ranges[name_color[1]] = range @@ -289,18 +317,20 @@ config = { end)(), } -- Adds `{element}_persistency` property with table of flags when the element should be visible (`{paused = true}`) -for _, name in ipairs({'timeline', 'controls', 'volume', 'top_bar', 'speed'}) do - local option_name = name .. '_persistency' +for _, name in ipairs({ "timeline", "controls", "volume", "top_bar", "speed" }) do + local option_name = name .. "_persistency" local value, flags = options[option_name], {} - if type(value) == 'string' then - for _, state in ipairs(split(value, ' *, *')) do flags[state] = true end + if type(value) == "string" then + for _, state in ipairs(split(value, " *, *")) do + flags[state] = true + end end config[option_name] = flags end --[[ STATE ]] -display = {width = 1280, height = 720, scale_x = 1, scale_y = 1, initialized = false} +display = { width = 1280, height = 720, scale_x = 1, scale_y = 1, initialized = false } cursor = { x = 0, y = 0, @@ -325,20 +355,24 @@ cursor = { local enable_mbtn_left = (cursor.on_primary_down or cursor.on_primary_up) ~= nil local enable_wheel = (cursor.on_wheel_down or cursor.on_wheel_up) ~= nil if enable_mbtn_left ~= cursor.mbtn_left_enabled then - mp[(enable_mbtn_left and 'enable' or 'disable') .. '_key_bindings']('mbtn_left') + mp[(enable_mbtn_left and "enable" or "disable") .. "_key_bindings"]("mbtn_left") cursor.mbtn_left_enabled = enable_mbtn_left end if enable_wheel ~= cursor.wheel_enabled then - mp[(enable_wheel and 'enable' or 'disable') .. '_key_bindings']('wheel') + mp[(enable_wheel and "enable" or "disable") .. "_key_bindings"]("wheel") cursor.wheel_enabled = enable_wheel end end, -- Cursor auto-hiding after period of inactivity autohide = function() - if not cursor.on_primary_up and not Menu:is_open() then handle_mouse_leave() end + if not cursor.on_primary_up and not Menu:is_open() then + handle_mouse_leave() + end end, autohide_timer = (function() - local timer = mp.add_timeout(mp.get_property_native('cursor-autohide') / 1000, function() cursor.autohide() end) + local timer = mp.add_timeout(mp.get_property_native("cursor-autohide") / 1000, function() + cursor.autohide() + end) timer:kill() return timer end)(), @@ -347,21 +381,27 @@ cursor = { cursor.autohide_timer:kill() cursor.autohide_timer:resume() end - end + end, } state = { platform = (function() - local platform = mp.get_property_native('platform') + local platform = mp.get_property_native("platform") if platform then - if itable_index_of({'windows', 'darwin'}, platform) then return platform end + if itable_index_of({ "windows", "darwin" }, platform) then + return platform + end else - if os.getenv('windir') ~= nil then return 'windows' end - local homedir = os.getenv('HOME') - if homedir ~= nil and string.sub(homedir, 1, 6) == '/Users' then return 'darwin' end + if os.getenv("windir") ~= nil then + return "windows" + end + local homedir = os.getenv("HOME") + if homedir ~= nil and string.sub(homedir, 1, 6) == "/Users" then + return "darwin" + end end - return 'linux' + return "linux" end)(), - cwd = mp.get_property('working-directory'), + cwd = mp.get_property("working-directory"), path = nil, -- current file path or URL title = nil, alt_title = nil, @@ -370,14 +410,14 @@ state = { duration = nil, -- current media duration time_human = nil, -- current playback time in human format destination_time_human = nil, -- depends on options.destination_time - pause = mp.get_property_native('pause'), + pause = mp.get_property_native("pause"), chapters = {}, current_chapter = nil, chapter_ranges = {}, - border = mp.get_property_native('border'), - fullscreen = mp.get_property_native('fullscreen'), - maximized = mp.get_property_native('window-maximized'), - fullormaxed = mp.get_property_native('fullscreen') or mp.get_property_native('window-maximized'), + border = mp.get_property_native("border"), + fullscreen = mp.get_property_native("fullscreen"), + maximized = mp.get_property_native("window-maximized"), + fullormaxed = mp.get_property_native("fullscreen") or mp.get_property_native("window-maximized"), render_timer = nil, render_last_time = 0, volume = nil, @@ -410,31 +450,33 @@ state = { margin_right = 0, hidpi_scale = 1, } -thumbnail = {width = 0, height = 0, disabled = false} +thumbnail = { width = 0, height = 0, disabled = false } external = {} -- Properties set by external scripts key_binding_overwrites = {} -- Table of key_binding:mpv_command -Elements = require('uosc_shared/elements/Elements') -Menu = require('uosc_shared/elements/Menu') +Elements = require("uosc_shared/elements/Elements") +Menu = require("uosc_shared/elements/Menu") -- State dependent utilities -require('uosc_shared/lib/utils') -require('uosc_shared/lib/text') -require('uosc_shared/lib/ass') -require('uosc_shared/lib/menus') +require("uosc_shared/lib/utils") +require("uosc_shared/lib/text") +require("uosc_shared/lib/ass") +require("uosc_shared/lib/menus") --[[ STATE UPDATERS ]] function update_display_dimensions() local scale = (state.hidpi_scale or 1) * options.ui_scale local real_width, real_height = mp.get_osd_size() - if real_width <= 0 then return end + if real_width <= 0 then + return + end local scaled_width, scaled_height = round(real_width / scale), round(real_height / scale) display.width, display.height = scaled_width, scaled_height display.scale_x, display.scale_y = real_width / scaled_width, real_height / scaled_height display.initialized = true -- Tell elements about this - Elements:trigger('display') + Elements:trigger("display") -- Some elements probably changed their rectangles as a reaction to `display` Elements:update_proximities() @@ -444,7 +486,7 @@ end function update_fullormaxed() state.fullormaxed = state.fullscreen or state.maximized update_display_dimensions() - Elements:trigger('prop_fullormaxed', state.fullormaxed) + Elements:trigger("prop_fullormaxed", state.fullormaxed) update_cursor_position(INFINITY, INFINITY) end @@ -453,9 +495,9 @@ function update_human_times() state.time_human = format_time(state.time, state.duration) if state.duration then local speed = state.speed or 1 - if options.destination_time == 'playtime-remaining' then + if options.destination_time == "playtime-remaining" then state.destination_time_human = format_time((state.time - state.duration) / speed, state.duration) - elseif options.destination_time == 'total' then + elseif options.destination_time == "total" then state.destination_time_human = format_time(state.duration, state.duration) else state.destination_time_human = format_time(state.time - state.duration, state.duration) @@ -470,54 +512,82 @@ end -- Notifies other scripts such as console about where the unoccupied parts of the screen are. function update_margins() - if display.height == 0 then return end + if display.height == 0 then + return + end - local function is_persistent(element) return element and element.enabled and element:is_persistent() end + local function is_persistent(element) + return element and element.enabled and element:is_persistent() + end local timeline, top_bar, controls, volume = Elements.timeline, Elements.top_bar, Elements.controls, Elements.volume -- margins are normalized to window size local left, right, top, bottom = 0, 0, 0, 0 - if is_persistent(controls) then bottom = (display.height - controls.ay) / display.height - elseif is_persistent(timeline) then bottom = (display.height - timeline.ay) / display.height end - - if is_persistent(top_bar) then top = top_bar.title_by / display.height end - - if is_persistent(volume) then - if options.volume == 'left' then left = volume.bx / display.width - elseif options.volume == 'right' then right = volume.ax / display.width end + if is_persistent(controls) then + bottom = (display.height - controls.ay) / display.height + elseif is_persistent(timeline) then + bottom = (display.height - timeline.ay) / display.height end - if top == state.margin_top and bottom == state.margin_bottom and - left == state.margin_left and right == state.margin_right then return end + if is_persistent(top_bar) then + top = top_bar.title_by / display.height + end + + if is_persistent(volume) then + if options.volume == "left" then + left = volume.bx / display.width + elseif options.volume == "right" then + right = volume.ax / display.width + end + end + + if + top == state.margin_top + and bottom == state.margin_bottom + and left == state.margin_left + and right == state.margin_right + then + return + end state.margin_top = top state.margin_bottom = bottom state.margin_left = left state.margin_right = right - utils.shared_script_property_set('osc-margins', string.format('%f,%f,%f,%f', 0, 0, top, bottom)) - mp.set_property_native('user-data/osc/margins', { l = left, r = right, t = top, b = bottom }) + utils.shared_script_property_set("osc-margins", string.format("%f,%f,%f,%f", 0, 0, top, bottom)) + mp.set_property_native("user-data/osc/margins", { l = left, r = right, t = top, b = bottom }) - if not options.adjust_osd_margins then return end + if not options.adjust_osd_margins then + return + end local osd_margin_y, osd_margin_x, osd_factor_x = 0, 0, display.width / display.height * 720 - if config.osd_alignment_y == 'bottom' then osd_margin_y = round(bottom * 720) - elseif config.osd_alignment_y == 'top' then osd_margin_y = round(top * 720) end - if config.osd_alignment_x == 'left' then osd_margin_x = round(left * osd_factor_x) - elseif config.osd_alignment_x == 'right' then osd_margin_x = round(right * osd_factor_x) end - mp.set_property_native('osd-margin-y', osd_margin_y + config.osd_margin_y) - mp.set_property_native('osd-margin-x', osd_margin_x + config.osd_margin_x) + if config.osd_alignment_y == "bottom" then + osd_margin_y = round(bottom * 720) + elseif config.osd_alignment_y == "top" then + osd_margin_y = round(top * 720) + end + if config.osd_alignment_x == "left" then + osd_margin_x = round(left * osd_factor_x) + elseif config.osd_alignment_x == "right" then + osd_margin_x = round(right * osd_factor_x) + end + mp.set_property_native("osd-margin-y", osd_margin_y + config.osd_margin_y) + mp.set_property_native("osd-margin-x", osd_margin_x + config.osd_margin_x) end function create_state_setter(name, callback) return function(_, value) set_state(name, value) - if callback then callback() end + if callback then + callback() + end request_render() end end function set_state(name, value) state[name] = value - Elements:trigger('prop_' .. name, value) + Elements:trigger("prop_" .. name, value) end function update_cursor_position(x, y) @@ -527,8 +597,11 @@ function update_cursor_position(x, y) -- displays the top bar, so we hardcode cursor position as infinity until -- we receive a first real mouse move event with coordinates other than 0,0. if not state.first_real_mouse_move_received then - if x > 0 and y > 0 then state.first_real_mouse_move_received = true - else x, y = INFINITY, INFINITY end + if x > 0 and y > 0 then + state.first_real_mouse_move_received = true + else + x, y = INFINITY, INFINITY + end end -- add 0.5 to be in the middle of the pixel @@ -539,13 +612,13 @@ function update_cursor_position(x, y) if cursor.x == INFINITY or cursor.y == INFINITY then cursor.hidden = true - Elements:trigger('global_mouse_leave') + Elements:trigger("global_mouse_leave") elseif cursor.hidden then cursor.hidden = false - Elements:trigger('global_mouse_enter') + Elements:trigger("global_mouse_enter") end - Elements:proximity_trigger('mouse_move') + Elements:proximity_trigger("mouse_move") cursor.queue_autohide() end @@ -554,10 +627,10 @@ end function handle_mouse_leave() -- Slowly fadeout elements that are currently visible - for _, element_name in ipairs({'timeline', 'volume', 'top_bar'}) do + for _, element_name in ipairs({ "timeline", "volume", "top_bar" }) do local element = Elements[element_name] if element and element.proximity > 0 then - element:tween_property('forced_visibility', element:get_visibility(), 0, function() + element:tween_property("forced_visibility", element:get_visibility(), 0, function() element.forced_visibility = nil end) end @@ -569,50 +642,65 @@ end function handle_file_end() local resume = false if not state.loop_file then - if state.has_playlist then resume = state.shuffle and navigate_playlist(1) - else resume = options.autoload and navigate_directory(1) end + if state.has_playlist then + resume = state.shuffle and navigate_playlist(1) + else + resume = options.autoload and navigate_directory(1) + end end -- Resume only when navigation happened - if resume then mp.command('set pause no') end + if resume then + mp.command("set pause no") + end end local file_end_timer = mp.add_timeout(1, handle_file_end) file_end_timer:kill() function load_file_index_in_current_directory(index) - if not state.path or is_protocol(state.path) then return end + if not state.path or is_protocol(state.path) then + return + end local serialized = serialize_path(state.path) if serialized and serialized.dirname then local files = read_directory(serialized.dirname, config.types.autoload) - if not files then return end + if not files then + return + end sort_filenames(files) - if index < 0 then index = #files + index + 1 end + if index < 0 then + index = #files + index + 1 + end if files[index] then - mp.commandv('loadfile', join_path(serialized.dirname, files[index])) + mp.commandv("loadfile", join_path(serialized.dirname, files[index])) end end end function update_render_delay(name, fps) - if fps then state.render_delay = 1 / fps end + if fps then + state.render_delay = 1 / fps + end end function observe_display_fps(name, fps) if fps then mp.unobserve_property(update_render_delay) mp.unobserve_property(observe_display_fps) - mp.observe_property('display-fps', 'native', update_render_delay) + mp.observe_property("display-fps", "native", update_render_delay) end end function select_current_chapter() local current_chapter if state.time and state.chapters then - _, current_chapter = itable_find(state.chapters, function(c) return state.time >= c.time end, true) + _, current_chapter = itable_find(state.chapters, function(c) + return state.time >= c.time + end, true) end - set_state('current_chapter', current_chapter) + set_state("current_chapter", current_chapter) end --[[ STATE HOOKS ]] @@ -622,25 +710,42 @@ if options.click_threshold > 0 then -- Executes custom command for clicks shorter than `options.click_threshold` -- while filtering out double clicks. local click_time = options.click_threshold / 1000 - local doubleclick_time = mp.get_property_native('input-doubleclick-time') / 1000 + local doubleclick_time = mp.get_property_native("input-doubleclick-time") / 1000 local last_down, last_up = 0, 0 local click_timer = mp.add_timeout(math.max(click_time, doubleclick_time), function() local delta = last_up - last_down - if delta > 0 and delta < click_time and delta > 0.02 then mp.command(options.click_command) end + if delta > 0 and delta < click_time and delta > 0.02 then + mp.command(options.click_command) + end end) click_timer:kill() - mp.set_key_bindings({{'mbtn_left', - function() last_up = mp.get_time() end, - function() - last_down = mp.get_time() - if click_timer:is_enabled() then click_timer:kill() else click_timer:resume() end - end, - },}, 'mouse_movement', 'force') - mp.enable_key_bindings('mouse_movement', 'allow-vo-dragging+allow-hide-cursor') + mp.set_key_bindings( + { + { + "mbtn_left", + function() + last_up = mp.get_time() + end, + function() + last_down = mp.get_time() + if click_timer:is_enabled() then + click_timer:kill() + else + click_timer:resume() + end + end, + }, + }, + "mouse_movement", + "force" + ) + mp.enable_key_bindings("mouse_movement", "allow-vo-dragging+allow-hide-cursor") end function handle_mouse_pos(_, mouse) - if not mouse then return end + if not mouse then + return + end if cursor.hover_raw and not mouse.hover then handle_mouse_leave() else @@ -648,15 +753,19 @@ function handle_mouse_pos(_, mouse) end cursor.hover_raw = mouse.hover end -mp.observe_property('mouse-pos', 'native', handle_mouse_pos) -mp.observe_property('osc', 'bool', function(name, value) if value == true then mp.set_property('osc', 'no') end end) -mp.register_event('file-loaded', function() - set_state('path', normalize_path(mp.get_property_native('path'))) - Elements:flash({'top_bar'}) +mp.observe_property("mouse-pos", "native", handle_mouse_pos) +mp.observe_property("osc", "bool", function(name, value) + if value == true then + mp.set_property("osc", "no") + end end) -mp.register_event('end-file', function(event) - set_state('path', nil) - if event.reason == 'eof' then +mp.register_event("file-loaded", function() + set_state("path", normalize_path(mp.get_property_native("path"))) + Elements:flash({ "top_bar" }) +end) +mp.register_event("end-file", function(event) + set_state("path", nil) + if event.reason == "eof" then file_end_timer:kill() handle_file_end() end @@ -665,138 +774,180 @@ end) do local function update_state_with_template(prop, template) -- escape ASS, and strip newlines and trailing slashes and trim whitespace - local tmp = mp.command_native({'expand-text', template}):gsub('\\n', ' '):gsub('[\\%s]+$', ''):gsub('^%s+', '') + local tmp = + mp.command_native({ "expand-text", template }):gsub("\\n", " "):gsub("[\\%s]+$", ""):gsub("^%s+", "") set_state(prop, ass_escape(tmp)) end local function add_template_listener(template, callback) local props = get_expansion_props(template) for prop, _ in pairs(props) do - mp.observe_property(prop, 'native', callback) + mp.observe_property(prop, "native", callback) + end + if not next(props) then + callback() end - if not next(props) then callback() end end - local function remove_template_listener(callback) mp.unobserve_property(callback) end + local function remove_template_listener(callback) + mp.unobserve_property(callback) + end -- Main title - if #options.top_bar_title > 0 and options.top_bar_title ~= 'no' then - if options.top_bar_title == 'yes' then + if #options.top_bar_title > 0 and options.top_bar_title ~= "no" then + if options.top_bar_title == "yes" then local template = nil - local function update_title() update_state_with_template('title', template) end - mp.observe_property('title', 'string', function(_, title) + local function update_title() + update_state_with_template("title", template) + end + mp.observe_property("title", "string", function(_, title) remove_template_listener(update_title) template = title if template then - if template:sub(-6) == ' - mpv' then template = template:sub(1, -7) end + if template:sub(-6) == " - mpv" then + template = template:sub(1, -7) + end add_template_listener(template, update_title) end end) - elseif type(options.top_bar_title) == 'string' then + elseif type(options.top_bar_title) == "string" then add_template_listener(options.top_bar_title, function() - update_state_with_template('title', options.top_bar_title) + update_state_with_template("title", options.top_bar_title) end) end end -- Alt title - if #options.top_bar_alt_title > 0 and options.top_bar_alt_title ~= 'no' then + if #options.top_bar_alt_title > 0 and options.top_bar_alt_title ~= "no" then add_template_listener(options.top_bar_alt_title, function() - update_state_with_template('alt_title', options.top_bar_alt_title) + update_state_with_template("alt_title", options.top_bar_alt_title) end) end end -mp.observe_property('playback-time', 'number', create_state_setter('time', function() - -- Create a file-end event that triggers right before file ends - file_end_timer:kill() - if state.duration and state.time and not state.pause then - local remaining = (state.duration - state.time) / state.speed - if remaining < 5 then - local timeout = remaining - 0.02 - if timeout > 0 then - file_end_timer.timeout = timeout - file_end_timer:resume() - else handle_file_end() end +mp.observe_property( + "playback-time", + "number", + create_state_setter("time", function() + -- Create a file-end event that triggers right before file ends + file_end_timer:kill() + if state.duration and state.time and not state.pause then + local remaining = (state.duration - state.time) / state.speed + if remaining < 5 then + local timeout = remaining - 0.02 + if timeout > 0 then + file_end_timer.timeout = timeout + file_end_timer:resume() + else + handle_file_end() + end + end + end + + update_human_times() + select_current_chapter() + end) +) +mp.observe_property("duration", "number", create_state_setter("duration", update_human_times)) +mp.observe_property("speed", "number", create_state_setter("speed", update_human_times)) +mp.observe_property("track-list", "native", function(name, value) + -- checks the file dispositions + local types = { sub = 0, image = 0, audio = 0, video = 0 } + for _, track in ipairs(value) do + if track.type == "video" then + if track.image or track.albumart then + types.image = types.image + 1 + else + types.video = types.video + 1 + end + elseif types[track.type] then + types[track.type] = types[track.type] + 1 end end - - update_human_times() - select_current_chapter() -end)) -mp.observe_property('duration', 'number', create_state_setter('duration', update_human_times)) -mp.observe_property('speed', 'number', create_state_setter('speed', update_human_times)) -mp.observe_property('track-list', 'native', function(name, value) - -- checks the file dispositions - local types = {sub = 0, image = 0, audio = 0, video = 0} - for _, track in ipairs(value) do - if track.type == 'video' then - if track.image or track.albumart then types.image = types.image + 1 - else types.video = types.video + 1 end - elseif types[track.type] then types[track.type] = types[track.type] + 1 end + set_state("is_audio", types.video == 0 and types.audio > 0) + set_state("is_image", types.image > 0 and types.video == 0 and types.audio == 0) + set_state("has_audio", types.audio > 0) + set_state("has_many_audio", types.audio > 1) + set_state("has_sub", types.sub > 0) + set_state("has_many_sub", types.sub > 1) + set_state("is_video", types.video > 0) + set_state("has_many_video", types.video > 1) + Elements:trigger("dispositions") +end) +mp.observe_property("editions", "number", function(_, editions) + if editions then + set_state("has_many_edition", editions > 1) end - set_state('is_audio', types.video == 0 and types.audio > 0) - set_state('is_image', types.image > 0 and types.video == 0 and types.audio == 0) - set_state('has_audio', types.audio > 0) - set_state('has_many_audio', types.audio > 1) - set_state('has_sub', types.sub > 0) - set_state('has_many_sub', types.sub > 1) - set_state('is_video', types.video > 0) - set_state('has_many_video', types.video > 1) - Elements:trigger('dispositions') + Elements:trigger("dispositions") end) -mp.observe_property('editions', 'number', function(_, editions) - if editions then set_state('has_many_edition', editions > 1) end - Elements:trigger('dispositions') -end) -mp.observe_property('chapter-list', 'native', function(_, chapters) +mp.observe_property("chapter-list", "native", function(_, chapters) local chapters, chapter_ranges = serialize_chapters(chapters), {} - if chapters then chapters, chapter_ranges = serialize_chapter_ranges(chapters) end - set_state('chapters', chapters) - set_state('chapter_ranges', chapter_ranges) - set_state('has_chapter', #chapters > 0) + if chapters then + chapters, chapter_ranges = serialize_chapter_ranges(chapters) + end + set_state("chapters", chapters) + set_state("chapter_ranges", chapter_ranges) + set_state("has_chapter", #chapters > 0) select_current_chapter() - Elements:trigger('dispositions') + Elements:trigger("dispositions") end) -mp.observe_property('border', 'bool', create_state_setter('border')) -mp.observe_property('loop-file', 'native', create_state_setter('loop_file')) -mp.observe_property('ab-loop-a', 'number', create_state_setter('ab_loop_a')) -mp.observe_property('ab-loop-b', 'number', create_state_setter('ab_loop_b')) -mp.observe_property('playlist-pos-1', 'number', create_state_setter('playlist_pos')) -mp.observe_property('playlist-count', 'number', function(_, value) - set_state('playlist_count', value) - set_state('has_playlist', value > 1) - Elements:trigger('dispositions') +mp.observe_property("border", "bool", create_state_setter("border")) +mp.observe_property("loop-file", "native", create_state_setter("loop_file")) +mp.observe_property("ab-loop-a", "number", create_state_setter("ab_loop_a")) +mp.observe_property("ab-loop-b", "number", create_state_setter("ab_loop_b")) +mp.observe_property("playlist-pos-1", "number", create_state_setter("playlist_pos")) +mp.observe_property("playlist-count", "number", function(_, value) + set_state("playlist_count", value) + set_state("has_playlist", value > 1) + Elements:trigger("dispositions") end) -mp.observe_property('fullscreen', 'bool', create_state_setter('fullscreen', update_fullormaxed)) -mp.observe_property('window-maximized', 'bool', create_state_setter('maximized', update_fullormaxed)) -mp.observe_property('idle-active', 'bool', function(_, idle) - set_state('is_idle', idle) - Elements:trigger('dispositions') +mp.observe_property("fullscreen", "bool", create_state_setter("fullscreen", update_fullormaxed)) +mp.observe_property("window-maximized", "bool", create_state_setter("maximized", update_fullormaxed)) +mp.observe_property("idle-active", "bool", function(_, idle) + set_state("is_idle", idle) + Elements:trigger("dispositions") end) -mp.observe_property('pause', 'bool', create_state_setter('pause', function() file_end_timer:kill() end)) -mp.observe_property('volume', 'number', create_state_setter('volume')) -mp.observe_property('volume-max', 'number', create_state_setter('volume_max')) -mp.observe_property('mute', 'bool', create_state_setter('mute')) -mp.observe_property('osd-dimensions', 'native', function(name, val) +mp.observe_property( + "pause", + "bool", + create_state_setter("pause", function() + file_end_timer:kill() + end) +) +mp.observe_property("volume", "number", create_state_setter("volume")) +mp.observe_property("volume-max", "number", create_state_setter("volume_max")) +mp.observe_property("mute", "bool", create_state_setter("mute")) +mp.observe_property("osd-dimensions", "native", function(name, val) update_display_dimensions() request_render() end) -mp.observe_property('display-hidpi-scale', 'native', create_state_setter('hidpi_scale', update_display_dimensions)) -mp.observe_property('cache', 'string', create_state_setter('cache')) -mp.observe_property('cache-buffering-state', 'number', create_state_setter('cache_buffering')) -mp.observe_property('demuxer-via-network', 'native', create_state_setter('is_stream', function() - Elements:trigger('dispositions') -end)) -mp.observe_property('demuxer-cache-state', 'native', function(prop, cache_state) +mp.observe_property("display-hidpi-scale", "native", create_state_setter("hidpi_scale", update_display_dimensions)) +mp.observe_property("cache", "string", create_state_setter("cache")) +mp.observe_property("cache-buffering-state", "number", create_state_setter("cache_buffering")) +mp.observe_property( + "demuxer-via-network", + "native", + create_state_setter("is_stream", function() + Elements:trigger("dispositions") + end) +) +mp.observe_property("demuxer-cache-state", "native", function(prop, cache_state) local cached_ranges, bof, eof, uncached_ranges = nil, nil, nil, nil if cache_state then - cached_ranges, bof, eof = cache_state['seekable-ranges'], cache_state['bof-cached'], cache_state['eof-cached'] - set_state('cache_underrun', cache_state['underrun']) - else cached_ranges = {} end + cached_ranges, bof, eof = cache_state["seekable-ranges"], cache_state["bof-cached"], cache_state["eof-cached"] + set_state("cache_underrun", cache_state["underrun"]) + else + cached_ranges = {} + end - if not (state.duration and (#cached_ranges > 0 or state.cache == 'yes' or - (state.cache == 'auto' and state.is_stream))) then - if state.uncached_ranges then set_state('uncached_ranges', nil) end + if + not ( + state.duration + and (#cached_ranges > 0 or state.cache == "yes" or (state.cache == "auto" and state.is_stream)) + ) + then + if state.uncached_ranges then + set_state("uncached_ranges", nil) + end return end @@ -804,18 +955,24 @@ mp.observe_property('demuxer-cache-state', 'native', function(prop, cache_state) local ranges = {} for _, range in ipairs(cached_ranges) do ranges[#ranges + 1] = { - math.max(range['start'] or 0, 0), - math.min(range['end'] or state.duration, state.duration), + math.max(range["start"] or 0, 0), + math.min(range["end"] or state.duration, state.duration), } end - table.sort(ranges, function(a, b) return a[1] < b[1] end) - if bof then ranges[1][1] = 0 end - if eof then ranges[#ranges][2] = state.duration end + table.sort(ranges, function(a, b) + return a[1] < b[1] + end) + if bof then + ranges[1][1] = 0 + end + if eof then + ranges[#ranges][2] = state.duration + end -- Invert cached ranges into uncached ranges, as that's what we're rendering - local inverted_ranges = {{0, state.duration}} + local inverted_ranges = { { 0, state.duration } } for _, cached in pairs(ranges) do inverted_ranges[#inverted_ranges][2] = cached[1] - inverted_ranges[#inverted_ranges + 1] = {cached[2], state.duration} + inverted_ranges[#inverted_ranges + 1] = { cached[2], state.duration } end uncached_ranges = {} local last_range = nil @@ -830,12 +987,12 @@ mp.observe_property('demuxer-cache-state', 'native', function(prop, cache_state) end end - set_state('uncached_ranges', uncached_ranges) + set_state("uncached_ranges", uncached_ranges) end) -mp.observe_property('display-fps', 'native', observe_display_fps) -mp.observe_property('estimated-display-fps', 'native', update_render_delay) -mp.observe_property('eof-reached', 'native', create_state_setter('eof_reached')) -mp.observe_property('core-idle', 'native', create_state_setter('core_idle')) +mp.observe_property("display-fps", "native", observe_display_fps) +mp.observe_property("estimated-display-fps", "native", update_render_delay) +mp.observe_property("eof-reached", "native", create_state_setter("eof_reached")) +mp.observe_property("core-idle", "native", create_state_setter("core_idle")) --[[ KEY BINDS ]] @@ -849,18 +1006,18 @@ function make_cursor_handler(event, cb) end mp.set_key_bindings({ { - 'mbtn_left', - make_cursor_handler('on_primary_up'), - make_cursor_handler('on_primary_down', function(...) - handle_mouse_pos(nil, mp.get_property_native('mouse-pos')) + "mbtn_left", + make_cursor_handler("on_primary_up"), + make_cursor_handler("on_primary_down", function(...) + handle_mouse_pos(nil, mp.get_property_native("mouse-pos")) end), }, - {'mbtn_left_dbl', 'ignore'}, -}, 'mbtn_left', 'force') + { "mbtn_left_dbl", "ignore" }, +}, "mbtn_left", "force") mp.set_key_bindings({ - {'wheel_up', make_cursor_handler('on_wheel_up')}, - {'wheel_down', make_cursor_handler('on_wheel_down')}, -}, 'wheel', 'force') + { "wheel_up", make_cursor_handler("on_wheel_up") }, + { "wheel_down", make_cursor_handler("on_wheel_down") }, +}, "wheel", "force") -- Adds a key binding that respects rerouting set by `key_binding_overwrites` table. ---@param name string @@ -868,41 +1025,69 @@ mp.set_key_bindings({ ---@param flags nil|string function bind_command(name, callback, flags) mp.add_key_binding(nil, name, function(...) - if key_binding_overwrites[name] then mp.command(key_binding_overwrites[name]) - else callback(...) end + if key_binding_overwrites[name] then + mp.command(key_binding_overwrites[name]) + else + callback(...) + end end, flags) end -bind_command('toggle-ui', function() Elements:toggle({'timeline', 'controls', 'volume', 'top_bar'}) end) -bind_command('flash-ui', function() Elements:flash({'timeline', 'controls', 'volume', 'top_bar'}) end) -bind_command('flash-timeline', function() Elements:flash({'timeline'}) end) -bind_command('flash-top-bar', function() Elements:flash({'top_bar'}) end) -bind_command('flash-volume', function() Elements:flash({'volume'}) end) -bind_command('flash-speed', function() Elements:flash({'speed'}) end) -bind_command('flash-pause-indicator', function() Elements:flash({'pause_indicator'}) end) -bind_command('toggle-progress', function() +bind_command("toggle-ui", function() + Elements:toggle({ "timeline", "controls", "volume", "top_bar" }) +end) +bind_command("flash-ui", function() + Elements:flash({ "timeline", "controls", "volume", "top_bar" }) +end) +bind_command("flash-timeline", function() + Elements:flash({ "timeline" }) +end) +bind_command("flash-top-bar", function() + Elements:flash({ "top_bar" }) +end) +bind_command("flash-volume", function() + Elements:flash({ "volume" }) +end) +bind_command("flash-speed", function() + Elements:flash({ "speed" }) +end) +bind_command("flash-pause-indicator", function() + Elements:flash({ "pause_indicator" }) +end) +bind_command("toggle-progress", function() local timeline = Elements.timeline if timeline.size_min_override then - timeline:tween_property('size_min_override', timeline.size_min_override, timeline.size_min, function() + timeline:tween_property("size_min_override", timeline.size_min_override, timeline.size_min, function() timeline.size_min_override = nil end) else - timeline:tween_property('size_min_override', timeline.size_min, 0) + timeline:tween_property("size_min_override", timeline.size_min, 0) end end) -bind_command('toggle-title', function() Elements.top_bar:toggle_title() end) -bind_command('decide-pause-indicator', function() Elements.pause_indicator:decide() end) -bind_command('menu', function() toggle_menu_with_items() end) -bind_command('menu-blurred', function() toggle_menu_with_items({mouse_nav = true}) end) +bind_command("toggle-title", function() + Elements.top_bar:toggle_title() +end) +bind_command("decide-pause-indicator", function() + Elements.pause_indicator:decide() +end) +bind_command("menu", function() + toggle_menu_with_items() +end) +bind_command("menu-blurred", function() + toggle_menu_with_items({ mouse_nav = true }) +end) local track_loaders = { - {name = 'subtitles', prop = 'sub', allowed_types = itable_join(config.types.video, config.types.subtitle)}, - {name = 'audio', prop = 'audio', allowed_types = itable_join(config.types.video, config.types.audio)}, - {name = 'video', prop = 'video', allowed_types = config.types.video}, + { name = "subtitles", prop = "sub", allowed_types = itable_join(config.types.video, config.types.subtitle) }, + { name = "audio", prop = "audio", allowed_types = itable_join(config.types.video, config.types.audio) }, + { name = "video", prop = "video", allowed_types = config.types.video }, } for _, loader in ipairs(track_loaders) do - local menu_type = 'load-' .. loader.name + local menu_type = "load-" .. loader.name bind_command(menu_type, function() - if Menu:is_open(menu_type) then Menu:close() return end + if Menu:is_open(menu_type) then + Menu:close() + return + end local path = state.path if path then @@ -916,125 +1101,148 @@ for _, loader in ipairs(track_loaders) do if not path then path = get_default_directory() end - open_file_navigation_menu( - path, - function(path) mp.commandv(loader.prop .. '-add', path) end, - {type = menu_type, title = 'Load ' .. loader.name, allowed_types = loader.allowed_types} - ) + open_file_navigation_menu(path, function(path) + mp.commandv(loader.prop .. "-add", path) + end, { type = menu_type, title = "Load " .. loader.name, allowed_types = loader.allowed_types }) end) end -bind_command('subtitles', create_select_tracklist_type_menu_opener( - 'Subtitles', 'sub', 'sid', 'script-binding uosc/load-subtitles' -)) -bind_command('audio', create_select_tracklist_type_menu_opener( - 'Audio', 'audio', 'aid', 'script-binding uosc/load-audio' -)) -bind_command('video', create_select_tracklist_type_menu_opener( - 'Video', 'video', 'vid', 'script-binding uosc/load-video' -)) -bind_command('playlist', create_self_updating_menu_opener({ - title = 'Playlist', - type = 'playlist', - list_prop = 'playlist', - serializer = function(playlist) - local items = {} - for index, item in ipairs(playlist) do - local is_url = item.filename:find('://') - local item_title = type(item.title) == 'string' and #item.title > 0 and item.title or false - items[index] = { - title = item_title or (is_url and item.filename or serialize_path(item.filename).basename), - hint = tostring(index), - active = item.current, - value = index, - } - end - return items - end, - on_select = function(index) mp.commandv('set', 'playlist-pos-1', tostring(index)) end, - on_move_item = function(from, to) - mp.commandv('playlist-move', tostring(math.max(from, to) - 1), tostring(math.min(from, to) - 1)) - end, - on_delete_item = function(index) mp.commandv('playlist-remove', tostring(index - 1)) end, -})) -bind_command('chapters', create_self_updating_menu_opener({ - title = 'Chapters', - type = 'chapters', - list_prop = 'chapter-list', - active_prop = 'chapter', - serializer = function(chapters, current_chapter) - local items = {} - chapters = normalize_chapters(chapters) - for index, chapter in ipairs(chapters) do - items[index] = { - title = chapter.title or '', - hint = format_time(chapter.time, state.duration), - value = index, - active = index - 1 == current_chapter, - } - end - return items - end, - on_select = function(index) mp.commandv('set', 'chapter', tostring(index - 1)) end, -})) -bind_command('editions', create_self_updating_menu_opener({ - title = 'Editions', - type = 'editions', - list_prop = 'edition-list', - active_prop = 'current-edition', - serializer = function(editions, current_id) - local items = {} - for _, edition in ipairs(editions or {}) do - items[#items + 1] = { - title = edition.title or 'Edition', - hint = tostring(edition.id + 1), - value = edition.id, - active = edition.id == current_id, - } - end - return items - end, - on_select = function(id) mp.commandv('set', 'edition', id) end, -})) -bind_command('show-in-directory', function() +bind_command( + "subtitles", + create_select_tracklist_type_menu_opener("Subtitles", "sub", "sid", "script-binding uosc/load-subtitles") +) +bind_command( + "audio", + create_select_tracklist_type_menu_opener("Audio", "audio", "aid", "script-binding uosc/load-audio") +) +bind_command( + "video", + create_select_tracklist_type_menu_opener("Video", "video", "vid", "script-binding uosc/load-video") +) +bind_command( + "playlist", + create_self_updating_menu_opener({ + title = "Playlist", + type = "playlist", + list_prop = "playlist", + serializer = function(playlist) + local items = {} + for index, item in ipairs(playlist) do + local is_url = item.filename:find("://") + local item_title = type(item.title) == "string" and #item.title > 0 and item.title or false + items[index] = { + title = item_title or (is_url and item.filename or serialize_path(item.filename).basename), + hint = tostring(index), + active = item.current, + value = index, + } + end + return items + end, + on_select = function(index) + mp.commandv("set", "playlist-pos-1", tostring(index)) + end, + on_move_item = function(from, to) + mp.commandv("playlist-move", tostring(math.max(from, to) - 1), tostring(math.min(from, to) - 1)) + end, + on_delete_item = function(index) + mp.commandv("playlist-remove", tostring(index - 1)) + end, + }) +) +bind_command( + "chapters", + create_self_updating_menu_opener({ + title = "Chapters", + type = "chapters", + list_prop = "chapter-list", + active_prop = "chapter", + serializer = function(chapters, current_chapter) + local items = {} + chapters = normalize_chapters(chapters) + for index, chapter in ipairs(chapters) do + items[index] = { + title = chapter.title or "", + hint = format_time(chapter.time, state.duration), + value = index, + active = index - 1 == current_chapter, + } + end + return items + end, + on_select = function(index) + mp.commandv("set", "chapter", tostring(index - 1)) + end, + }) +) +bind_command( + "editions", + create_self_updating_menu_opener({ + title = "Editions", + type = "editions", + list_prop = "edition-list", + active_prop = "current-edition", + serializer = function(editions, current_id) + local items = {} + for _, edition in ipairs(editions or {}) do + items[#items + 1] = { + title = edition.title or "Edition", + hint = tostring(edition.id + 1), + value = edition.id, + active = edition.id == current_id, + } + end + return items + end, + on_select = function(id) + mp.commandv("set", "edition", id) + end, + }) +) +bind_command("show-in-directory", function() -- Ignore URLs - if not state.path or is_protocol(state.path) then return end + if not state.path or is_protocol(state.path) then + return + end - if state.platform == 'windows' then - utils.subprocess_detached({args = {'explorer', '/select,', state.path}, cancellable = false}) - elseif state.platform == 'macos' then - utils.subprocess_detached({args = {'open', '-R', state.path}, cancellable = false}) - elseif state.platform == 'linux' then - local result = utils.subprocess({args = {'nautilus', state.path}, cancellable = false}) + if state.platform == "windows" then + utils.subprocess_detached({ args = { "explorer", "/select,", state.path }, cancellable = false }) + elseif state.platform == "macos" then + utils.subprocess_detached({ args = { "open", "-R", state.path }, cancellable = false }) + elseif state.platform == "linux" then + local result = utils.subprocess({ args = { "nautilus", state.path }, cancellable = false }) -- Fallback opens the folder with xdg-open instead if result.status ~= 0 then - utils.subprocess({args = {'xdg-open', serialize_path(state.path).dirname}, cancellable = false}) + utils.subprocess({ args = { "xdg-open", serialize_path(state.path).dirname }, cancellable = false }) end end end) -bind_command('stream-quality', function() - if Menu:is_open('stream-quality') then Menu:close() return end +bind_command("stream-quality", function() + if Menu:is_open("stream-quality") then + Menu:close() + return + end - local ytdl_format = mp.get_property_native('ytdl-format') + local ytdl_format = mp.get_property_native("ytdl-format") local items = {} for _, height in ipairs(config.stream_quality_options) do - local format = 'bestvideo[height<=?' .. height .. ']+bestaudio/best[height<=?' .. height .. ']' - items[#items + 1] = {title = height .. 'p', value = format, active = format == ytdl_format} + local format = "bestvideo[height<=?" .. height .. "]+bestaudio/best[height<=?" .. height .. "]" + items[#items + 1] = { title = height .. "p", value = format, active = format == ytdl_format } end - Menu:open({type = 'stream-quality', title = 'Stream quality', items = items}, function(format) - mp.set_property('ytdl-format', format) + Menu:open({ type = "stream-quality", title = "Stream quality", items = items }, function(format) + mp.set_property("ytdl-format", format) -- Reload the video to apply new format -- This is taken from https://github.com/jgreco/mpv-youtube-quality -- which is in turn taken from https://github.com/4e6/mpv-reload/ -- Dunno if playlist_pos shenanigans below are necessary. - local playlist_pos = mp.get_property_number('playlist-pos') - local duration = mp.get_property_native('duration') - local time_pos = mp.get_property('time-pos') + local playlist_pos = mp.get_property_number("playlist-pos") + local duration = mp.get_property_native("duration") + local time_pos = mp.get_property("time-pos") - mp.set_property_number('playlist-pos', playlist_pos) + mp.set_property_number("playlist-pos", playlist_pos) -- Tries to determine live stream vs. pre-recorded VOD. VOD has non-zero -- duration property. When reloading VOD, to keep the current time position @@ -1043,15 +1251,18 @@ bind_command('stream-quality', function() -- That's the reason we don't pass the offset when reloading streams. if duration and duration > 0 then local function seeker() - mp.commandv('seek', time_pos, 'absolute') + mp.commandv("seek", time_pos, "absolute") mp.unregister_event(seeker) end - mp.register_event('file-loaded', seeker) + mp.register_event("file-loaded", seeker) end end) end) -bind_command('open-file', function() - if Menu:is_open('open-file') then Menu:close() return end +bind_command("open-file", function() + if Menu:is_open("open-file") then + Menu:close() + return + end local directory local active_file @@ -1071,191 +1282,245 @@ bind_command('open-file', function() end if not directory then - msg.error('Couldn\'t serialize path "' .. state.path .. '".') + msg.error("Couldn't serialize path \"" .. state.path .. '".') return end -- Update active file in directory navigation menu local function handle_file_loaded() - if Menu:is_open('open-file') then - Elements.menu:activate_one_value(normalize_path(mp.get_property_native('path'))) + if Menu:is_open("open-file") then + Elements.menu:activate_one_value(normalize_path(mp.get_property_native("path"))) end end - open_file_navigation_menu( - directory, - function(path) mp.commandv('loadfile', path) end, - { - type = 'open-file', - allowed_types = config.types.media, - active_path = active_file, - on_open = function() mp.register_event('file-loaded', handle_file_loaded) end, - on_close = function() mp.unregister_event(handle_file_loaded) end, - } - ) + open_file_navigation_menu(directory, function(path) + mp.commandv("loadfile", path) + end, { + type = "open-file", + allowed_types = config.types.media, + active_path = active_file, + on_open = function() + mp.register_event("file-loaded", handle_file_loaded) + end, + on_close = function() + mp.unregister_event(handle_file_loaded) + end, + }) end) -bind_command('shuffle', function() set_state('shuffle', not state.shuffle) end) -bind_command('items', function() +bind_command("shuffle", function() + set_state("shuffle", not state.shuffle) +end) +bind_command("items", function() if state.has_playlist then - mp.command('script-binding uosc/playlist') + mp.command("script-binding uosc/playlist") else - mp.command('script-binding uosc/open-file') + mp.command("script-binding uosc/open-file") end end) -bind_command('next', function() navigate_item(1) end) -bind_command('prev', function() navigate_item(-1) end) -bind_command('next-file', function() navigate_directory(1) end) -bind_command('prev-file', function() navigate_directory(-1) end) -bind_command('first', function() +bind_command("next", function() + navigate_item(1) +end) +bind_command("prev", function() + navigate_item(-1) +end) +bind_command("next-file", function() + navigate_directory(1) +end) +bind_command("prev-file", function() + navigate_directory(-1) +end) +bind_command("first", function() if state.has_playlist then - mp.commandv('set', 'playlist-pos-1', '1') + mp.commandv("set", "playlist-pos-1", "1") else load_file_index_in_current_directory(1) end end) -bind_command('last', function() +bind_command("last", function() if state.has_playlist then - mp.commandv('set', 'playlist-pos-1', tostring(state.playlist_count)) + mp.commandv("set", "playlist-pos-1", tostring(state.playlist_count)) else load_file_index_in_current_directory(-1) end end) -bind_command('first-file', function() load_file_index_in_current_directory(1) end) -bind_command('last-file', function() load_file_index_in_current_directory(-1) end) -bind_command('delete-file-next', function() +bind_command("first-file", function() + load_file_index_in_current_directory(1) +end) +bind_command("last-file", function() + load_file_index_in_current_directory(-1) +end) +bind_command("delete-file-next", function() local next_file = nil local is_local_file = state.path and not is_protocol(state.path) if is_local_file then - if Menu:is_open('open-file') then Elements.menu:delete_value(state.path) end + if Menu:is_open("open-file") then + Elements.menu:delete_value(state.path) + end end if state.has_playlist then - mp.commandv('playlist-remove', 'current') + mp.commandv("playlist-remove", "current") else if is_local_file then local paths, current_index = get_adjacent_files(state.path, config.types.autoload) if paths and current_index then local index, path = decide_navigation_in_list(paths, current_index, 1) - if path then next_file = path end + if path then + next_file = path + end end end - if next_file then mp.commandv('loadfile', next_file) - else mp.commandv('stop') end + if next_file then + mp.commandv("loadfile", next_file) + else + mp.commandv("stop") + end end - if is_local_file then delete_file(state.path) end + if is_local_file then + delete_file(state.path) + end end) -bind_command('delete-file-quit', function() - mp.command('stop') - if state.path and not is_protocol(state.path) then delete_file(state.path) end - mp.command('quit') +bind_command("delete-file-quit", function() + mp.command("stop") + if state.path and not is_protocol(state.path) then + delete_file(state.path) + end + mp.command("quit") end) -bind_command('audio-device', create_self_updating_menu_opener({ - title = 'Audio devices', - type = 'audio-device-list', - list_prop = 'audio-device-list', - active_prop = 'audio-device', - serializer = function(audio_device_list, current_device) - current_device = current_device or 'auto' - local ao = mp.get_property('current-ao') or '' - local items = {} - for _, device in ipairs(audio_device_list) do - if device.name == 'auto' or string.match(device.name, '^' .. ao) then - local hint = string.match(device.name, ao .. '/(.+)') - if not hint then hint = device.name end - items[#items + 1] = { - title = device.description, - hint = hint, - active = device.name == current_device, - value = device.name, - } +bind_command( + "audio-device", + create_self_updating_menu_opener({ + title = "Audio devices", + type = "audio-device-list", + list_prop = "audio-device-list", + active_prop = "audio-device", + serializer = function(audio_device_list, current_device) + current_device = current_device or "auto" + local ao = mp.get_property("current-ao") or "" + local items = {} + for _, device in ipairs(audio_device_list) do + if device.name == "auto" or string.match(device.name, "^" .. ao) then + local hint = string.match(device.name, ao .. "/(.+)") + if not hint then + hint = device.name + end + items[#items + 1] = { + title = device.description, + hint = hint, + active = device.name == current_device, + value = device.name, + } + end end - end - return items - end, - on_select = function(name) mp.commandv('set', 'audio-device', name) end, -})) -bind_command('open-config-directory', function() - local config_path = mp.command_native({'expand-path', '~~/mpv.conf'}) + return items + end, + on_select = function(name) + mp.commandv("set", "audio-device", name) + end, + }) +) +bind_command("open-config-directory", function() + local config_path = mp.command_native({ "expand-path", "~~/mpv.conf" }) local config = serialize_path(normalize_path(config_path)) if config then local args - if state.platform == 'windows' then - args = {'explorer', '/select,', config.path} - elseif state.platform == 'macos' then - args = {'open', '-R', config.path} - elseif state.platform == 'linux' then - args = {'xdg-open', config.dirname} + if state.platform == "windows" then + args = { "explorer", "/select,", config.path } + elseif state.platform == "macos" then + args = { "open", "-R", config.path } + elseif state.platform == "linux" then + args = { "xdg-open", config.dirname } end - utils.subprocess_detached({args = args, cancellable = false}) + utils.subprocess_detached({ args = args, cancellable = false }) else - msg.error('Couldn\'t serialize config path "' .. config_path .. '".') + msg.error("Couldn't serialize config path \"" .. config_path .. '".') end end) --[[ MESSAGE HANDLERS ]] -mp.register_script_message('show-submenu', function(id) toggle_menu_with_items({submenu = id}) end) -mp.register_script_message('show-submenu-blurred', function(id) - toggle_menu_with_items({submenu = id, mouse_nav = true}) +mp.register_script_message("show-submenu", function(id) + toggle_menu_with_items({ submenu = id }) end) -mp.register_script_message('get-version', function(script) - mp.commandv('script-message-to', script, 'uosc-version', config.version) +mp.register_script_message("show-submenu-blurred", function(id) + toggle_menu_with_items({ submenu = id, mouse_nav = true }) end) -mp.register_script_message('open-menu', function(json, submenu_id) +mp.register_script_message("get-version", function(script) + mp.commandv("script-message-to", script, "uosc-version", config.version) +end) +mp.register_script_message("open-menu", function(json, submenu_id) local data = utils.parse_json(json) - if type(data) ~= 'table' or type(data.items) ~= 'table' then - msg.error('open-menu: received json didn\'t produce a table with menu configuration') + if type(data) ~= "table" or type(data.items) ~= "table" then + msg.error("open-menu: received json didn't produce a table with menu configuration") else - if data.type and Menu:is_open(data.type) then Menu:close() - else open_command_menu(data, {submenu = submenu_id, on_close = data.on_close}) end + if data.type and Menu:is_open(data.type) then + Menu:close() + else + open_command_menu(data, { submenu = submenu_id, on_close = data.on_close }) + end end end) -mp.register_script_message('update-menu', function(json) +mp.register_script_message("update-menu", function(json) local data = utils.parse_json(json) - if type(data) ~= 'table' or type(data.items) ~= 'table' then - msg.error('update-menu: received json didn\'t produce a table with menu configuration') + if type(data) ~= "table" or type(data.items) ~= "table" then + msg.error("update-menu: received json didn't produce a table with menu configuration") else local menu = data.type and Menu:is_open(data.type) - if menu then menu:update(data) - else open_command_menu(data) end + if menu then + menu:update(data) + else + open_command_menu(data) + end end end) -mp.register_script_message('thumbfast-info', function(json) +mp.register_script_message("thumbfast-info", function(json) local data = utils.parse_json(json) - if type(data) ~= 'table' or not data.width or not data.height then + if type(data) ~= "table" or not data.width or not data.height then thumbnail.disabled = true - msg.error('thumbfast-info: received json didn\'t produce a table with thumbnail information') + msg.error("thumbfast-info: received json didn't produce a table with thumbnail information") else thumbnail = data request_render() end end) -mp.register_script_message('set', function(name, value) +mp.register_script_message("set", function(name, value) external[name] = value - Elements:trigger('external_prop_' .. name, value) + Elements:trigger("external_prop_" .. name, value) end) -mp.register_script_message('toggle-elements', function(elements) Elements:toggle(split(elements, ' *, *')) end) -mp.register_script_message('set-min-visibility', function(visibility, elements) +mp.register_script_message("toggle-elements", function(elements) + Elements:toggle(split(elements, " *, *")) +end) +mp.register_script_message("set-min-visibility", function(visibility, elements) local fraction = tonumber(visibility) - local ids = split(elements and elements ~= '' and elements or 'timeline,controls,volume,top_bar', ' *, *') - if fraction then Elements:set_min_visibility(clamp(0, fraction, 1), ids) end + local ids = split(elements and elements ~= "" and elements or "timeline,controls,volume,top_bar", " *, *") + if fraction then + Elements:set_min_visibility(clamp(0, fraction, 1), ids) + end +end) +mp.register_script_message("flash-elements", function(elements) + Elements:flash(split(elements, " *, *")) +end) +mp.register_script_message("overwrite-binding", function(name, command) + key_binding_overwrites[name] = command end) -mp.register_script_message('flash-elements', function(elements) Elements:flash(split(elements, ' *, *')) end) -mp.register_script_message('overwrite-binding', function(name, command) key_binding_overwrites[name] = command end) --[[ ELEMENTS ]] -require('uosc_shared/elements/WindowBorder'):new() -require('uosc_shared/elements/BufferingIndicator'):new() -require('uosc_shared/elements/PauseIndicator'):new() -require('uosc_shared/elements/TopBar'):new() -require('uosc_shared/elements/Timeline'):new() -if options.controls and options.controls ~= 'never' then require('uosc_shared/elements/Controls'):new() end -if itable_index_of({'left', 'right'}, options.volume) then require('uosc_shared/elements/Volume'):new() end -require('uosc_shared/elements/Curtain'):new() +require("uosc_shared/elements/WindowBorder"):new() +require("uosc_shared/elements/BufferingIndicator"):new() +require("uosc_shared/elements/PauseIndicator"):new() +require("uosc_shared/elements/TopBar"):new() +require("uosc_shared/elements/Timeline"):new() +if options.controls and options.controls ~= "never" then + require("uosc_shared/elements/Controls"):new() +end +if itable_index_of({ "left", "right" }, options.volume) then + require("uosc_shared/elements/Volume"):new() +end +require("uosc_shared/elements/Curtain"):new() diff --git a/nvim/.config/nvim/after/ftplugin/gitcommit.lua b/nvim/.config/nvim/after/ftplugin/gitcommit.lua index b02d561..383eb85 100644 --- a/nvim/.config/nvim/after/ftplugin/gitcommit.lua +++ b/nvim/.config/nvim/after/ftplugin/gitcommit.lua @@ -1,5 +1,5 @@ -local ns = vim.api.nvim_create_namespace('gitcommit') -vim.api.nvim_set_hl(ns, 'ColorColumn', { bg = '#a33a3a', blend = 90 }) +local ns = vim.api.nvim_create_namespace("gitcommit") +vim.api.nvim_set_hl(ns, "ColorColumn", { bg = "#a33a3a", blend = 90 }) vim.api.nvim_win_set_hl_ns(0, ns) vim.bo.textwidth = 72 -vim.wo.colorcolumn = '+0' +vim.wo.colorcolumn = "+0" diff --git a/nvim/.config/nvim/after/ftplugin/quarto.lua b/nvim/.config/nvim/after/ftplugin/quarto.lua index fe290a1..d0cc225 100644 --- a/nvim/.config/nvim/after/ftplugin/quarto.lua +++ b/nvim/.config/nvim/after/ftplugin/quarto.lua @@ -1,90 +1,91 @@ -- Start quarto session local startsession = function(file, args) - file = file or "/tmp/jupyter-magma-session.json" - if args then file = args[0] end - vim.fn.jobstart({ "jupyter", "console", "-f", file }, { - on_stdout = function(_) - vim.cmd("MagmaInit " .. file) - vim.cmd("JupyterAttach " .. file) - end, - on_exit = function(_) - vim.notify(string.format("jupyter kernel stopped: %s", file), vim.log.levels.INFO) - end, - stdin = nil - }) + file = file or "/tmp/jupyter-magma-session.json" + if args then + file = args[0] + end + vim.fn.jobstart({ "jupyter", "console", "-f", file }, { + on_stdout = function(_) + vim.cmd("MagmaInit " .. file) + vim.cmd("JupyterAttach " .. file) + end, + on_exit = function(_) + vim.notify(string.format("jupyter kernel stopped: %s", file), vim.log.levels.INFO) + end, + stdin = nil, + }) end -vim.api.nvim_create_user_command("JupyterStart", function() startsession() end, - {}) +vim.api.nvim_create_user_command("JupyterStart", function() + startsession() +end, {}) local map = vim.keymap.set -- filetype mappings -- PLUGIN: magma-nvim -- Operate jupyter notebooks from within vim -map('n', 'cc', ':MagmaEvaluateLine', { silent = true }) -map('n', 'C', '?^```{jV/```k:MagmaEvaluateVisual', - { silent = true, desc = 'Evaluate current code cell' }) -map('x', 'c', ':MagmaEvaluateVisual', { silent = true }) -map('n', 'c', "nvim_exec('MagmaEvaluateOperator', v:true)", - { expr = true, silent = true, desc = '+code-evaluation' }) -map('n', 'cr', ':MagmaReevaluateCell', { silent = true }) -map('n', 'cu', ':MagmaShowOutput', { silent = true }) -map('n', 'cU', ':noautocmd :MagmaEnterOutput', - { silent = true, desc = 'MagmaEnterOutput' }) -map('n', 'cd', ':MagmaDelete', { silent = true }) -map('n', 'cs', ':MagmaInterrupt') -map('n', 'ci', ':MagmaInit ') -map('n', 'cD', ':MagmaDeinit') -map('n', 'cR', ':MagmaRestart') +map("n", "cc", ":MagmaEvaluateLine", { silent = true }) +map( + "n", + "C", + "?^```{jV/```k:MagmaEvaluateVisual", + { silent = true, desc = "Evaluate current code cell" } +) +map("x", "c", ":MagmaEvaluateVisual", { silent = true }) +map( + "n", + "c", + "nvim_exec('MagmaEvaluateOperator', v:true)", + { expr = true, silent = true, desc = "+code-evaluation" } +) +map("n", "cr", ":MagmaReevaluateCell", { silent = true }) +map("n", "cu", ":MagmaShowOutput", { silent = true }) +map("n", "cU", ":noautocmd :MagmaEnterOutput", { silent = true, desc = "MagmaEnterOutput" }) +map("n", "cd", ":MagmaDelete", { silent = true }) +map("n", "cs", ":MagmaInterrupt") +map("n", "ci", ":MagmaInit ") +map("n", "cD", ":MagmaDeinit") +map("n", "cR", ":MagmaRestart") -- jump to beginning of previous/ next cell code -map('n', ']c', '/^```{}:nohl', { desc = 'Next quarto cell' }) -map('n', '[c', '?^```n}:nohl', { desc = 'Previous quarto cell' }) +map("n", "]c", "/^```{}:nohl", { desc = "Next quarto cell" }) +map("n", "[c", "?^```n}:nohl", { desc = "Previous quarto cell" }) -- insert cell header above/below -map('n', 'co', 'o```{python}```k', - { desc = 'Insert quarto cell below' }) -map('n', 'cO', 'O```{python}```k', - { desc = 'Insert quarto cell above' }) +map("n", "co", "o```{python}```k", { desc = "Insert quarto cell below" }) +map("n", "cO", "O```{python}```k", { desc = "Insert quarto cell above" }) local bufnr = 0 -map('n', '[d', 'lua vim.diagnostic.goto_prev()', - { buffer = bufnr, desc = 'Previous diagnostic' }) -map('n', ']d', 'lua vim.diagnostic.goto_next()', - { buffer = bufnr, desc = 'Next diagnostic' }) -map('n', '[e', - 'lua vim.diagnostic.goto_prev({severity = vim.diagnostic.severity.ERROR})', - { buffer = bufnr, desc = 'Previous error' }) -map('n', ']e', - 'lua vim.diagnostic.goto_next({severity = vim.diagnostic.severity.ERROR})', - { buffer = bufnr, desc = 'Next error' }) +map("n", "[d", "lua vim.diagnostic.goto_prev()", { buffer = bufnr, desc = "Previous diagnostic" }) +map("n", "]d", "lua vim.diagnostic.goto_next()", { buffer = bufnr, desc = "Next diagnostic" }) +map( + "n", + "[e", + "lua vim.diagnostic.goto_prev({severity = vim.diagnostic.severity.ERROR})", + { buffer = bufnr, desc = "Previous error" } +) +map( + "n", + "]e", + "lua vim.diagnostic.goto_next({severity = vim.diagnostic.severity.ERROR})", + { buffer = bufnr, desc = "Next error" } +) -- TODO find better way to enable lsp key mappings for quarto buffers -local prefix = require('which-key').register -prefix({ ['l'] = { name = "+lsp" } }) -map('n', 'li', 'LspInfo', - { buffer = bufnr, desc = 'Lsp Info' }) -map('n', 'ld', 'lua vim.diagnostic.open_float()', - { buffer = bufnr, desc = 'Line diagnostics' }) -map('n', 'la', 'lua vim.lsp.buf.code_action()', - { buffer = bufnr, desc = 'Codeactions' }) -map('n', 'ln', 'lua vim.lsp.buf.rename()', - { buffer = bufnr, desc = 'Rename element' }) -map('n', 'lr', 'lua vim.lsp.buf.references()', - { buffer = bufnr, desc = 'References' }) +local prefix = require("which-key").register +prefix({ ["l"] = { name = "+lsp" } }) +map("n", "li", "LspInfo", { buffer = bufnr, desc = "Lsp Info" }) +map("n", "ld", "lua vim.diagnostic.open_float()", { buffer = bufnr, desc = "Line diagnostics" }) +map("n", "la", "lua vim.lsp.buf.code_action()", { buffer = bufnr, desc = "Codeactions" }) +map("n", "ln", "lua vim.lsp.buf.rename()", { buffer = bufnr, desc = "Rename element" }) +map("n", "lr", "lua vim.lsp.buf.references()", { buffer = bufnr, desc = "References" }) -map('n', 'K', 'lua vim.lsp.buf.hover()', - { buffer = bufnr, desc = 'Hover definition' }) -map('n', 'gd', 'lua vim.lsp.buf.definition()', - { buffer = bufnr, desc = 'Definition' }) -map('n', 'gD', 'lua vim.lsp.buf.declaration()', - { buffer = bufnr, desc = 'Declaration' }) -map('n', 'gs', 'lua vim.lsp.buf.signature_help()', - { buffer = bufnr, desc = 'Signature help' }) -map('n', 'gI', 'lua vim.lsp.buf.implementation()', - { buffer = bufnr, desc = 'Implementation' }) -map('n', 'gt', 'lua vim.lsp.buf.type_definition()', - { buffer = bufnr, desc = 'Type definition' }) +map("n", "K", "lua vim.lsp.buf.hover()", { buffer = bufnr, desc = "Hover definition" }) +map("n", "gd", "lua vim.lsp.buf.definition()", { buffer = bufnr, desc = "Definition" }) +map("n", "gD", "lua vim.lsp.buf.declaration()", { buffer = bufnr, desc = "Declaration" }) +map("n", "gs", "lua vim.lsp.buf.signature_help()", { buffer = bufnr, desc = "Signature help" }) +map("n", "gI", "lua vim.lsp.buf.implementation()", { buffer = bufnr, desc = "Implementation" }) +map("n", "gt", "lua vim.lsp.buf.type_definition()", { buffer = bufnr, desc = "Type definition" }) -if vim.b['sessionfile'] == nil then - vim.b['sessionfile'] = vim.fn.tempname() .. '.json' - startsession(vim.b['sessionfile']) +if vim.b["sessionfile"] == nil then + vim.b["sessionfile"] = vim.fn.tempname() .. ".json" + startsession(vim.b["sessionfile"]) end diff --git a/nvim/.config/nvim/init.lua b/nvim/.config/nvim/init.lua index bfc8a17..6684b1e 100644 --- a/nvim/.config/nvim/init.lua +++ b/nvim/.config/nvim/init.lua @@ -2,15 +2,18 @@ -- https://github.com/elianiva/dotfiles/ - with much gratitude local api = vim.api -api.nvim_exec2('runtime abbrev.vim', {}) +api.nvim_exec2("runtime abbrev.vim", {}) local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" if not vim.loop.fs_stat(lazypath) then - vim.fn.system({ - "git", "clone", "--filter=blob:none", - "https://github.com/folke/lazy.nvim.git", "--branch=stable", -- latest stable release - lazypath - }) + vim.fn.system({ + "git", + "clone", + "--filter=blob:none", + "https://github.com/folke/lazy.nvim.git", + "--branch=stable", -- latest stable release + lazypath, + }) end vim.opt.rtp:prepend(lazypath) @@ -18,14 +21,14 @@ vim.opt.rtp:prepend(lazypath) -- needs to be set before lazy.nvim is loaded vim.g.mapleader = " " -require('settings') -require('autocmds') +require("settings") +require("autocmds") require("lazy").setup("plugins", { - defaults = { version = "*" }, - performance = { rtp = { disabled_plugins = { "netrw", "netrwPlugin" } } } + defaults = { version = "*" }, + performance = { rtp = { disabled_plugins = { "netrw", "netrwPlugin" } } }, }) -require('look') -require('maps') +require("look") +require("maps") -- to include e.g. the spell dictionaries for vim vim.opt.rtp:append(vim.fn.stdpath("data") .. "/site") diff --git a/nvim/.config/nvim/lua/autocmds.lua b/nvim/.config/nvim/lua/autocmds.lua index ce53e7f..d4f08ab 100644 --- a/nvim/.config/nvim/lua/autocmds.lua +++ b/nvim/.config/nvim/lua/autocmds.lua @@ -1,33 +1,37 @@ -- Highlight whatever is being yanked vim.api.nvim_create_autocmd({ "TextYankPost" }, { - command = 'silent! lua require"vim.highlight".on_yank{timeout=500}', - desc = "Highlight yanked text whenevery yanking something", - group = vim.api.nvim_create_augroup('highlightyanks', { clear = true }) + command = 'silent! lua require"vim.highlight".on_yank{timeout=500}', + desc = "Highlight yanked text whenevery yanking something", + group = vim.api.nvim_create_augroup("highlightyanks", { clear = true }), }) -- Special setting for editing gopass files - make sure nothing leaks outside the directories it is supposed to vim.api.nvim_create_autocmd({ "BufNewFile", "BufRead" }, { - pattern = { - "/dev/shm/gopass.*", "/dev/shm/pass.?*/?*.txt", - "$TMPDIR/pass.?*/?*.txt", "/tmp/pass.?*/?*.txt" - }, - command = 'setlocal noswapfile nobackup noundofile nowritebackup viminfo=', - desc = "Don't leak any information when editing potential password files", - group = vim.api.nvim_create_augroup('passnoleak', { clear = true }) + pattern = { + "/dev/shm/gopass.*", + "/dev/shm/pass.?*/?*.txt", + "$TMPDIR/pass.?*/?*.txt", + "/tmp/pass.?*/?*.txt", + }, + command = "setlocal noswapfile nobackup noundofile nowritebackup viminfo=", + desc = "Don't leak any information when editing potential password files", + group = vim.api.nvim_create_augroup("passnoleak", { clear = true }), }) -- fixing neovim opening up at same moment as alacritty (see https://github.com/neovim/neovim/issues/11330) vim.api.nvim_create_autocmd({ "VimEnter" }, { - callback = function() - local pid, WINCH = vim.fn.getpid(), vim.loop.constants.SIGWINCH - vim.defer_fn(function() vim.loop.kill(pid, WINCH) end, 20) - end, - desc = "Fix neovim sizing issues if opening same time as alacritty", - group = vim.api.nvim_create_augroup('alacritty_fixsize', { clear = true }) + callback = function() + local pid, WINCH = vim.fn.getpid(), vim.loop.constants.SIGWINCH + vim.defer_fn(function() + vim.loop.kill(pid, WINCH) + end, 20) + end, + desc = "Fix neovim sizing issues if opening same time as alacritty", + group = vim.api.nvim_create_augroup("alacritty_fixsize", { clear = true }), }) -- remove line numbers from terminal buffers vim.api.nvim_create_autocmd({ "TermOpen" }, { - pattern = '*', - command = 'setlocal nonumber norelativenumber' + pattern = "*", + command = "setlocal nonumber norelativenumber", }) diff --git a/nvim/.config/nvim/lua/look.lua b/nvim/.config/nvim/lua/look.lua index 05fa4a8..8622ada 100644 --- a/nvim/.config/nvim/lua/look.lua +++ b/nvim/.config/nvim/lua/look.lua @@ -1,15 +1,17 @@ -local colorsfile = vim.fn.stdpath('state') .. '/colorscheme.lua' +local colorsfile = vim.fn.stdpath("state") .. "/colorscheme.lua" local function source_colors() - if vim.fn.filereadable(colorsfile) == 1 then - vim.cmd("source " .. colorsfile) - end + if vim.fn.filereadable(colorsfile) == 1 then + vim.cmd("source " .. colorsfile) + end end -- set on startup source_colors() -- continuously watch colors file for changes -local fwatch = require('fwatch') +local fwatch = require("fwatch") fwatch.watch(colorsfile, { - on_event = vim.schedule_wrap(function() source_colors() end) + on_event = vim.schedule_wrap(function() + source_colors() + end), }) diff --git a/nvim/.config/nvim/lua/maps.lua b/nvim/.config/nvim/lua/maps.lua index c57495e..307e49a 100644 --- a/nvim/.config/nvim/lua/maps.lua +++ b/nvim/.config/nvim/lua/maps.lua @@ -1,5 +1,5 @@ local map = vim.keymap.set -local prefix = require('which-key').register +local prefix = require("which-key").register -- The general ideas behind these mappings: -- @@ -10,73 +10,77 @@ local prefix = require('which-key').register -- In other words mostly filetype specific mappings -- backspace to switch to alternate (last) buffer -map('n', '', '') +map("n", "", "") -- since u undoes, would it not make sense that U redoes? -map('n', 'U', '') +map("n", "U", "") -- d-motion puts the last 'deleted' thing into the default register to paste; -- use D-motion to truly delete something into nothingness and keep whatever -- you want in your register, ready to paste -map('n', 'D', '"_d') +map("n", "D", '"_d') -- I don't particularly need ex mode (at least, yet) but faster macro access is nice -map('n', 'Q', '@') +map("n", "Q", "@") -- stronger versions of left,right - move all the way to beginning/end of line -map('n', 'H', '^') -map('n', 'L', '$') +map("n", "H", "^") +map("n", "L", "$") -- when in softwrapped files, allow moving through the visible lines with j/k -- but when prepending a number jump *exactly* as many lines, wrapped or not -- This makes relative linenumbers much more useful in prose docs since they -- are always exactly correct local function wrap_up() - if vim.v.count == 0 then return 'gk' end - return 'k' + if vim.v.count == 0 then + return "gk" + end + return "k" end local function wrap_down() - if vim.v.count == 0 then return 'gj' end - return 'j' + if vim.v.count == 0 then + return "gj" + end + return "j" end -map('n', 'k', wrap_up, { expr = true }) -map('n', 'j', wrap_down, { expr = true }) +map("n", "k", wrap_up, { expr = true }) +map("n", "j", wrap_down, { expr = true }) -- move around between matching brackets with tab -map('n', '', '%') +map("n", "", "%") -- when in insertion mode, C-u uppercases the current word, C-l lowercases it, -map('i', '', 'gUiw`]a') -map('i', '', 'guiw`]a') +map("i", "", "gUiw`]a") +map("i", "", "guiw`]a") -- Add undo break-points at punctuations for plaintext editing for _, char in pairs({ ",", ".", ";", "?", "!" }) do - map("i", char, string.format("%su", char)) + map("i", char, string.format("%su", char)) end -- yank current filename/filepath to f buffer -map('n', 'yp', ':let @p = expand("%")', { desc = 'yank filename' }) -map('n', 'yP', ':let @p = expand("%:p")', { desc = 'yank filepath' }) +map("n", "yp", ':let @p = expand("%")', { desc = "yank filename" }) +map("n", "yP", ':let @p = expand("%:p")', { desc = "yank filepath" }) -- repeat the last substitute command with all its flags preserved -map('n', '&', ':&&') +map("n", "&", ":&&") -- bracket pairings to go to the next/previous of: -- (works with count prefixes) -- Argument list -map('n', '[a', ':previous') -map('n', ']a', ':next') +map("n", "[a", ":previous") +map("n", "]a", ":next") -- Buffers -map('n', '[b', ':bprevious') -map('n', ']b', ':bnext') +map("n", "[b", ":bprevious") +map("n", "]b", ":bnext") -- Quickfix list -map('n', '[q', ':cprevious') -map('n', ']q', ':cnext') +map("n", "[q", ":cprevious") +map("n", "]q", ":cnext") -- Location list -map('n', '[l', ':lprevious') -map('n', ']l', ':lnext') +map("n", "[l", ":lprevious") +map("n", "]l", ":lnext") -- maps the leader for buffer local mappings -- since we are (atm) using sneak to go fwd/bwd in fFtT searches, comma does @@ -86,198 +90,193 @@ vim.g.maplocalleader = "," -- If we mapped localleader to comma, we can still get to its original function -- by douple-tapping it. -- FIXME does this work still (and is it necessary)? -if vim.g.maplocalleader == ',' then - map('', ',,', ',') - vim.keymap.del('', ',,', { silent = true }) +if vim.g.maplocalleader == "," then + map("", ",,", ",") + vim.keymap.del("", ",,", { silent = true }) end -- remove search highlights by pressing space+/ -map('n', '/', ':noh', { desc = 'remove highlights' }) +map("n", "/", ":noh", { desc = "remove highlights" }) -- split buffers vertically/horizontally with the leader \ or - (mirrors my -- tmux setup) -map('n', '-', ':sp', { desc = 'open horiz split' }) -map('n', '\\', ':vsp', { desc = 'open vert split' }) +map("n", "-", ":sp", { desc = "open horiz split" }) +map("n", "\\", ":vsp", { desc = "open vert split" }) -- 'open new buffer' with leader-t (opens new buffer containing current dir and switches to it) -map('n', 't', ':vsp | Vifm', { desc = 'open buffer' }) +map("n", "t", ":vsp | Vifm", { desc = "open buffer" }) -- open actual new tab with leader-T -map('n', 'T', ':tabedit | Vifm', { desc = 'open tab' }) +map("n", "T", ":tabedit | Vifm", { desc = "open tab" }) -- select the whole buffer with -a -map('n', 'a', 'ggVG', { desc = 'select all' }) +map("n", "a", "ggVG", { desc = "select all" }) -- PLUGIN: Navigator.nvim -map('n', 'h', 'lua require("Navigator").left()', { silent = true }) -map('n', 'k', 'lua require("Navigator").up()', { silent = true }) -map('n', 'l', 'lua require("Navigator").right()', { silent = true }) -map('n', 'j', 'lua require("Navigator").down()', { silent = true }) -map('n', 'p', 'lua require("Navigator").previous()', - { silent = true }) +map("n", "h", 'lua require("Navigator").left()', { silent = true }) +map("n", "k", 'lua require("Navigator").up()', { silent = true }) +map("n", "l", 'lua require("Navigator").right()', { silent = true }) +map("n", "j", 'lua require("Navigator").down()', { silent = true }) +map("n", "p", 'lua require("Navigator").previous()', { silent = true }) -- PLUGIN: Vifm.vim -- open/close file tree with leader-e -map('n', 'e', ':Vifm', { desc = 'browse files' }) +map("n", "e", ":Vifm", { desc = "browse files" }) -- open current file tree with current file directory -map('n', 'E', ':Vifm getcwd()', { desc = 'browse project' }) +map("n", "E", ":Vifm getcwd()", { desc = "browse project" }) -- set 'v'im-related options -prefix({ ['v'] = { name = "+vim" } }) -map('n', 'vc', ':Vifm ' .. vim.fn.stdpath('config') .. '/lua', - { desc = 'open config' }) -map('n', 'vh', ":lua require 'telescope.builtin'.help_tags()", - { desc = 'help tags' }) -map('n', 'vH', ":lua require 'telescope.builtin'.man_pages()", - { desc = 'man pages' }) -map('n', 'vC', - ":lua require 'telescope.builtin'.colorscheme(require 'telescope.themes'.get_ivy())", - { desc = 'colorschemes' }) -map('n', 'vl', ":Lazy", { desc = 'Lazy' }) -map('n', 'vm', ":Mason", { desc = 'Mason' }) +prefix({ ["v"] = { name = "+vim" } }) +map("n", "vc", ":Vifm " .. vim.fn.stdpath("config") .. "/lua", { desc = "open config" }) +map("n", "vh", ":lua require 'telescope.builtin'.help_tags()", { desc = "help tags" }) +map("n", "vH", ":lua require 'telescope.builtin'.man_pages()", { desc = "man pages" }) +map( + "n", + "vC", + ":lua require 'telescope.builtin'.colorscheme(require 'telescope.themes'.get_ivy())", + { desc = "colorschemes" } +) +map("n", "vl", ":Lazy", { desc = "Lazy" }) +map("n", "vm", ":Mason", { desc = "Mason" }) -- Set vim to distraction free prose mode with F11 -map('n', 'vz', ':ZenMode', { silent = true }) +map("n", "vz", ":ZenMode", { silent = true }) -- PLUGIN: Telescope GLOBAL FUZZY FINDING -- buffers and files in current workdir -prefix({ ['f'] = { name = '+find' } }) -map('n', 'fb', - ":lua require 'telescope.builtin'.buffers(require 'telescope.themes'.get_ivy())", - { desc = 'list buffers' }) +prefix({ ["f"] = { name = "+find" } }) +map( + "n", + "fb", + ":lua require 'telescope.builtin'.buffers(require 'telescope.themes'.get_ivy())", + { desc = "list buffers" } +) -- most recently used / MRU, bound to S since it is essentially a larger -- go-back intention than just buffers -map('n', 'fo', - ":lua require 'telescope.builtin'.oldfiles(require 'telescope.themes'.get_ivy())", - { desc = 'list old files' }) +map( + "n", + "fo", + ":lua require 'telescope.builtin'.oldfiles(require 'telescope.themes'.get_ivy())", + { desc = "list old files" } +) -- fuzzy find files in cwd -map('n', 'ff', ":lua require 'telescope.builtin'.find_files()", - { desc = 'find files' }) +map("n", "ff", ":lua require 'telescope.builtin'.find_files()", { desc = "find files" }) -- fuzzy find hidden files in cwd -map('n', 'fh', - ":lua require 'telescope.builtin'.find_files({hidden=true})", - { desc = 'find hidden files' }) +map("n", "fh", ":lua require 'telescope.builtin'.find_files({hidden=true})", { desc = "find hidden files" }) -- general full-text search in cwd with rg -map('n', 'fw', ":lua require 'telescope.builtin'.live_grep()", - { desc = 'grep search' }) +map("n", "fw", ":lua require 'telescope.builtin'.live_grep()", { desc = "grep search" }) -- git status -map('n', 'fg', ":lua require 'telescope.builtin'.git_status()", - { desc = 'git status' }) +map("n", "fg", ":lua require 'telescope.builtin'.git_status()", { desc = "git status" }) -- git buffercommits -map('n', 'fc', ":lua require 'telescope.builtin'.git_bcommits()", - { desc = 'git buffer commits' }) +map("n", "fc", ":lua require 'telescope.builtin'.git_bcommits()", { desc = "git buffer commits" }) -- git commitlog -map('n', 'fl', ":lua require 'telescope.builtin'.git_commits()", - { desc = 'git commit log' }) +map("n", "fl", ":lua require 'telescope.builtin'.git_commits()", { desc = "git commit log" }) -- spell suggestions -map('n', 'z=', - ":lua require 'telescope.builtin'.spell_suggest(require 'telescope.themes'.get_ivy())") +map("n", "z=", ":lua require 'telescope.builtin'.spell_suggest(require 'telescope.themes'.get_ivy())") -- Format current Paragraph (esp useful in prose writing) -map('n', 'q', 'gqap', - { silent = true, desc = 'Format current paragraph' }) -map('x', 'q', 'gq', { silent = true, desc = 'Format {motion}' }) -map('n', 'Q', 'vapJgqap', - { silent = true, desc = 'Unformat then format paragraph' }) +map("n", "q", "gqap", { silent = true, desc = "Format current paragraph" }) +map("x", "q", "gq", { silent = true, desc = "Format {motion}" }) +map("n", "Q", "vapJgqap", { silent = true, desc = "Unformat then format paragraph" }) -map('n', 'mp', 'MarkdownPreviewToggle', - { desc = 'Toggle md preview' }) +map("n", "mp", "MarkdownPreviewToggle", { desc = "Toggle md preview" }) -- FORMAT code with -- PLUGIN: formatter.nvim -map('n', 'f', ':FormatLock') -map('n', 'F', ':FormatWriteLock') +map("n", "f", ":FormatLock") +map("n", "F", ":FormatWriteLock") -- SPELL CHECKING -- Move to the prev/next spelling error with [S ]S -- Move to the prev/next spelling error or suggestion with [s ]s -prefix({ ['Z'] = { name = '+Spelling' } }) -map('n', 'ZZ', ':setlocal spell! spelllang=en_us,de_de', - { desc = 'Toggle spellcheck' }) -map('n', 'ZE', ':setlocal spell! spelllang=en_us', - { desc = 'Toggle EN spellcheck' }) -map('n', 'ZG', ':setlocal spell! spelllang=de_de', - { desc = 'Toggle DE spellcheck' }) +prefix({ ["Z"] = { name = "+Spelling" } }) +map("n", "ZZ", ":setlocal spell! spelllang=en_us,de_de", { desc = "Toggle spellcheck" }) +map("n", "ZE", ":setlocal spell! spelllang=en_us", { desc = "Toggle EN spellcheck" }) +map("n", "ZG", ":setlocal spell! spelllang=de_de", { desc = "Toggle DE spellcheck" }) -- undo last spelling mistake from insert and normal mode -map('i', '', 'u[s1z=`]au') -map('n', 'z', 'ms[s1z=`s', { desc = 'Fix last spell error' }) +map("i", "", "u[s1z=`]au") +map("n", "z", "ms[s1z=`s", { desc = "Fix last spell error" }) -- PLUGIN: mini.nvim -prefix({ ['s'] = { name = '+show' } }) -map('n', 'sm', ':lua MiniMap.toggle()', - { silent = true, desc = 'toggle minimap' }) -map('n', 'ss', ":lua MiniStarter.open()", { desc = 'show startpage' }) +prefix({ ["s"] = { name = "+show" } }) +map("n", "sm", ":lua MiniMap.toggle()", { silent = true, desc = "toggle minimap" }) +map("n", "ss", ":lua MiniStarter.open()", { desc = "show startpage" }) -- PLUGIN: symbols-outline.nvim -map('n', 'so', 'SymbolsOutline', - { silent = true, desc = 'toggle symbol outline' }) +map("n", "so", "SymbolsOutline", { silent = true, desc = "toggle symbol outline" }) -- PLUGIN: nvim-tree -map('n', 'se', 'NvimTreeToggle', - { silent = true, desc = 'toggle filetree' }) +map("n", "se", "NvimTreeToggle", { silent = true, desc = "toggle filetree" }) -- PLUGIN: easy-align -- Start interactive EasyAlign in visual mode (e.g. vipga) -map('x', 'ga', '(EasyAlign)') +map("x", "ga", "(EasyAlign)") -- Start interactive EasyAlign for a motion/text object (e.g. gaip) -map('n', 'ga', '(EasyAlign)') +map("n", "ga", "(EasyAlign)") -- trim trailing whitespaces with mini.nvim trailspace -map("n", "w", function() require("mini.trailspace").trim() end, - { noremap = true }) +map("n", "w", function() + require("mini.trailspace").trim() +end, { noremap = true }) -- PLUGIN: dial-increment -map("n", "", '(dial-increment)') -map("n", "", '(dial-decrement)') -map("v", "", '(dial-increment)') -map("v", "", '(dial-increment)') -map("v", "g", 'g(dial-increment)') -map("v", "g", 'g(dial-increment)') +map("n", "", "(dial-increment)") +map("n", "", "(dial-decrement)") +map("v", "", "(dial-increment)") +map("v", "", "(dial-increment)") +map("v", "g", "g(dial-increment)") +map("v", "g", "g(dial-increment)") -- PLUGIN: zettelkasten.nvim -map('n', '', [[:silent lua require 'zettelkasten'.link_follow()]]) -map('v', '', [[:lua require 'zettelkasten'.link_follow(true)]]) -prefix({ ['n'] = { name = '+notes' } }) -map('n', 'ni', [[:lua require 'zettelkasten'.index_open() ]], - { desc = "index page" }) +map("n", "", [[:silent lua require 'zettelkasten'.link_follow()]]) +map("v", "", [[:lua require 'zettelkasten'.link_follow(true)]]) +prefix({ ["n"] = { name = "+notes" } }) +map("n", "ni", [[:lua require 'zettelkasten'.index_open() ]], { desc = "index page" }) -- PLUGIN: zk -map('n', 'nn', "ZkNotes { sort = { 'modified' } }", - { desc = "note list" }) -map("n", "nf", "ZkNotes { sort = { 'modified' }, match = { vim.fn.input('Search: ') } }", - { desc = "note search" }) -map('n', 'nt', "ZkTags", - { desc = "note tags" }) -map('n', 'nc', "ZkCd", - { desc = "notes directory" }) -prefix({ ['n'] = { name = '+note' } }) -map('n', 'nl', "ZkLinks", - { desc = "note links" }) -map('n', 'nb', "ZkLinks", - { desc = "note backlinks" }) -map('n', 'nn', "ZkNew { title = vim.fn.input('Title: ') }", - { desc = "new note" }) -prefix({ ['n'] = { name = '+note', mode = "v" } }) -map('v', 'nn', ":ZkNewFromTitleSelection", - { desc = "title from selection" }) -map('v', 'nN', ":ZkNewFromContentSelection", - { desc = "content from selection" }) -map('v', 'nf', ":ZkMatch", - { desc = "find note from selection" }) +map("n", "nn", "ZkNotes { sort = { 'modified' } }", { desc = "note list" }) +map( + "n", + "nf", + "ZkNotes { sort = { 'modified' }, match = { vim.fn.input('Search: ') } }", + { desc = "note search" } +) +map("n", "nt", "ZkTags", { desc = "note tags" }) +map("n", "nc", "ZkCd", { desc = "notes directory" }) +prefix({ ["n"] = { name = "+note" } }) +map("n", "nl", "ZkLinks", { desc = "note links" }) +map("n", "nb", "ZkLinks", { desc = "note backlinks" }) +map("n", "nn", "ZkNew { title = vim.fn.input('Title: ') }", { desc = "new note" }) +prefix({ ["n"] = { name = "+note", mode = "v" } }) +map("v", "nn", ":ZkNewFromTitleSelection", { desc = "title from selection" }) +map("v", "nN", ":ZkNewFromContentSelection", { desc = "content from selection" }) +map("v", "nf", ":ZkMatch", { desc = "find note from selection" }) -- PLUGIN: toggleterm.nvim -- create a lazygit window, set up in toggleterm settings -map('n', 'G', ':Lazygit') +map("n", "G", ":Lazygit") -prefix({ ['s'] = { name = '+set' } }) +prefix({ ["s"] = { name = "+set" } }) -- PLUGIN: wrapping.nvim -map('n', 'sw', [[:lua require('wrapping').toggle_wrap_mode() ]], - { silent = true, desc = 'toggle wrap mode' }) +map( + "n", + "sw", + [[:lua require('wrapping').toggle_wrap_mode() ]], + { silent = true, desc = "toggle wrap mode" } +) -- PLUGIN: easyread.nvim -map('n', 'ss', ':EasyreadToggle', { silent = true, desc = 'toggle speedreading' }) +map("n", "ss", ":EasyreadToggle", { silent = true, desc = "toggle speedreading" }) -- PLUGIN: nabla.nvim -map('n', 'sv', 'lua require("nabla").popup()', { silent = true, desc = 'latex formula popup' }) -map('n', 'sV', 'lua require("nabla").toggle_virt({autogen = true, silent = true})', - { silent = true, desc = 'toggle formula notation' }) +map("n", "sv", 'lua require("nabla").popup()', { silent = true, desc = "latex formula popup" }) +map( + "n", + "sV", + 'lua require("nabla").toggle_virt({autogen = true, silent = true})', + { silent = true, desc = "toggle formula notation" } +) -- PLUGIN: nvim-colorizer -map('n', 'sc', 'ColorizerToggle', { silent = true, desc = 'toggle colorizer' }) -map('n', 'sC', 'lua require("colorizer").attach_to_buffer(0, {mode = "background"} )', - { silent = true, desc = 'colorize background' }) +map("n", "sc", "ColorizerToggle", { silent = true, desc = "toggle colorizer" }) +map( + "n", + "sC", + 'lua require("colorizer").attach_to_buffer(0, {mode = "background"} )', + { silent = true, desc = "colorize background" } +) diff --git a/nvim/.config/nvim/lua/personal/pandoc_complete.lua b/nvim/.config/nvim/lua/personal/pandoc_complete.lua index 689a9bc..6ebfb56 100644 --- a/nvim/.config/nvim/lua/personal/pandoc_complete.lua +++ b/nvim/.config/nvim/lua/personal/pandoc_complete.lua @@ -1,12 +1,11 @@ local M = {} function M.getCompletionItems(prefix) - -- define your total completion items - local items = vim.api.nvim_call_function('pandoc#completion#Complete', - {0, prefix}) - return items + -- define your total completion items + local items = vim.api.nvim_call_function("pandoc#completion#Complete", { 0, prefix }) + return items end -M.complete_item = {item = M.getCompletionItems} +M.complete_item = { item = M.getCompletionItems } return M diff --git a/nvim/.config/nvim/lua/personal/scratchpad.lua b/nvim/.config/nvim/lua/personal/scratchpad.lua index 8390c16..676d9c4 100644 --- a/nvim/.config/nvim/lua/personal/scratchpad.lua +++ b/nvim/.config/nvim/lua/personal/scratchpad.lua @@ -9,34 +9,41 @@ window, otherwise opens a new split. The buffer, by default is set to the pandoc filetype. This can be changed by setting the `g:scratchpad_ft` variable or the `b:scratchpad_ft` variable to the intended filetype. -]] -- +]] +-- local api = vim.api local M = {} -local function isempty(s) return s == nil or s == '' end +local function isempty(s) + return s == nil or s == "" +end function M.create(split, ft) - -- should we create a new split or switch out the current buffer? - if isempty(split) then - split = false - else - split = true - end - -- which filetype to set for the scratchpad, defaults to pandoc - if isempty(ft) then - ft = vim.b["scratchpad_ft"] or vim.g["scratchpad_ft"] or "pandoc" - end + -- should we create a new split or switch out the current buffer? + if isempty(split) then + split = false + else + split = true + end + -- which filetype to set for the scratchpad, defaults to pandoc + if isempty(ft) then + ft = vim.b["scratchpad_ft"] or vim.g["scratchpad_ft"] or "pandoc" + end - local buf = api.nvim_create_buf(false, true) - if buf == 0 then print("Error opening scratch buffer.") end - api.nvim_buf_set_option(buf, "bufhidden", "hide") - api.nvim_buf_set_option(buf, "buftype", "nofile") - api.nvim_buf_set_option(buf, "swapfile", false) - api.nvim_buf_set_option(buf, "filetype", ft) + local buf = api.nvim_create_buf(false, true) + if buf == 0 then + print("Error opening scratch buffer.") + end + api.nvim_buf_set_option(buf, "bufhidden", "hide") + api.nvim_buf_set_option(buf, "buftype", "nofile") + api.nvim_buf_set_option(buf, "swapfile", false) + api.nvim_buf_set_option(buf, "filetype", ft) - if split then api.nvim_command('vsplit new') end -- i think this is the only way to interact with the buffers creating a new split - -- switch to scratchpad - api.nvim_win_set_buf(0, buf) + if split then + api.nvim_command("vsplit new") + end -- i think this is the only way to interact with the buffers creating a new split + -- switch to scratchpad + api.nvim_win_set_buf(0, buf) end return M diff --git a/nvim/.config/nvim/lua/plug/_gitsigns.lua b/nvim/.config/nvim/lua/plug/_gitsigns.lua index 6cbb465..06ff500 100644 --- a/nvim/.config/nvim/lua/plug/_gitsigns.lua +++ b/nvim/.config/nvim/lua/plug/_gitsigns.lua @@ -1,44 +1,56 @@ -require('gitsigns').setup { - numhl = true, - signcolumn = false, - on_attach = function(bufnr) - local gs = package.loaded.gitsigns +require("gitsigns").setup({ + numhl = true, + signcolumn = false, + on_attach = function(bufnr) + local gs = package.loaded.gitsigns - local function map(mode, l, r, opts) - opts = opts or {} - opts.buffer = bufnr - vim.keymap.set(mode, l, r, opts) - end + local function map(mode, l, r, opts) + opts = opts or {} + opts.buffer = bufnr + vim.keymap.set(mode, l, r, opts) + end - -- Navigation - map('n', ']h', function() - if vim.wo.diff then return ']h' end - vim.schedule(function() gs.next_hunk() end) - return '' - end, { expr = true }) + -- Navigation + map("n", "]h", function() + if vim.wo.diff then + return "]h" + end + vim.schedule(function() + gs.next_hunk() + end) + return "" + end, { expr = true }) - map('n', '[h', function() - if vim.wo.diff then return '[h' end - vim.schedule(function() gs.prev_hunk() end) - return '' - end, { expr = true }) + map("n", "[h", function() + if vim.wo.diff then + return "[h" + end + vim.schedule(function() + gs.prev_hunk() + end) + return "" + end, { expr = true }) - -- Actions - require('which-key').register({ ['h'] = { name = '+git' } }) - map({ 'n', 'v' }, 'hs', ':Gitsigns stage_hunk', { desc = 'stage hunk' }) - map({ 'n', 'v' }, 'hr', ':Gitsigns reset_hunk', { desc = 'reset hunk' }) - map('n', 'hS', gs.stage_buffer, { desc = 'stage buffer' }) - map('n', 'hu', gs.undo_stage_hunk, { desc = 'undo stage hunk' }) - map('n', 'hR', gs.reset_buffer, { desc = 'reset buffer' }) - map('n', 'hp', gs.preview_hunk, { desc = 'preview hunk' }) - map('n', 'hb', function() gs.blame_line { full = true } end, { desc = 'blame line' }) - map('n', 'hB', gs.toggle_current_line_blame, { desc = 'toggle blame' }) - map('n', 'hd', gs.diffthis, { desc = 'diffthis' }) - map('n', 'hD', function() gs.diffthis('~') end, { desc = 'diffbase' }) - map('n', 'ht', gs.toggle_deleted, { desc = 'toggle deleted' }) + -- Actions + require("which-key").register({ ["h"] = { name = "+git" } }) + map({ "n", "v" }, "hs", ":Gitsigns stage_hunk", { desc = "stage hunk" }) + map({ "n", "v" }, "hr", ":Gitsigns reset_hunk", { desc = "reset hunk" }) + map("n", "hS", gs.stage_buffer, { desc = "stage buffer" }) + map("n", "hu", gs.undo_stage_hunk, { desc = "undo stage hunk" }) + map("n", "hR", gs.reset_buffer, { desc = "reset buffer" }) + map("n", "hp", gs.preview_hunk, { desc = "preview hunk" }) + map("n", "hb", function() + gs.blame_line({ full = true }) + end, { desc = "blame line" }) + map("n", "hB", gs.toggle_current_line_blame, { desc = "toggle blame" }) + map("n", "hd", gs.diffthis, { desc = "diffthis" }) + map("n", "hD", function() + gs.diffthis("~") + end, { desc = "diffbase" }) + map("n", "ht", gs.toggle_deleted, { desc = "toggle deleted" }) - -- Text object - map({ 'o', 'x' }, 'ih', ':Gitsigns select_hunk') - map({ 'o', 'x' }, 'ah', ':Gitsigns select_hunk') - end -} + -- Text object + map({ "o", "x" }, "ih", ":Gitsigns select_hunk") + map({ "o", "x" }, "ah", ":Gitsigns select_hunk") + end, +}) diff --git a/nvim/.config/nvim/lua/plug/_lualine.lua b/nvim/.config/nvim/lua/plug/_lualine.lua index 5c36f14..e978425 100644 --- a/nvim/.config/nvim/lua/plug/_lualine.lua +++ b/nvim/.config/nvim/lua/plug/_lualine.lua @@ -1,28 +1,28 @@ -require('lualine').setup { - options = { - icons_enabled = true, - theme = 'auto', - component_separators = {left = '', right = ''}, - section_separators = {left = '', right = ''}, - disabled_filetypes = {}, - always_divide_middle = true - }, - sections = { - lualine_a = {'mode'}, - lualine_b = {'branch', 'diff', 'diagnostics'}, - lualine_c = {'filename'}, - lualine_x = {'encoding', 'fileformat', 'filetype'}, - lualine_y = {'progress', 'location'}, - lualine_z = {'hostname'} - }, - inactive_sections = { - lualine_a = {}, - lualine_b = {'branch', 'diff'}, - lualine_c = {'filename'}, - lualine_x = {}, - lualine_y = {'location'}, - lualine_z = {} - }, - tabline = {}, - extensions = {'quickfix', 'toggleterm'} -} +require("lualine").setup({ + options = { + icons_enabled = true, + theme = "auto", + component_separators = { left = "", right = "" }, + section_separators = { left = "", right = "" }, + disabled_filetypes = {}, + always_divide_middle = true, + }, + sections = { + lualine_a = { "mode" }, + lualine_b = { "branch", "diff", "diagnostics" }, + lualine_c = { "filename" }, + lualine_x = { "encoding", "fileformat", "filetype" }, + lualine_y = { "progress", "location" }, + lualine_z = { "hostname" }, + }, + inactive_sections = { + lualine_a = {}, + lualine_b = { "branch", "diff" }, + lualine_c = { "filename" }, + lualine_x = {}, + lualine_y = { "location" }, + lualine_z = {}, + }, + tabline = {}, + extensions = { "quickfix", "toggleterm" }, +}) diff --git a/nvim/.config/nvim/lua/plug/_mini.lua b/nvim/.config/nvim/lua/plug/_mini.lua index 58872cf..d9194e8 100644 --- a/nvim/.config/nvim/lua/plug/_mini.lua +++ b/nvim/.config/nvim/lua/plug/_mini.lua @@ -1,38 +1,38 @@ -require('mini.ai').setup() -require('mini.comment').setup({ - hooks = { - pre = function() - require('ts_context_commentstring.internal').update_commentstring() - end - } +require("mini.ai").setup() +require("mini.comment").setup({ + hooks = { + pre = function() + require("ts_context_commentstring.internal").update_commentstring() + end, + }, }) -require('mini.cursorword').setup({ delay = 500 }) -require('mini.fuzzy').setup() -require('mini.indentscope').setup({ - symbol = "│", - draw = { animation = require('mini.indentscope').gen_animation.none() }, - options = { indent_at_cursor = false } +require("mini.cursorword").setup({ delay = 500 }) +require("mini.fuzzy").setup() +require("mini.indentscope").setup({ + symbol = "│", + draw = { animation = require("mini.indentscope").gen_animation.none() }, + options = { indent_at_cursor = false }, }) -require('mini.map').setup() +require("mini.map").setup() -- require('mini.move').setup() -- has not hit stable yet -require('mini.pairs').setup() -require('mini.trailspace').setup() +require("mini.pairs").setup() +require("mini.trailspace").setup() -local starter = require('mini.starter') +local starter = require("mini.starter") starter.setup({ - evaluate_single = true, - items = { - starter.sections.builtin_actions(), - starter.sections.recent_files(10, false), - starter.sections.recent_files(10, true), - -- Use this if you set up 'mini.sessions' - starter.sections.telescope() - }, - content_hooks = { - starter.gen_hook.adding_bullet(), - starter.gen_hook.padding(3, 2), - starter.gen_hook.aligning('center', 'center') - } + evaluate_single = true, + items = { + starter.sections.builtin_actions(), + starter.sections.recent_files(10, false), + starter.sections.recent_files(10, true), + -- Use this if you set up 'mini.sessions' + starter.sections.telescope(), + }, + content_hooks = { + starter.gen_hook.adding_bullet(), + starter.gen_hook.padding(3, 2), + starter.gen_hook.aligning("center", "center"), + }, }) -vim.api.nvim_set_hl(0, 'MiniCursorword', { bold = true }) +vim.api.nvim_set_hl(0, "MiniCursorword", { bold = true }) diff --git a/nvim/.config/nvim/lua/plug/_telescope.lua b/nvim/.config/nvim/lua/plug/_telescope.lua index e867e6b..cb70f13 100644 --- a/nvim/.config/nvim/lua/plug/_telescope.lua +++ b/nvim/.config/nvim/lua/plug/_telescope.lua @@ -4,40 +4,61 @@ -- rg (ripgrep) for in-text searches -- fd for quicker directory structure searches -- lsp for a variety of lsp queries -require("telescope").setup { - defaults = { - vimgrep_arguments = { - 'rg', '--ignore-vcs', '--hidden', '--color=never', '--no-heading', - '--with-filename', '--line-number', '--column', '--smart-case' - }, - generic_sorter = require('mini.fuzzy').get_telescope_sorter - }, - defaults = { - -- Appearance - prompt_prefix = ' ', - selection_caret = '󰳟 ', - color_devicons = true - }, - pickers = { - buffers = {theme = "ivy"}, - oldfiles = {theme = "ivy"}, - find_files = { - theme = "dropdown", - -- nice minimal picker design - borderchars = { - {'─', '│', '─', '│', '┌', '┐', '┘', '└'}, - prompt = {"─", "│", " ", "│", '┌', '┐', "│", "│"}, - results = { - "─", "│", "─", "│", "├", "┤", "┘", "└" - }, - preview = { - '─', '│', '─', '│', '┌', '┐', '┘', '└' - } - }, - width = 0.8, - previewer = false, - prompt_title = false - } - } -} +require("telescope").setup({ + defaults = { + vimgrep_arguments = { + "rg", + "--ignore-vcs", + "--hidden", + "--color=never", + "--no-heading", + "--with-filename", + "--line-number", + "--column", + "--smart-case", + }, + generic_sorter = require("mini.fuzzy").get_telescope_sorter, + }, + defaults = { + -- Appearance + prompt_prefix = " ", + selection_caret = "󰳟 ", + color_devicons = true, + }, + pickers = { + buffers = { theme = "ivy" }, + oldfiles = { theme = "ivy" }, + find_files = { + theme = "dropdown", + -- nice minimal picker design + borderchars = { + { "─", "│", "─", "│", "┌", "┐", "┘", "└" }, + prompt = { "─", "│", " ", "│", "┌", "┐", "│", "│" }, + results = { + "─", + "│", + "─", + "│", + "├", + "┤", + "┘", + "└", + }, + preview = { + "─", + "│", + "─", + "│", + "┌", + "┐", + "┘", + "└", + }, + }, + width = 0.8, + previewer = false, + prompt_title = false, + }, + }, +}) require("telescope").load_extension("fzf") diff --git a/nvim/.config/nvim/lua/plug/_toggleterm.lua b/nvim/.config/nvim/lua/plug/_toggleterm.lua index 83694d8..9ffc6b7 100644 --- a/nvim/.config/nvim/lua/plug/_toggleterm.lua +++ b/nvim/.config/nvim/lua/plug/_toggleterm.lua @@ -1,16 +1,18 @@ -require("toggleterm").setup { - open_mapping = [[=]], - insert_mappings = false -- don't map the key in insert mode -} +require("toggleterm").setup({ + open_mapping = [[=]], + insert_mappings = false, -- don't map the key in insert mode +}) -local Terminal = require('toggleterm.terminal').Terminal +local Terminal = require("toggleterm.terminal").Terminal -- create a lazygit window with the lazygit command local lazygit = Terminal:new({ - cmd = "lazygit", - hidden = true, - direction = 'float', - float_opts = { border = "curved" } + cmd = "lazygit", + hidden = true, + direction = "float", + float_opts = { border = "curved" }, }) -function _Lazygit_toggle() lazygit:toggle() end +function _Lazygit_toggle() + lazygit:toggle() +end vim.cmd([[command! Lazygit :lua _Lazygit_toggle()]]) diff --git a/nvim/.config/nvim/lua/plug/_treesitter.lua b/nvim/.config/nvim/lua/plug/_treesitter.lua index 2a57eff..4c80ceb 100644 --- a/nvim/.config/nvim/lua/plug/_treesitter.lua +++ b/nvim/.config/nvim/lua/plug/_treesitter.lua @@ -1,21 +1,21 @@ -local rainbow = require('ts-rainbow') -require 'nvim-treesitter.configs'.setup { - -- one of "all", "maintained" (parsers with maintainers), or a list of languages - ensure_installed = "all", - highlight = { enable = true }, - incremental_selection = { enable = true }, - textobjects = { enable = true }, - indent = { enable = true }, +local rainbow = require("ts-rainbow") +require("nvim-treesitter.configs").setup({ + -- one of "all", "maintained" (parsers with maintainers), or a list of languages + ensure_installed = "all", + highlight = { enable = true }, + incremental_selection = { enable = true }, + textobjects = { enable = true }, + indent = { enable = true }, - -- enable rainbow brackets, needs p00f/nvim-ts-rainbow - rainbow = { - enable = true, - strategy = { rainbow.strategy.global } - }, + -- enable rainbow brackets, needs p00f/nvim-ts-rainbow + rainbow = { + enable = true, + strategy = { rainbow.strategy.global }, + }, - -- for improved commentstrings, needs corresponding plugin - context_commentstring = { - enable = true, - enable_autocmd = false -- since we run it as a hook from the mini.comment plugin - } -} + -- for improved commentstrings, needs corresponding plugin + context_commentstring = { + enable = true, + enable_autocmd = false, -- since we run it as a hook from the mini.comment plugin + }, +}) diff --git a/nvim/.config/nvim/lua/plugins.lua b/nvim/.config/nvim/lua/plugins.lua index 4ac28a7..c20fd0e 100644 --- a/nvim/.config/nvim/lua/plugins.lua +++ b/nvim/.config/nvim/lua/plugins.lua @@ -1,261 +1,321 @@ local writing_ft = { "quarto", "pandoc", "markdown", "text", "tex" } return { - -- essential - { 'numToStr/Navigator.nvim', branch = "master", config = true }, -- allow seamless navigation between vim buffers and tmux/wezterm splits - { 'jeffkreeftmeijer/vim-numbertoggle', event = "BufEnter" }, -- toggles numbers to absolute for all buffers but the current which is relative - { 'ojroques/vim-oscyank', event = "VeryLazy" }, -- yank from *anywhere* (even ssh session) to clipboard, using :OSCYank - { 'ggandor/lightspeed.nvim', event = "VeryLazy" }, -- jump between letters with improved fFtT quicksearch, mimics sneak - { - 'lewis6991/gitsigns.nvim', -- show vcs changes on left-hand gutter - config = function() require('plug._gitsigns') end, - event = "BufRead" - }, { "m4xshen/smartcolumn.nvim", config = true }, -- auto-hiding colorcolumn - -- files - { 'vifm/vifm.vim' }, -- integrate file manager - { - 'nvim-tree/nvim-tree.lua', -- integrate file tree - config = true, - dependencies = { 'nvim-tree/nvim-web-devicons', config = true }, - cmd = "NvimTreeToggle" - }, -- colors - { - 'RRethy/nvim-base16', - event = "BufWinEnter", - dependencies = { 'rktjmp/fwatch.nvim' } - }, { - 'NvChad/nvim-colorizer.lua', -- color hex, named colors in the correct preview scheme - config = function() - require('colorizer').setup({ - user_default_options = { mode = 'virtualtext' } - }) - end, - event = "VeryLazy" -}, -- editing - { 'kylechui/nvim-surround', version = '*', config = true, event = "VeryLazy" }, -- surround things with other things using ys/cs/ds - { - 'monaqa/dial.nvim', -- extend the ^a / ^x possibilities to dates, hex, alphabets, markdown headers - config = function() - local augend = require("dial.augend") - require("dial.config").augends:register_group { - -- default augends used when no group name is specified - default = { - augend.integer.alias.decimal, augend.integer.alias.hex, - augend.date.alias["%Y/%m/%d"], - augend.date.alias["%Y-%m-%d"], augend.date.alias["%m/%d"], - augend.date.alias["%H:%M:%S"], augend.date.alias["%H:%M"], - augend.constant.alias.de_weekday_full, - augend.constant.alias.de_weekday, - augend.constant.alias.bool, augend.semver.alias.semver, - augend.constant.alias.Alpha, augend.constant.alias.alpha, - augend.hexcolor.new { case = "lower" }, augend.constant.new { - elements = { "and", "or" }, - word = true, -- if false, "sand" is incremented into "sor", "doctor" into "doctand", etc. - cyclic = true -- "or" is incremented into "and". - }, - augend.constant - .new { - elements = { "&&", "||" }, - word = false, - cyclic = true - } - } - } - end, - event = "VeryLazy" - }, { - 'tommcdo/vim-exchange', -- adds exchange operator with cx. common use: cxiw . on 2 words to switch - event = "VeryLazy" -}, { - 'junegunn/vim-easy-align', -- Align tables and other alignable things - event = "VeryLazy" -}, { 'edKotinsky/Arduino.nvim', ft = 'arduino', config = true }, -- statusline - { - 'nvim-lualine/lualine.nvim', - requires = { 'nvim-tree/nvim-web-devicons', config = true }, - config = function() require('plug._lualine') end - }, -- writing - { 'vim-pandoc/vim-criticmarkup', ft = writing_ft }, { - 'jbyuki/nabla.nvim', - ft = writing_ft, - config = function() - require('nabla').enable_virt({ autogen = true, silent = true }) - end -}, { - 'mickael-menu/zk-nvim', - config = function() require('zk').setup({ picker = "telescope" }) end -}, { - 'andrewferrier/wrapping.nvim', - config = function() - require('wrapping').setup({ - create_keymappings = false, - notify_on_switch = false - }) - end -}, { - 'quarto-dev/quarto-nvim', - dependencies = { - 'jmbuhr/otter.nvim', 'neovim/nvim-lspconfig', - 'vim-pandoc/vim-pandoc-syntax', 'hrsh7th/nvim-cmp', - 'nvim-treesitter/nvim-treesitter' - }, - config = function() - require 'quarto'.setup { - lspFeatures = { - enabled = true, - languages = { 'r', 'python', 'julia' }, - diagnostics = { enabled = true, triggers = { "BufWrite" } }, - completion = { enabled = true } - } - } - end, - ft = "quarto" -}, { - "lkhphuc/jupyter-kernel.nvim", - config = true, - cmd = "JupyterAttach", - build = ":UpdateRemotePlugins", - keys = { - { - "ck", - "JupyterInspect", - desc = "Inspect object in kernel" - } - } -}, { 'micarmst/vim-spellsync', event = "VeryLazy" }, -- personal dict improvements for git sync - { 'folke/zen-mode.nvim', config = true, event = "VeryLazy" }, -- provide distraction free writing - { 'folke/twilight.nvim', event = "VeryLazy" }, -- provide even distraction free-er writing (lowlight paragraphs) - { - 'JellyApple102/easyread.nvim', - config = true, - ft = writing_ft, - cmd = 'EasyreadToggle' - }, -- enable 'speed-reading' mode (bionic reading) - { 'marty-oehme/zettelkasten.nvim', ft = writing_ft, event = "VeryLazy" }, -- simple static markdown linking - { - "iamcco/markdown-preview.nvim", -- generate an auto-updating html preview for md files - build = function() vim.fn["mkdp#util#install"]() end, - ft = writing_ft - }, -- languages - { 'euclidianAce/BetterLua.vim', ft = 'lua' }, -- better syntax highlighting for lua - { 'aliou/bats.vim', ft = { "bash", "sh", "zsh", "bats" } }, -- enable syntax for bats shell-code testing library + -- essential + { "numToStr/Navigator.nvim", branch = "master", config = true }, -- allow seamless navigation between vim buffers and tmux/wezterm splits + { "jeffkreeftmeijer/vim-numbertoggle", event = "BufEnter" }, -- toggles numbers to absolute for all buffers but the current which is relative + { "ojroques/vim-oscyank", event = "VeryLazy" }, -- yank from *anywhere* (even ssh session) to clipboard, using :OSCYank + { "ggandor/lightspeed.nvim", event = "VeryLazy" }, -- jump between letters with improved fFtT quicksearch, mimics sneak + { + "lewis6991/gitsigns.nvim", -- show vcs changes on left-hand gutter + config = function() + require("plug._gitsigns") + end, + event = "BufRead", + }, + { "m4xshen/smartcolumn.nvim", config = true }, -- auto-hiding colorcolumn + -- files + { "vifm/vifm.vim" }, -- integrate file manager + { + "nvim-tree/nvim-tree.lua", -- integrate file tree + config = true, + dependencies = { "nvim-tree/nvim-web-devicons", config = true }, + cmd = "NvimTreeToggle", + }, -- colors + { + "RRethy/nvim-base16", + event = "BufWinEnter", + dependencies = { "rktjmp/fwatch.nvim" }, + }, + { + "NvChad/nvim-colorizer.lua", -- color hex, named colors in the correct preview scheme + config = function() + require("colorizer").setup({ + user_default_options = { mode = "virtualtext" }, + }) + end, + event = "VeryLazy", + }, -- editing + { "kylechui/nvim-surround", version = "*", config = true, event = "VeryLazy" }, -- surround things with other things using ys/cs/ds + { + "monaqa/dial.nvim", -- extend the ^a / ^x possibilities to dates, hex, alphabets, markdown headers + config = function() + local augend = require("dial.augend") + require("dial.config").augends:register_group({ + -- default augends used when no group name is specified + default = { + augend.integer.alias.decimal, + augend.integer.alias.hex, + augend.date.alias["%Y/%m/%d"], + augend.date.alias["%Y-%m-%d"], + augend.date.alias["%m/%d"], + augend.date.alias["%H:%M:%S"], + augend.date.alias["%H:%M"], + augend.constant.alias.de_weekday_full, + augend.constant.alias.de_weekday, + augend.constant.alias.bool, + augend.semver.alias.semver, + augend.constant.alias.Alpha, + augend.constant.alias.alpha, + augend.hexcolor.new({ case = "lower" }), + augend.constant.new({ + elements = { "and", "or" }, + word = true, -- if false, "sand" is incremented into "sor", "doctor" into "doctand", etc. + cyclic = true, -- "or" is incremented into "and". + }), + augend.constant.new({ + elements = { "&&", "||" }, + word = false, + cyclic = true, + }), + }, + }) + end, + event = "VeryLazy", + }, + { + "tommcdo/vim-exchange", -- adds exchange operator with cx. common use: cxiw . on 2 words to switch + event = "VeryLazy", + }, + { + "junegunn/vim-easy-align", -- Align tables and other alignable things + event = "VeryLazy", + }, + { "edKotinsky/Arduino.nvim", ft = "arduino", config = true }, -- statusline + { + "nvim-lualine/lualine.nvim", + requires = { "nvim-tree/nvim-web-devicons", config = true }, + config = function() + require("plug._lualine") + end, + }, -- writing + { "vim-pandoc/vim-criticmarkup", ft = writing_ft }, + { + "jbyuki/nabla.nvim", + ft = writing_ft, + config = function() + require("nabla").enable_virt({ autogen = true, silent = true }) + end, + }, + { + "mickael-menu/zk-nvim", + config = function() + require("zk").setup({ picker = "telescope" }) + end, + }, + { + "andrewferrier/wrapping.nvim", + config = function() + require("wrapping").setup({ + create_keymappings = false, + notify_on_switch = false, + }) + end, + }, + { + "quarto-dev/quarto-nvim", + dependencies = { + "jmbuhr/otter.nvim", + "neovim/nvim-lspconfig", + "vim-pandoc/vim-pandoc-syntax", + "hrsh7th/nvim-cmp", + "nvim-treesitter/nvim-treesitter", + }, + config = function() + require("quarto").setup({ + lspFeatures = { + enabled = true, + languages = { "r", "python", "julia" }, + diagnostics = { enabled = true, triggers = { "BufWrite" } }, + completion = { enabled = true }, + }, + }) + end, + ft = "quarto", + }, + { + "lkhphuc/jupyter-kernel.nvim", + config = true, + cmd = "JupyterAttach", + build = ":UpdateRemotePlugins", + keys = { + { + "ck", + "JupyterInspect", + desc = "Inspect object in kernel", + }, + }, + }, + { "micarmst/vim-spellsync", event = "VeryLazy" }, -- personal dict improvements for git sync + { "folke/zen-mode.nvim", config = true, event = "VeryLazy" }, -- provide distraction free writing + { "folke/twilight.nvim", event = "VeryLazy" }, -- provide even distraction free-er writing (lowlight paragraphs) + { + "JellyApple102/easyread.nvim", + config = true, + ft = writing_ft, + cmd = "EasyreadToggle", + }, -- enable 'speed-reading' mode (bionic reading) + { "marty-oehme/zettelkasten.nvim", ft = writing_ft, event = "VeryLazy" }, -- simple static markdown linking + { + "iamcco/markdown-preview.nvim", -- generate an auto-updating html preview for md files + build = function() + vim.fn["mkdp#util#install"]() + end, + ft = writing_ft, + }, -- languages + { "euclidianAce/BetterLua.vim", ft = "lua" }, -- better syntax highlighting for lua + { "aliou/bats.vim", ft = { "bash", "sh", "zsh", "bats" } }, -- enable syntax for bats shell-code testing library - -- REPL work - { - 'WhiteBlackGoose/magma-nvim-goose', - build = ":UpdateRemotePlugins", - config = function() - vim.g.magma_image_provider = "kitty" - vim.g.magma_automatically_open_output = false - end - }, { - 'echasnovski/mini.nvim', - version = '*', - config = function() require('plug._mini') end -}, { - "akinsho/nvim-toggleterm.lua", -- simpler, programmable and multiple terminal toggling for nvim - config = function() require('plug._toggleterm') end -}, - { - "folke/which-key.nvim", - config = function() require("which-key").setup {} end - }, -- fuzzy matching - { "nvim-telescope/telescope-fzf-native.nvim", build = 'make' }, { - "nvim-telescope/telescope.nvim", - dependencies = { "nvim-lua/popup.nvim", "nvim-lua/plenary.nvim" }, - config = function() require('plug._telescope') end -}, { - "dense-analysis/neural", - dependencies = { "MunifTanjim/nui.nvim", "elpiloto/significant.nvim" }, - config = function() - require('neural').setup({ - source = { openai = { api_key = vim.env.OPENAI_API_KEY } } - }) - end -}, -- treesitter - { - 'nvim-treesitter/nvim-treesitter', - build = ':TSUpdate', - config = function() require('plug._treesitter') end, - event = "BufReadPre", - -- rainbow brackets using treesitter - -- show current cursor context at top of buffer - -- improves commenting plugin above by using ts - dependencies = { - 'https://gitlab.com/HiPhish/nvim-ts-rainbow2.git', - { 'romgrk/nvim-treesitter-context', config = true }, - 'JoosepAlviste/nvim-ts-context-commentstring' - } - }, { 'nvim-treesitter/playground', cmd = "TSPlaygroundToggle" }, -- interactively view and query the treesitter tree - { - 'RRethy/nvim-treesitter-textsubjects', -- allows using . and ; to target treesitter branches - config = function() - require 'nvim-treesitter.configs'.setup { - textsubjects = { - enable = true, - keymaps = { - ['.'] = 'textsubjects-smart', - [';'] = 'textsubjects-container-outer' - } - } - } - end, - event = "BufReadPre" - }, { - -- lsp - "VonHeikemen/lsp-zero.nvim", - dependencies = { - { "neovim/nvim-lspconfig", branch = "master" }, - "williamboman/mason.nvim", "williamboman/mason-lspconfig.nvim", { - "hrsh7th/nvim-cmp", - branch = "main", - dependencies = { - "andersevenrud/cmp-tmux", "cbarrete/completion-vcard", - "f3fora/cmp-spell", "hrsh7th/cmp-nvim-lsp", - "hrsh7th/cmp-path", "hrsh7th/cmp-buffer", - "hrsh7th/cmp-calc", "hrsh7th/cmp-cmdline", - "hrsh7th/cmp-nvim-lua", "dmitmel/cmp-digraphs", - "jc-doyle/cmp-pandoc-references", - "kdheepak/cmp-latex-symbols", "lukas-reineke/cmp-rg", - "crispgm/cmp-beancount", "ray-x/cmp-treesitter", - "saadparwaiz1/cmp_luasnip" - } - }, "L3MON4D3/LuaSnip", "rafamadriz/friendly-snippets", - -- { "lukas-reineke/lsp-format.nvim", config = true }, - { "j-hui/fidget.nvim", config = true }, -- loading animations for some LSP - { - "jay-babu/mason-null-ls.nvim", - event = { "BufReadPre", "BufNewFile" }, - dependencies = { - "williamboman/mason.nvim", "jose-elias-alvarez/null-ls.nvim" - }, - } - }, - config = function() require('plug._lsp') end, - branch = "v2.x" -}, { 'simrat39/symbols-outline.nvim', config = true, event = "VeryLazy" }, -- vista-like outline view for code - { 'ray-x/lsp_signature.nvim', config = true }, -- UI improvements - { 'stevearc/dressing.nvim', config = true }, { - 'rcarriga/nvim-notify', - config = function() vim.notify = require("notify") end -} - -- { -- REQUIRES custom `yay -S --asdeps lua51-lyaml invocation` AND is suuper slow - -- "jghauser/papis.nvim", - -- after = { "telescope.nvim", "nvim-cmp" }, - -- dependencies = { - -- "kkharji/sqlite.lua", "nvim-lua/plenary.nvim", - -- "MunifTanjim/nui.nvim", "nvim-treesitter/nvim-treesitter" - -- }, - -- ft = writing_ft, - -- rocks = { "lyaml" }, - -- config = function() - -- require('papis').setup({ - -- papis_python = { - -- dir = "/home/marty/documents/library/academia", - -- info_name = "info.yaml", - -- notes_name = [[notes.qmd]] - -- } - -- }) - -- end - -- } + -- REPL work + { + "WhiteBlackGoose/magma-nvim-goose", + build = ":UpdateRemotePlugins", + config = function() + vim.g.magma_image_provider = "kitty" + vim.g.magma_automatically_open_output = false + end, + }, + { + "echasnovski/mini.nvim", + version = "*", + config = function() + require("plug._mini") + end, + }, + { + "akinsho/nvim-toggleterm.lua", -- simpler, programmable and multiple terminal toggling for nvim + config = function() + require("plug._toggleterm") + end, + }, + { + "folke/which-key.nvim", + config = function() + require("which-key").setup({}) + end, + }, -- fuzzy matching + { "nvim-telescope/telescope-fzf-native.nvim", build = "make" }, + { + "nvim-telescope/telescope.nvim", + dependencies = { "nvim-lua/popup.nvim", "nvim-lua/plenary.nvim" }, + config = function() + require("plug._telescope") + end, + }, + { + "dense-analysis/neural", + dependencies = { "MunifTanjim/nui.nvim", "elpiloto/significant.nvim" }, + config = function() + require("neural").setup({ + source = { openai = { api_key = vim.env.OPENAI_API_KEY } }, + }) + end, + }, -- treesitter + { + "nvim-treesitter/nvim-treesitter", + build = ":TSUpdate", + config = function() + require("plug._treesitter") + end, + event = "BufReadPre", + -- rainbow brackets using treesitter + -- show current cursor context at top of buffer + -- improves commenting plugin above by using ts + dependencies = { + "https://gitlab.com/HiPhish/nvim-ts-rainbow2.git", + { "romgrk/nvim-treesitter-context", config = true }, + "JoosepAlviste/nvim-ts-context-commentstring", + }, + }, + { "nvim-treesitter/playground", cmd = "TSPlaygroundToggle" }, -- interactively view and query the treesitter tree + { + "RRethy/nvim-treesitter-textsubjects", -- allows using . and ; to target treesitter branches + config = function() + require("nvim-treesitter.configs").setup({ + textsubjects = { + enable = true, + keymaps = { + ["."] = "textsubjects-smart", + [";"] = "textsubjects-container-outer", + }, + }, + }) + end, + event = "BufReadPre", + }, + { + -- lsp + "VonHeikemen/lsp-zero.nvim", + dependencies = { + { "neovim/nvim-lspconfig", branch = "master" }, + "williamboman/mason.nvim", + "williamboman/mason-lspconfig.nvim", + { + "hrsh7th/nvim-cmp", + branch = "main", + dependencies = { + "andersevenrud/cmp-tmux", + "cbarrete/completion-vcard", + "f3fora/cmp-spell", + "hrsh7th/cmp-nvim-lsp", + "hrsh7th/cmp-path", + "hrsh7th/cmp-buffer", + "hrsh7th/cmp-calc", + "hrsh7th/cmp-cmdline", + "hrsh7th/cmp-nvim-lua", + "dmitmel/cmp-digraphs", + "jc-doyle/cmp-pandoc-references", + "kdheepak/cmp-latex-symbols", + "lukas-reineke/cmp-rg", + "crispgm/cmp-beancount", + "ray-x/cmp-treesitter", + "saadparwaiz1/cmp_luasnip", + }, + }, + "L3MON4D3/LuaSnip", + "rafamadriz/friendly-snippets", + -- { "lukas-reineke/lsp-format.nvim", config = true }, + { "j-hui/fidget.nvim", config = true }, -- loading animations for some LSP + { + "jay-babu/mason-null-ls.nvim", + event = { "BufReadPre", "BufNewFile" }, + dependencies = { + "williamboman/mason.nvim", + "jose-elias-alvarez/null-ls.nvim", + }, + }, + }, + config = function() + require("plug._lsp") + end, + branch = "v2.x", + }, + { "simrat39/symbols-outline.nvim", config = true, event = "VeryLazy" }, -- vista-like outline view for code + { "ray-x/lsp_signature.nvim", config = true }, -- UI improvements + { "stevearc/dressing.nvim", config = true }, + { + "rcarriga/nvim-notify", + config = function() + vim.notify = require("notify") + end, + }, + -- { -- REQUIRES custom `yay -S --asdeps lua51-lyaml invocation` AND is suuper slow + -- "jghauser/papis.nvim", + -- after = { "telescope.nvim", "nvim-cmp" }, + -- dependencies = { + -- "kkharji/sqlite.lua", "nvim-lua/plenary.nvim", + -- "MunifTanjim/nui.nvim", "nvim-treesitter/nvim-treesitter" + -- }, + -- ft = writing_ft, + -- rocks = { "lyaml" }, + -- config = function() + -- require('papis').setup({ + -- papis_python = { + -- dir = "/home/marty/documents/library/academia", + -- info_name = "info.yaml", + -- notes_name = [[notes.qmd]] + -- } + -- }) + -- end + -- } } diff --git a/nvim/.config/nvim/lua/settings.lua b/nvim/.config/nvim/lua/settings.lua index 512855e..34108de 100644 --- a/nvim/.config/nvim/lua/settings.lua +++ b/nvim/.config/nvim/lua/settings.lua @@ -1,68 +1,70 @@ local default_builtins_disabled = { "netrw", "netrwPlugin" } local disable_builtins = function(builtins) - for _, plugin in pairs(builtins) do vim.g["loaded_" .. plugin] = 1 end + for _, plugin in pairs(builtins) do + vim.g["loaded_" .. plugin] = 1 + end end local o = { - termguicolors = true, - -- sets tabs to be 2 characters, expanded into spaces, but still removable with - -- one press of backspace. - -- great explanation: http://vimcasts.org/transcripts/2/en/ - tabstop = 4, - shiftwidth = 4, - softtabstop = 4, - expandtab = true, - -- make jumplist behave more like browser, when jumping back - -- and then adding a new jump discards all the previous - -- 'future-jump' tree, making more sense for wiki-like movement - jumpoptions = 'stack', - -- set cursor line highlighting, esp useful when using with bracket - -- highlighting and you don't know which side of the brace the cursor is on - cursorline = true, - -- shows linenumbers relative to the one you are on, for easy movement and - -- dNUMBERd deletions - number = true, - relativenumber = true, - -- puts the numbers into the signcolumn so when git/lsp show signs there's no jump - signcolumn = 'number', - -- keeps an undofile next to files so that you can even undo if vim is closed - -- in between - undofile = true, - -- TODO o.undodir = '~/.cache/nvim/undodir' + termguicolors = true, + -- sets tabs to be 2 characters, expanded into spaces, but still removable with + -- one press of backspace. + -- great explanation: http://vimcasts.org/transcripts/2/en/ + tabstop = 4, + shiftwidth = 4, + softtabstop = 4, + expandtab = true, + -- make jumplist behave more like browser, when jumping back + -- and then adding a new jump discards all the previous + -- 'future-jump' tree, making more sense for wiki-like movement + jumpoptions = "stack", + -- set cursor line highlighting, esp useful when using with bracket + -- highlighting and you don't know which side of the brace the cursor is on + cursorline = true, + -- shows linenumbers relative to the one you are on, for easy movement and + -- dNUMBERd deletions + number = true, + relativenumber = true, + -- puts the numbers into the signcolumn so when git/lsp show signs there's no jump + signcolumn = "number", + -- keeps an undofile next to files so that you can even undo if vim is closed + -- in between + undofile = true, + -- TODO o.undodir = '~/.cache/nvim/undodir' - -- ignores case by default but will use case when search is specifically not - -- all lowercased - ignorecase = true, - smartcase = true, - -- shows previews of what substitute command will do (and a couple others) - inccommand = 'split', - -- disables showing us the current mode in the command line since airline takes - -- care of it - showmode = false, - -- turn off modeline, to ensure security observation - modeline = false, - -- i feel foldlevel 2 is generally pretty usable, for headlines and similar - -- set to use treesitter in treesitter config - foldlevel = 2, - conceallevel = 2, - -- enable mouse, doesn't bug me and might come in useful at some point - mouse = 'a', - -- pump all clippings into the system clipboard - clipboard = 'unnamedplus', - -- turn of automatic resizing of individual splits - equalalways = false, - -- make sure there's always *some* context below cursor - scrolloff = 4, - -- open new buffers bottomright - splitright = true, - splitbelow = true, - -- remove command line if no command is currently present - cmdheight = 0, + -- ignores case by default but will use case when search is specifically not + -- all lowercased + ignorecase = true, + smartcase = true, + -- shows previews of what substitute command will do (and a couple others) + inccommand = "split", + -- disables showing us the current mode in the command line since airline takes + -- care of it + showmode = false, + -- turn off modeline, to ensure security observation + modeline = false, + -- i feel foldlevel 2 is generally pretty usable, for headlines and similar + -- set to use treesitter in treesitter config + foldlevel = 2, + conceallevel = 2, + -- enable mouse, doesn't bug me and might come in useful at some point + mouse = "a", + -- pump all clippings into the system clipboard + clipboard = "unnamedplus", + -- turn of automatic resizing of individual splits + equalalways = false, + -- make sure there's always *some* context below cursor + scrolloff = 4, + -- open new buffers bottomright + splitright = true, + splitbelow = true, + -- remove command line if no command is currently present + cmdheight = 0, } for k, v in pairs(o) do - vim.opt[k] = v + vim.opt[k] = v end -vim.api.nvim_set_var('tex_flavor', 'latex') +vim.api.nvim_set_var("tex_flavor", "latex") disable_builtins(default_builtins_disabled) diff --git a/nvim/.config/nvim/lua/util/highlight.lua b/nvim/.config/nvim/lua/util/highlight.lua index 1465788..d4c2e99 100644 --- a/nvim/.config/nvim/lua/util/highlight.lua +++ b/nvim/.config/nvim/lua/util/highlight.lua @@ -3,15 +3,15 @@ -- usage example - italicize comments: -- set_hl("Comment", { gui = "italic" }) return function(group, options) - local bg = options.bg == nil and "" or "guibg=" .. options.bg - local fg = options.fg == nil and "" or "guifg=" .. options.fg - local gui = options.gui == nil and "" or "gui=" .. options.gui - local link = options.link or false - local target = options.target + local bg = options.bg == nil and "" or "guibg=" .. options.bg + local fg = options.fg == nil and "" or "guifg=" .. options.fg + local gui = options.gui == nil and "" or "gui=" .. options.gui + local link = options.link or false + local target = options.target - if not link then - vim.cmd(string.format("hi %s %s %s %s", group, bg, fg, gui)) - else - vim.cmd(string.format("hi! link", group, target)) - end + if not link then + vim.cmd(string.format("hi %s %s %s %s", group, bg, fg, gui)) + else + vim.cmd(string.format("hi! link", group, target)) + end end diff --git a/nvim/.config/nvim/lua/util/pyenv.lua b/nvim/.config/nvim/lua/util/pyenv.lua index 0beb2d2..9e24b2d 100644 --- a/nvim/.config/nvim/lua/util/pyenv.lua +++ b/nvim/.config/nvim/lua/util/pyenv.lua @@ -1,45 +1,42 @@ -local util = require('lspconfig/util') +local util = require("lspconfig/util") local path = util.path local T = {} local exepath = vim.fn.exepath local path_sep = function() - local is_win = vim.loop.os_uname().sysname:find('Windows') - if is_win then - return '\\' - else - return '/' - end + local is_win = vim.loop.os_uname().sysname:find("Windows") + if is_win then + return "\\" + else + return "/" + end end -- from https://github.com/ray-x/navigator.lua/issues/247#issue-1465308677 T.get_path = function(workspace) - -- Use activated virtualenv. - if vim.env.VIRTUAL_ENV then - return path.join(vim.env.VIRTUAL_ENV, 'bin', 'python'), 'virtual env' - end + -- Use activated virtualenv. + if vim.env.VIRTUAL_ENV then + return path.join(vim.env.VIRTUAL_ENV, "bin", "python"), "virtual env" + end - -- Find and use virtualenv in workspace directory. - for _, pattern in ipairs({ '*', '.*' }) do - local match = vim.fn.glob(path.join(workspace, pattern, 'pyvenv.cfg')) - local sep = path_sep() - local py = 'bin' .. sep .. 'python' - if match ~= '' then - match = string.gsub(match, 'pyvenv.cfg', py) - return match, string.format('venv base folder: %s', match) - end - match = vim.fn.glob(path.join(workspace, pattern, 'poetry.lock')) - if match ~= '' then - local venv_base_folder = vim.fn.trim(vim.fn.system( - 'poetry env info -p')) - return path.join(venv_base_folder, 'bin', 'python'), - string.format('venv base folder: %s', venv_base_folder) - end - end + -- Find and use virtualenv in workspace directory. + for _, pattern in ipairs({ "*", ".*" }) do + local match = vim.fn.glob(path.join(workspace, pattern, "pyvenv.cfg")) + local sep = path_sep() + local py = "bin" .. sep .. "python" + if match ~= "" then + match = string.gsub(match, "pyvenv.cfg", py) + return match, string.format("venv base folder: %s", match) + end + match = vim.fn.glob(path.join(workspace, pattern, "poetry.lock")) + if match ~= "" then + local venv_base_folder = vim.fn.trim(vim.fn.system("poetry env info -p")) + return path.join(venv_base_folder, "bin", "python"), string.format("venv base folder: %s", venv_base_folder) + end + end - -- Fallback to system Python. - return exepath('python3') or exepath('python') or 'python', - 'fallback to system python path' + -- Fallback to system Python. + return exepath("python3") or exepath("python") or "python", "fallback to system python path" end return T diff --git a/office/.config/imapfilter/accounts.lua b/office/.config/imapfilter/accounts.lua index 8d48286..ad871e8 100644 --- a/office/.config/imapfilter/accounts.lua +++ b/office/.config/imapfilter/accounts.lua @@ -1,15 +1,13 @@ local accounts = {} -local _, gmailuser = pipe_from( - 'pass show misc/gmail-app-password | grep username | cut -d: -f2') -local _, gmailpass = pipe_from( - 'pass show misc/gmail-app-password | head -n1') +local _, gmailuser = pipe_from("pass show misc/gmail-app-password | grep username | cut -d: -f2") +local _, gmailpass = pipe_from("pass show misc/gmail-app-password | head -n1") -- Setup an imap account called gmail -accounts.gmail = IMAP { - server = "imap.gmail.com", - username = gmailuser, - password = gmailpass, - ssl = "auto" -} +accounts.gmail = IMAP({ + server = "imap.gmail.com", + username = gmailuser, + password = gmailpass, + ssl = "auto", +}) return accounts diff --git a/office/.config/imapfilter/config.lua b/office/.config/imapfilter/config.lua index c95aca5..5fd4e34 100644 --- a/office/.config/imapfilter/config.lua +++ b/office/.config/imapfilter/config.lua @@ -20,67 +20,64 @@ CONTINUOUS = false UPDATE_TIME = 120 -- implement simple wait function in case server does not support IDLE mode -function sleep(n) os.execute("sleep " .. tonumber(n)) end +function sleep(n) + os.execute("sleep " .. tonumber(n)) +end -- will set filters to be grabbed from XDG-compliant filter directory -- can be overridden with env var IMAPFILTER_FILTERDIR function getConfigDir() - -- -- set directory for imapfilter files - local configdir - if os.getenv("IMAPFILTER_CONFIGDIR") then - configdir = os.getenv("IMAPFILTER_CONFIGDIR") - elseif os.getenv("XDG_CONFIG_HOME") then - configdir = os.getenv("XDG_CONFIG_HOME") .. "/imapfilter" - else - configdir = os.getenv("HOME") .. "/.config/imapfilter" - end - return configdir + -- -- set directory for imapfilter files + local configdir + if os.getenv("IMAPFILTER_CONFIGDIR") then + configdir = os.getenv("IMAPFILTER_CONFIGDIR") + elseif os.getenv("XDG_CONFIG_HOME") then + configdir = os.getenv("XDG_CONFIG_HOME") .. "/imapfilter" + else + configdir = os.getenv("HOME") .. "/.config/imapfilter" + end + return configdir end -- will set filters to be grabbed from XDG-compliant filter directory -- can be overridden with env var IMAPFILTER_FILTERDIR function getFilterDir() - -- -- set directory for imapfilter files - local imapfilterdir - if os.getenv("IMAPFILTER_FILTERDIR") then - imapfilterdir = os.getenv("IMAPFILTER_FILTERDIR") - else - imapfilterdir = configDir .. "/filters" - end - return imapfilterdir + -- -- set directory for imapfilter files + local imapfilterdir + if os.getenv("IMAPFILTER_FILTERDIR") then + imapfilterdir = os.getenv("IMAPFILTER_FILTERDIR") + else + imapfilterdir = configDir .. "/filters" + end + return imapfilterdir end -- dirlist, from https://stackoverflow.com/a/25266573 function applyFilters(dir) - local p = io.popen('find "' .. dir .. '" -type f -name "*.lua"') -- Open directory look for files, save data in p. By giving '-type f' as parameter, it returns all files. - for file in p:lines() do -- Loop through all files - loadfile(file)() - end + local p = io.popen('find "' .. dir .. '" -type f -name "*.lua"') -- Open directory look for files, save data in p. By giving '-type f' as parameter, it returns all files. + for file in p:lines() do -- Loop through all files + loadfile(file)() + end end -- create global variable containing the configuration files configDir = getConfigDir() -assert(configDir, - "No configuration directory found. Ensure " .. os.getenv("HOME") .. - "/.config/imapfilter exists.") +assert(configDir, "No configuration directory found. Ensure " .. os.getenv("HOME") .. "/.config/imapfilter exists.") -- create global variable containing account access accounts = loadfile(configDir .. "/accounts.lua")() -assert(accounts, - "No accounts configured. Ensure accounts.lua exists and returns a table of accounts.") +assert(accounts, "No accounts configured. Ensure accounts.lua exists and returns a table of accounts.") -- immediately act on the filters once applyFilters(getFilterDir()) -- continuously watch for mail if needed while CONTINUOUS == true do - local has_idle = accounts.gmail['Inbox']:enter_idle() - applyFilters(getFilterDir()) + local has_idle = accounts.gmail["Inbox"]:enter_idle() + applyFilters(getFilterDir()) - if has_idle == false then - print( - "Server does not support idle, application will be polling again in " .. - UPDATE_TIME .. "minutes.") - sleep(UPDATE_TIME) - end + if has_idle == false then + print("Server does not support idle, application will be polling again in " .. UPDATE_TIME .. "minutes.") + sleep(UPDATE_TIME) + end end diff --git a/office/.config/imapfilter/filters/rollup-dump.lua b/office/.config/imapfilter/filters/rollup-dump.lua index e26f9bb..f6a923a 100644 --- a/office/.config/imapfilter/filters/rollup-dump.lua +++ b/office/.config/imapfilter/filters/rollup-dump.lua @@ -1,40 +1,39 @@ function sendToFolder(folderFrom, folderTo, senders) - local messages = folderFrom:select_all() - for _, sender in pairs(senders) do - local filtered = messages:contain_from(sender) - filtered:mark_seen() - filtered:move_messages(folderTo) - end + local messages = folderFrom:select_all() + for _, sender in pairs(senders) do + local filtered = messages:contain_from(sender) + filtered:mark_seen() + filtered:move_messages(folderTo) + end end -- will set filters to be grabbed from XDG-compliant filter directory -- can be overridden with env var IMAPFILTER_ROLLUPFILE function getRollupFile(fname) - local f - local fname = fname or "rollup.txt" - if os.getenv("IMAPFILTER_ROLLUPFILE") then - f = os.getenv("IMAPFILTER_ROLLUPFILE") - elseif os.getenv("XDG_DATA_HOME") then - f = os.getenv("XDG_DATA_HOME") .. "/imapfilter/" .. fname - else - f = os.getenv("HOME") .. "/.local/share/imapfilter/" .. fname - end - return f + local f + local fname = fname or "rollup.txt" + if os.getenv("IMAPFILTER_ROLLUPFILE") then + f = os.getenv("IMAPFILTER_ROLLUPFILE") + elseif os.getenv("XDG_DATA_HOME") then + f = os.getenv("XDG_DATA_HOME") .. "/imapfilter/" .. fname + else + f = os.getenv("HOME") .. "/.local/share/imapfilter/" .. fname + end + return f end function getSenderList(rollupfile) - local rollupSenders = {} + local rollupSenders = {} - local file = io.open(rollupfile) - if file then - for line in file:lines() do table.insert(rollupSenders, line) end - else - print( - "rollup did not find rollup.txt file containing mail addresses at " .. - rollupfile or ". Skipping.") - end - return rollupSenders + local file = io.open(rollupfile) + if file then + for line in file:lines() do + table.insert(rollupSenders, line) + end + else + print("rollup did not find rollup.txt file containing mail addresses at " .. rollupfile or ". Skipping.") + end + return rollupSenders end -sendToFolder(accounts.gmail["Inbox"], accounts.gmail["Dump"], - getSenderList(getRollupFile())) +sendToFolder(accounts.gmail["Inbox"], accounts.gmail["Dump"], getSenderList(getRollupFile())) diff --git a/terminal/.config/wezterm/events.lua b/terminal/.config/wezterm/events.lua index 4b64134..2dbccf2 100644 --- a/terminal/.config/wezterm/events.lua +++ b/terminal/.config/wezterm/events.lua @@ -1,75 +1,81 @@ -local wezterm = require 'wezterm' -local io = require 'io' -local os = require 'os' +local wezterm = require("wezterm") +local io = require("io") +local os = require("os") local act = wezterm.action local function setup() - local function isViProcess(pane) - return pane:get_foreground_process_name():find('n?vim') ~= nil - end + local function isViProcess(pane) + return pane:get_foreground_process_name():find("n?vim") ~= nil + end - local function conditionalActivatePane(window, pane, pane_direction, - vim_direction) - if (isViProcess(pane)) then - window:perform_action(act.Multiple { - act.SendKey { key = 'w', mods = 'CTRL' }, - act.SendKey { key = vim_direction } - }, pane) - else - window:perform_action(act.ActivatePaneDirection(pane_direction), - pane) - end - end + local function conditionalActivatePane(window, pane, pane_direction, vim_direction) + if isViProcess(pane) then + window:perform_action( + act.Multiple({ + act.SendKey({ key = "w", mods = "CTRL" }), + act.SendKey({ key = vim_direction }), + }), + pane + ) + else + window:perform_action(act.ActivatePaneDirection(pane_direction), pane) + end + end - wezterm.on('ActivatePaneDirection-right', function(window, pane) - conditionalActivatePane(window, pane, 'Right', 'l') - end) - wezterm.on('ActivatePaneDirection-left', function(window, pane) - conditionalActivatePane(window, pane, 'Left', 'h') - end) - wezterm.on('ActivatePaneDirection-up', function(window, pane) - conditionalActivatePane(window, pane, 'Up', 'k') - end) - wezterm.on('ActivatePaneDirection-down', function(window, pane) - conditionalActivatePane(window, pane, 'Down', 'j') - end) + wezterm.on("ActivatePaneDirection-right", function(window, pane) + conditionalActivatePane(window, pane, "Right", "l") + end) + wezterm.on("ActivatePaneDirection-left", function(window, pane) + conditionalActivatePane(window, pane, "Left", "h") + end) + wezterm.on("ActivatePaneDirection-up", function(window, pane) + conditionalActivatePane(window, pane, "Up", "k") + end) + wezterm.on("ActivatePaneDirection-down", function(window, pane) + conditionalActivatePane(window, pane, "Down", "j") + end) - -- Retrieve the current scrollback text and send to editor - wezterm.on('edit-scrollback', function(window, pane) - local viewport_text = pane:get_lines_as_text(10000) + -- Retrieve the current scrollback text and send to editor + wezterm.on("edit-scrollback", function(window, pane) + local viewport_text = pane:get_lines_as_text(10000) - -- Create a temporary file to pass to vim - local name = os.tmpname() + -- Create a temporary file to pass to vim + local name = os.tmpname() - local f = io.open(name, 'w+') - if f == nil then return false end - f:write(viewport_text) - f:flush() - f:close() + local f = io.open(name, "w+") + if f == nil then + return false + end + f:write(viewport_text) + f:flush() + f:close() - -- Open a new window running vim and tell it to open the file - window:perform_action(act.SpawnCommandInNewTab { - args = { (os.getenv('EDITOR') or 'vi'), name } - }, pane) + -- Open a new window running vim and tell it to open the file + window:perform_action( + act.SpawnCommandInNewTab({ + args = { (os.getenv("EDITOR") or "vi"), name }, + }), + pane + ) - -- Wait time for vim to read the file before we remove it. - wezterm.sleep_ms(1000) - os.remove(name) - end) + -- Wait time for vim to read the file before we remove it. + wezterm.sleep_ms(1000) + os.remove(name) + end) - wezterm.on("toggle-leader", function(window, pane) - wezterm.log_info("toggling the leader") - local overrides = window:get_config_overrides() or {} - if not overrides.leader then - wezterm.log_info("leader wasn't set") - overrides.leader = { key = "s", mods = "SUPER" }; - else - wezterm.log_info("leader was set") - overrides.leader = nil - end + wezterm.on("toggle-leader", function(window, pane) + wezterm.log_info("toggling the leader") + local overrides = window:get_config_overrides() or {} + if not overrides.leader then + wezterm.log_info("leader wasn't set") + overrides.leader = { key = "s", mods = "SUPER" } + else + wezterm.log_info("leader was set") + overrides.leader = nil + end - window:set_config_overrides(overrides) - end) + window:set_config_overrides(overrides) + end) end return { setup = setup } diff --git a/terminal/.config/wezterm/maps.lua b/terminal/.config/wezterm/maps.lua index 5c65624..ff220c1 100644 --- a/terminal/.config/wezterm/maps.lua +++ b/terminal/.config/wezterm/maps.lua @@ -1,127 +1,137 @@ -local wezterm = require('wezterm') +local wezterm = require("wezterm") local act = wezterm.action local keys = { - { key = 'O', mods = 'CTRL', action = act.ShowDebugOverlay }, - - { key = '[', mods = 'CTRL', action = act.ScrollToPrompt(-1) }, - { key = ']', mods = 'CTRL', action = act.ScrollToPrompt(1) }, - { -- vertical pane - key = '\\', - mods = 'LEADER', - action = act.SplitHorizontal { domain = 'CurrentPaneDomain' } - }, { -- horizontal pane - key = '-', - mods = 'LEADER', - action = act.SplitVertical { domain = 'CurrentPaneDomain' } - }, -- pane movement keys - { - key = 'h', - mods = 'CTRL', - action = act.EmitEvent 'ActivatePaneDirection-left' - }, - { - key = 'j', - mods = 'CTRL', - action = act.EmitEvent 'ActivatePaneDirection-down' - }, - { - key = 'k', - mods = 'CTRL', - action = act.EmitEvent 'ActivatePaneDirection-up' - }, { - key = 'l', - mods = 'CTRL', - action = act.EmitEvent 'ActivatePaneDirection-right' - }, { key = 'z', mods = 'LEADER', action = act.TogglePaneZoomState }, - { key = ' ', mods = 'LEADER', action = act.RotatePanes 'Clockwise' }, - { key = 'q', mods = 'LEADER', action = act.PaneSelect { mode = 'Activate' } }, - { - key = 'Q', - mods = 'LEADER', - action = act.PaneSelect { mode = 'SwapWithActive' } - }, { key = 'c', mods = 'LEADER', action = act.SpawnTab 'CurrentPaneDomain' }, - { key = ',', mods = 'LEADER', action = act.MoveTabRelative(-1) }, - { key = '.', mods = 'LEADER', action = act.MoveTabRelative(1) }, -- workspace selection - { - key = 's', - mods = 'LEADER', - action = act.ShowLauncherArgs { flags = 'FUZZY|WORKSPACES' } - }, { key = 't', mods = 'LEADER', action = act.ShowTabNavigator }, - { key = '[', mods = 'LEADER', action = act.ActivateCopyMode }, { - key = 'r', - mods = 'LEADER', - action = act.ActivateKeyTable { - name = 'resize_pane', - one_shot = false, - timeout_milliseconds = 2000, - replace_current = true - } - }, { key = 'f', mods = 'LEADER', action = act.QuickSelect }, { - key = 'F', - mods = 'LEADER', - action = wezterm.action.QuickSelectArgs { - patterns = { "https?://\\S+" }, - action = wezterm.action_callback( - function(window, pane) - local url = window:get_selection_text_for_pane(pane) - wezterm.log_info("opening: " .. url) - wezterm.open_with(url) - end) - } - }, { - key = '/', - mods = 'LEADER', - action = act.Search('CurrentSelectionOrEmptyString') - }, { - key = 'b', - mods = 'LEADER', - action = act.ActivateKeyTable { - name = 'scroll_mode', - one_shot = false, - replace_current = true, - timeout_milliseconds = 15000 - } - }, { key = 'e', mods = 'LEADER', action = act.EmitEvent 'edit-scrollback' }, - { - key = 'l', - mods = 'LEADER', - action = act.EmitEvent 'ActivatePaneDirection-Right' - }, { key = 'a', mods = 'CTRL|ALT', action = act.EmitEvent 'toggle-leader' } + { key = "O", mods = "CTRL", action = act.ShowDebugOverlay }, + { key = "[", mods = "CTRL", action = act.ScrollToPrompt(-1) }, + { key = "]", mods = "CTRL", action = act.ScrollToPrompt(1) }, + { -- vertical pane + key = "\\", + mods = "LEADER", + action = act.SplitHorizontal({ domain = "CurrentPaneDomain" }), + }, + { -- horizontal pane + key = "-", + mods = "LEADER", + action = act.SplitVertical({ domain = "CurrentPaneDomain" }), + }, -- pane movement keys + { + key = "h", + mods = "CTRL", + action = act.EmitEvent("ActivatePaneDirection-left"), + }, + { + key = "j", + mods = "CTRL", + action = act.EmitEvent("ActivatePaneDirection-down"), + }, + { + key = "k", + mods = "CTRL", + action = act.EmitEvent("ActivatePaneDirection-up"), + }, + { + key = "l", + mods = "CTRL", + action = act.EmitEvent("ActivatePaneDirection-right"), + }, + { key = "z", mods = "LEADER", action = act.TogglePaneZoomState }, + { key = " ", mods = "LEADER", action = act.RotatePanes("Clockwise") }, + { key = "q", mods = "LEADER", action = act.PaneSelect({ mode = "Activate" }) }, + { + key = "Q", + mods = "LEADER", + action = act.PaneSelect({ mode = "SwapWithActive" }), + }, + { key = "c", mods = "LEADER", action = act.SpawnTab("CurrentPaneDomain") }, + { key = ",", mods = "LEADER", action = act.MoveTabRelative(-1) }, + { key = ".", mods = "LEADER", action = act.MoveTabRelative(1) }, -- workspace selection + { + key = "s", + mods = "LEADER", + action = act.ShowLauncherArgs({ flags = "FUZZY|WORKSPACES" }), + }, + { key = "t", mods = "LEADER", action = act.ShowTabNavigator }, + { key = "[", mods = "LEADER", action = act.ActivateCopyMode }, + { + key = "r", + mods = "LEADER", + action = act.ActivateKeyTable({ + name = "resize_pane", + one_shot = false, + timeout_milliseconds = 2000, + replace_current = true, + }), + }, + { key = "f", mods = "LEADER", action = act.QuickSelect }, + { + key = "F", + mods = "LEADER", + action = wezterm.action.QuickSelectArgs({ + patterns = { "https?://\\S+" }, + action = wezterm.action_callback(function(window, pane) + local url = window:get_selection_text_for_pane(pane) + wezterm.log_info("opening: " .. url) + wezterm.open_with(url) + end), + }), + }, + { + key = "/", + mods = "LEADER", + action = act.Search("CurrentSelectionOrEmptyString"), + }, + { + key = "b", + mods = "LEADER", + action = act.ActivateKeyTable({ + name = "scroll_mode", + one_shot = false, + replace_current = true, + timeout_milliseconds = 15000, + }), + }, + { key = "e", mods = "LEADER", action = act.EmitEvent("edit-scrollback") }, + { + key = "l", + mods = "LEADER", + action = act.EmitEvent("ActivatePaneDirection-Right"), + }, + { key = "a", mods = "CTRL|ALT", action = act.EmitEvent("toggle-leader") }, } -- Leader + number to activate that tab for i = 1, 8 do - table.insert(keys, { - key = tostring(i), - mods = 'LEADER', - action = act.ActivateTab(i - 1) - }) + table.insert(keys, { + key = tostring(i), + mods = "LEADER", + action = act.ActivateTab(i - 1), + }) end -- key table sub modes local key_tables = { - -- mode to change size of any panes - resize_pane = { - { key = 'h', action = act.AdjustPaneSize { 'Left', 1 } }, - { key = 'l', action = act.AdjustPaneSize { 'Right', 1 } }, - { key = 'k', action = act.AdjustPaneSize { 'Up', 1 } }, - { key = 'j', action = act.AdjustPaneSize { 'Down', 1 } }, - { key = 'H', action = act.AdjustPaneSize { 'Left', 10 } }, - { key = 'L', action = act.AdjustPaneSize { 'Right', 10 } }, - { key = 'K', action = act.AdjustPaneSize { 'Up', 10 } }, - { key = 'J', action = act.AdjustPaneSize { 'Down', 10 } }, - { key = 'Escape', action = 'PopKeyTable' } - }, - scroll_mode = { - { key = 'y', mods = 'CTRL', action = act.ScrollByLine(-1) }, - { key = 'e', mods = 'CTRL', action = act.ScrollByLine(1) }, - { key = 'f', mods = 'CTRL', action = act.ScrollByPage(1) }, - { key = 'b', mods = 'CTRL', action = act.ScrollByPage(-1) }, - { key = 'd', mods = 'CTRL', action = act.ScrollByPage(0.5) }, - { key = 'u', mods = 'CTRL', action = act.ScrollByPage(-0.5) }, - { key = 'g', mods = 'CTRL', action = act.ScrollToTop }, - { key = 'G', mods = 'CTRL', action = act.ScrollToBottom }, - { key = 'Escape', action = 'PopKeyTable' } - } + -- mode to change size of any panes + resize_pane = { + { key = "h", action = act.AdjustPaneSize({ "Left", 1 }) }, + { key = "l", action = act.AdjustPaneSize({ "Right", 1 }) }, + { key = "k", action = act.AdjustPaneSize({ "Up", 1 }) }, + { key = "j", action = act.AdjustPaneSize({ "Down", 1 }) }, + { key = "H", action = act.AdjustPaneSize({ "Left", 10 }) }, + { key = "L", action = act.AdjustPaneSize({ "Right", 10 }) }, + { key = "K", action = act.AdjustPaneSize({ "Up", 10 }) }, + { key = "J", action = act.AdjustPaneSize({ "Down", 10 }) }, + { key = "Escape", action = "PopKeyTable" }, + }, + scroll_mode = { + { key = "y", mods = "CTRL", action = act.ScrollByLine(-1) }, + { key = "e", mods = "CTRL", action = act.ScrollByLine(1) }, + { key = "f", mods = "CTRL", action = act.ScrollByPage(1) }, + { key = "b", mods = "CTRL", action = act.ScrollByPage(-1) }, + { key = "d", mods = "CTRL", action = act.ScrollByPage(0.5) }, + { key = "u", mods = "CTRL", action = act.ScrollByPage(-0.5) }, + { key = "g", mods = "CTRL", action = act.ScrollToTop }, + { key = "G", mods = "CTRL", action = act.ScrollToBottom }, + { key = "Escape", action = "PopKeyTable" }, + }, } return { keys = keys, key_tables = key_tables } diff --git a/terminal/.config/wezterm/statusbar.lua b/terminal/.config/wezterm/statusbar.lua index 020d882..4160779 100644 --- a/terminal/.config/wezterm/statusbar.lua +++ b/terminal/.config/wezterm/statusbar.lua @@ -1,46 +1,47 @@ -local wezterm = require 'wezterm' +local wezterm = require("wezterm") -local function basename(s) return string.gsub(s or '', '(.*[/\\])(.*)', '%2') end +local function basename(s) + return string.gsub(s or "", "(.*[/\\])(.*)", "%2") +end -local SEPARATOR = ' | ' +local SEPARATOR = " | " local function setup() - -- STATUSBAR - -- show currently active key table in lower right status bar - -- mimicing Vim modes - wezterm.on('update-status', function(window, pane) - local displayed = { left = {}, right = {} } - local keytable = window:active_key_table() - if keytable then - displayed.left[#displayed.left + 1] = 'MODE: ' .. keytable - end + -- STATUSBAR + -- show currently active key table in lower right status bar + -- mimicing Vim modes + wezterm.on("update-status", function(window, pane) + local displayed = { left = {}, right = {} } + local keytable = window:active_key_table() + if keytable then + displayed.left[#displayed.left + 1] = "MODE: " .. keytable + end - local workspace = window:active_workspace() - if workspace and workspace ~= 'default' then - displayed.left[#displayed.left + 1] = 'WORKSPACE: ' .. workspace - end + local workspace = window:active_workspace() + if workspace and workspace ~= "default" then + displayed.left[#displayed.left + 1] = "WORKSPACE: " .. workspace + end - local bat = '' - for _, b in ipairs(wezterm.battery_info()) do - bat = '🔋 ' .. string.format('%.0f%%', b.state_of_charge * 100) .. - ' ' .. b.state - end - displayed.right[#displayed.right + 1] = bat + local bat = "" + for _, b in ipairs(wezterm.battery_info()) do + bat = "🔋 " .. string.format("%.0f%%", b.state_of_charge * 100) .. " " .. b.state + end + displayed.right[#displayed.right + 1] = bat - local currentprogram = pane:get_foreground_process_name() - displayed.right[#displayed.right + 1] = basename(currentprogram) + local currentprogram = pane:get_foreground_process_name() + displayed.right[#displayed.right + 1] = basename(currentprogram) - local statusleft = '' - for _, v in ipairs(displayed.left) do - statusleft = statusleft .. v .. SEPARATOR - end - local statusright = '' - for _, v in ipairs(displayed.right) do - statusright = statusright .. v .. SEPARATOR - end + local statusleft = "" + for _, v in ipairs(displayed.left) do + statusleft = statusleft .. v .. SEPARATOR + end + local statusright = "" + for _, v in ipairs(displayed.right) do + statusright = statusright .. v .. SEPARATOR + end - window:set_left_status(statusleft or '') - window:set_right_status(statusright or '') - end) + window:set_left_status(statusleft or "") + window:set_right_status(statusright or "") + end) end return { setup = setup } diff --git a/terminal/.config/wezterm/wezterm.lua b/terminal/.config/wezterm/wezterm.lua index 6ab66c5..57db5b8 100644 --- a/terminal/.config/wezterm/wezterm.lua +++ b/terminal/.config/wezterm/wezterm.lua @@ -1,82 +1,78 @@ -local wezterm = require 'wezterm' +local wezterm = require("wezterm") -local maps = require 'maps' +local maps = require("maps") -require 'statusbar'.setup() -require 'events'.setup() +require("statusbar").setup() +require("events").setup() local function file_exists(name) - local f = io.open(name, "r") - if f ~= nil then - io.close(f) - return true - else - return false - end + local f = io.open(name, "r") + if f ~= nil then + io.close(f) + return true + else + return false + end end -- automatically reload colors file -local colorsfile = (os.getenv('XDG_STATE_HOME') or - (os.getenv('HOME') .. '/.local/state')) .. - '/wezterm/colors.toml' +local colorsfile = (os.getenv("XDG_STATE_HOME") or (os.getenv("HOME") .. "/.local/state")) .. "/wezterm/colors.toml" local colors = {} if file_exists(colorsfile) == true then - wezterm.add_to_config_reload_watch_list(colorsfile) - colors = wezterm.color.load_scheme(colorsfile) + wezterm.add_to_config_reload_watch_list(colorsfile) + colors = wezterm.color.load_scheme(colorsfile) end local settings = { - enable_wayland = true, - hide_tab_bar_if_only_one_tab = true, - use_fancy_tab_bar = false, - tab_bar_at_bottom = true, - window_padding = { left = 0, right = 0, top = 0, bottom = 0 }, - colors = colors, - color_scheme = "Nord (base16)", -- will be overwritten by colors - -- default_prog = {"nu"}, - scrollback_lines = 10000, - font = wezterm.font('Iosevka Nerd Font'), - -- add cursive italic font from Victor font for all weights - font_rules = { - { - intensity = 'Bold', - italic = true, - font = wezterm.font { - family = 'VictorMono Nerd Font', - weight = 'Bold', - style = 'Italic', - }, - }, - { - italic = true, - intensity = 'Half', - font = wezterm.font { - family = 'VictorMono Nerd Font', - weight = 'DemiBold', - style = 'Italic', - }, - }, - { - italic = true, - intensity = 'Normal', - font = wezterm.font { - family = 'VictorMono Nerd Font', - style = 'Italic', - }, - }, - }, - line_height = 1.0, - leader = { key = 'a', mods = 'CTRL', timeout_milliseconds = 1500 }, - keys = maps.keys, - key_tables = maps.key_tables, - mouse_bindings = { - { - event = { Up = { streak = 1, button = 'Left' } }, - mods = 'NONE', - action = wezterm.action - .CompleteSelectionOrOpenLinkAtMouseCursor - 'ClipboardAndPrimarySelection' - } - } + enable_wayland = true, + hide_tab_bar_if_only_one_tab = true, + use_fancy_tab_bar = false, + tab_bar_at_bottom = true, + window_padding = { left = 0, right = 0, top = 0, bottom = 0 }, + colors = colors, + color_scheme = "Nord (base16)", -- will be overwritten by colors + -- default_prog = {"nu"}, + scrollback_lines = 10000, + font = wezterm.font("Iosevka Nerd Font"), + -- add cursive italic font from Victor font for all weights + font_rules = { + { + intensity = "Bold", + italic = true, + font = wezterm.font({ + family = "VictorMono Nerd Font", + weight = "Bold", + style = "Italic", + }), + }, + { + italic = true, + intensity = "Half", + font = wezterm.font({ + family = "VictorMono Nerd Font", + weight = "DemiBold", + style = "Italic", + }), + }, + { + italic = true, + intensity = "Normal", + font = wezterm.font({ + family = "VictorMono Nerd Font", + style = "Italic", + }), + }, + }, + line_height = 1.0, + leader = { key = "a", mods = "CTRL", timeout_milliseconds = 1500 }, + keys = maps.keys, + key_tables = maps.key_tables, + mouse_bindings = { + { + event = { Up = { streak = 1, button = "Left" } }, + mods = "NONE", + action = wezterm.action.CompleteSelectionOrOpenLinkAtMouseCursor("ClipboardAndPrimarySelection"), + }, + }, } return settings