From a7d54a2a26645000237e9bbaa23708766da641c1 Mon Sep 17 00:00:00 2001 From: DarkMatter-999 Date: Sun, 27 Aug 2023 10:40:30 +0530 Subject: [PATCH 01/22] Added support for MINGW neovim install on Windows hosts --- lua/presence/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/presence/init.lua b/lua/presence/init.lua index 1d2aefe..c3b31c1 100644 --- a/lua/presence/init.lua +++ b/lua/presence/init.lua @@ -181,7 +181,7 @@ end -- Normalize the OS name from uname function Presence.get_os_name(uname) - if uname.sysname:find("Windows") then + if uname.sysname:find("Windows") or uname.sysname:find("MINGW") then return "windows" elseif uname.sysname:find("Darwin") then return "macos" From a451c6b3092c7ef01bf9c42b019bb33877ff3fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Mon, 25 Sep 2023 21:12:21 +0200 Subject: [PATCH 02/22] feat(Flatpak): Add flatpak socket path Look for the Flatpak socket path before normal socket lookup Properly fixes andweeb/presence.nvim#42 --- lua/presence/init.lua | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/lua/presence/init.lua b/lua/presence/init.lua index 1d2aefe..5018ab8 100644 --- a/lua/presence/init.lua +++ b/lua/presence/init.lua @@ -391,13 +391,24 @@ function Presence:get_discord_socket_path() "TMPDIR", } - for i = 1, #env_vars do - local var = env_vars[i] - local path = os.getenv(var) - if path then - self.log:debug(string.format("Using runtime path: %s", path)) - sock_path = path:match("/$") and path..sock_name or path.."/"..sock_name - break + local xdg_path = os.getenv("XDG_RUNTIME_DIR") + if xdg_path then + -- Append app/com.discordapp.Discord/ to the end of the path (make sure that / is at the end of xdg_path before appending) + xdg_path = xdg_path and xdg_path:match("/$") and xdg_path.."app/com.discordapp.Discord" or xdg_path.."/app/com.discordapp.Discord" + self.log:debug(string.format("Using XDG runtime path: %s", xdg_path)) + sock_path = xdg_path:match("/$") and xdg_path..sock_name or xdg_path.."/"..sock_name + end + + -- If the socket path is still nil, check other temp directories + if not sock_path then + for i = 1, #env_vars do + local var = env_vars[i] + local path = os.getenv(var) + if path then + self.log:debug(string.format("Using runtime path: %s", path)) + sock_path = path:match("/$") and path..sock_name or path.."/"..sock_name + break + end end end end From ff8442c16844bf3299d39cf5d9faf19c6cad455e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Mon, 25 Sep 2023 21:17:30 +0200 Subject: [PATCH 03/22] fix(README): delete obsolete link to fix Flatpak --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index c0e409e..565d916 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ Use your favorite plugin manager #### Notes * Requires [Neovim 0.5](https://github.com/neovim/neovim/releases/tag/v0.5.0) or higher * Rich Presence should work automatically after installation (unless you're using WSL, in which case [see here](https://github.com/andweeb/presence.nvim/wiki/Rich-Presence-in-WSL)) -* If you're using an unofficial Discord package on Linux ([flatpak](https://flathub.org/apps/details/com.discordapp.Discord), [snap](https://snapcraft.io/discord), etc.), you may need to follow some instructions to expose the Discord socket on your system (e.g. [flatpak instructions](https://github.com/flathub/com.discordapp.Discord/wiki/Rich-Precense-(discord-rpc))) ## Configuration Configuration is not necessary for Rich Presence to work. But for those that want to override the default configs, the following options are available to configure in either Lua or VimL. From 0601dd513ee2a1f0286245a6843b9b64cea5af87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Mon, 25 Sep 2023 22:11:48 +0200 Subject: [PATCH 04/22] fix(Flatpak): clear sock_path if the flatpak socket wasn't found --- lua/presence/init.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lua/presence/init.lua b/lua/presence/init.lua index 5018ab8..d3ddd70 100644 --- a/lua/presence/init.lua +++ b/lua/presence/init.lua @@ -397,6 +397,8 @@ function Presence:get_discord_socket_path() xdg_path = xdg_path and xdg_path:match("/$") and xdg_path.."app/com.discordapp.Discord" or xdg_path.."/app/com.discordapp.Discord" self.log:debug(string.format("Using XDG runtime path: %s", xdg_path)) sock_path = xdg_path:match("/$") and xdg_path..sock_name or xdg_path.."/"..sock_name + -- Check if the socket path exists and if not set it to nil + sock_path = vim.fn.filereadable(sock_path) == 1 and sock_path or nil end -- If the socket path is still nil, check other temp directories From 098de1e6301f6967edb210024755847c744f2e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Thu, 28 Sep 2023 21:57:22 +0200 Subject: [PATCH 05/22] fix(Luacheck) --- .github/workflows/CI.yml | 14 -------------- .github/workflows/luacheck.yml | 10 ++++++++++ 2 files changed, 10 insertions(+), 14 deletions(-) delete mode 100644 .github/workflows/CI.yml create mode 100644 .github/workflows/luacheck.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml deleted file mode 100644 index 2bec080..0000000 --- a/.github/workflows/CI.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: CI - -on: - push: - pull_request: - -jobs: - check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: nebularg/actions-luacheck@v1 - with: - files: 'lua' diff --git a/.github/workflows/luacheck.yml b/.github/workflows/luacheck.yml new file mode 100644 index 0000000..571609c --- /dev/null +++ b/.github/workflows/luacheck.yml @@ -0,0 +1,10 @@ +name: Luacheck +on: [push, pull_request] +jobs: + sile: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Luacheck linter + uses: lunarmodules/luacheck@v1 From cf7d940395b87f541e436b6e9fbc5a418a86fdfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Thu, 28 Sep 2023 21:59:46 +0200 Subject: [PATCH 06/22] fix(formatting): line too long --- lua/presence/init.lua | 147 ++++++++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 64 deletions(-) diff --git a/lua/presence/init.lua b/lua/presence/init.lua index d3ddd70..6e68ba2 100644 --- a/lua/presence/init.lua +++ b/lua/presence/init.lua @@ -72,7 +72,7 @@ function Presence:setup(...) -- Support setup invocation via both dot and colon syntax. -- To maintain backwards compatibility, colon syntax will still -- be supported, but dot syntax should be recommended. - local args = {...} + local args = { ... } local options = args[1] if #args == 0 then options = self @@ -89,7 +89,7 @@ function Presence:setup(...) -- Get operating system information including path separator -- http://www.lua.org/manual/5.3/manual.html#pdf-package.config local uname = vim.loop.os_uname() - local separator = package.config:sub(1,1) + local separator = package.config:sub(1, 1) local wsl_distro_name = os.getenv("WSL_DISTRO_NAME") local os_name = self.get_os_name(uname) self.os = { @@ -102,7 +102,7 @@ function Presence:setup(...) local setup_message_fmt = "Setting up plugin for %s" if self.os.name then local setup_message = self.os.is_wsl - and string.format(setup_message_fmt.." in WSL (%s)", self.os.name, vim.inspect(wsl_distro_name)) + and string.format(setup_message_fmt .. " in WSL (%s)", self.os.name, vim.inspect(wsl_distro_name)) or string.format(setup_message_fmt, self.os.name) self.log:debug(setup_message) else @@ -158,7 +158,9 @@ function Presence:setup(...) -- Seed instance id using unique socket path local seed_nums = {} - self.socket:gsub(".", function(c) table.insert(seed_nums, c:byte()) end) + self.socket:gsub(".", function(c) + table.insert(seed_nums, c:byte()) + end) self.id = self.discord.generate_uuid(tonumber(table.concat(seed_nums)) / os.clock()) self.log:debug(string.format("Using id %s", self.id)) @@ -215,9 +217,7 @@ function Presence:set_option(option, default, validate) self:check_dup_options(option) end - self.options[option] = self.options[option] or - vim.g[g_variable] or - default + self.options[option] = self.options[option] or vim.g[g_variable] or default end -- Check and warn for duplicate user-defined options @@ -321,17 +321,19 @@ function Presence:connect(on_done) if err == "EISCONN" then self.log:info("Already connected to Discord") elseif err == "ECONNREFUSED" then - self.log:warn("Failed to connect to Discord: "..err.." (is Discord running?)") + self.log:warn("Failed to connect to Discord: " .. err .. " (is Discord running?)") return elseif err then - self.log:error("Failed to connect to Discord: "..err) + self.log:error("Failed to connect to Discord: " .. err) return end self.log:info("Connected to Discord") self.is_connected = true - if on_done then on_done() end + if on_done then + on_done() + end end) end @@ -350,7 +352,7 @@ function Presence:authorize(on_done) self.is_authorized = true return on_done() elseif err then - self.log:error("Failed to authorize with Discord: "..err) + self.log:error("Failed to authorize with Discord: " .. err) self.is_authorized = false return end @@ -358,7 +360,9 @@ function Presence:authorize(on_done) self.log:info(string.format("Authorized with Discord for %s", response.data.user.username)) self.is_authorized = true - if on_done then on_done() end + if on_done then + on_done() + end end) end @@ -369,18 +373,16 @@ function Presence:get_discord_socket_path() if self.os.is_wsl then -- Use socket created by relay for WSL - sock_path = "/var/run/"..sock_name + sock_path = "/var/run/" .. sock_name elseif self.os.name == "windows" then -- Use named pipe in NPFS for Windows - sock_path = [[\\.\pipe\]]..sock_name + sock_path = [[\\.\pipe\]] .. sock_name elseif self.os.name == "macos" then -- Use $TMPDIR for macOS local path = os.getenv("TMPDIR") if path then - sock_path = path:match("/$") - and path..sock_name - or path.."/"..sock_name + sock_path = path:match("/$") and path .. sock_name or path .. "/" .. sock_name end elseif self.os.name == "linux" then -- Check various temp directory environment variables @@ -393,10 +395,12 @@ function Presence:get_discord_socket_path() local xdg_path = os.getenv("XDG_RUNTIME_DIR") if xdg_path then - -- Append app/com.discordapp.Discord/ to the end of the path (make sure that / is at the end of xdg_path before appending) - xdg_path = xdg_path and xdg_path:match("/$") and xdg_path.."app/com.discordapp.Discord" or xdg_path.."/app/com.discordapp.Discord" + -- Append app/com.discordapp.Discord/ to the end of the path (make sure that / is at the end of xdg_path + -- before appending) + xdg_path = xdg_path and xdg_path:match("/$") and xdg_path .. "app/com.discordapp.Discord" + or xdg_path .. "/app/com.discordapp.Discord" self.log:debug(string.format("Using XDG runtime path: %s", xdg_path)) - sock_path = xdg_path:match("/$") and xdg_path..sock_name or xdg_path.."/"..sock_name + sock_path = xdg_path:match("/$") and xdg_path .. sock_name or xdg_path .. "/" .. sock_name -- Check if the socket path exists and if not set it to nil sock_path = vim.fn.filereadable(sock_path) == 1 and sock_path or nil end @@ -408,7 +412,7 @@ function Presence:get_discord_socket_path() local path = os.getenv(var) if path then self.log:debug(string.format("Using runtime path: %s", path)) - sock_path = path:match("/$") and path..sock_name or path.."/"..sock_name + sock_path = path:match("/$") and path .. sock_name or path .. "/" .. sock_name break end end @@ -436,9 +440,7 @@ function Presence:get_project_name(file_path) -- TODO: Only checks for a git repository, could add more checks here -- Might want to run this in a background process depending on performance local project_path_cmd = "git rev-parse --show-toplevel" - project_path_cmd = file_path - and string.format([[cd "%s" && %s]], file_path, project_path_cmd) - or project_path_cmd + project_path_cmd = file_path and string.format([[cd "%s" && %s]], file_path, project_path_cmd) or project_path_cmd local project_path = vim.fn.system(project_path_cmd) project_path = vim.trim(project_path) @@ -489,8 +491,8 @@ end -- Get the status text for the current buffer function Presence:get_status_text(filename) - local file_explorer = file_explorers[vim.bo.filetype:match "[^%d]+"] - or file_explorers[(filename or ""):match "[^%d]+"] + local file_explorer = file_explorers[vim.bo.filetype:match("[^%d]+")] + or file_explorers[(filename or ""):match("[^%d]+")] local plugin_manager = plugin_managers[vim.bo.filetype] if file_explorer then @@ -499,7 +501,9 @@ function Presence:get_status_text(filename) return self:format_status_text("plugin_manager", plugin_manager) end - if not filename or filename == "" then return nil end + if not filename or filename == "" then + return nil + end if vim.bo.modifiable and not vim.bo.readonly then if vim.bo.filetype == "gitcommit" then @@ -586,7 +590,9 @@ function Presence:get_nvim_socket_paths(on_done) end local function handle_data(_, data) - if not data then return end + if not data then + return + end for i = 1, #data do local socket = parser.parse and parser.parse(vim.trim(data[i])) or vim.trim(data[i]) @@ -597,7 +603,9 @@ function Presence:get_nvim_socket_paths(on_done) end local function handle_error(_, data) - if not data then return end + if not data then + return + end if data[1] ~= "" then self.log:error(string.format("Unable to get nvim socket paths: %s", data[1])) @@ -626,7 +634,7 @@ function Presence.discord_event(on_ready) return end - local args = {...} + local args = { ... } local callback = function() on_ready(self, unpack(args)) end @@ -680,22 +688,24 @@ function Presence:check_blacklist(buffer, parent_dirpath, project_dirpath) -- Loop over the values to see if the provided project/path is in the blacklist for _, val in pairs(blacklist_table) do -- Matches buffer exactly - if buffer:match(val) == buffer then return true end + if buffer:match(val) == buffer then + return true + end -- Match parent either by Lua pattern or by plain string - local is_parent_directory_blacklisted = parent_dirpath and - ((parent_dirpath:match(val) == parent_dirpath or - parent_dirname:match(val) == parent_dirname) or - (parent_dirpath:find(val, nil, true) or - parent_dirname:find(val, nil, true))) + local is_parent_directory_blacklisted = parent_dirpath + and ( + (parent_dirpath:match(val) == parent_dirpath or parent_dirname:match(val) == parent_dirname) + or (parent_dirpath:find(val, nil, true) or parent_dirname:find(val, nil, true)) + ) if is_parent_directory_blacklisted then return true end -- Match project either by Lua pattern or by plain string - local is_project_directory_blacklisted = project_dirpath and - ((project_dirpath:match(val) == project_dirpath or - project_dirname:match(val) == project_dirname) or - (project_dirpath:find(val, nil, true) or - project_dirname:find(val, nil, true))) + local is_project_directory_blacklisted = project_dirpath + and ( + (project_dirpath:match(val) == project_dirpath or project_dirname:match(val) == project_dirname) + or (project_dirpath:find(val, nil, true) or project_dirname:find(val, nil, true)) + ) if is_project_directory_blacklisted then return true end @@ -721,9 +731,7 @@ function Presence:get_buttons(buffer, parent_dirpath) -- Escape quotes in the file path local path = parent_dirpath:gsub([["]], [[\"]]) local git_url_cmd = "git config --get remote.origin.url" - local cmd = path - and string.format([[cd "%s" && %s]], path, git_url_cmd) - or git_url_cmd + local cmd = path and string.format([[cd "%s" && %s]], path, git_url_cmd) or git_url_cmd -- Trim and coerce empty string value to null repo_url = vim.trim(vim.fn.system(cmd)) @@ -738,7 +746,6 @@ function Presence:get_buttons(buffer, parent_dirpath) -- Default behavior to show a "View Repository" button if the repo URL is valid if repo_url then - -- Check if repo url uses short ssh syntax local domain, project = repo_url:match("^git@(.+):(.+)$") if domain and project then @@ -894,9 +901,11 @@ function Presence:update_for_buffer(buffer, should_debounce) if self.workspaces[project_path] then self.workspaces[project_path].updated_at = activity_set_at - activity.timestamps = self.options.show_time == 1 and { - start = self.workspaces[project_path].started_at, - } or nil + activity.timestamps = self.options.show_time == 1 + and { + start = self.workspaces[project_path].started_at, + } + or nil else self.workspaces[project_path] = { started_at = activity_set_at, @@ -949,16 +958,18 @@ end -- Update Rich Presence for the current or provided vim buffer for an authorized connection Presence.update = Presence.discord_event(function(self, buffer, should_debounce) -- Default update to not debounce by default - if should_debounce == nil then should_debounce = false end + if should_debounce == nil then + should_debounce = false + end -- Debounce Rich Presence updates (default to 10 seconds): -- https://discord.com/developers/docs/rich-presence/how-to#updating-presence local last_updated_at = self.last_activity.set_at local debounce_timeout = self.options.debounce_timeout - local should_skip = - should_debounce and - debounce_timeout and - last_updated_at and os.time() - last_updated_at <= debounce_timeout + local should_skip = should_debounce + and debounce_timeout + and last_updated_at + and os.time() - last_updated_at <= debounce_timeout if should_skip then local message_fmt = "Last activity sent was within %d seconds ago, skipping..." @@ -1047,7 +1058,7 @@ function Presence:register_and_sync_peer(id, socket) [self.id] = { socket = self.socket, workspace = self.workspace, - } + }, } for peer_id, peer in pairs(self.peers) do if peer_id ~= id then @@ -1055,11 +1066,15 @@ function Presence:register_and_sync_peer(id, socket) end end - self:call_remote_method(socket, "sync_self", {{ - last_activity = self.last_activity, - peers = peers, - workspaces = self.workspaces, - }}) + self:call_remote_method( + socket, + "sync_self", + { { + last_activity = self.last_activity, + peers = peers, + workspaces = self.workspaces, + } } + ) end -- Register self to any remote Neovim instances @@ -1137,11 +1152,15 @@ function Presence:sync_self_activity() end end - self:call_remote_method(peer.socket, "sync_peer_activity", {{ - last_activity = self.last_activity, - peers = peers, - workspaces = self.workspaces, - }}) + self:call_remote_method( + peer.socket, + "sync_peer_activity", + { { + last_activity = self.last_activity, + peers = peers, + workspaces = self.workspaces, + } } + ) end end From bd122273b1517afe09ce74ffe23400c4ed756bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Thu, 28 Sep 2023 22:19:36 +0200 Subject: [PATCH 07/22] docs(README): Update README for my repo and fix formatting --- README.md | 97 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 565d916..c985040 100644 --- a/README.md +++ b/README.md @@ -7,27 +7,51 @@ demo.gif ## Features -* Light and unobtrusive -* No Python/Node providers (or CoC) required -* Cross-platform support: macOS, nixOS, Linux[\*](#notes), Windows[\*](https://github.com/andweeb/presence.nvim/projects/1#card-60537963), WSL[\*](https://github.com/andweeb/presence.nvim/wiki/Rich-Presence-in-WSL) -* Startup time is fast(er than other Rich Presence plugins, by [kind of a lot](https://github.com/andweeb/presence.nvim/wiki/Plugin-Comparisons)) -* Written in Lua and [highly configurable](#configuration) in Lua (but also configurable in VimL if you want) -* Manages Rich Presence across multiple Neovim instances in various environments (tmux panes/windows, ssh sessions, terminal tabs/windows, etc.) + +- Light and unobtrusive +- No Python/Node providers (or CoC) required +- Cross-platform support: macOS, nixOS, Linux[\*](#notes), +Windows, WSL +- Startup time is fast(er than other Rich Presence plugins, by [kind of a lot](https://github.com/andweeb/presence.nvim/wiki/Plugin-Comparisons)) +- Written in Lua and [highly configurable](#configuration) in Lua +(but also configurable in VimL if you want) +- Manages Rich Presence across multiple Neovim instances in various environments +(tmux panes/windows, ssh sessions, terminal tabs/windows, etc.) +- Now with Flatpak support! ## Installation + Use your favorite plugin manager -* [vim-plug](https://github.com/junegunn/vim-plug): `Plug 'andweeb/presence.nvim'` -* [packer.nvim](https://github.com/wbthomason/packer.nvim): `use 'andweeb/presence.nvim'` -#### Notes -* Requires [Neovim 0.5](https://github.com/neovim/neovim/releases/tag/v0.5.0) or higher -* Rich Presence should work automatically after installation (unless you're using WSL, in which case [see here](https://github.com/andweeb/presence.nvim/wiki/Rich-Presence-in-WSL)) +- [vim-plug](https://github.com/junegunn/vim-plug): `Plug 'jiriks74/presence.nvim'` +- [packer.nvim](https://github.com/wbthomason/packer.nvim): `use 'jiriks74/presence.nvim'` +- [lazy.nvim](https://github.com/folke/lazy.nvim): + +```lua +{ + "jiriks74/presence.nvim", + event = "UIEnter", +}, +``` + + +### Notes + +- Requires [Neovim 0.5](https://github.com/neovim/neovim/releases/tag/v0.5.0) +or higher +- Rich Presence should work automatically after installation +(unless you're using WSL, in which case [see here](https://github.com/andweeb/presence.nvim/wiki/Rich-Presence-in-WSL)) ## Configuration -Configuration is not necessary for Rich Presence to work. But for those that want to override the default configs, the following options are available to configure in either Lua or VimL. + +Configuration is not necesary unless you want to override the default config. + +If you want to change the default config here are your options in Lua and VimL: ### Lua -Require the plugin and call `setup` with a config table with one or more of the following keys: + +Require the plugin and call `setup` with a config table with one or more of the +following keys: ```lua -- The setup config table shows all available config options with their default values: @@ -57,7 +81,9 @@ require("presence").setup({ ``` ### VimL + Or if global variables are more your thing, you can use any of the following instead: + ```viml " General options let g:presence_auto_update = 1 @@ -83,23 +109,38 @@ let g:presence_line_number_text = "Line %s out of %s" ``` ## Troubleshooting -* Ensure that Discord is running -* Ensure that your Neovim version is 0.5 or higher -* Ensure Game Activity is enabled in your Discord settings -* Enable logging and inspect the logs after opening a buffer - * Set the [`log_level`](#lua) setup option or [`g:presence_log_level`](#viml) to `"debug"` - * Load a file and inspect the logs with `:messages` -* If there is a `Failed to determine Discord IPC socket` error, your particular OS may not yet be supported - * If you don't see an existing [issue](https://github.com/andweeb/presence.nvim/issues) or [card](https://github.com/andweeb/presence.nvim/projects/1#column-14183588) for your OS, create a prefixed [issue](https://github.com/andweeb/presence.nvim/issues/new) (e.g. `[Void Linux]`) -* Still not working and need help? Create a new [issue](https://github.com/andweeb/presence.nvim/issues)! + +- Ensure that Discord is running +- Ensure that your Neovim version is 0.5 or higher +- Ensure Game Activity is enabled in your Discord settings +- Enable logging and inspect the logs after opening a buffer + - Set the [`log_level`](#lua) setup option or [`g:presence_log_level`](#viml) + to `"debug"` + - Load a file and inspect the logs with `:messages` +- If there is a `Failed to determine Discord IPC socket` error, your particular +OS may not yet be supported + - If you don't see an existing [issue](https://github.com/jiriks74/presence.nvim/issues) + or [card](https://github.com/jiriks74/presence.nvim/projects/1#column-14183588) + for your OS, create a prefixed [issue](https://github.com/jiriks74/presence.nvim/issues/new) + (e.g. `[Void Linux]`) +- Still not working and need help? Create a new [issue](https://github.com/jiriks74/presence.nvim/issues)! ## Development -* Clone the repo: `git clone https://github.com/andweeb/presence.nvim.git` -* Enable [logging](#configuration) and ensure that `presence.nvim` is **_not_** in the list of vim plugins in your config -* Run `nvim` with your local changes: `nvim --cmd 'set rtp+=path/to/your/local/presence.nvim' file.txt` -* Ensure that there are no [luacheck](https://github.com/mpeterv/luacheck/) errors: `luacheck lua` + +- Clone the repo: `git clone https://github.com/jiriks74/presence.nvim.git` +- Enable [logging](#configuration) and ensure that `presence.nvim` is **_not_** +in the list of vim plugins in your config +- Run `nvim` with your local changes: `nvim --cmd +'set rtp+=path/to/your/local/presence.nvim' file.txt` +- Ensure that there are no [luacheck](https://github.com/mpeterv/luacheck/) +errors: `luacheck lua` ## Contributing -Pull requests are very welcome, feel free to open an issue to work on any of the open [todo items](https://github.com/andweeb/presence.nvim/projects/1?add_cards_query=is%3Aopen) or message [droob#1322](https://discordapp.com/users/241953146232897550) on Discord! -Asset additions and changes are also welcome! Supported file types can be found in [`file_assets.lua`](lua/presence/file_assets.lua) and their referenced asset files can be found [in this folder](https://www.dropbox.com/sh/j8913f0gav3toeh/AADxjn0NuTprGFtv3Il1Pqz-a?dl=0). +Pull requests are very welcome, feel free to open an issue to work on +or message [@jiriks74](https://discordapp.com/users/517810049360461837) on my +[Discord server](https://discord.gg/cCq3qcB4jB)! + +Asset additions and changes are also welcome! Supported file types can be found in +[`file_assets.lua`](lua/presence/file_assets.lua) and their referenced asset files +can be found [in this folder](https://www.dropbox.com/sh/j8913f0gav3toeh/AADxjn0NuTprGFtv3Il1Pqz-a?dl=0). From 56026a476f63aad2eba3a03aa63229cefabcb230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Thu, 28 Sep 2023 22:34:52 +0200 Subject: [PATCH 08/22] docs: Add conventional Commits, clarify discord username --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c985040..2744b6c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits&logoColor=white)](https://conventionalcommits.org) + presence.nvim​ **[Features](#features)** | **[Installation](#installation)** | **[Configuration](#configuration)** | **[Troubleshooting](#troubleshooting)** | **[Development](#development)** | **[Contributing](#contributing)** @@ -137,8 +139,11 @@ errors: `luacheck lua` ## Contributing +**Please use [Conventional Commits](https://www.conventionalcommits.org/) if you want to contribute. +It makes everyones jobs easier.** + Pull requests are very welcome, feel free to open an issue to work on -or message [@jiriks74](https://discordapp.com/users/517810049360461837) on my +or message [me (@jiriks74)](https://discordapp.com/users/517810049360461837) on my [Discord server](https://discord.gg/cCq3qcB4jB)! Asset additions and changes are also welcome! Supported file types can be found in From 85180bcc85c425119f5cdd463326cd6ba58baa26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Fri, 29 Sep 2023 00:56:29 +0200 Subject: [PATCH 09/22] feat: custom image (#6) * allow custom main image * undo for general option * fix warning: line is too long --------- Co-authored-by: SIGMazer --- lua/presence/init.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lua/presence/init.lua b/lua/presence/init.lua index e8a3d53..590eec0 100644 --- a/lua/presence/init.lua +++ b/lua/presence/init.lua @@ -118,8 +118,8 @@ function Presence:setup(...) self:set_option("auto_update", 1) self:set_option("client_id", "793271441293967371") self:set_option("debounce_timeout", 10) - self:set_option("main_image", "neovim") self:set_option("neovim_image_text", "The One True Text Editor") + self:set_option("main_image", "neovim") self:set_option("enable_line_number", false) -- Status text options self:set_option("editing_text", "Editing %s") @@ -841,8 +841,10 @@ function Presence:update_for_buffer(buffer, should_debounce) local file_text = description or name local neovim_image_text = self.options.neovim_image_text local use_file_as_main_image = self.options.main_image == "file" + local use_neovim_as_main_image = self.options.main_image == "neovim" local assets = { - large_image = use_file_as_main_image and asset_key or "neovim", + large_image = use_file_as_main_image and asset_key or use_neovim_as_main_image + and "neovim" or self.options.main_image, large_text = use_file_as_main_image and file_text or neovim_image_text, small_image = use_file_as_main_image and "neovim" or asset_key, small_text = use_file_as_main_image and neovim_image_text or file_text, From 2d57aa297b05489783c5ddb7452d074a67654748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Fri, 29 Sep 2023 01:34:08 +0200 Subject: [PATCH 10/22] fix(Presence:authorize): attempt to index local 'response' (a nil value) Co-authored-by: Taken --- lua/presence/init.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lua/presence/init.lua b/lua/presence/init.lua index 590eec0..d3dddac 100644 --- a/lua/presence/init.lua +++ b/lua/presence/init.lua @@ -357,7 +357,12 @@ function Presence:authorize(on_done) return end - self.log:info(string.format("Authorized with Discord for %s", response.data.user.username)) + if not response then + self.log:info(string.format("Authorized with Discord for %s", "Response was nil")) + else + self.log:info(string.format("Authorized with Discord for %s", response.data.user.username)) + end + self.is_authorized = true if on_done then From fb129b1de74cf651ade037e4d80914b201ee2239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Fri, 29 Sep 2023 01:57:42 +0200 Subject: [PATCH 11/22] feat(ci/cd): add stylua action (#12) * feat(ci/cd): add stylua action * docs: Add StyLua disclaimer to README * fix(ci/cd-stylua): Secret started with GITHUB_ * fix(ci/cd-stylua): Missing name, on, jobs --- .github/workflows/stylua.yml | 11 +++++++++++ README.md | 2 ++ 2 files changed, 13 insertions(+) create mode 100644 .github/workflows/stylua.yml diff --git a/.github/workflows/stylua.yml b/.github/workflows/stylua.yml new file mode 100644 index 0000000..f1dd81d --- /dev/null +++ b/.github/workflows/stylua.yml @@ -0,0 +1,11 @@ +name: StyLua +on: [push, pull_request] +jobs: + StyLua check: + - uses: actions/checkout@v3 + - uses: JohnnyMorganz/stylua-action@v3 + with: + token: ${{ secrets.GH_TOKEN }} + version: v0.8.2 # NOTE: we recommend pinning to a specific version in case of formatting changes + # CLI arguments + args: --check . diff --git a/README.md b/README.md index 2744b6c..fe70930 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,8 @@ errors: `luacheck lua` **Please use [Conventional Commits](https://www.conventionalcommits.org/) if you want to contribute. It makes everyones jobs easier.** +**This project uses [StyLua](https://github.com/JohnnyMorganz/StyLua). Please format your code using StyLua for better readability** + Pull requests are very welcome, feel free to open an issue to work on or message [me (@jiriks74)](https://discordapp.com/users/517810049360461837) on my [Discord server](https://discord.gg/cCq3qcB4jB)! From 804f14842ff04756a586479fa1e4aa783aab671c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Fri, 29 Sep 2023 02:03:51 +0200 Subject: [PATCH 12/22] fix(ci/cd-luacheck): Rename job from sile to Luacheck (#13) --- .github/workflows/luacheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/luacheck.yml b/.github/workflows/luacheck.yml index 571609c..87145d9 100644 --- a/.github/workflows/luacheck.yml +++ b/.github/workflows/luacheck.yml @@ -1,7 +1,7 @@ name: Luacheck on: [push, pull_request] jobs: - sile: + Luacheck: runs-on: ubuntu-latest steps: - name: Checkout From e9db37bcfac6a678ddb722960f2191126fec8490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Fri, 29 Sep 2023 02:08:42 +0200 Subject: [PATCH 13/22] fix(ci/cd-stylua): Missing runs on, steps, names (#14) * fix(ci/cd-luacheck): Rename job from sile to Luacheck * fix(ci/cd-stylua): Missing runs on, steps, names --- .github/workflows/stylua.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/stylua.yml b/.github/workflows/stylua.yml index f1dd81d..8c9c71e 100644 --- a/.github/workflows/stylua.yml +++ b/.github/workflows/stylua.yml @@ -2,10 +2,14 @@ name: StyLua on: [push, pull_request] jobs: StyLua check: - - uses: actions/checkout@v3 - - uses: JohnnyMorganz/stylua-action@v3 - with: - token: ${{ secrets.GH_TOKEN }} - version: v0.8.2 # NOTE: we recommend pinning to a specific version in case of formatting changes - # CLI arguments - args: --check . + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: StyLua check + uses: JohnnyMorganz/stylua-action@v3 + with: + token: ${{ secrets.GH_TOKEN }} + version: v0.8.2 # NOTE: we recommend pinning to a specific version in case of formatting changes + # CLI arguments + args: --check . From 9e478c9960a722170beaa21e4b650a5672014619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Fri, 29 Sep 2023 02:14:06 +0200 Subject: [PATCH 14/22] fix(ci/cd-stylua): cannot contain spaces, invalid StyLua version (#15) * fix(ci/cd-stylua): cannot contain spaces * fix(ci/cs-stylua): invalid version number --- .github/workflows/stylua.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stylua.yml b/.github/workflows/stylua.yml index 8c9c71e..639c7e1 100644 --- a/.github/workflows/stylua.yml +++ b/.github/workflows/stylua.yml @@ -1,7 +1,7 @@ name: StyLua on: [push, pull_request] jobs: - StyLua check: + StyLuacheck: runs-on: ubuntu-latest steps: - name: Checkout @@ -10,6 +10,6 @@ jobs: uses: JohnnyMorganz/stylua-action@v3 with: token: ${{ secrets.GH_TOKEN }} - version: v0.8.2 # NOTE: we recommend pinning to a specific version in case of formatting changes + version: v0.18.2 # NOTE: we recommend pinning to a specific version in case of formatting changes # CLI arguments args: --check . From c295e1e14ff3628403f7dc64311ac69d5acfafef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Fri, 29 Sep 2023 02:14:33 +0200 Subject: [PATCH 15/22] fix(formatting): Fix formatting using stylua (#11) --- lua/deps/msgpack.lua | 714 ++++++----- lua/deps/serpent.lua | 383 ++++-- lua/deps/struct.lua | 339 +++-- lua/lib/log.lua | 40 +- lua/presence/discord.lua | 263 ++-- lua/presence/file_assets.lua | 524 ++++---- lua/presence/file_explorers.lua | 14 +- lua/presence/init.lua | 2029 +++++++++++++++--------------- lua/presence/plugin_managers.lua | 4 +- 9 files changed, 2238 insertions(+), 2072 deletions(-) diff --git a/lua/deps/msgpack.lua b/lua/deps/msgpack.lua index a4b928b..d7ccfdd 100644 --- a/lua/deps/msgpack.lua +++ b/lua/deps/msgpack.lua @@ -9,120 +9,140 @@ local double_encode_count = 0 -- cache bitops local band, rshift = luabit.band, luabit.brshift if not rshift then -- luajit differ from luabit - rshift = luabit.rshift + rshift = luabit.rshift end -local function byte_mod(x,v) - if x < 0 then - x = x + 256 - end - return (x%v) +local function byte_mod(x, v) + if x < 0 then + x = x + 256 + end + return (x % v) end - -- buffer local strbuf = "" -- for unpacking local strary = {} -- for packing -local function strary_append_int16(n,h) - if n < 0 then - n = n + 65536 - end - table.insert( strary, tostr(h, math.floor(n / 256), n % 256 ) ) +local function strary_append_int16(n, h) + if n < 0 then + n = n + 65536 + end + table.insert(strary, tostr(h, math.floor(n / 256), n % 256)) end -local function strary_append_int32(n,h) - if n < 0 then - n = n + 4294967296 - end - table.insert(strary, tostr(h, - math.floor(n / 16777216), - math.floor(n / 65536) % 256, - math.floor(n / 256) % 256, - n % 256 )) +local function strary_append_int32(n, h) + if n < 0 then + n = n + 4294967296 + end + table.insert( + strary, + tostr(h, math.floor(n / 16777216), math.floor(n / 65536) % 256, math.floor(n / 256) % 256, n % 256) + ) end local doubleto8bytes local strary_append_double = function(n) - -- assume double - double_encode_count = double_encode_count + 1 - local b = doubleto8bytes(n) - table.insert( strary, tostr(0xcb)) - table.insert( strary, string.reverse(b) ) -- reverse: make big endian double precision + -- assume double + double_encode_count = double_encode_count + 1 + local b = doubleto8bytes(n) + table.insert(strary, tostr(0xcb)) + table.insert(strary, string.reverse(b)) -- reverse: make big endian double precision end --- IEEE 754 -- out little endian doubleto8bytes = function(x) - local function grab_byte(v) - return math.floor(v / 256), tostr(math.fmod(math.floor(v), 256)) - end - local sign = 0 - if x < 0 then sign = 1; x = -x end - local mantissa, exponent = math.frexp(x) - if x == 0 then -- zero - mantissa, exponent = 0, 0 - elseif x == 1/0 then - mantissa, exponent = 0, 2047 - else - mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53) - exponent = exponent + 1022 - end - - local v, byte = "" -- convert to bytes - x = mantissa - for _ = 1,6 do - _, byte = grab_byte(x); v = v..byte -- 47:0 - end - x, byte = grab_byte(exponent * 16 + x); v = v..byte -- 55:48 - x, byte = grab_byte(sign * 128 + x); v = v..byte -- 63:56 - return v, x + local function grab_byte(v) + return math.floor(v / 256), tostr(math.fmod(math.floor(v), 256)) + end + local sign = 0 + if x < 0 then + sign = 1 + x = -x + end + local mantissa, exponent = math.frexp(x) + if x == 0 then -- zero + mantissa, exponent = 0, 0 + elseif x == 1 / 0 then + mantissa, exponent = 0, 2047 + else + mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53) + exponent = exponent + 1022 + end + + local v, byte = "" -- convert to bytes + x = mantissa + for _ = 1, 6 do + _, byte = grab_byte(x) + v = v .. byte -- 47:0 + end + x, byte = grab_byte(exponent * 16 + x) + v = v .. byte -- 55:48 + x, byte = grab_byte(sign * 128 + x) + v = v .. byte -- 63:56 + return v, x end local function bitstofrac(ary) - local x = 0 - local cur = 0.5 - for _, v in ipairs(ary) do - x = x + cur * v - cur = cur / 2 - end - return x + local x = 0 + local cur = 0.5 + for _, v in ipairs(ary) do + x = x + cur * v + cur = cur / 2 + end + return x end local function bytestobits(ary) - local out={} - for _, v in ipairs(ary) do - for j = 0, 7, 1 do - table.insert(out, band( rshift(v,7-j), 1 ) ) - end - end - return out + local out = {} + for _, v in ipairs(ary) do + for j = 0, 7, 1 do + table.insert(out, band(rshift(v, 7 - j), 1)) + end + end + return out end -- get little endian local function bytestodouble(v) - -- sign:1bit - -- exp: 11bit (2048, bias=1023) - local sign = math.floor(v:byte(8) / 128) - local exp = band( v:byte(8), 127 ) * 16 + rshift( v:byte(7), 4 ) - 1023 -- bias - -- frac: 52 bit - local fracbytes = { - band( v:byte(7), 15 ), v:byte(6), v:byte(5), v:byte(4), v:byte(3), v:byte(2), v:byte(1) -- big endian - } - local bits = bytestobits(fracbytes) - - for _ = 1, 4 do table.remove(bits,1) end - - if sign == 1 then sign = -1 else sign = 1 end - - local frac = bitstofrac(bits) - if exp == -1023 and frac==0 then return 0 end - if exp == 1024 and frac==0 then return 1/0 *sign end - - local real = math.ldexp(1+frac,exp) - - return real * sign + -- sign:1bit + -- exp: 11bit (2048, bias=1023) + local sign = math.floor(v:byte(8) / 128) + local exp = band(v:byte(8), 127) * 16 + rshift(v:byte(7), 4) - 1023 -- bias + -- frac: 52 bit + local fracbytes = { + band(v:byte(7), 15), + v:byte(6), + v:byte(5), + v:byte(4), + v:byte(3), + v:byte(2), + v:byte(1), -- big endian + } + local bits = bytestobits(fracbytes) + + for _ = 1, 4 do + table.remove(bits, 1) + end + + if sign == 1 then + sign = -1 + else + sign = 1 + end + + local frac = bitstofrac(bits) + if exp == -1023 and frac == 0 then + return 0 + end + if exp == 1024 and frac == 0 then + return 1 / 0 * sign + end + + local real = math.ldexp(1 + frac, exp) + + return real * sign end --- packers @@ -130,265 +150,285 @@ end local packers = {} packers.dynamic = function(data) - local t = type(data) - return packers[t](data) + local t = type(data) + return packers[t](data) end packers["nil"] = function() - table.insert( strary, tostr(0xc0)) + table.insert(strary, tostr(0xc0)) end packers.boolean = function(data) - if data then -- pack true - table.insert( strary, tostr(0xc3)) - else -- pack false - table.insert( strary, tostr(0xc2)) - end + if data then -- pack true + table.insert(strary, tostr(0xc3)) + else -- pack false + table.insert(strary, tostr(0xc2)) + end end packers.number = function(n) - if math.floor(n) == n then -- integer - if n >= 0 then -- positive integer - if n < 128 then -- positive fixnum - table.insert( strary, tostr(n)) - elseif n < 256 then -- uint8 - table.insert(strary, tostr(0xcc,n)) - elseif n < 65536 then -- uint16 - strary_append_int16(n,0xcd) - elseif n < 4294967296 then -- uint32 - strary_append_int32(n,0xce) - else -- lua cannot handle uint64, so double - strary_append_double(n) - end - else -- negative integer - if n >= -32 then -- negative fixnum - table.insert( strary, tostr( 0xe0 + ((n+256)%32)) ) - elseif n >= -128 then -- int8 - table.insert( strary, tostr(0xd0,byte_mod(n,0x100))) - elseif n >= -32768 then -- int16 - strary_append_int16(n,0xd1) - elseif n >= -2147483648 then -- int32 - strary_append_int32(n,0xd2) - else -- lua cannot handle int64, so double - strary_append_double(n) - end - end - else -- floating point - strary_append_double(n) - end + if math.floor(n) == n then -- integer + if n >= 0 then -- positive integer + if n < 128 then -- positive fixnum + table.insert(strary, tostr(n)) + elseif n < 256 then -- uint8 + table.insert(strary, tostr(0xcc, n)) + elseif n < 65536 then -- uint16 + strary_append_int16(n, 0xcd) + elseif n < 4294967296 then -- uint32 + strary_append_int32(n, 0xce) + else -- lua cannot handle uint64, so double + strary_append_double(n) + end + else -- negative integer + if n >= -32 then -- negative fixnum + table.insert(strary, tostr(0xe0 + ((n + 256) % 32))) + elseif n >= -128 then -- int8 + table.insert(strary, tostr(0xd0, byte_mod(n, 0x100))) + elseif n >= -32768 then -- int16 + strary_append_int16(n, 0xd1) + elseif n >= -2147483648 then -- int32 + strary_append_int32(n, 0xd2) + else -- lua cannot handle int64, so double + strary_append_double(n) + end + end + else -- floating point + strary_append_double(n) + end end packers.string = function(data) - local n = #data - if n < 32 then - table.insert( strary, tostr( 0xa0+n ) ) - elseif n < 65536 then - strary_append_int16(n,0xda) - elseif n < 4294967296 then - strary_append_int32(n,0xdb) - else - error("overflow") - end - table.insert( strary, data) + local n = #data + if n < 32 then + table.insert(strary, tostr(0xa0 + n)) + elseif n < 65536 then + strary_append_int16(n, 0xda) + elseif n < 4294967296 then + strary_append_int32(n, 0xdb) + else + error("overflow") + end + table.insert(strary, data) end packers["function"] = function() - error("unimplemented:function") + error("unimplemented:function") end packers.userdata = function() - error("unimplemented:userdata") + error("unimplemented:userdata") end packers.thread = function() - error("unimplemented:thread") + error("unimplemented:thread") end packers.table = function(data) - local is_map,ndata,nmax = false,0,0 - for k,_ in pairs(data) do - if type(k) == "number" then - if k > nmax then nmax = k end - else is_map = true end - ndata = ndata+1 - end - if is_map then -- pack as map - if ndata < 16 then - table.insert( strary, tostr(0x80+ndata)) - elseif ndata < 65536 then - strary_append_int16(ndata,0xde) - elseif ndata < 4294967296 then - strary_append_int32(ndata,0xdf) - else - error("overflow") - end - for k,v in pairs(data) do - packers[type(k)](k) - packers[type(v)](v) - end - else -- pack as array - if nmax < 16 then - table.insert( strary, tostr( 0x90+nmax ) ) - elseif nmax < 65536 then - strary_append_int16(nmax,0xdc) - elseif nmax < 4294967296 then - strary_append_int32(nmax,0xdd) - else - error("overflow") - end - for i=1,nmax do packers[type(data[i])](data[i]) end - end + local is_map, ndata, nmax = false, 0, 0 + for k, _ in pairs(data) do + if type(k) == "number" then + if k > nmax then + nmax = k + end + else + is_map = true + end + ndata = ndata + 1 + end + if is_map then -- pack as map + if ndata < 16 then + table.insert(strary, tostr(0x80 + ndata)) + elseif ndata < 65536 then + strary_append_int16(ndata, 0xde) + elseif ndata < 4294967296 then + strary_append_int32(ndata, 0xdf) + else + error("overflow") + end + for k, v in pairs(data) do + packers[type(k)](k) + packers[type(v)](v) + end + else -- pack as array + if nmax < 16 then + table.insert(strary, tostr(0x90 + nmax)) + elseif nmax < 65536 then + strary_append_int16(nmax, 0xdc) + elseif nmax < 4294967296 then + strary_append_int32(nmax, 0xdd) + else + error("overflow") + end + for i = 1, nmax do + packers[type(data[i])](data[i]) + end + end end -- types decoding local types_map = { - [0xc0] = "nil", - [0xc2] = "false", - [0xc3] = "true", - [0xca] = "float", - [0xcb] = "double", - [0xcc] = "uint8", - [0xcd] = "uint16", - [0xce] = "uint32", - [0xcf] = "uint64", - [0xd0] = "int8", - [0xd1] = "int16", - [0xd2] = "int32", - [0xd3] = "int64", - [0xda] = "raw16", - [0xdb] = "raw32", - [0xdc] = "array16", - [0xdd] = "array32", - [0xde] = "map16", - [0xdf] = "map32", + [0xc0] = "nil", + [0xc2] = "false", + [0xc3] = "true", + [0xca] = "float", + [0xcb] = "double", + [0xcc] = "uint8", + [0xcd] = "uint16", + [0xce] = "uint32", + [0xcf] = "uint64", + [0xd0] = "int8", + [0xd1] = "int16", + [0xd2] = "int32", + [0xd3] = "int64", + [0xda] = "raw16", + [0xdb] = "raw32", + [0xdc] = "array16", + [0xdd] = "array32", + [0xde] = "map16", + [0xdf] = "map32", } local type_for = function(n) - - if types_map[n] then return types_map[n] - elseif n < 0xc0 then - if n < 0x80 then return "fixnum_posi" - elseif n < 0x90 then return "fixmap" - elseif n < 0xa0 then return "fixarray" - else return "fixraw" end - elseif n > 0xdf then return "fixnum_neg" - else return "undefined" end + if types_map[n] then + return types_map[n] + elseif n < 0xc0 then + if n < 0x80 then + return "fixnum_posi" + elseif n < 0x90 then + return "fixmap" + elseif n < 0xa0 then + return "fixarray" + else + return "fixraw" + end + elseif n > 0xdf then + return "fixnum_neg" + else + return "undefined" + end end local types_len_map = { - uint16 = 2, uint32 = 4, uint64 = 8, - int16 = 2, int32 = 4, int64 = 8, - float = 4, double = 8, + uint16 = 2, + uint32 = 4, + uint64 = 8, + int16 = 2, + int32 = 4, + int64 = 8, + float = 4, + double = 8, } - - - --- unpackers local unpackers = {} -local unpack_number = function(offset,ntype,nlen) - local b1,b2,b3,b4,b5,b6,b7,b8 - if nlen>=2 then - b1,b2 = string.byte( strbuf, offset+1, offset+2 ) - end - if nlen>=4 then - b3,b4 = string.byte( strbuf, offset+3, offset+4 ) - end - if nlen>=8 then - b5,b6,b7,b8 = string.byte( strbuf, offset+5, offset+8 ) - end - - if ntype == "uint16_t" then - return b1 * 256 + b2 - elseif ntype == "uint32_t" then - return b1*65536*256 + b2*65536 + b3 * 256 + b4 - elseif ntype == "int16_t" then - local n = b1 * 256 + b2 - local nn = (65536 - n)*-1 - if nn == -65536 then nn = 0 end - return nn - elseif ntype == "int32_t" then - local n = b1*65536*256 + b2*65536 + b3 * 256 + b4 - local nn = ( 4294967296 - n ) * -1 - if nn == -4294967296 then nn = 0 end - return nn - elseif ntype == "double_t" then - local s = tostr(b8,b7,b6,b5,b4,b3,b2,b1) - double_decode_count = double_decode_count + 1 - local n = bytestodouble( s ) - return n - else - error("unpack_number: not impl:" .. ntype ) - end +local unpack_number = function(offset, ntype, nlen) + local b1, b2, b3, b4, b5, b6, b7, b8 + if nlen >= 2 then + b1, b2 = string.byte(strbuf, offset + 1, offset + 2) + end + if nlen >= 4 then + b3, b4 = string.byte(strbuf, offset + 3, offset + 4) + end + if nlen >= 8 then + b5, b6, b7, b8 = string.byte(strbuf, offset + 5, offset + 8) + end + + if ntype == "uint16_t" then + return b1 * 256 + b2 + elseif ntype == "uint32_t" then + return b1 * 65536 * 256 + b2 * 65536 + b3 * 256 + b4 + elseif ntype == "int16_t" then + local n = b1 * 256 + b2 + local nn = (65536 - n) * -1 + if nn == -65536 then + nn = 0 + end + return nn + elseif ntype == "int32_t" then + local n = b1 * 65536 * 256 + b2 * 65536 + b3 * 256 + b4 + local nn = (4294967296 - n) * -1 + if nn == -4294967296 then + nn = 0 + end + return nn + elseif ntype == "double_t" then + local s = tostr(b8, b7, b6, b5, b4, b3, b2, b1) + double_decode_count = double_decode_count + 1 + local n = bytestodouble(s) + return n + else + error("unpack_number: not impl:" .. ntype) + end end - - local function unpacker_number(offset) - local obj_type = type_for( string.byte( strbuf, offset+1, offset+1 ) ) - local nlen = types_len_map[obj_type] - local ntype - if (obj_type == "float") then - error("float is not implemented") - else - ntype = obj_type .. "_t" - end - return offset+nlen+1,unpack_number(offset+1,ntype,nlen) -end - -local function unpack_map(offset,n) - local r = {} - local k,v - for _ = 1, n do - offset,k = unpackers.dynamic(offset) - assert(offset) - offset,v = unpackers.dynamic(offset) - assert(offset) - r[k] = v - end - return offset,r -end - -local function unpack_array(offset,n) - local r = {} - for i=1,n do - offset,r[i] = unpackers.dynamic(offset) - assert(offset) - end - return offset,r + local obj_type = type_for(string.byte(strbuf, offset + 1, offset + 1)) + local nlen = types_len_map[obj_type] + local ntype + if obj_type == "float" then + error("float is not implemented") + else + ntype = obj_type .. "_t" + end + return offset + nlen + 1, unpack_number(offset + 1, ntype, nlen) +end + +local function unpack_map(offset, n) + local r = {} + local k, v + for _ = 1, n do + offset, k = unpackers.dynamic(offset) + assert(offset) + offset, v = unpackers.dynamic(offset) + assert(offset) + r[k] = v + end + return offset, r +end + +local function unpack_array(offset, n) + local r = {} + for i = 1, n do + offset, r[i] = unpackers.dynamic(offset) + assert(offset) + end + return offset, r end function unpackers.dynamic(offset) - if offset >= #strbuf then error("need more data") end - local obj_type = type_for( string.byte( strbuf, offset+1, offset+1 ) ) - return unpackers[obj_type](offset) + if offset >= #strbuf then + error("need more data") + end + local obj_type = type_for(string.byte(strbuf, offset + 1, offset + 1)) + return unpackers[obj_type](offset) end function unpackers.undefined() - error("unimplemented:undefined") + error("unimplemented:undefined") end unpackers["nil"] = function(offset) - return offset+1,nil + return offset + 1, nil end unpackers["false"] = function(offset) - return offset+1,false + return offset + 1, false end unpackers["true"] = function(offset) - return offset+1,true + return offset + 1, true end unpackers.fixnum_posi = function(offset) - return offset+1, string.byte(strbuf, offset+1, offset+1) + return offset + 1, string.byte(strbuf, offset + 1, offset + 1) end unpackers.uint8 = function(offset) - return offset+2, string.byte(strbuf, offset+2, offset+2) + return offset + 2, string.byte(strbuf, offset + 2, offset + 2) end unpackers.uint16 = unpacker_number @@ -396,18 +436,18 @@ unpackers.uint32 = unpacker_number unpackers.uint64 = unpacker_number unpackers.fixnum_neg = function(offset) - -- alternative to cast below: - local n = string.byte( strbuf, offset+1, offset+1) - local nn = ( 256 - n ) * -1 - return offset+1, nn + -- alternative to cast below: + local n = string.byte(strbuf, offset + 1, offset + 1) + local nn = (256 - n) * -1 + return offset + 1, nn end unpackers.int8 = function(offset) - local i = string.byte( strbuf, offset+2, offset+2 ) - if i > 127 then - i = (256 - i ) * -1 - end - return offset+2, i + local i = string.byte(strbuf, offset + 2, offset + 2) + if i > 127 then + i = (256 - i) * -1 + end + return offset + 2, i end unpackers.int16 = unpacker_number @@ -418,92 +458,96 @@ unpackers.float = unpacker_number unpackers.double = unpacker_number unpackers.fixraw = function(offset) - local n = byte_mod( string.byte( strbuf, offset+1, offset+1) ,0x1f+1) - -- print("unpackers.fixraw: offset:", offset, "#buf:", #buf, "n:",n ) - local b - if ( #strbuf - 1 - offset ) < n then - error("require more data") - end + local n = byte_mod(string.byte(strbuf, offset + 1, offset + 1), 0x1f + 1) + -- print("unpackers.fixraw: offset:", offset, "#buf:", #buf, "n:",n ) + local b + if (#strbuf - 1 - offset) < n then + error("require more data") + end - if n > 0 then - b = string.sub( strbuf, offset + 1 + 1, offset + 1 + 1 + n - 1 ) - else - b = "" - end - return offset+n+1, b + if n > 0 then + b = string.sub(strbuf, offset + 1 + 1, offset + 1 + 1 + n - 1) + else + b = "" + end + return offset + n + 1, b end unpackers.raw16 = function(offset) - local n = unpack_number(offset+1,"uint16_t",2) - if ( #strbuf - 1 - 2 - offset ) < n then - error("require more data") - end - local b = string.sub( strbuf, offset+1+1+2, offset+1 + 1+2 + n - 1 ) - return offset+n+3, b + local n = unpack_number(offset + 1, "uint16_t", 2) + if (#strbuf - 1 - 2 - offset) < n then + error("require more data") + end + local b = string.sub(strbuf, offset + 1 + 1 + 2, offset + 1 + 1 + 2 + n - 1) + return offset + n + 3, b end unpackers.raw32 = function(offset) - local n = unpack_number(offset+1,"uint32_t",4) - if ( #strbuf - 1 - 4 - offset ) < n then - error( "require more data (possibly bug)") - end - local b = string.sub( strbuf, offset+1+ 1+4, offset+1 + 1+4 +n -1 ) - return offset+n+5,b + local n = unpack_number(offset + 1, "uint32_t", 4) + if (#strbuf - 1 - 4 - offset) < n then + error("require more data (possibly bug)") + end + local b = string.sub(strbuf, offset + 1 + 1 + 4, offset + 1 + 1 + 4 + n - 1) + return offset + n + 5, b end unpackers.fixarray = function(offset) - return unpack_array( offset+1,byte_mod( string.byte( strbuf, offset+1,offset+1),0x0f+1)) + return unpack_array(offset + 1, byte_mod(string.byte(strbuf, offset + 1, offset + 1), 0x0f + 1)) end unpackers.array16 = function(offset) - return unpack_array(offset+3,unpack_number(offset+1,"uint16_t",2)) + return unpack_array(offset + 3, unpack_number(offset + 1, "uint16_t", 2)) end unpackers.array32 = function(offset) - return unpack_array(offset+5,unpack_number(offset+1,"uint32_t",4)) + return unpack_array(offset + 5, unpack_number(offset + 1, "uint32_t", 4)) end unpackers.fixmap = function(offset) - return unpack_map(offset+1,byte_mod( string.byte( strbuf, offset+1,offset+1),0x0f+1)) + return unpack_map(offset + 1, byte_mod(string.byte(strbuf, offset + 1, offset + 1), 0x0f + 1)) end unpackers.map16 = function(offset) - return unpack_map(offset+3,unpack_number(offset+1,"uint16_t",2)) + return unpack_map(offset + 3, unpack_number(offset + 1, "uint16_t", 2)) end unpackers.map32 = function(offset) - return unpack_map(offset+5,unpack_number(offset+1,"uint32_t",4)) + return unpack_map(offset + 5, unpack_number(offset + 1, "uint32_t", 4)) end -- Main functions local ljp_pack = function(data) - strary={} - packers.dynamic(data) - local s = table.concat(strary,"") - return s + strary = {} + packers.dynamic(data) + local s = table.concat(strary, "") + return s end -local ljp_unpack = function(s,offset) - if offset == nil then offset = 0 end - if type(s) ~= "string" then return false,"invalid argument" end - local data - strbuf = s - offset,data = unpackers.dynamic(offset) - return offset,data +local ljp_unpack = function(s, offset) + if offset == nil then + offset = 0 + end + if type(s) ~= "string" then + return false, "invalid argument" + end + local data + strbuf = s + offset, data = unpackers.dynamic(offset) + return offset, data end local function ljp_stat() - return { - double_decode_count = double_decode_count, - double_encode_count = double_encode_count - } + return { + double_decode_count = double_decode_count, + double_encode_count = double_encode_count, + } end local msgpack = { - pack = ljp_pack, - unpack = ljp_unpack, - stat = ljp_stat + pack = ljp_pack, + unpack = ljp_unpack, + stat = ljp_stat, } return msgpack diff --git a/lua/deps/serpent.lua b/lua/deps/serpent.lua index a043713..4503686 100644 --- a/lua/deps/serpent.lua +++ b/lua/deps/serpent.lua @@ -1,140 +1,265 @@ local n, v = "serpent", "0.302" -- (C) 2012-18 Paul Kulchenko; MIT License local c, d = "Paul Kulchenko", "Lua serializer and pretty printer" -local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'} -local badtype = {thread = true, userdata = true, cdata = true} +local snum = { + [tostring(1 / 0)] = "1/0 --[[math.huge]]", + [tostring(-1 / 0)] = "-1/0 --[[-math.huge]]", + [tostring(0 / 0)] = "0/0", +} +local badtype = { thread = true, userdata = true, cdata = true } local getmetatable = debug and debug.getmetatable or getmetatable -local pairs = function(t) return next, t end -- avoid using __pairs in Lua 5.2+ +local pairs = function(t) + return next, t +end -- avoid using __pairs in Lua 5.2+ local keyword, globals, G = {}, {}, (_G or _ENV) -for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false', - 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', - 'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end -for k,v in pairs(G) do globals[v] = k end -- build func to name mapping -for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'}) do - for k,v in pairs(type(G[g]) == 'table' and G[g] or {}) do globals[v] = g..'.'..k end end +for _, k in ipairs({ + "and", + "break", + "do", + "else", + "elseif", + "end", + "false", + "for", + "function", + "goto", + "if", + "in", + "local", + "nil", + "not", + "or", + "repeat", + "return", + "then", + "true", + "until", + "while", +}) do + keyword[k] = true +end +for k, v in pairs(G) do + globals[v] = k +end -- build func to name mapping +for _, g in ipairs({ "coroutine", "debug", "io", "math", "string", "table", "os" }) do + for k, v in pairs(type(G[g]) == "table" and G[g] or {}) do + globals[v] = g .. "." .. k + end +end local function s(t, opts) - local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum - local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge - local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge) - local maxlen, metatostring = tonumber(opts.maxlength), opts.metatostring - local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge) - local numformat = opts.numformat or "%.17g" - local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0 - local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)", - -- tostring(val) is needed because __tostring may return a non-string value - function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return tostring(syms[s]) end)) end - local function safestr(s) return type(s) == "number" and tostring(huge and snum[tostring(s)] or numformat:format(s)) - or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026 - or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end - local function comment(s,l) return comm and (l or 0) < comm and ' --[['..select(2, pcall(tostring, s))..']]' or '' end - local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal - and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end - local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r'] - local n = name == nil and '' or name - local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n] - local safe = plain and n or '['..safestr(n)..']' - return (path or '')..(plain and path and '.' or '')..safe, safe end - local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding - local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'} - local function padnum(d) return ("%0"..tostring(maxn).."d"):format(tonumber(d)) end - table.sort(k, function(a,b) - -- sort numeric keys first: k[key] is not nil for numerical keys - return (k[a] ~= nil and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum)) - < (k[b] ~= nil and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end - local function val2str(t, name, indent, insref, path, plainindex, level) - local ttype, level, mt = type(t), (level or 0), getmetatable(t) - local spath, sname = safename(path, name) - local tag = plainindex and - ((type(name) == "number") and '' or name..space..'='..space) or - (name ~= nil and sname..space..'='..space or '') - if seen[t] then -- already seen this element - sref[#sref+1] = spath..space..'='..space..seen[t] - return tag..'nil'..comment('ref', level) end - -- protect from those cases where __tostring may fail - if type(mt) == 'table' and metatostring ~= false then - local to, tr = pcall(function() return mt.__tostring(t) end) - local so, sr = pcall(function() return mt.__serialize(t) end) - if (to or so) then -- knows how to serialize itself - seen[t] = insref or spath - t = so and sr or tr - ttype = type(t) - end -- new value falls through to be serialized - end - if ttype == "table" then - if level >= maxl then return tag..'{}'..comment('maxlvl', level) end - seen[t] = insref or spath - if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty - if maxlen and maxlen < 0 then return tag..'{}'..comment('maxlen', level) end - local maxn, o, out = math.min(#t, maxnum or #t), {}, {} - for key = 1, maxn do o[key] = key end - if not maxnum or #o < maxnum then - local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables - for key in pairs(t) do if o[key] ~= key then n = n + 1; o[n] = key end end end - if maxnum and #o > maxnum then o[maxnum+1] = nil end - if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end - local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output) - for n, key in ipairs(o) do - local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse - if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing - or opts.keyallow and not opts.keyallow[key] - or opts.keyignore and opts.keyignore[key] - or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types - or sparse and value == nil then -- skipping nils; do nothing - elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then - if not seen[key] and not globals[key] then - sref[#sref+1] = 'placeholder' - local sname = safename(iname, gensym(key)) -- iname is table for local variables - sref[#sref] = val2str(key,sname,indent,sname,iname,true) end - sref[#sref+1] = 'placeholder' - local path = seen[t]..'['..tostring(seen[key] or globals[key] or gensym(key))..']' - sref[#sref] = path..space..'='..space..tostring(seen[value] or val2str(value,nil,indent,path)) - else - out[#out+1] = val2str(value,key,indent,nil,seen[t],plainindex,level+1) - if maxlen then - maxlen = maxlen - #out[#out] - if maxlen < 0 then break end - end - end - end - local prefix = string.rep(indent or '', level) - local head = indent and '{\n'..prefix..indent or '{' - local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space)) - local tail = indent and "\n"..prefix..'}' or '}' - return (custom and custom(tag,head,body,tail,level) or tag..head..body..tail)..comment(t, level) - elseif badtype[ttype] then - seen[t] = insref or spath - return tag..globerr(t, level) - elseif ttype == 'function' then - seen[t] = insref or spath - if opts.nocode then return tag.."function() --[[..skipped..]] end"..comment(t, level) end - local ok, res = pcall(string.dump, t) - local func = ok and "((loadstring or load)("..safestr(res)..",'@serialized'))"..comment(t, level) - return tag..(func or globerr(t, level)) - else return tag..safestr(t) end -- handle all other types - end - local sepr = indent and "\n" or ";"..space - local body = val2str(t, name, indent) -- this call also populates sref - local tail = #sref>1 and table.concat(sref, sepr)..sepr or '' - local warn = opts.comment and #sref>1 and space.."--[[incomplete output with shared/self-references skipped]]" or '' - return not name and body..warn or "do local "..body..sepr..tail.."return "..name..sepr.."end" + local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum + local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge + local space, maxl = (opts.compact and "" or " "), (opts.maxlevel or math.huge) + local maxlen, metatostring = tonumber(opts.maxlength), opts.metatostring + local iname, comm = "_" .. (name or ""), opts.comment and (tonumber(opts.comment) or math.huge) + local numformat = opts.numformat or "%.17g" + local seen, sref, syms, symn = {}, { "local " .. iname .. "={}" }, {}, 0 + local function gensym(val) + return "_" + .. ( + tostring(tostring(val)):gsub("[^%w]", ""):gsub( + "(%d%w+)", + -- tostring(val) is needed because __tostring may return a non-string value + function(s) + if not syms[s] then + symn = symn + 1 + syms[s] = symn + end + return tostring(syms[s]) + end + ) + ) + end + local function safestr(s) + return type(s) == "number" and tostring(huge and snum[tostring(s)] or numformat:format(s)) + or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026 + or ("%q"):format(s):gsub("\010", "n"):gsub("\026", "\\026") + end + local function comment(s, l) + return comm and (l or 0) < comm and " --[[" .. select(2, pcall(tostring, s)) .. "]]" or "" + end + local function globerr(s, l) + return globals[s] and globals[s] .. comment(s, l) + or not fatal and safestr(select(2, pcall(tostring, s))) + or error("Can't serialize " .. tostring(s)) + end + local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r'] + local n = name == nil and "" or name + local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n] + local safe = plain and n or "[" .. safestr(n) .. "]" + return (path or "") .. (plain and path and "." or "") .. safe, safe + end + local alphanumsort = type(opts.sortkeys) == "function" and opts.sortkeys + or function(k, o, n) -- k=keys, o=originaltable, n=padding + local maxn, to = tonumber(n) or 12, { number = "a", string = "b" } + local function padnum(d) + return ("%0" .. tostring(maxn) .. "d"):format(tonumber(d)) + end + table.sort(k, function(a, b) + -- sort numeric keys first: k[key] is not nil for numerical keys + return (k[a] ~= nil and 0 or to[type(a)] or "z") .. (tostring(a):gsub("%d+", padnum)) + < (k[b] ~= nil and 0 or to[type(b)] or "z") .. (tostring(b):gsub("%d+", padnum)) + end) + end + local function val2str(t, name, indent, insref, path, plainindex, level) + local ttype, level, mt = type(t), (level or 0), getmetatable(t) + local spath, sname = safename(path, name) + local tag = plainindex and ((type(name) == "number") and "" or name .. space .. "=" .. space) + or (name ~= nil and sname .. space .. "=" .. space or "") + if seen[t] then -- already seen this element + sref[#sref + 1] = spath .. space .. "=" .. space .. seen[t] + return tag .. "nil" .. comment("ref", level) + end + -- protect from those cases where __tostring may fail + if type(mt) == "table" and metatostring ~= false then + local to, tr = pcall(function() + return mt.__tostring(t) + end) + local so, sr = pcall(function() + return mt.__serialize(t) + end) + if to or so then -- knows how to serialize itself + seen[t] = insref or spath + t = so and sr or tr + ttype = type(t) + end -- new value falls through to be serialized + end + if ttype == "table" then + if level >= maxl then + return tag .. "{}" .. comment("maxlvl", level) + end + seen[t] = insref or spath + if next(t) == nil then + return tag .. "{}" .. comment(t, level) + end -- table empty + if maxlen and maxlen < 0 then + return tag .. "{}" .. comment("maxlen", level) + end + local maxn, o, out = math.min(#t, maxnum or #t), {}, {} + for key = 1, maxn do + o[key] = key + end + if not maxnum or #o < maxnum then + local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables + for key in pairs(t) do + if o[key] ~= key then + n = n + 1 + o[n] = key + end + end + end + if maxnum and #o > maxnum then + o[maxnum + 1] = nil + end + if opts.sortkeys and #o > maxn then + alphanumsort(o, t, opts.sortkeys) + end + local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output) + for n, key in ipairs(o) do + local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse + if + opts.valignore and opts.valignore[value] -- skip ignored values; do nothing + or opts.keyallow and not opts.keyallow[key] + or opts.keyignore and opts.keyignore[key] + or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types + or sparse and value == nil + then -- skipping nils; do nothing + elseif ktype == "table" or ktype == "function" or badtype[ktype] then + if not seen[key] and not globals[key] then + sref[#sref + 1] = "placeholder" + local sname = safename(iname, gensym(key)) -- iname is table for local variables + sref[#sref] = val2str(key, sname, indent, sname, iname, true) + end + sref[#sref + 1] = "placeholder" + local path = seen[t] .. "[" .. tostring(seen[key] or globals[key] or gensym(key)) .. "]" + sref[#sref] = path + .. space + .. "=" + .. space + .. tostring(seen[value] or val2str(value, nil, indent, path)) + else + out[#out + 1] = val2str(value, key, indent, nil, seen[t], plainindex, level + 1) + if maxlen then + maxlen = maxlen - #out[#out] + if maxlen < 0 then + break + end + end + end + end + local prefix = string.rep(indent or "", level) + local head = indent and "{\n" .. prefix .. indent or "{" + local body = table.concat(out, "," .. (indent and "\n" .. prefix .. indent or space)) + local tail = indent and "\n" .. prefix .. "}" or "}" + return (custom and custom(tag, head, body, tail, level) or tag .. head .. body .. tail) .. comment(t, level) + elseif badtype[ttype] then + seen[t] = insref or spath + return tag .. globerr(t, level) + elseif ttype == "function" then + seen[t] = insref or spath + if opts.nocode then + return tag .. "function() --[[..skipped..]] end" .. comment(t, level) + end + local ok, res = pcall(string.dump, t) + local func = ok and "((loadstring or load)(" .. safestr(res) .. ",'@serialized'))" .. comment(t, level) + return tag .. (func or globerr(t, level)) + else + return tag .. safestr(t) + end -- handle all other types + end + local sepr = indent and "\n" or ";" .. space + local body = val2str(t, name, indent) -- this call also populates sref + local tail = #sref > 1 and table.concat(sref, sepr) .. sepr or "" + local warn = opts.comment and #sref > 1 and space .. "--[[incomplete output with shared/self-references skipped]]" + or "" + return not name and body .. warn or "do local " .. body .. sepr .. tail .. "return " .. name .. sepr .. "end" end local function deserialize(data, opts) - local env = (opts and opts.safe == false) and G - or setmetatable({}, { - __index = function(t,k) return t end, - __call = function(t,...) error("cannot call functions") end - }) - local f, res = (loadstring or load)('return '..data, nil, nil, env) - if not f then f, res = (loadstring or load)(data, nil, nil, env) end - if not f then return f, res end - if setfenv then setfenv(f, env) end - return pcall(f) + local env = (opts and opts.safe == false) and G + or setmetatable({}, { + __index = function(t, k) + return t + end, + __call = function(t, ...) + error("cannot call functions") + end, + }) + local f, res = (loadstring or load)("return " .. data, nil, nil, env) + if not f then + f, res = (loadstring or load)(data, nil, nil, env) + end + if not f then + return f, res + end + if setfenv then + setfenv(f, env) + end + return pcall(f) end -local function merge(a, b) if b then for k,v in pairs(b) do a[k] = v end end; return a; end -return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s, - load = deserialize, - dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end, - line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end, - block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end } +local function merge(a, b) + if b then + for k, v in pairs(b) do + a[k] = v + end + end + return a +end +return { + _NAME = n, + _COPYRIGHT = c, + _DESCRIPTION = d, + _VERSION = v, + serialize = s, + load = deserialize, + dump = function(a, opts) + return s(a, merge({ name = "_", compact = true, sparse = true }, opts)) + end, + line = function(a, opts) + return s(a, merge({ sortkeys = true, comment = true }, opts)) + end, + block = function(a, opts) + return s(a, merge({ indent = " ", sortkeys = true, comment = true }, opts)) + end, +} diff --git a/lua/deps/struct.lua b/lua/deps/struct.lua index 54b32d4..1b0e814 100644 --- a/lua/deps/struct.lua +++ b/lua/deps/struct.lua @@ -1,179 +1,178 @@ local struct = {} function struct.pack(format, ...) - local stream = {} - local vars = {...} - local endianness = true - - for i = 1, format:len() do - local opt = format:sub(i, i) - - if opt == '<' then - endianness = true - elseif opt == '>' then - endianness = false - elseif opt:find('[bBhHiIlL]') then - local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1 - local val = tonumber(table.remove(vars, 1)) - - local bytes = {} - for _ = 1, n do - table.insert(bytes, string.char(val % (2 ^ 8))) - val = math.floor(val / (2 ^ 8)) - end - - if not endianness then - table.insert(stream, string.reverse(table.concat(bytes))) - else - table.insert(stream, table.concat(bytes)) - end - elseif opt:find('[fd]') then - local val = tonumber(table.remove(vars, 1)) - local sign = 0 - - if val < 0 then - sign = 1 - val = -val - end - - local mantissa, exponent = math.frexp(val) - if val == 0 then - mantissa = 0 - exponent = 0 - else - mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, (opt == 'd') and 53 or 24) - exponent = exponent + ((opt == 'd') and 1022 or 126) - end - - local bytes = {} - if opt == 'd' then - val = mantissa - for _ = 1, 6 do - table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) - val = math.floor(val / (2 ^ 8)) - end - else - table.insert(bytes, string.char(math.floor(mantissa) % (2 ^ 8))) - val = math.floor(mantissa / (2 ^ 8)) - table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) - val = math.floor(val / (2 ^ 8)) - end - - table.insert(bytes, string.char(math.floor(exponent * ((opt == 'd') and 16 or 128) + val) % (2 ^ 8))) - val = math.floor((exponent * ((opt == 'd') and 16 or 128) + val) / (2 ^ 8)) - table.insert(bytes, string.char(math.floor(sign * 128 + val) % (2 ^ 8))) - - if not endianness then - table.insert(stream, string.reverse(table.concat(bytes))) - else - table.insert(stream, table.concat(bytes)) - end - elseif opt == 's' then - table.insert(stream, tostring(table.remove(vars, 1))) - table.insert(stream, string.char(0)) - elseif opt == 'c' then - local n = format:sub(i + 1):match('%d+') - local str = tostring(table.remove(vars, 1)) - local len = tonumber(n) - if len <= 0 then - len = str:len() - end - if len - str:len() > 0 then - str = str .. string.rep(' ', len - str:len()) - end - table.insert(stream, str:sub(1, len)) - end - end - - return table.concat(stream) + local stream = {} + local vars = { ... } + local endianness = true + + for i = 1, format:len() do + local opt = format:sub(i, i) + + if opt == "<" then + endianness = true + elseif opt == ">" then + endianness = false + elseif opt:find("[bBhHiIlL]") then + local n = opt:find("[hH]") and 2 or opt:find("[iI]") and 4 or opt:find("[lL]") and 8 or 1 + local val = tonumber(table.remove(vars, 1)) + + local bytes = {} + for _ = 1, n do + table.insert(bytes, string.char(val % (2 ^ 8))) + val = math.floor(val / (2 ^ 8)) + end + + if not endianness then + table.insert(stream, string.reverse(table.concat(bytes))) + else + table.insert(stream, table.concat(bytes)) + end + elseif opt:find("[fd]") then + local val = tonumber(table.remove(vars, 1)) + local sign = 0 + + if val < 0 then + sign = 1 + val = -val + end + + local mantissa, exponent = math.frexp(val) + if val == 0 then + mantissa = 0 + exponent = 0 + else + mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, (opt == "d") and 53 or 24) + exponent = exponent + ((opt == "d") and 1022 or 126) + end + + local bytes = {} + if opt == "d" then + val = mantissa + for _ = 1, 6 do + table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) + val = math.floor(val / (2 ^ 8)) + end + else + table.insert(bytes, string.char(math.floor(mantissa) % (2 ^ 8))) + val = math.floor(mantissa / (2 ^ 8)) + table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) + val = math.floor(val / (2 ^ 8)) + end + + table.insert(bytes, string.char(math.floor(exponent * ((opt == "d") and 16 or 128) + val) % (2 ^ 8))) + val = math.floor((exponent * ((opt == "d") and 16 or 128) + val) / (2 ^ 8)) + table.insert(bytes, string.char(math.floor(sign * 128 + val) % (2 ^ 8))) + + if not endianness then + table.insert(stream, string.reverse(table.concat(bytes))) + else + table.insert(stream, table.concat(bytes)) + end + elseif opt == "s" then + table.insert(stream, tostring(table.remove(vars, 1))) + table.insert(stream, string.char(0)) + elseif opt == "c" then + local n = format:sub(i + 1):match("%d+") + local str = tostring(table.remove(vars, 1)) + local len = tonumber(n) + if len <= 0 then + len = str:len() + end + if len - str:len() > 0 then + str = str .. string.rep(" ", len - str:len()) + end + table.insert(stream, str:sub(1, len)) + end + end + + return table.concat(stream) end function struct.unpack(format, stream, pos) - local vars = {} - local iterator = pos or 1 - local endianness = true - - for i = 1, format:len() do - local opt = format:sub(i, i) - - if opt == '<' then - endianness = true - elseif opt == '>' then - endianness = false - elseif opt:find('[bBhHiIlL]') then - local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1 - local signed = opt:lower() == opt - - local val = 0 - for j = 1, n do - local byte = string.byte(stream:sub(iterator, iterator)) - if endianness then - val = val + byte * (2 ^ ((j - 1) * 8)) - else - val = val + byte * (2 ^ ((n - j) * 8)) - end - iterator = iterator + 1 - end - - if signed and val >= 2 ^ (n * 8 - 1) then - val = val - 2 ^ (n * 8) - end - - table.insert(vars, math.floor(val)) - elseif opt:find('[fd]') then - local n = (opt == 'd') and 8 or 4 - local x = stream:sub(iterator, iterator + n - 1) - iterator = iterator + n - - if not endianness then - x = string.reverse(x) - end - - local sign = 1 - local mantissa = string.byte(x, (opt == 'd') and 7 or 3) % ((opt == 'd') and 16 or 128) - for j = n - 2, 1, -1 do - mantissa = mantissa * (2 ^ 8) + string.byte(x, j) - end - - if string.byte(x, n) > 127 then - sign = -1 - end - - local exponent = (string.byte(x, n) % 128) * ((opt == 'd') and 16 or 2) + - math.floor(string.byte(x, n - 1) / - ((opt == 'd') and 16 or 128)) - if exponent == 0 then - table.insert(vars, 0.0) - else - mantissa = (math.ldexp(mantissa, (opt == 'd') and -52 or -23) + 1) * sign - table.insert(vars, math.ldexp(mantissa, exponent - ((opt == 'd') and 1023 or 127))) - end - elseif opt == 's' then - local bytes = {} - for j = iterator, stream:len() do - if stream:sub(j,j) == string.char(0) or stream:sub(j) == '' then - break - end - - table.insert(bytes, stream:sub(j, j)) - end - - local str = table.concat(bytes) - iterator = iterator + str:len() + 1 - table.insert(vars, str) - elseif opt == 'c' then - local n = format:sub(i + 1):match('%d+') - local len = tonumber(n) - if len <= 0 then - len = table.remove(vars) - end - - table.insert(vars, stream:sub(iterator, iterator + len - 1)) - iterator = iterator + len - end - end - - return unpack(vars) + local vars = {} + local iterator = pos or 1 + local endianness = true + + for i = 1, format:len() do + local opt = format:sub(i, i) + + if opt == "<" then + endianness = true + elseif opt == ">" then + endianness = false + elseif opt:find("[bBhHiIlL]") then + local n = opt:find("[hH]") and 2 or opt:find("[iI]") and 4 or opt:find("[lL]") and 8 or 1 + local signed = opt:lower() == opt + + local val = 0 + for j = 1, n do + local byte = string.byte(stream:sub(iterator, iterator)) + if endianness then + val = val + byte * (2 ^ ((j - 1) * 8)) + else + val = val + byte * (2 ^ ((n - j) * 8)) + end + iterator = iterator + 1 + end + + if signed and val >= 2 ^ (n * 8 - 1) then + val = val - 2 ^ (n * 8) + end + + table.insert(vars, math.floor(val)) + elseif opt:find("[fd]") then + local n = (opt == "d") and 8 or 4 + local x = stream:sub(iterator, iterator + n - 1) + iterator = iterator + n + + if not endianness then + x = string.reverse(x) + end + + local sign = 1 + local mantissa = string.byte(x, (opt == "d") and 7 or 3) % ((opt == "d") and 16 or 128) + for j = n - 2, 1, -1 do + mantissa = mantissa * (2 ^ 8) + string.byte(x, j) + end + + if string.byte(x, n) > 127 then + sign = -1 + end + + local exponent = (string.byte(x, n) % 128) * ((opt == "d") and 16 or 2) + + math.floor(string.byte(x, n - 1) / ((opt == "d") and 16 or 128)) + if exponent == 0 then + table.insert(vars, 0.0) + else + mantissa = (math.ldexp(mantissa, (opt == "d") and -52 or -23) + 1) * sign + table.insert(vars, math.ldexp(mantissa, exponent - ((opt == "d") and 1023 or 127))) + end + elseif opt == "s" then + local bytes = {} + for j = iterator, stream:len() do + if stream:sub(j, j) == string.char(0) or stream:sub(j) == "" then + break + end + + table.insert(bytes, stream:sub(j, j)) + end + + local str = table.concat(bytes) + iterator = iterator + str:len() + 1 + table.insert(vars, str) + elseif opt == "c" then + local n = format:sub(i + 1):match("%d+") + local len = tonumber(n) + if len <= 0 then + len = table.remove(vars) + end + + table.insert(vars, stream:sub(iterator, iterator + len - 1)) + iterator = iterator + len + end + end + + return unpack(vars) end return struct diff --git a/lua/lib/log.lua b/lua/lib/log.lua index b7b71e4..dcacdb3 100644 --- a/lua/lib/log.lua +++ b/lua/lib/log.lua @@ -2,37 +2,37 @@ local Log = {} Log.codes = {} Log.levels = { - { "debug", "Comment" }, - { "info", "None" }, - { "warn", "WarningMsg" }, - { "error", "ErrorMsg" }, + { "debug", "Comment" }, + { "info", "None" }, + { "warn", "WarningMsg" }, + { "error", "ErrorMsg" }, } function Log:init(options) - self.level = options.level - return self + self.level = options.level + return self end -- Initialize logger with log functions for each level for i = 1, #Log.levels do - local level, hl = unpack(Log.levels[i]) + local level, hl = unpack(Log.levels[i]) - Log.codes[level] = i + Log.codes[level] = i - Log[level] = function(self, message) - -- Skip if log level is not set or the log is below the configured or default level - if not self.level or self.codes[level] < self.codes[self.level] or type(message) ~= "string" then - return - end + Log[level] = function(self, message) + -- Skip if log level is not set or the log is below the configured or default level + if not self.level or self.codes[level] < self.codes[self.level] or type(message) ~= "string" then + return + end - vim.schedule(function() - local escaped_message = vim.fn.escape(message, '"'):gsub("\n", "\\n") + vim.schedule(function() + local escaped_message = vim.fn.escape(message, '"'):gsub("\n", "\\n") - vim.cmd(string.format("echohl %s", hl)) - vim.cmd(string.format([[echom "[%s] %s"]], "presence.nvim", escaped_message)) - vim.cmd("echohl NONE") - end) - end + vim.cmd(string.format("echohl %s", hl)) + vim.cmd(string.format([[echom "[%s] %s"]], "presence.nvim", escaped_message)) + vim.cmd("echohl NONE") + end) + end end return Log diff --git a/lua/presence/discord.lua b/lua/presence/discord.lua index 83fd77a..6ab2f55 100644 --- a/lua/presence/discord.lua +++ b/lua/presence/discord.lua @@ -1,9 +1,9 @@ local Discord = {} Discord.opcodes = { - auth = 0, - frame = 1, - closed = 2, + auth = 0, + frame = 1, + closed = 2, } -- Discord RPC Subscription events @@ -11,190 +11,187 @@ Discord.opcodes = { -- Ready: https://discord.com/developers/docs/topics/rpc#ready -- Error: https://discord.com/developers/docs/topics/rpc#error Discord.events = { - READY = "READY", - ERROR = "ERROR", + READY = "READY", + ERROR = "ERROR", } local struct = require("deps.struct") -- Initialize a new Discord RPC client function Discord:init(options) - self.log = options.logger - self.client_id = options.client_id - self.ipc_socket = options.ipc_socket + self.log = options.logger + self.client_id = options.client_id + self.ipc_socket = options.ipc_socket - self.pipe = vim.loop.new_pipe(false) + self.pipe = vim.loop.new_pipe(false) - return self + return self end -- Connect to the local Discord RPC socket -- TODO Might need to check for pipes ranging from discord-ipc-0 to discord-ipc-9: -- https://github.com/discord/discord-rpc/blob/master/documentation/hard-mode.md#notes function Discord:connect(on_connect) - if self.pipe:is_closing() then - self.pipe = vim.loop.new_pipe(false) - end + if self.pipe:is_closing() then + self.pipe = vim.loop.new_pipe(false) + end - self.pipe:connect(self.ipc_socket, on_connect) + self.pipe:connect(self.ipc_socket, on_connect) end function Discord:is_connected() - return self.pipe:is_active() + return self.pipe:is_active() end -- Disconnect from the local Discord RPC socket function Discord:disconnect(on_close) - self.pipe:shutdown() - if not self.pipe:is_closing() then - self.pipe:close(on_close) - end + self.pipe:shutdown() + if not self.pipe:is_closing() then + self.pipe:close(on_close) + end end -- Make a remote procedure call to Discord -- Callback argument in format: on_response(error[, response_table]) function Discord:call(opcode, payload, on_response) - self.encode_json(payload, function(success, body) - if not success then - self.log:warn(string.format("Failed to encode payload: %s", vim.inspect(body))) - return - end - - -- Start reading for the response - self.pipe:read_start(function(...) - self:read_message(payload.nonce, on_response, ...) - end) - - -- Construct message denoting little endian, auth opcode, msg length - local message = struct.pack(" 1 - local s = is_plural and "s" or "" - self.log:debug(string.format("Using custom-defined button%s", s)) - - return self.options.buttons - end - - -- Retrieve the git repository URL - local repo_url - if parent_dirpath then - -- Escape quotes in the file path - local path = parent_dirpath:gsub([["]], [[\"]]) - local git_url_cmd = "git config --get remote.origin.url" - local cmd = path and string.format([[cd "%s" && %s]], path, git_url_cmd) or git_url_cmd - - -- Trim and coerce empty string value to null - repo_url = vim.trim(vim.fn.system(cmd)) - repo_url = repo_url ~= "" and repo_url or nil - end - - -- User configured a function to dynamically create buttons table - if type(self.options.buttons) == "function" then - self.log:debug("Using custom-defined button config function") - return self.options.buttons(buffer, repo_url) - end - - -- Default behavior to show a "View Repository" button if the repo URL is valid - if repo_url then - -- Check if repo url uses short ssh syntax - local domain, project = repo_url:match("^git@(.+):(.+)$") - if domain and project then - self.log:debug(string.format("Repository URL uses short ssh syntax: %s", repo_url)) - repo_url = string.format("https://%s/%s", domain, project) - end - - -- Check if repo url uses a valid protocol - local protocols = { - "ftp", - "git", - "http", - "https", - "ssh", - } - local protocol, relative = repo_url:match("^(.+)://(.+)$") - if not vim.tbl_contains(protocols, protocol) or not relative then - self.log:debug(string.format("Repository URL uses invalid protocol: %s", repo_url)) - return nil - end - - -- Check if repo url has the user specified - local user, path = relative:match("^(.+)@(.+)$") - if user and path then - self.log:debug(string.format("Repository URL has user specified: %s", repo_url)) - repo_url = string.format("https://%s", path) - else - repo_url = string.format("https://%s", relative) - end - - self.log:debug(string.format("Adding button with repository URL: %s", repo_url)) - - return { - { label = "View Repository", url = repo_url }, - } - end - - return nil + -- User configured a static buttons table + if type(self.options.buttons) == "table" then + local is_plural = #self.options.buttons > 1 + local s = is_plural and "s" or "" + self.log:debug(string.format("Using custom-defined button%s", s)) + + return self.options.buttons + end + + -- Retrieve the git repository URL + local repo_url + if parent_dirpath then + -- Escape quotes in the file path + local path = parent_dirpath:gsub([["]], [[\"]]) + local git_url_cmd = "git config --get remote.origin.url" + local cmd = path and string.format([[cd "%s" && %s]], path, git_url_cmd) or git_url_cmd + + -- Trim and coerce empty string value to null + repo_url = vim.trim(vim.fn.system(cmd)) + repo_url = repo_url ~= "" and repo_url or nil + end + + -- User configured a function to dynamically create buttons table + if type(self.options.buttons) == "function" then + self.log:debug("Using custom-defined button config function") + return self.options.buttons(buffer, repo_url) + end + + -- Default behavior to show a "View Repository" button if the repo URL is valid + if repo_url then + -- Check if repo url uses short ssh syntax + local domain, project = repo_url:match("^git@(.+):(.+)$") + if domain and project then + self.log:debug(string.format("Repository URL uses short ssh syntax: %s", repo_url)) + repo_url = string.format("https://%s/%s", domain, project) + end + + -- Check if repo url uses a valid protocol + local protocols = { + "ftp", + "git", + "http", + "https", + "ssh", + } + local protocol, relative = repo_url:match("^(.+)://(.+)$") + if not vim.tbl_contains(protocols, protocol) or not relative then + self.log:debug(string.format("Repository URL uses invalid protocol: %s", repo_url)) + return nil + end + + -- Check if repo url has the user specified + local user, path = relative:match("^(.+)@(.+)$") + if user and path then + self.log:debug(string.format("Repository URL has user specified: %s", repo_url)) + repo_url = string.format("https://%s", path) + else + repo_url = string.format("https://%s", relative) + end + + self.log:debug(string.format("Adding button with repository URL: %s", repo_url)) + + return { + { label = "View Repository", url = repo_url }, + } + end + + return nil end -- Update Rich Presence for the provided vim buffer function Presence:update_for_buffer(buffer, should_debounce) - -- Avoid unnecessary updates if the previous activity was for the current buffer - -- (allow same-buffer updates when line numbers are enabled) - if self.options.enable_line_number == 0 and self.last_activity.file == buffer then - self.log:debug(string.format("Activity already set for %s, skipping...", buffer)) - return - end - - -- Parse vim buffer - local filename = self.get_filename(buffer, self.os.path_separator) - local parent_dirpath = self.get_dir_path(buffer, self.os.path_separator) - local extension = filename and self.get_file_extension(filename) or nil - self.log:debug(string.format("Parsed filename %s with %s extension", filename, extension or "no")) - - -- Return early if there is no valid activity status text to set - local status_text = self:get_status_text(filename) - if not status_text then - return self.log:debug("No status text for the given buffer, skipping...") - end - - -- Get project information - self.log:debug(string.format("Getting project name for %s...", parent_dirpath)) - local project_name, project_path = self:get_project_name(parent_dirpath) - - -- Check for blacklist - local is_blacklisted = #self.options.blacklist > 0 and self:check_blacklist(buffer, parent_dirpath, project_path) - if is_blacklisted then - self.last_activity.file = buffer - self.log:debug("Either project or directory name is blacklisted, skipping...") - self:cancel() - return - end - - local activity_set_at = os.time() - -- If we shouldn't debounce and we trigger an activity, keep this value the same. - -- Otherwise set it to the current time. - local relative_activity_set_at = should_debounce and self.last_activity.relative_set_at or os.time() - - self.log:debug(string.format("Setting activity for %s...", buffer and #buffer > 0 and buffer or "unnamed buffer")) - - -- Determine image text and asset key - local name = filename - local asset_key = "code" - local description = filename - local file_asset = self.options.file_assets[filename] or self.options.file_assets[extension] - if file_asset then - name, asset_key, description = unpack(file_asset) - self.log:debug(string.format("Using file asset: %s", vim.inspect(file_asset))) - end - - -- Construct activity asset information - local file_text = description or name - local neovim_image_text = self.options.neovim_image_text - local use_file_as_main_image = self.options.main_image == "file" - local use_neovim_as_main_image = self.options.main_image == "neovim" - local assets = { - large_image = use_file_as_main_image and asset_key or use_neovim_as_main_image - and "neovim" or self.options.main_image, - large_text = use_file_as_main_image and file_text or neovim_image_text, - small_image = use_file_as_main_image and "neovim" or asset_key, - small_text = use_file_as_main_image and neovim_image_text or file_text, - } - - local activity = { - state = status_text, - assets = assets, - timestamps = self.options.show_time == 1 and { - start = relative_activity_set_at, - } or nil, - } - - -- Add button that links to the git workspace remote origin url - if self.options.buttons ~= 0 then - local buttons = self:get_buttons(buffer, parent_dirpath) - if buttons then - self.log:debug(string.format("Attaching buttons to activity: %s", vim.inspect(buttons))) - activity.buttons = buttons - end - end - - -- Get the current line number and line count if the user has set the enable_line_number option - if self.options.enable_line_number == 1 then - self.log:debug("Getting line number for current buffer...") - - local line_number = vim.api.nvim_win_get_cursor(0)[1] - local line_count = vim.api.nvim_buf_line_count(0) - local line_number_text = self:format_status_text("line_number", line_number, line_count) - - activity.details = line_number_text - - self.workspace = nil - self.last_activity = { - id = self.id, - file = buffer, - set_at = activity_set_at, - relative_set_at = relative_activity_set_at, - workspace = nil, - } - else - -- Include project details if available and if the user hasn't set the enable_line_number option - if project_name then - self.log:debug(string.format("Detected project: %s", project_name)) - - activity.details = self:format_status_text("workspace", project_name, buffer) - - self.workspace = project_path - self.last_activity = { - id = self.id, - file = buffer, - set_at = activity_set_at, - relative_set_at = relative_activity_set_at, - workspace = project_path, - } - - if self.workspaces[project_path] then - self.workspaces[project_path].updated_at = activity_set_at - activity.timestamps = self.options.show_time == 1 - and { - start = self.workspaces[project_path].started_at, - } - or nil - else - self.workspaces[project_path] = { - started_at = activity_set_at, - updated_at = activity_set_at, - } - end - else - self.log:debug("No project detected") - - self.workspace = nil - self.last_activity = { - id = self.id, - file = buffer, - set_at = activity_set_at, - relative_set_at = relative_activity_set_at, - workspace = nil, - } - - -- When no project is detected, set custom workspace text if: - -- * The custom function returns custom workspace text - -- * The configured workspace text does not contain a directive - -- (can't use the `format_status_text` method here) - local workspace_text = self.options.workspace_text - if type(workspace_text) == "function" then - local custom_workspace_text = workspace_text(nil, buffer) - if custom_workspace_text then - activity.details = custom_workspace_text - end - elseif not workspace_text:find("%s") then - activity.details = workspace_text - end - end - end - - -- Sync activity to all peers - self.log:debug("Sync activity to all peers...") - self:sync_self_activity() - - self.log:debug("Setting Discord activity...") - self.discord:set_activity(activity, function(err) - if err then - self.log:error(string.format("Failed to set activity in Discord: %s", err)) - return - end - - self.log:info(string.format("Set activity in Discord for %s", filename)) - end) + -- Avoid unnecessary updates if the previous activity was for the current buffer + -- (allow same-buffer updates when line numbers are enabled) + if self.options.enable_line_number == 0 and self.last_activity.file == buffer then + self.log:debug(string.format("Activity already set for %s, skipping...", buffer)) + return + end + + -- Parse vim buffer + local filename = self.get_filename(buffer, self.os.path_separator) + local parent_dirpath = self.get_dir_path(buffer, self.os.path_separator) + local extension = filename and self.get_file_extension(filename) or nil + self.log:debug(string.format("Parsed filename %s with %s extension", filename, extension or "no")) + + -- Return early if there is no valid activity status text to set + local status_text = self:get_status_text(filename) + if not status_text then + return self.log:debug("No status text for the given buffer, skipping...") + end + + -- Get project information + self.log:debug(string.format("Getting project name for %s...", parent_dirpath)) + local project_name, project_path = self:get_project_name(parent_dirpath) + + -- Check for blacklist + local is_blacklisted = #self.options.blacklist > 0 and self:check_blacklist(buffer, parent_dirpath, project_path) + if is_blacklisted then + self.last_activity.file = buffer + self.log:debug("Either project or directory name is blacklisted, skipping...") + self:cancel() + return + end + + local activity_set_at = os.time() + -- If we shouldn't debounce and we trigger an activity, keep this value the same. + -- Otherwise set it to the current time. + local relative_activity_set_at = should_debounce and self.last_activity.relative_set_at or os.time() + + self.log:debug(string.format("Setting activity for %s...", buffer and #buffer > 0 and buffer or "unnamed buffer")) + + -- Determine image text and asset key + local name = filename + local asset_key = "code" + local description = filename + local file_asset = self.options.file_assets[filename] or self.options.file_assets[extension] + if file_asset then + name, asset_key, description = unpack(file_asset) + self.log:debug(string.format("Using file asset: %s", vim.inspect(file_asset))) + end + + -- Construct activity asset information + local file_text = description or name + local neovim_image_text = self.options.neovim_image_text + local use_file_as_main_image = self.options.main_image == "file" + local use_neovim_as_main_image = self.options.main_image == "neovim" + local assets = { + large_image = use_file_as_main_image and asset_key + or use_neovim_as_main_image and "neovim" + or self.options.main_image, + large_text = use_file_as_main_image and file_text or neovim_image_text, + small_image = use_file_as_main_image and "neovim" or asset_key, + small_text = use_file_as_main_image and neovim_image_text or file_text, + } + + local activity = { + state = status_text, + assets = assets, + timestamps = self.options.show_time == 1 and { + start = relative_activity_set_at, + } or nil, + } + + -- Add button that links to the git workspace remote origin url + if self.options.buttons ~= 0 then + local buttons = self:get_buttons(buffer, parent_dirpath) + if buttons then + self.log:debug(string.format("Attaching buttons to activity: %s", vim.inspect(buttons))) + activity.buttons = buttons + end + end + + -- Get the current line number and line count if the user has set the enable_line_number option + if self.options.enable_line_number == 1 then + self.log:debug("Getting line number for current buffer...") + + local line_number = vim.api.nvim_win_get_cursor(0)[1] + local line_count = vim.api.nvim_buf_line_count(0) + local line_number_text = self:format_status_text("line_number", line_number, line_count) + + activity.details = line_number_text + + self.workspace = nil + self.last_activity = { + id = self.id, + file = buffer, + set_at = activity_set_at, + relative_set_at = relative_activity_set_at, + workspace = nil, + } + else + -- Include project details if available and if the user hasn't set the enable_line_number option + if project_name then + self.log:debug(string.format("Detected project: %s", project_name)) + + activity.details = self:format_status_text("workspace", project_name, buffer) + + self.workspace = project_path + self.last_activity = { + id = self.id, + file = buffer, + set_at = activity_set_at, + relative_set_at = relative_activity_set_at, + workspace = project_path, + } + + if self.workspaces[project_path] then + self.workspaces[project_path].updated_at = activity_set_at + activity.timestamps = self.options.show_time == 1 + and { + start = self.workspaces[project_path].started_at, + } + or nil + else + self.workspaces[project_path] = { + started_at = activity_set_at, + updated_at = activity_set_at, + } + end + else + self.log:debug("No project detected") + + self.workspace = nil + self.last_activity = { + id = self.id, + file = buffer, + set_at = activity_set_at, + relative_set_at = relative_activity_set_at, + workspace = nil, + } + + -- When no project is detected, set custom workspace text if: + -- * The custom function returns custom workspace text + -- * The configured workspace text does not contain a directive + -- (can't use the `format_status_text` method here) + local workspace_text = self.options.workspace_text + if type(workspace_text) == "function" then + local custom_workspace_text = workspace_text(nil, buffer) + if custom_workspace_text then + activity.details = custom_workspace_text + end + elseif not workspace_text:find("%s") then + activity.details = workspace_text + end + end + end + + -- Sync activity to all peers + self.log:debug("Sync activity to all peers...") + self:sync_self_activity() + + self.log:debug("Setting Discord activity...") + self.discord:set_activity(activity, function(err) + if err then + self.log:error(string.format("Failed to set activity in Discord: %s", err)) + return + end + + self.log:info(string.format("Set activity in Discord for %s", filename)) + end) end -- Update Rich Presence for the current or provided vim buffer for an authorized connection Presence.update = Presence.discord_event(function(self, buffer, should_debounce) - -- Default update to not debounce by default - if should_debounce == nil then - should_debounce = false - end - - -- Debounce Rich Presence updates (default to 10 seconds): - -- https://discord.com/developers/docs/rich-presence/how-to#updating-presence - local last_updated_at = self.last_activity.set_at - local debounce_timeout = self.options.debounce_timeout - local should_skip = should_debounce - and debounce_timeout - and last_updated_at - and os.time() - last_updated_at <= debounce_timeout - - if should_skip then - local message_fmt = "Last activity sent was within %d seconds ago, skipping..." - self.log:debug(string.format(message_fmt, debounce_timeout)) - return - end - - if buffer then - self:update_for_buffer(buffer, should_debounce) - else - vim.schedule(function() - self:update_for_buffer(self.get_current_buffer(), should_debounce) - end) - end + -- Default update to not debounce by default + if should_debounce == nil then + should_debounce = false + end + + -- Debounce Rich Presence updates (default to 10 seconds): + -- https://discord.com/developers/docs/rich-presence/how-to#updating-presence + local last_updated_at = self.last_activity.set_at + local debounce_timeout = self.options.debounce_timeout + local should_skip = should_debounce + and debounce_timeout + and last_updated_at + and os.time() - last_updated_at <= debounce_timeout + + if should_skip then + local message_fmt = "Last activity sent was within %d seconds ago, skipping..." + self.log:debug(string.format(message_fmt, debounce_timeout)) + return + end + + if buffer then + self:update_for_buffer(buffer, should_debounce) + else + vim.schedule(function() + self:update_for_buffer(self.get_current_buffer(), should_debounce) + end) + end end) -------------------------------------------------- @@ -999,190 +1000,190 @@ end) -- Register some remote peer function Presence:register_peer(id, socket) - self.log:debug(string.format("Registering peer %s...", id)) + self.log:debug(string.format("Registering peer %s...", id)) - self.peers[id] = { - socket = socket, - workspace = nil, - } + self.peers[id] = { + socket = socket, + workspace = nil, + } - self.log:info(string.format("Registered peer %s", id)) + self.log:info(string.format("Registered peer %s", id)) end -- Unregister some remote peer function Presence:unregister_peer(id, peer) - self.log:debug(string.format("Unregistering peer %s... %s", id, vim.inspect(peer))) - - -- Remove workspace if no other peers share the same workspace - -- Initialize to remove if the workspace differs from the local workspace, check peers below - local should_remove_workspace = peer.workspace ~= self.workspace - - local peers = {} - for peer_id, peer_data in pairs(self.peers) do - -- Omit peer from peers list - if peer_id ~= id then - peers[peer_id] = peer_data - - -- Should not remove workspace if another peer shares the workspace - if should_remove_workspace and peer.workspace == peer_data.workspace then - should_remove_workspace = false - end - end - end - - self.peers = peers - - -- Update workspaces if necessary - local workspaces = {} - if should_remove_workspace then - self.log:debug(string.format("Should remove workspace %s", peer.workspace)) - for workspace, data in pairs(self.workspaces) do - if workspace ~= peer.workspace then - workspaces[workspace] = data - end - end - - self.workspaces = workspaces - end - - self.log:info(string.format("Unregistered peer %s", id)) + self.log:debug(string.format("Unregistering peer %s... %s", id, vim.inspect(peer))) + + -- Remove workspace if no other peers share the same workspace + -- Initialize to remove if the workspace differs from the local workspace, check peers below + local should_remove_workspace = peer.workspace ~= self.workspace + + local peers = {} + for peer_id, peer_data in pairs(self.peers) do + -- Omit peer from peers list + if peer_id ~= id then + peers[peer_id] = peer_data + + -- Should not remove workspace if another peer shares the workspace + if should_remove_workspace and peer.workspace == peer_data.workspace then + should_remove_workspace = false + end + end + end + + self.peers = peers + + -- Update workspaces if necessary + local workspaces = {} + if should_remove_workspace then + self.log:debug(string.format("Should remove workspace %s", peer.workspace)) + for workspace, data in pairs(self.workspaces) do + if workspace ~= peer.workspace then + workspaces[workspace] = data + end + end + + self.workspaces = workspaces + end + + self.log:info(string.format("Unregistered peer %s", id)) end -- Unregister some remote peer and set activity function Presence:unregister_peer_and_set_activity(id, peer) - self:unregister_peer(id, peer) - self:update() + self:unregister_peer(id, peer) + self:update() end -- Register a remote peer and sync its data function Presence:register_and_sync_peer(id, socket) - self:register_peer(id, socket) - - self.log:debug("Syncing data with newly registered peer...") - - -- Initialize the remote peer's list including self - local peers = { - [self.id] = { - socket = self.socket, - workspace = self.workspace, - }, - } - for peer_id, peer in pairs(self.peers) do - if peer_id ~= id then - peers[peer_id] = peer - end - end - - self:call_remote_method( - socket, - "sync_self", - { { - last_activity = self.last_activity, - peers = peers, - workspaces = self.workspaces, - } } - ) + self:register_peer(id, socket) + + self.log:debug("Syncing data with newly registered peer...") + + -- Initialize the remote peer's list including self + local peers = { + [self.id] = { + socket = self.socket, + workspace = self.workspace, + }, + } + for peer_id, peer in pairs(self.peers) do + if peer_id ~= id then + peers[peer_id] = peer + end + end + + self:call_remote_method( + socket, + "sync_self", + { { + last_activity = self.last_activity, + peers = peers, + workspaces = self.workspaces, + } } + ) end -- Register self to any remote Neovim instances -- Simply emits to all nvim sockets as we have not yet been synced with peer list function Presence:register_self() - self:get_nvim_socket_paths(function(sockets) - if #sockets == 0 then - self.log:debug("No other remote nvim instances") - return - end + self:get_nvim_socket_paths(function(sockets) + if #sockets == 0 then + self.log:debug("No other remote nvim instances") + return + end - self.log:debug(string.format("Registering as a new peer to %d instance(s)...", #sockets)) + self.log:debug(string.format("Registering as a new peer to %d instance(s)...", #sockets)) - -- Register and sync state with one of the sockets - self:call_remote_method(sockets[1], "register_and_sync_peer", { self.id, self.socket }) + -- Register and sync state with one of the sockets + self:call_remote_method(sockets[1], "register_and_sync_peer", { self.id, self.socket }) - if #sockets == 1 then - return - end + if #sockets == 1 then + return + end - for i = 2, #sockets do - self:call_remote_method(sockets[i], "register_peer", { self.id, self.socket }) - end - end) + for i = 2, #sockets do + self:call_remote_method(sockets[i], "register_peer", { self.id, self.socket }) + end + end) end -- Unregister self to all peers function Presence:unregister_self() - local self_as_peer = { - socket = self.socket, - workspace = self.workspace, - } - - local i = 1 - for id, peer in pairs(self.peers) do - if self.options.auto_update and i == 1 then - self.log:debug(string.format("Unregistering self and setting activity for peer %s...", id)) - self:call_remote_method(peer.socket, "unregister_peer_and_set_activity", { self.id, self_as_peer }) - else - self.log:debug(string.format("Unregistering self to peer %s...", id)) - self:call_remote_method(peer.socket, "unregister_peer", { self.id, self_as_peer }) - end - i = i + 1 - end + local self_as_peer = { + socket = self.socket, + workspace = self.workspace, + } + + local i = 1 + for id, peer in pairs(self.peers) do + if self.options.auto_update and i == 1 then + self.log:debug(string.format("Unregistering self and setting activity for peer %s...", id)) + self:call_remote_method(peer.socket, "unregister_peer_and_set_activity", { self.id, self_as_peer }) + else + self.log:debug(string.format("Unregistering self to peer %s...", id)) + self:call_remote_method(peer.socket, "unregister_peer", { self.id, self_as_peer }) + end + i = i + 1 + end end -- Sync self with data from a remote peer function Presence:sync_self(data) - self.log:debug(string.format("Syncing data from remote peer...", vim.inspect(data))) + self.log:debug(string.format("Syncing data from remote peer...", vim.inspect(data))) - for key, value in pairs(data) do - self[key] = value - end + for key, value in pairs(data) do + self[key] = value + end - self.log:info("Synced runtime data from remote peer") + self.log:info("Synced runtime data from remote peer") end -- Sync activity set by self to all peers function Presence:sync_self_activity() - local self_as_peer = { - socket = self.socket, - workspace = self.workspace, - } - - for id, peer in pairs(self.peers) do - self.log:debug(string.format("Syncing activity to peer %s...", id)) - - local peers = { [self.id] = self_as_peer } - for peer_id, peer_data in pairs(self.peers) do - if peer_id ~= id then - peers[peer_id] = { - socket = peer_data.socket, - workspace = peer_data.workspace, - } - end - end - - self:call_remote_method( - peer.socket, - "sync_peer_activity", - { { - last_activity = self.last_activity, - peers = peers, - workspaces = self.workspaces, - } } - ) - end + local self_as_peer = { + socket = self.socket, + workspace = self.workspace, + } + + for id, peer in pairs(self.peers) do + self.log:debug(string.format("Syncing activity to peer %s...", id)) + + local peers = { [self.id] = self_as_peer } + for peer_id, peer_data in pairs(self.peers) do + if peer_id ~= id then + peers[peer_id] = { + socket = peer_data.socket, + workspace = peer_data.workspace, + } + end + end + + self:call_remote_method( + peer.socket, + "sync_peer_activity", + { { + last_activity = self.last_activity, + peers = peers, + workspaces = self.workspaces, + } } + ) + end end -- Sync activity set by peer function Presence:sync_peer_activity(data) - self.log:debug(string.format("Syncing peer activity %s...", vim.inspect(data))) - self:cancel() - self:sync_self(data) + self.log:debug(string.format("Syncing peer activity %s...", vim.inspect(data))) + self:cancel() + self:sync_self(data) end function Presence:stop() - self.log:debug("Disconnecting from Discord...") - self.discord:disconnect(function() - self.log:info("Disconnected from Discord") - end) + self.log:debug("Disconnecting from Discord...") + self.discord:disconnect(function() + self.log:info("Disconnected from Discord") + end) end -------------------------------------------------- @@ -1191,98 +1192,98 @@ end -- FocusGained events force-update the presence for the current buffer unless it's a quickfix window function Presence:handle_focus_gained() - self.log:debug("Handling FocusGained event...") + self.log:debug("Handling FocusGained event...") - -- Skip a potentially extraneous update call on initial startup if tmux is being used - -- (See https://github.com/neovim/neovim/issues/14572) - if next(self.last_activity) == nil and os.getenv("TMUX") then - self.log:debug("Skipping presence update for FocusGained event triggered by tmux...") - return - end + -- Skip a potentially extraneous update call on initial startup if tmux is being used + -- (See https://github.com/neovim/neovim/issues/14572) + if next(self.last_activity) == nil and os.getenv("TMUX") then + self.log:debug("Skipping presence update for FocusGained event triggered by tmux...") + return + end - if vim.bo.filetype == "qf" then - self.log:debug("Skipping presence update for quickfix window...") - return - end + if vim.bo.filetype == "qf" then + self.log:debug("Skipping presence update for quickfix window...") + return + end - self:update() + self:update() end -- TextChanged events debounce current buffer presence updates function Presence:handle_text_changed() - self.log:debug("Handling TextChanged event...") - self:update(nil, true) + self.log:debug("Handling TextChanged event...") + self:update(nil, true) end -- VimLeavePre events unregister the leaving instance to all peers and sets activity for the first peer function Presence:handle_vim_leave_pre() - self.log:debug("Handling VimLeavePre event...") - self:unregister_self() - self:cancel() + self.log:debug("Handling VimLeavePre event...") + self:unregister_self() + self:cancel() end -- WinEnter events force-update the current buffer presence unless it's a quickfix window function Presence:handle_win_enter() - self.log:debug("Handling WinEnter event...") + self.log:debug("Handling WinEnter event...") - vim.schedule(function() - if vim.bo.filetype == "qf" then - self.log:debug("Skipping presence update for quickfix window...") - return - end + vim.schedule(function() + if vim.bo.filetype == "qf" then + self.log:debug("Skipping presence update for quickfix window...") + return + end - self:update() - end) + self:update() + end) end -- WinLeave events cancel the current buffer presence function Presence:handle_win_leave() - self.log:debug("Handling WinLeave event...") - - local current_window = vim.api.nvim_get_current_win() - - vim.schedule(function() - -- Avoid canceling presence when switching to a quickfix window - if vim.bo.filetype == "qf" then - self.log:debug("Not canceling presence due to switching to quickfix window...") - return - end - - -- Avoid canceling presence when switching between windows - if current_window ~= vim.api.nvim_get_current_win() then - self.log:debug("Not canceling presence due to switching to a window within the same instance...") - return - end - - self.log:debug("Canceling presence due to leaving window...") - self:cancel() - end) + self.log:debug("Handling WinLeave event...") + + local current_window = vim.api.nvim_get_current_win() + + vim.schedule(function() + -- Avoid canceling presence when switching to a quickfix window + if vim.bo.filetype == "qf" then + self.log:debug("Not canceling presence due to switching to quickfix window...") + return + end + + -- Avoid canceling presence when switching between windows + if current_window ~= vim.api.nvim_get_current_win() then + self.log:debug("Not canceling presence due to switching to a window within the same instance...") + return + end + + self.log:debug("Canceling presence due to leaving window...") + self:cancel() + end) end -- BufEnter events force-update the presence for the current buffer unless it's a quickfix window function Presence:handle_buf_enter() - self.log:debug("Handling BufEnter event...") + self.log:debug("Handling BufEnter event...") - if vim.bo.filetype == "qf" then - self.log:debug("Skipping presence update for quickfix window...") - return - end + if vim.bo.filetype == "qf" then + self.log:debug("Skipping presence update for quickfix window...") + return + end - self:update() + self:update() end -- BufAdd events force-update the presence for the current buffer unless it's a quickfix window function Presence:handle_buf_add() - self.log:debug("Handling BufAdd event...") + self.log:debug("Handling BufAdd event...") - vim.schedule(function() - if vim.bo.filetype == "qf" then - self.log:debug("Skipping presence update for quickfix window...") - return - end + vim.schedule(function() + if vim.bo.filetype == "qf" then + self.log:debug("Skipping presence update for quickfix window...") + return + end - self:update() - end) + self:update() + end) end return Presence diff --git a/lua/presence/plugin_managers.lua b/lua/presence/plugin_managers.lua index 3789ba3..7b92a43 100644 --- a/lua/presence/plugin_managers.lua +++ b/lua/presence/plugin_managers.lua @@ -1,5 +1,5 @@ -- Different plugin manager names return { - ["packer"] = "packer", - ["vim-plug"] = "vim-plug", + ["packer"] = "packer", + ["vim-plug"] = "vim-plug", } From faa6371d23b7ee01be3eabd4bcc97ef9d1242e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Fri, 29 Sep 2023 02:41:15 +0200 Subject: [PATCH 16/22] feat(bug_template): add checks, add log formating (#16) --- .github/ISSUE_TEMPLATE/bug_report.yml | 59 +++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index ae98a5a..580930c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,47 +1,98 @@ name: Issue report description: Report any errors, bugs, or unexpected behaviors related to presence.nvim +title: "[Bug]: " labels: [bug] +assignees: + - jiriks74 body: - type: markdown attributes: value: | Before reporting, please search [existing issues](https://github.com/andweeb/presence.nvim/issues) and make sure that presence.nvim is updated to the latest version. + + - type: checkboxes + attributes: + label: Are you on the latest version? + options: + - label: I have updated to the latest version. + required: true + + - type: checkboxes + attributes: + label: Have you tried it with default config? + options: + - label: I have tried the default config. + required: true + - type: textarea attributes: label: "Description" description: "A short summary of the error, bug, or unexpected behavior you're facing." validations: required: true + - type: textarea attributes: label: "Neovim version" description: "Output of `nvim --version`" render: markdown placeholder: | - NVIM v0.6.0-dev+209-g0603eba6e + NVIM: v0.6.0-dev+209-g0603eba6e Build type: Release - LuaJIT 2.1.0-beta3 + LuaJIT: 2.1.0-beta3 + value: | + NVIM: + Build type: + LuaJIT: validations: required: true + - type: input attributes: label: "OS information" placeholder: "macOS 12.0.1" validations: required: true + - type: textarea attributes: label: "Steps to reproduce" - description: "Steps to reproduce the issue with your config(s) if applicable" + description: "Steps to reproduce the issue with your config(s) if applicable." placeholder: | 1. Setup presence.nvim with `require("presence"):setup({...})` 2. Run Neovim with `nvim test.txt` 3. ... validations: required: true + - type: textarea attributes: label: "Logs" - description: "The full list of `:messages` from one or more `nvim` instances" + description: "The full list of `:messages` from one or more `nvim` instances.\nPlease insert the logs into code blocks." + placeholder: | +
+ + ``` + [presence.nvim] Using runtime path: /run/user/1000 + [presence.nvim] Using Discord IPC socket path: /run/user/1000/discord-ipc-0 + [presence.nvim] Checking Discord IPC socket at /run/user/1000/discord-ipc-0... + ``` + +
+ value: | +
+ + ``` + + ``` + +
validations: required: true + + - type: textarea + attributes: + label: "Aditional info" + description: "If you'd like to add anything else put it here." + validations: + required: false From dbb8f797f4355db3aa1519eaeb133a8df981e96f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Fri, 29 Sep 2023 02:51:28 +0200 Subject: [PATCH 17/22] feat(featreq_template): convert from markdown to issue form (#18) --- .github/ISSUE_TEMPLATE/feature_request.md | 20 ------------- .github/ISSUE_TEMPLATE/feature_request.yml | 34 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 20 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 11fc491..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..6de2161 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,34 @@ +name: Issue report +description: Report any errors, bugs, or unexpected behaviors related to presence.nvim +title: "[FEAT]: " +labels: [enhancement] +assignees: + - jiriks74 +body: + - type: textarea + attributes: + label: Is your feature request related to a problem? + description: A clear and concise description of what the problem is. Ex. I'm always frustrated when... + validations: + required: true + + - type: textarea + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + validations: + required: false + + - type: textarea + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: false + + - type: textarea + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. + validations: + required: false From 5fa130577a183772d76f4a741521860bdd5ee06f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Fri, 29 Sep 2023 02:56:40 +0200 Subject: [PATCH 18/22] fix(featreq_template): change name from Issue report to Feature request (#20) --- .github/ISSUE_TEMPLATE/feature_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 6de2161..9ebd200 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,4 +1,4 @@ -name: Issue report +name: Feature request description: Report any errors, bugs, or unexpected behaviors related to presence.nvim title: "[FEAT]: " labels: [enhancement] From 9ddeff558bff9b0f93e0b094559ed60087014102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Thu, 9 Nov 2023 15:59:42 +0100 Subject: [PATCH 19/22] fix: move to a new application id (#23) to prevent potential issues with the original project --- README.md | 4 ++-- lua/presence/init.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fe70930..29e3119 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ require("presence").setup({ auto_update = true, -- Update activity based on autocmd events (if `false`, map or manually execute `:lua package.loaded.presence:update()`) neovim_image_text = "The One True Text Editor", -- Text displayed when hovered over the Neovim image main_image = "neovim", -- Main image display (either "neovim" or "file") - client_id = "793271441293967371", -- Use your own Discord application client id (not recommended) + client_id = "1172122807501594644", -- Use your own Discord application client id (not recommended) log_level = nil, -- Log messages at or above this level (one of the following: "debug", "info", "warn", "error") debounce_timeout = 10, -- Number of seconds to debounce events (or calls to `:lua package.loaded.presence:update(, true)`) enable_line_number = false, -- Displays the current line number instead of the current project @@ -91,7 +91,7 @@ Or if global variables are more your thing, you can use any of the following ins let g:presence_auto_update = 1 let g:presence_neovim_image_text = "The One True Text Editor" let g:presence_main_image = "neovim" -let g:presence_client_id = "793271441293967371" +let g:presence_client_id = "1172122807501594644" let g:presence_log_level let g:presence_debounce_timeout = 10 let g:presence_enable_line_number = 0 diff --git a/lua/presence/init.lua b/lua/presence/init.lua index efdfebb..c913242 100644 --- a/lua/presence/init.lua +++ b/lua/presence/init.lua @@ -116,7 +116,7 @@ function Presence:setup(...) -- General options self:set_option("auto_update", 1) - self:set_option("client_id", "793271441293967371") + self:set_option("client_id", "1172122807501594644") self:set_option("debounce_timeout", 10) self:set_option("neovim_image_text", "The One True Text Editor") self:set_option("main_image", "neovim") From 1570ddd51bdf8696b4aca4d022358333522a268e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Mon, 4 Dec 2023 23:16:58 +0100 Subject: [PATCH 20/22] fix: move to a new application id (#26) to prevent potential issues with the original project From 68086bdb283d63fd75da56d1b7d3a70c5424ebbb Mon Sep 17 00:00:00 2001 From: Andrey Kaipov <9457739+andreykaipov@users.noreply.github.com> Date: Sun, 11 Feb 2024 18:35:20 -0500 Subject: [PATCH 21/22] make nvim socket path discovery more permissive --- lua/presence/init.lua | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/lua/presence/init.lua b/lua/presence/init.lua index c913242..1972fc5 100644 --- a/lua/presence/init.lua +++ b/lua/presence/init.lua @@ -531,8 +531,8 @@ function Presence:get_nvim_socket_paths(on_done) if self.os.is_wsl then -- TODO: There needs to be a better way of doing this... no support for ss/netstat? -- (See https://github.com/microsoft/WSL/issues/2249) - local cmd_fmt = "for file in %s/nvim*; do echo $file/0; done" - local shell_cmd = string.format(cmd_fmt, vim.loop.os_tmpdir() or "/tmp") + local cmd_fmt = "for file in %s/nvim*; do echo $file; done" + local shell_cmd = string.format(cmd_fmt, vim.fn.stdpath("run") or "/tmp") cmd = { "sh", @@ -558,14 +558,14 @@ function Presence:get_nvim_socket_paths(on_done) cmd = table.concat({ "netstat -u", - [[grep --color=never "nvim.*/0"]], + [[grep -E --color=never ".+nvim.+"]], }, "|") elseif self.os.name == "linux" then if vim.fn.executable("ss") == 1 then -- Use `ss` if available cmd = table.concat({ "ss -lx", - [[grep "nvim.*/0"]], + [[grep -E ".+nvim.+"]], }, "|") -- Define ss output parser @@ -576,7 +576,7 @@ function Presence:get_nvim_socket_paths(on_done) -- Use `netstat` if available cmd = table.concat({ "netstat -u", - [[grep --color=never "nvim.*/0"]], + [[grep -E --color=never ".+nvim.+"]], }, "|") -- Define netstat output parser @@ -1074,15 +1074,13 @@ function Presence:register_and_sync_peer(id, socket) end end - self:call_remote_method( - socket, - "sync_self", - { { + self:call_remote_method(socket, "sync_self", { + { last_activity = self.last_activity, peers = peers, workspaces = self.workspaces, - } } - ) + }, + }) end -- Register self to any remote Neovim instances @@ -1160,15 +1158,13 @@ function Presence:sync_self_activity() end end - self:call_remote_method( - peer.socket, - "sync_peer_activity", - { { + self:call_remote_method(peer.socket, "sync_peer_activity", { + { last_activity = self.last_activity, peers = peers, workspaces = self.workspaces, - } } - ) + }, + }) end end From fee2f8d2bd603fc0fa102deb45d03191d3738ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=A0tefka?= Date: Wed, 28 Feb 2024 18:37:44 +0100 Subject: [PATCH 22/22] fix(docs): Remove issues reported by Markdownlint (#37) * fix(docs): Remove inline HTML elements * fix(docs): Lines longer than 80 characters * fix(docs): Lines over 80 chars * fix: docs * fix(docs): readability * fix(docs): MD043 - Required heading structure * Revert "fix(docs): MD043 - Required heading structure" This reverts commit fa2179bc85d94354026191a31cb11a52bee4cfd7. --- README.md | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 29e3119..912282b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ -[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits&logoColor=white)](https://conventionalcommits.org) +# ![presence.nvim](https://gist.githubusercontent.com/andweeb/df3216345530234289b87cf5080c2c60/raw/8de399cfed82c137f793e9f580027b5246bc4379/presence.nvim.png) -presence.nvim​ +This repository uses +[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits&logoColor=white)](https://conventionalcommits.org) -**[Features](#features)** | **[Installation](#installation)** | **[Configuration](#configuration)** | **[Troubleshooting](#troubleshooting)** | **[Development](#development)** | **[Contributing](#contributing)** +**[Features](#features)** | **[Installation](#installation)** | +**[Configuration](#configuration)** | **[Troubleshooting](#troubleshooting)** | +**[Development](#development)** | **[Contributing](#contributing)** > Discord [Rich Presence](https://discord.com/rich-presence) plugin for [Neovim](https://neovim.io) -demo.gif +![Presence demo](https://gist.githubusercontent.com/andweeb/df3216345530234289b87cf5080c2c60/raw/ad916fec8de921d0021801a0af877a5349621e7e/presence-demo-a.gif) ## Features @@ -14,7 +17,8 @@ - No Python/Node providers (or CoC) required - Cross-platform support: macOS, nixOS, Linux[\*](#notes), Windows, WSL -- Startup time is fast(er than other Rich Presence plugins, by [kind of a lot](https://github.com/andweeb/presence.nvim/wiki/Plugin-Comparisons)) +- Startup time is fast(er than other Rich Presence plugins, by +[kind of a lot](https://github.com/andweeb/presence.nvim/wiki/Plugin-Comparisons)) - Written in Lua and [highly configurable](#configuration) in Lua (but also configurable in VimL if you want) - Manages Rich Presence across multiple Neovim instances in various environments @@ -36,13 +40,13 @@ Use your favorite plugin manager }, ``` - ### Notes - Requires [Neovim 0.5](https://github.com/neovim/neovim/releases/tag/v0.5.0) or higher - Rich Presence should work automatically after installation -(unless you're using WSL, in which case [see here](https://github.com/andweeb/presence.nvim/wiki/Rich-Presence-in-WSL)) +(unless you're using WSL, in which case +[see here](https://github.com/andweeb/presence.nvim/wiki/Rich-Presence-in-WSL)) ## Configuration @@ -121,11 +125,14 @@ let g:presence_line_number_text = "Line %s out of %s" - Load a file and inspect the logs with `:messages` - If there is a `Failed to determine Discord IPC socket` error, your particular OS may not yet be supported - - If you don't see an existing [issue](https://github.com/jiriks74/presence.nvim/issues) + - If you don't see an existing + [issue](https://github.com/jiriks74/presence.nvim/issues) or [card](https://github.com/jiriks74/presence.nvim/projects/1#column-14183588) - for your OS, create a prefixed [issue](https://github.com/jiriks74/presence.nvim/issues/new) + for your OS, create a prefixed + [issue](https://github.com/jiriks74/presence.nvim/issues/new) (e.g. `[Void Linux]`) -- Still not working and need help? Create a new [issue](https://github.com/jiriks74/presence.nvim/issues)! +- Still not working and need help? Create a new +[issue](https://github.com/jiriks74/presence.nvim/issues)! ## Development @@ -139,10 +146,12 @@ errors: `luacheck lua` ## Contributing -**Please use [Conventional Commits](https://www.conventionalcommits.org/) if you want to contribute. +**Please use [Conventional Commits](https://www.conventionalcommits.org/) +if you want to contribute. It makes everyones jobs easier.** -**This project uses [StyLua](https://github.com/JohnnyMorganz/StyLua). Please format your code using StyLua for better readability** +**This project uses [StyLua](https://github.com/JohnnyMorganz/StyLua). +Please format your code using StyLua for better readability** Pull requests are very welcome, feel free to open an issue to work on or message [me (@jiriks74)](https://discordapp.com/users/517810049360461837) on my