From f92f56d42ed7ae8bc7ac96d44b0f63661131c895 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Wed, 27 Apr 2022 21:53:35 +0200 Subject: [PATCH 01/16] Manage return values from "replacer" function in `Doc:replace` Before the addition of multi-cursor support, we just returned the second return value of the "replacer" function to the caller. With the introduction of multi-cursors, we naively summed the second return values for each cursor. In some cases the "replacer" function doesn't return any second value, so we tried to do math with `nil`, thus throwing errors. Now the second return value is added to a table which is then returned to the caller. --- data/core/doc/init.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index f9369a46..460e387d 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -448,7 +448,7 @@ end function Doc:replace_cursor(idx, line1, col1, line2, col2, fn) local old_text = self:get_text(line1, col1, line2, col2) - local new_text, n = fn(old_text) + local new_text, res = fn(old_text) if old_text ~= new_text then self:insert(line2, col2, new_text) self:remove(line1, col1, line2, col2) @@ -457,22 +457,22 @@ function Doc:replace_cursor(idx, line1, col1, line2, col2, fn) self:set_selections(idx, line1, col1, line2, col2) end end - return n + return res end function Doc:replace(fn) - local has_selection, n = false, 0 + local has_selection, results = false, { } for idx, line1, col1, line2, col2 in self:get_selections(true) do if line1 ~= line2 or col1 ~= col2 then - n = n + self:replace_cursor(idx, line1, col1, line2, col2, fn) + results[idx] = self:replace_cursor(idx, line1, col1, line2, col2, fn) has_selection = true end end if not has_selection then self:set_selection(table.unpack(self.selections)) - n = n + self:replace_cursor(1, 1, 1, #self.lines, #self.lines[#self.lines], fn) + results[1] = self:replace_cursor(1, 1, 1, #self.lines, #self.lines[#self.lines], fn) end - return n + return results end From 93be54e9c3c1c143e7a276347f6f4896cda30827 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Thu, 5 May 2022 12:35:21 -0400 Subject: [PATCH 02/16] meson: install docs/api to datadir for lsp support --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 0e5c62ac..f303049c 100644 --- a/meson.build +++ b/meson.build @@ -174,6 +174,7 @@ endif install_data('licenses/licenses.md', install_dir : lite_docdir) +install_subdir('docs/api' , install_dir : lite_datadir, strip_directory: true) install_subdir('data/core' , install_dir : lite_datadir, exclude_files : 'start.lua') foreach data_module : ['fonts', 'plugins', 'colors'] install_subdir(join_paths('data', data_module), install_dir : lite_datadir) From 359880e963b64a0a6c348f0574e494872f9058e1 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Wed, 11 May 2022 00:20:13 -0400 Subject: [PATCH 03/16] c core: fix extra utf8 build conflict on windows --- data/core/utf8string.lua | 2 ++ docs/api/{utf8.lua => utf8extra.lua} | 46 +++++++++++++++------------- src/api/api.c | 4 +-- src/api/utf8.c | 2 +- 4 files changed, 30 insertions(+), 24 deletions(-) rename docs/api/{utf8.lua => utf8extra.lua} (84%) diff --git a/data/core/utf8string.lua b/data/core/utf8string.lua index 1a2da19b..a22a0ef6 100644 --- a/data/core/utf8string.lua +++ b/data/core/utf8string.lua @@ -2,6 +2,8 @@ -- inject utf8 functions to strings -------------------------------------------------------------------------------- +local utf8 = require "utf8extra" + string.ubyte = utf8.byte string.uchar = utf8.char string.ufind = utf8.find diff --git a/docs/api/utf8.lua b/docs/api/utf8extra.lua similarity index 84% rename from docs/api/utf8.lua rename to docs/api/utf8extra.lua index d4dff5a9..1ff4dcb6 100644 --- a/docs/api/utf8.lua +++ b/docs/api/utf8extra.lua @@ -1,19 +1,23 @@ ---@meta +---Additional utf8 support not provided by lua. +---@class utf8extra +utf8extra = {} + ---UTF-8 equivalent of string.byte ---@param s string ---@param i? integer ---@param j? integer ---@return integer ---@return ... -function utf8.byte(s, i, j) end +function utf8extra.byte(s, i, j) end ---UTF-8 equivalent of string.char ---@param byte integer ---@param ... integer ---@return string ---@return ... -function utf8.char(byte, ...) end +function utf8extra.char(byte, ...) end ---UTF-8 equivalent of string.find ---@param s string @@ -23,14 +27,14 @@ function utf8.char(byte, ...) end ---@return integer start ---@return integer end ---@return ... captured -function utf8.find(s, pattern, init, plain) end +function utf8extra.find(s, pattern, init, plain) end ---UTF-8 equivalent of string.gmatch ---@param s string ---@param pattern string ---@param init? integer ---@return fun():string, ... -function utf8.gmatch(s, pattern, init) end +function utf8extra.gmatch(s, pattern, init) end ---UTF-8 equivalent of string.gsub ---@param s string @@ -39,41 +43,41 @@ function utf8.gmatch(s, pattern, init) end ---@param n integer ---@return string ---@return integer count -function utf8.gsub(s, pattern, repl, n) end +function utf8extra.gsub(s, pattern, repl, n) end ---UTF-8 equivalent of string.len ---@param s string ---@return integer -function utf8.len(s) end +function utf8extra.len(s) end ---UTF-8 equivalent of string.lower ---@param s string ---@return string -function utf8.lower(s) end +function utf8extra.lower(s) end ---UTF-8 equivalent of string.match ---@param s string ---@param pattern string ---@param init? integer ---@return string | number captured -function utf8.match(s, pattern, init) end +function utf8extra.match(s, pattern, init) end ---UTF-8 equivalent of string.reverse ---@param s string ---@return string -function utf8.reverse(s) end +function utf8extra.reverse(s) end ---UTF-8 equivalent of string.sub ---@param s string ---@param i integer ---@param j? integer ---@return string -function utf8.sub(s, i, j) end +function utf8extra.sub(s, i, j) end ---UTF-8 equivalent of string.upper ---@param s string ---@return string -function utf8.upper(s) end +function utf8extra.upper(s) end ---Escape a str to UTF-8 format string. It support several escape format: ---* %ddd - which ddd is a decimal number at any length: change Unicode code point to UTF-8 format. @@ -91,7 +95,7 @@ function utf8.upper(s) end ---``` ---@param s string ---@return string utf8_string -function utf8.escape(s) end +function utf8extra.escape(s) end ---Convert UTF-8 position to byte offset. if only index is given, return byte ---offset of this UTF-8 char index. if both charpos and index is given, a new @@ -103,7 +107,7 @@ function utf8.escape(s) end ---@param index? integer ---@return integer charpos ---@return integer codepoint -function utf8.charpos(s, charpos, index) end +function utf8extra.charpos(s, charpos, index) end ---Iterate though the UTF-8 string s. If only s is given, it can used as a iterator: ---```lua @@ -120,7 +124,7 @@ function utf8.charpos(s, charpos, index) end ---@param index? integer ---@return integer charpos ---@return integer codepoint -function utf8.next(s, charpos, index) end +function utf8extra.next(s, charpos, index) end ---Insert a substring to s. If idx is given, insert substring before char at ---this index, otherwise substring will concat to s. idx can be negative. @@ -128,7 +132,7 @@ function utf8.next(s, charpos, index) end ---@param idx? integer ---@param substring string ---return string new_string -function utf8.insert(s, idx, substring) end +function utf8extra.insert(s, idx, substring) end ---Delete a substring in s. If neither start nor stop is given, delete the last ---UTF-8 char in s, otherwise delete char from start to end of s. if stop is @@ -138,7 +142,7 @@ function utf8.insert(s, idx, substring) end ---@param start? integer ---@param stop? integer ---return string new_string -function utf8.remove(s, start, stop) end +function utf8extra.remove(s, start, stop) end ---Calculate the width of UTF-8 string s. if ambi_is_double is given, the ---ambiguous width character's width is 2, otherwise it's 1. fullwidth/doublewidth @@ -150,7 +154,7 @@ function utf8.remove(s, start, stop) end ---@param ambi_is_double? boolean ---@param default_width? integer ---@return integer width -function utf8.width(s, ambi_is_double, default_width) end +function utf8extra.width(s, ambi_is_double, default_width) end ---Return the character index at given location in string s. this is a reverse ---operation of utf8.width(). this function returns a index of location, and a @@ -164,24 +168,24 @@ function utf8.width(s, ambi_is_double, default_width) end ---@return integer idx ---@return integer offset ---@return integer width -function utf8.widthindex(s, location, ambi_is_double, default_width) end +function utf8extra.widthindex(s, location, ambi_is_double, default_width) end ---Convert UTF-8 string s to title-case, used to compare by ignore case. if s ---is a number, it's treat as a code point and return a convert code point ---(number). utf8.lower/utf8.pper has the same extension. ---@param s string ---return string new_string -function utf8.title(s) end +function utf8extra.title(s) end ---Convert UTF-8 string s to folded case, used to compare by ignore case. if s ---is a number, it's treat as a code point and return a convert code point ---(number). utf8.lower/utf8.pper has the same extension. ---@param s string ---return string new_string -function utf8.fold(s) end +function utf8extra.fold(s) end ---Compare a and b without case, -1 means a < b, 0 means a == b and 1 means a > b. ---@param a string ---@param b string ---@return integer result -function utf8.ncasecmp(a, b) end +function utf8extra.ncasecmp(a, b) end diff --git a/src/api/api.c b/src/api/api.c index 67a05f19..acf6eec2 100644 --- a/src/api/api.c +++ b/src/api/api.c @@ -5,7 +5,7 @@ int luaopen_renderer(lua_State *L); int luaopen_regex(lua_State *L); int luaopen_process(lua_State *L); int luaopen_dirmonitor(lua_State* L); -int luaopen_utf8(lua_State* L); +int luaopen_utf8extra(lua_State* L); static const luaL_Reg libs[] = { { "system", luaopen_system }, @@ -13,7 +13,7 @@ static const luaL_Reg libs[] = { { "regex", luaopen_regex }, { "process", luaopen_process }, { "dirmonitor", luaopen_dirmonitor }, - { "utf8", luaopen_utf8 }, + { "utf8extra", luaopen_utf8extra }, { NULL, NULL } }; diff --git a/src/api/utf8.c b/src/api/utf8.c index e1a4ebfe..6f0d6c17 100644 --- a/src/api/utf8.c +++ b/src/api/utf8.c @@ -1264,7 +1264,7 @@ static const char UTF8PATT[] = "[\0-\x7F\xC2-\xF4][\x80-\xBF]*"; static const char UTF8PATT[] = "[%z\1-\x7F\xC2-\xF4][\x80-\xBF]*"; #endif -int luaopen_utf8 (lua_State *L) { +int luaopen_utf8extra (lua_State *L) { luaL_Reg libs[] = { #define ENTRY(name) { #name, Lutf8_##name } ENTRY(offset), From 1d1b3e0a09d6da91edd609643887127a725240d7 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Thu, 12 May 2022 18:33:56 -0400 Subject: [PATCH 04/16] Add utf8 support on doc lower and upper commands --- data/core/commands/doc.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/core/commands/doc.lua b/data/core/commands/doc.lua index ed35913a..87079180 100644 --- a/data/core/commands/doc.lua +++ b/data/core/commands/doc.lua @@ -180,7 +180,7 @@ local commands = { local line, col = doc():get_selection() doc():set_selection(line, col) end, - + ["doc:cut"] = function() cut_or_copy(true) end, @@ -402,11 +402,11 @@ local commands = { end, ["doc:upper-case"] = function() - doc():replace(string.upper) + doc():replace(string.uupper) end, ["doc:lower-case"] = function() - doc():replace(string.lower) + doc():replace(string.ulower) end, ["doc:go-to-line"] = function() From fd0a433f59ddcd6c6d1f8de83db2da1079b93868 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Thu, 12 May 2022 20:33:01 -0400 Subject: [PATCH 05/16] object: made is() stricter and added extends() Currently some plugins had/have issues with predicates that check if active view is a docview to perform certain operations like draw in the case of minimap or lineguide. Since is() was checking the entire inheritance tree it was returning true for views that inherit from the same parent, which caused CommandView to be matched along DocView, etc... This change does the following to solve the issue: * Make Object:is() only match the top level parent of the object which is more in line with what one would expect from a method named 'is'. * Introduces Object:extends() which keeps the same functionality that Object:is() offered before. --- data/core/object.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/data/core/object.lua b/data/core/object.lua index 0941ce5d..a57ac9e8 100644 --- a/data/core/object.lua +++ b/data/core/object.lua @@ -21,6 +21,14 @@ end function Object:is(T) + if getmetatable(self) == T then + return true + end + return false +end + + +function Object:extends(T) local mt = getmetatable(self) while mt do if mt == T then From b8ed4a43f62ec1ea857e3c005bf6c7a217d20f49 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Mon, 9 May 2022 20:02:32 -0400 Subject: [PATCH 06/16] keymap: changes and docs * Prevent adding duplicate bindings * Clean reverse_map on overwrite or add direct * Added get_bindings to complement get_binding * Added doc comments for easier comprehension * Check if command is function on add_direct --- data/core/keymap.lua | 144 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 116 insertions(+), 28 deletions(-) diff --git a/data/core/keymap.lua b/data/core/keymap.lua index d24eb85a..139aeb79 100644 --- a/data/core/keymap.lua +++ b/data/core/keymap.lua @@ -3,33 +3,111 @@ local command = require "core.command" local config = require "core.config" local keymap = {} +---@alias keymap.shortcut string +---@alias keymap.command string +---@alias keymap.modkey string +---@alias keymap.pressed boolean +---@alias keymap.map table +---@alias keymap.rmap table + +---Pressed status of mod keys. +---@type table keymap.modkeys = {} + +---List of commands assigned to a shortcut been the key of the map the shortcut. +---@type keymap.map keymap.map = {} + +---List of shortcuts assigned to a command been the key of the map the command. +---@type keymap.rmap keymap.reverse_map = {} local macos = PLATFORM == "Mac OS X" -- Thanks to mathewmariani, taken from his lite-macos github repository. local modkeys_os = require("core.modkeys-" .. (macos and "macos" or "generic")) + +---@type table local modkey_map = modkeys_os.map + +---@type keymap.modkey[] local modkeys = modkeys_os.keys -local function key_to_stroke(k) + +---Generates a stroke sequence including currently pressed mod keys. +---@param key string +---@return string +local function key_to_stroke(key) local stroke = "" for _, mk in ipairs(modkeys) do if keymap.modkeys[mk] then stroke = stroke .. mk .. "+" end end - return stroke .. k + return stroke .. key end +---Remove the given value from an array associated to a key in a table. +---@param tbl table The table containing the key +---@param k string The key containing the array +---@param v? string The value to remove from the array +local function remove_only(tbl, k, v) + if tbl[k] then + if v then + local j = 0 + for i=1, #tbl[k] do + while tbl[k][i + j] == v do + j = j + 1 + end + tbl[k][i] = tbl[k][i + j] + end + else + tbl[k] = nil + end + end +end + + +---Removes from a keymap.map the bindings that are already registered. +---@param map keymap.map +local function remove_duplicates(map) + for stroke, commands in pairs(map) do + if type(commands) == "string" or type(commands) == "function" then + commands = { commands } + end + if keymap.map[stroke] then + for _, registered_cmd in ipairs(keymap.map[stroke]) do + local j = 0 + for i=1, #commands do + while commands[i + j] == registered_cmd do + j = j + 1 + end + commands[i] = commands[i + j] + end + end + end + if #commands < 1 then + map[stroke] = nil + else + map[stroke] = commands + end + end +end + + +---Add bindings by replacing commands that were previously assigned to a shortcut. +---@param map keymap.map function keymap.add_direct(map) for stroke, commands in pairs(map) do - if type(commands) == "string" then + if type(commands) == "string" or type(commands) == "function" then commands = { commands } end + if keymap.map[stroke] then + for _, cmd in ipairs(keymap.map[stroke]) do + remove_only(keymap.reverse_map, cmd, stroke) + end + end keymap.map[stroke] = commands for _, cmd in ipairs(commands) do keymap.reverse_map[cmd] = keymap.reverse_map[cmd] or {} @@ -38,15 +116,23 @@ function keymap.add_direct(map) end end + +---Adds bindings by appending commands to already registered shortcut or by +---replacing currently assigned commands if overwrite is specified. +---@param map keymap.map +---@param overwrite? boolean function keymap.add(map, overwrite) + remove_duplicates(map) for stroke, commands in pairs(map) do if macos then stroke = stroke:gsub("%f[%a]ctrl%f[%A]", "cmd") end - if type(commands) == "string" or type(commands) == "function" then - commands = { commands } - end if overwrite then + if keymap.map[stroke] then + for _, cmd in ipairs(keymap.map[stroke]) do + remove_only(keymap.reverse_map, cmd, stroke) + end + end keymap.map[stroke] = commands else keymap.map[stroke] = keymap.map[stroke] or {} @@ -62,35 +148,34 @@ function keymap.add(map, overwrite) end -local function remove_only(tbl, k, v) - for key, values in pairs(tbl) do - if key == k then - if v then - for i, value in ipairs(values) do - if value == v then - table.remove(values, i) - end - end - else - tbl[key] = nil - end - break - end - end -end - - -function keymap.unbind(key, cmd) - remove_only(keymap.map, key, cmd) - remove_only(keymap.reverse_map, cmd, key) +---Unregisters the given shortcut and associated command. +---@param shortcut string +---@param cmd string +function keymap.unbind(shortcut, cmd) + remove_only(keymap.map, shortcut, cmd) + remove_only(keymap.reverse_map, cmd, shortcut) end +---Returns all the shortcuts associated to a command unpacked for easy assignment. +---@param cmd string +---@return ... function keymap.get_binding(cmd) return table.unpack(keymap.reverse_map[cmd] or {}) end +---Returns all the shortcuts associated to a command packed in a table. +---@param cmd string +---@return table | nil shortcuts +function keymap.get_bindings(cmd) + return keymap.reverse_map[cmd] +end + + +-------------------------------------------------------------------------------- +-- Events listening +-------------------------------------------------------------------------------- function keymap.on_key_pressed(k, ...) local mk = modkey_map[k] if mk then @@ -101,7 +186,7 @@ function keymap.on_key_pressed(k, ...) end else local stroke = key_to_stroke(k) - local commands, performed = keymap.map[stroke] + local commands, performed = keymap.map[stroke], false if commands then for _, cmd in ipairs(commands) do if type(cmd) == "function" then @@ -143,6 +228,9 @@ function keymap.on_key_released(k) end +-------------------------------------------------------------------------------- +-- Register default bindings +-------------------------------------------------------------------------------- if macos then local keymap_macos = require("core.keymap-macos") keymap_macos(keymap) From 59d91087e9b1ff3990f2d5543432531ee11ae5b0 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Thu, 12 May 2022 22:15:29 -0400 Subject: [PATCH 07/16] adjust and consolidate duplicated predicate code --- data/core/command.lua | 22 ++++++++++++++++++++-- data/core/contextmenu.lua | 9 +-------- data/core/statusview.lua | 17 +++-------------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/data/core/command.lua b/data/core/command.lua index bdc1ed34..39582de3 100644 --- a/data/core/command.lua +++ b/data/core/command.lua @@ -6,15 +6,33 @@ command.map = {} local always_true = function() return true end -function command.add(predicate, map) +---Used iternally by command.add, statusview, and contextmenu to generate a +---function with a condition to evaluate returning the boolean result of this +---evaluation. +--- +---If a string predicate is given it is treated as a require import that should +---return a valid object which is checked against the current active view, the +---sames applies if a table is given. A function that returns a boolean can be +---used instead to perform a custom evaluation, setting to nil means always +---evaluates to true. +--- +---@param predicate string | table | function +---@return function +function command.generate_predicate(predicate) predicate = predicate or always_true if type(predicate) == "string" then predicate = require(predicate) end if type(predicate) == "table" then local class = predicate - predicate = function() return core.active_view:is(class) end + predicate = function() return core.active_view:extends(class) end end + return predicate +end + + +function command.add(predicate, map) + predicate = command.generate_predicate(predicate) for name, fn in pairs(map) do assert(not command.map[name], "command already exists: " .. name) command.map[name] = { predicate = predicate, perform = fn } diff --git a/data/core/contextmenu.lua b/data/core/contextmenu.lua index b1a69d6d..94ef61f8 100644 --- a/data/core/contextmenu.lua +++ b/data/core/contextmenu.lua @@ -39,14 +39,7 @@ local function get_item_size(item) end function ContextMenu:register(predicate, items) - if type(predicate) == "string" then - predicate = require(predicate) - end - if type(predicate) == "table" then - local class = predicate - predicate = function() return core.active_view:is(class) end - end - + predicate = command.generate_predicate(predicate) local width, height = 0, 0 --precalculate the size of context menu for i, item in ipairs(items) do if item ~= DIVIDER then diff --git a/data/core/statusview.lua b/data/core/statusview.lua index b1a45385..542522c7 100644 --- a/data/core/statusview.lua +++ b/data/core/statusview.lua @@ -113,9 +113,6 @@ function StatusView.Item:hide() self.visible = false end ---Show the item on the status bar. function StatusView.Item:show() self.visible = true end ----Function assiged by default when user provides a nil predicate. -local function predicate_always_true() return true end - ---A condition to evaluate if the item should be displayed. If a string ---is given it is treated as a require import that should return a valid object ---which is checked against the current active view, the sames applies if a @@ -123,15 +120,7 @@ local function predicate_always_true() return true end ---perform a custom evaluation, setting to nil means always evaluates to true. ---@param predicate string | table | StatusView.Item.predicate function StatusView.Item:set_predicate(predicate) - predicate = predicate or predicate_always_true - if type(predicate) == "string" then - predicate = require(predicate) - end - if type(predicate) == "table" then - local class = predicate - predicate = function() return core.active_view:is(class) end - end - self.predicate = predicate + self.predicate = command.generate_predicate(predicate) end @@ -655,14 +644,14 @@ local function merge_deprecated_items(destination, items, alignment) local position = alignment == StatusView.Item.LEFT and "left" or "right" local item_start = StatusView.Item( - predicate_always_true, + nil, "deprecated:"..position.."-start", alignment ) item_start.get_item = items_start local item_end = StatusView.Item( - predicate_always_true, + nil, "deprecated:"..position.."-end", alignment ) From 94430bcbd2937aa944be72c31163a4428c14a653 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Wed, 11 May 2022 01:05:36 -0400 Subject: [PATCH 08/16] tokenizer: fix next utf8 char retrieval bug --- data/core/tokenizer.lua | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/data/core/tokenizer.lua b/data/core/tokenizer.lua index e0c630a4..ebe550ff 100644 --- a/data/core/tokenizer.lua +++ b/data/core/tokenizer.lua @@ -169,12 +169,12 @@ function tokenizer.tokenize(incoming_syntax, text, state) if p.whole_line[p_idx] and next > 1 then return end - -- go to the start of the next utf-8 character - while text:byte(next) and common.is_utf8_cont(text, next) do - next = next + 1 - end res = p.pattern and { text:ufind((at_start or p.whole_line[p_idx]) and "^" .. code or code, next) } - or { regex.match(code, text, next, (at_start or p.whole_line[p_idx]) and regex.ANCHORED or 0) } + or { regex.match(code, text, text:ucharpos(next), (at_start or p.whole_line[p_idx]) and regex.ANCHORED or 0) } + if p.regex and #res > 0 then -- set correct utf8 len for regex result + res[2] = res[1] + string.ulen(text:sub(res[1], res[2])) - 1 + res[1] = next + end if res[1] and close and target[3] then local count = 0 for i = res[1] - 1, 1, -1 do @@ -189,7 +189,8 @@ function tokenizer.tokenize(incoming_syntax, text, state) return table.unpack(res) end - while i <= #text do + local text_len = text:ulen() + while i <= text_len do -- continue trying to match the end pattern of a pair if we have a state set if current_pattern_idx > 0 then local p = current_syntax.patterns[current_pattern_idx] @@ -262,13 +263,8 @@ function tokenizer.tokenize(incoming_syntax, text, state) -- consume character if we didn't match if not matched then - local n = 0 - -- reach the next character - while text:byte(i + n + 1) and common.is_utf8_cont(text, i + n + 1) do - n = n + 1 - end - push_token(res, "normal", text:usub(i, i + n)) - i = i + n + 1 + push_token(res, "normal", text:usub(i, i)) + i = i + 1 end end From 8bda5d4198f750e237fc07d052030ed6f6df47a5 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Fri, 13 May 2022 12:18:32 -0400 Subject: [PATCH 09/16] docs: added font.group to renderer and other adjustments --- docs/api/renderer.lua | 57 +++++++++++++------------------------------ 1 file changed, 17 insertions(+), 40 deletions(-) diff --git a/docs/api/renderer.lua b/docs/api/renderer.lua index 7a9b636d..8738a7c1 100644 --- a/docs/api/renderer.lua +++ b/docs/api/renderer.lua @@ -6,7 +6,9 @@ renderer = {} --- ----Represents a color used by the rendering functions. +---Array of bytes that represents a color used by the rendering functions. +---Note: indexes for rgba are numerical 1 = r, 2 = g, 3 = b, 4 = a but for +---documentation purposes the letters r, g, b, a were used. ---@class renderer.color ---@field public r number Red ---@field public g number Green @@ -17,7 +19,7 @@ renderer.color = {} --- ---Represent options that affect a font's rendering. ---@class renderer.fontoptions ----@field public antialiasing "'grayscale'" | "'subpixel'" +---@field public antialiasing "'none'" | "'grayscale'" | "'subpixel'" ---@field public hinting "'slight'" | "'none'" | '"full"' -- @field public bold boolean -- @field public italic boolean @@ -38,6 +40,16 @@ renderer.font = {} ---@return renderer.font function renderer.font.load(path, size, options) end +--- +---Combines an array of fonts into a single one for broader charset support, +---the order of the list determines the fonts precedence when retrieving +---a symbol from it. +--- +---@param fonts renderer.font[] +--- +---@return renderer.font +function renderer.font.group(fonts) end + --- ---Clones a font object into a new one. --- @@ -80,25 +92,6 @@ function renderer.font:get_size() end ---@param size number function renderer.font:set_size(size) end ---- ----Assistive functionality to replace characters in a ----rendered text with other characters. ----@class renderer.replacements -renderer.replacements = {} - ---- ----Create a new character replacements object. ---- ----@return renderer.replacements -function renderer.replacements.new() end - ---- ----Add to internal map a character to character replacement. ---- ----@param original_char string Should be a single character like '\t' ----@param replacement_char string Should be a single character like 'ยป' -function renderer.replacements:add(original_char, replacement_char) end - --- ---Toggles drawing debugging rectangles on the currently rendered sections ---of the window to help troubleshoot the renderer. @@ -141,29 +134,13 @@ function renderer.set_clip_rect(x, y, width, height) end function renderer.draw_rect(x, y, width, height, color) end --- ----Draw text. +---Draw text and return the x coordinate where the text finished drawing. --- ---@param font renderer.font ---@param text string ---@param x number ---@param y number ---@param color renderer.color ----@param replace renderer.replacements ----@param color_replace renderer.color --- ----@return number x_subpixel -function renderer.draw_text(font, text, x, y, color, replace, color_replace) end - ---- ----Draw text at subpixel level. ---- ----@param font renderer.font ----@param text string ----@param x number ----@param y number ----@param color renderer.color ----@param replace renderer.replacements ----@param color_replace renderer.color ---- ----@return number x_subpixel -function renderer.draw_text_subpixel(font, text, x, y, color, replace, color_replace) end +---@return number x +function renderer.draw_text(font, text, x, y, color) end From d56f4e1ee553a1cb41ab333822903bcedcf9318a Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 21 Apr 2022 22:33:00 -0400 Subject: [PATCH 10/16] Modified autoreload to use new dirwatch infrastructure, and added in nagview to verify that fs changes don't stomp on our changes, unless you want them to. --- data/plugins/autoreload.lua | 51 +++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/data/plugins/autoreload.lua b/data/plugins/autoreload.lua index 9978092e..b65beaac 100644 --- a/data/plugins/autoreload.lua +++ b/data/plugins/autoreload.lua @@ -1,7 +1,10 @@ -- mod-version:2 -- lite-xl 2.0 local core = require "core" local config = require "core.config" +local style = require "core.style" local Doc = require "core.doc" +local common = require "core.common" +local dirwatch = require "core.dirwatch" local times = setmetatable({}, { __mode = "k" }) @@ -25,18 +28,44 @@ local function reload_doc(doc) core.log_quiet("Auto-reloaded doc \"%s\"", doc.filename) end -local on_modify = core.on_dirmonitor_modify - -core.on_dirmonitor_modify = function(dir, filepath) - local abs_filename = dir.name .. PATHSEP .. filepath - for _, doc in ipairs(core.docs) do - local info = system.get_file_info(doc.filename or "") - if doc.abs_filename == abs_filename and info and times[doc] ~= info.modified then - reload_doc(doc) - break - end +local function check_prompt_reload() + if core.active_view.doc and core.active_view.doc.deferred_reload then + local doc = core.active_view.doc + core.nag_view:show("File Changed", doc.filename .. " has changed. Reload this file?", { + { font = style.font, text = "Yes", default_yes = true }, + { font = style.font, text = "No" , default_no = true } + }, function(item) + if item.text == "Yes" then reload_doc(doc) end + doc.deferred_reload = false + end) end - on_modify(dir, filepath) +end + +local on_check = dirwatch.check +function dirwatch:check(change_callback, ...) + on_check(self, function(dir) + for _, doc in ipairs(core.docs) do + if dir == common.dirname(doc.abs_filename) then + local info = system.get_file_info(doc.filename or "") + if info and times[doc] ~= info.modified then + if not doc:is_dirty() then + reload_doc(doc) + else + doc.deferred_reload = true + end + break + end + end + end + change_callback(dir) + end, ...) + check_prompt_reload() +end + +local set_active_view = core.set_active_view +function core.set_active_view(view) + set_active_view(view) + check_prompt_reload() end -- patch `Doc.save|load` to store modified time From 173370694e73b6983808c73357e5bb4a3af3667f Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 28 Apr 2022 21:30:55 -0400 Subject: [PATCH 11/16] Split out reload functionality to actual document, and added in a thread to check the document, in the cases where it wouldn't be covered by dirwatch. --- data/core/commands/doc.lua | 4 +++ data/core/doc/init.lua | 10 ++++++++ data/plugins/autoreload.lua | 50 +++++++++++++++++++++++-------------- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/data/core/commands/doc.lua b/data/core/commands/doc.lua index 87079180..d470076f 100644 --- a/data/core/commands/doc.lua +++ b/data/core/commands/doc.lua @@ -466,6 +466,10 @@ local commands = { command.perform("doc:save-as") end end, + + ["doc:reload"] = function() + doc():reload() + end, ["file:rename"] = function() local old_filename = doc().filename diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index 460e387d..a2546246 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -80,6 +80,16 @@ function Doc:load(filename) end +function Doc:reload() + if self.filename then + local sel = { self:get_selection() } + self:load(self.filename) + self:clean() + self:set_selection(table.unpack(sel)) + end +end + + function Doc:save(filename, abs_filename) if not filename then assert(self.filename, "no filename set to default to") diff --git a/data/plugins/autoreload.lua b/data/plugins/autoreload.lua index b65beaac..45d7217f 100644 --- a/data/plugins/autoreload.lua +++ b/data/plugins/autoreload.lua @@ -14,17 +14,8 @@ local function update_time(doc) end local function reload_doc(doc) - local fp = io.open(doc.filename, "r") - local text = fp:read("*a") - fp:close() - - local sel = { doc:get_selection() } - doc:remove(1, 1, math.huge, math.huge) - doc:insert(1, 1, text:gsub("\r", ""):gsub("\n$", "")) - doc:set_selection(table.unpack(sel)) - + doc:reload() update_time(doc) - doc:clean() core.log_quiet("Auto-reloaded doc \"%s\"", doc.filename) end @@ -41,20 +32,23 @@ local function check_prompt_reload() end end +local function check_if_modified(doc) + local info = system.get_file_info(doc.filename or "") + if info and times[doc] ~= info.modified then + if not doc:is_dirty() then + reload_doc(doc) + else + doc.deferred_reload = true + end + end +end + local on_check = dirwatch.check function dirwatch:check(change_callback, ...) on_check(self, function(dir) for _, doc in ipairs(core.docs) do if dir == common.dirname(doc.abs_filename) then - local info = system.get_file_info(doc.filename or "") - if info and times[doc] ~= info.modified then - if not doc:is_dirty() then - reload_doc(doc) - else - doc.deferred_reload = true - end - break - end + check_if_modified(doc) end end change_callback(dir) @@ -62,10 +56,28 @@ function dirwatch:check(change_callback, ...) check_prompt_reload() end + local set_active_view = core.set_active_view function core.set_active_view(view) set_active_view(view) check_prompt_reload() + if view.doc and view.doc.abs_filename then + local should_poll = true + for i,v in ipairs(core.project_directories) do + if view.doc.abs_filename:find(v.name, 1, true) == 1 and not v.files_limit then + should_poll = false + end + end + if should_poll then + local doc = core.active_view.doc + core.add_thread(function() + while core.active_view.doc == doc do + check_if_modified(doc) + coroutine.yield(0.25) + end + end) + end + end end -- patch `Doc.save|load` to store modified time From 20dc101229b969bb67b94c824b23f23818ee3104 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Tue, 3 May 2022 23:24:26 -0400 Subject: [PATCH 12/16] As per request from jgmdev, added in ability to show nagview always. --- data/plugins/autoreload.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/data/plugins/autoreload.lua b/data/plugins/autoreload.lua index 45d7217f..fba22a23 100644 --- a/data/plugins/autoreload.lua +++ b/data/plugins/autoreload.lua @@ -6,6 +6,10 @@ local Doc = require "core.doc" local common = require "core.common" local dirwatch = require "core.dirwatch" +config.plugins.autoreload = common.merge({ + always_show_nagview = false +}, config.plugins.autoreload) + local times = setmetatable({}, { __mode = "k" }) local function update_time(doc) @@ -35,7 +39,7 @@ end local function check_if_modified(doc) local info = system.get_file_info(doc.filename or "") if info and times[doc] ~= info.modified then - if not doc:is_dirty() then + if not doc:is_dirty() and not config.plugins.autoreload.always_show_nagview then reload_doc(doc) else doc.deferred_reload = true From 5a0d213f3b420bb4ff6cf46edbd306d437026745 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 15 May 2022 13:25:03 -0400 Subject: [PATCH 13/16] Changed things over to use dirwatch. --- data/core/dirwatch.lua | 3 ++ data/plugins/autoreload.lua | 75 ++++++++++++++++++++++++------------- 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/data/core/dirwatch.lua b/data/core/dirwatch.lua index 314a33c0..c13e91ea 100644 --- a/data/core/dirwatch.lua +++ b/data/core/dirwatch.lua @@ -31,10 +31,13 @@ end -- In windows, this is a no-op for anything underneath a top-level directory, -- but code should be called anyway, so we can ensure that we have a proper -- experience across all platforms. Should be an absolute path. +-- Can also be called on individual files, though this should be used sparingly, +-- so as not to run into system limits (like in the autoreload plugin). function dirwatch:watch(directory, bool) if bool == false then return self:unwatch(directory) end if not self.watched[directory] and not self.scanned[directory] then if PLATFORM == "Windows" then + if system.get_file_info(directory).type ~= "dir" then return self:scan(directory) end if not self.windows_watch_top or directory:find(self.windows_watch_top, 1, true) ~= 1 then -- Get the highest level of directory that is common to this directory, and the original. local target = directory diff --git a/data/plugins/autoreload.lua b/data/plugins/autoreload.lua index fba22a23..4e8f5513 100644 --- a/data/plugins/autoreload.lua +++ b/data/plugins/autoreload.lua @@ -10,7 +10,16 @@ config.plugins.autoreload = common.merge({ always_show_nagview = false }, config.plugins.autoreload) +local function get_project_doc(doc) + for i, v in ipairs(core.project_directories) do + if doc.abs_filename:find(v.abs_filename, 1, true) == 1 then return v end + end + return nil +end + +local watch = dirwatch.new() local times = setmetatable({}, { __mode = "k" }) +local visible = setmetatable({}, { __mode = "k" }) local function update_time(doc) local info = system.get_file_info(doc.filename) @@ -23,9 +32,9 @@ local function reload_doc(doc) core.log_quiet("Auto-reloaded doc \"%s\"", doc.filename) end -local function check_prompt_reload() - if core.active_view.doc and core.active_view.doc.deferred_reload then - local doc = core.active_view.doc +local function check_prompt_reload(doc) + if doc or (core.active_view.doc and core.active_view.doc.deferred_reload) then + doc = doc or core.active_view.doc core.nag_view:show("File Changed", doc.filename .. " has changed. Reload this file?", { { font = style.font, text = "Yes", default_yes = true }, { font = style.font, text = "No" , default_no = true } @@ -36,6 +45,24 @@ local function check_prompt_reload() end end +local function doc_becomes_visible(doc) + if doc and not visible[doc] and doc.abs_filename then + visible[doc] = true + check_prompt_reload(doc) + local dir = get_project_doc(doc) + (dir and dir.watch or watch):watch(doc.abs_filename) + end +end + +local function doc_becomes_invisible(doc) + if doc and visible[doc] then + visible[doc] = false + local dir = get_project_doc(doc) + (dir and dir.watch or watch):unwatch(doc.abs_filename) + end +end + +>>>>>>> Stashed changes local function check_if_modified(doc) local info = system.get_file_info(doc.filename or "") if info and times[doc] ~= info.modified then @@ -51,7 +78,7 @@ local on_check = dirwatch.check function dirwatch:check(change_callback, ...) on_check(self, function(dir) for _, doc in ipairs(core.docs) do - if dir == common.dirname(doc.abs_filename) then + if dir == common.dirname(doc.abs_filename) or dir == doc.abs_filename then check_if_modified(doc) end end @@ -60,30 +87,28 @@ function dirwatch:check(change_callback, ...) check_prompt_reload() end - -local set_active_view = core.set_active_view +local core_set_active_view = core.set_active_view function core.set_active_view(view) - set_active_view(view) - check_prompt_reload() - if view.doc and view.doc.abs_filename then - local should_poll = true - for i,v in ipairs(core.project_directories) do - if view.doc.abs_filename:find(v.name, 1, true) == 1 and not v.files_limit then - should_poll = false - end - end - if should_poll then - local doc = core.active_view.doc - core.add_thread(function() - while core.active_view.doc == doc do - check_if_modified(doc) - coroutine.yield(0.25) - end - end) - end - end + core_set_active_view(view) + doc_becomes_visible(view.doc) end +local node_set_active_view = Node.set_active_view +function Node:set_active_view(view) + doc_becomes_invisible(self.active_view.doc) + node_set_active_view(self, view) + doc_becomes_visible(self.active_view.doc) +end + +core.add_thread(function() + while true do + -- because we already hook this function above; we only + -- need to check the file. + watch:check(function() end) + coroutine.yield(0.05) + end +end) + -- patch `Doc.save|load` to store modified time local load = Doc.load local save = Doc.save From daeb2a8e044aa8e26ad93e1e91e89a373c6508d2 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 15 May 2022 14:31:00 -0400 Subject: [PATCH 14/16] Made sure we redrew things, added in a contingency in 'save' for times when we load a non-existent file, and added some checks. --- data/core/dirwatch.lua | 4 +- data/plugins/autoreload.lua | 72 +++++++++++++++--------------------- src/api/dirmonitor/inotify.c | 2 +- 3 files changed, 34 insertions(+), 44 deletions(-) diff --git a/data/core/dirwatch.lua b/data/core/dirwatch.lua index c13e91ea..093e541c 100644 --- a/data/core/dirwatch.lua +++ b/data/core/dirwatch.lua @@ -35,9 +35,11 @@ end -- so as not to run into system limits (like in the autoreload plugin). function dirwatch:watch(directory, bool) if bool == false then return self:unwatch(directory) end + local info = system.get_file_info(directory) + if not info then return end if not self.watched[directory] and not self.scanned[directory] then if PLATFORM == "Windows" then - if system.get_file_info(directory).type ~= "dir" then return self:scan(directory) end + if info.type ~= "dir" then return self:scan(directory) end if not self.windows_watch_top or directory:find(self.windows_watch_top, 1, true) ~= 1 then -- Get the highest level of directory that is common to this directory, and the original. local target = directory diff --git a/data/plugins/autoreload.lua b/data/plugins/autoreload.lua index 4e8f5513..03243851 100644 --- a/data/plugins/autoreload.lua +++ b/data/plugins/autoreload.lua @@ -3,6 +3,7 @@ local core = require "core" local config = require "core.config" local style = require "core.style" local Doc = require "core.doc" +local Node = require "core.node" local common = require "core.common" local dirwatch = require "core.dirwatch" @@ -10,31 +11,30 @@ config.plugins.autoreload = common.merge({ always_show_nagview = false }, config.plugins.autoreload) -local function get_project_doc(doc) - for i, v in ipairs(core.project_directories) do - if doc.abs_filename:find(v.abs_filename, 1, true) == 1 then return v end - end - return nil -end - local watch = dirwatch.new() local times = setmetatable({}, { __mode = "k" }) local visible = setmetatable({}, { __mode = "k" }) +local function get_project_doc_watch(doc) + for i, v in ipairs(core.project_directories) do + if doc.abs_filename:find(v.name, 1, true) == 1 then return v.watch end + end + return watch +end + local function update_time(doc) - local info = system.get_file_info(doc.filename) - times[doc] = info.modified + times[doc] = system.get_file_info(doc.filename).modified end local function reload_doc(doc) doc:reload() update_time(doc) + core.redraw = true core.log_quiet("Auto-reloaded doc \"%s\"", doc.filename) end local function check_prompt_reload(doc) - if doc or (core.active_view.doc and core.active_view.doc.deferred_reload) then - doc = doc or core.active_view.doc + if doc and doc.deferred_reload then core.nag_view:show("File Changed", doc.filename .. " has changed. Reload this file?", { { font = style.font, text = "Yes", default_yes = true }, { font = style.font, text = "No" , default_no = true } @@ -45,32 +45,11 @@ local function check_prompt_reload(doc) end end -local function doc_becomes_visible(doc) - if doc and not visible[doc] and doc.abs_filename then - visible[doc] = true - check_prompt_reload(doc) - local dir = get_project_doc(doc) - (dir and dir.watch or watch):watch(doc.abs_filename) - end -end - -local function doc_becomes_invisible(doc) - if doc and visible[doc] then - visible[doc] = false - local dir = get_project_doc(doc) - (dir and dir.watch or watch):unwatch(doc.abs_filename) - end -end - ->>>>>>> Stashed changes -local function check_if_modified(doc) - local info = system.get_file_info(doc.filename or "") - if info and times[doc] ~= info.modified then - if not doc:is_dirty() and not config.plugins.autoreload.always_show_nagview then - reload_doc(doc) - else - doc.deferred_reload = true - end +local function doc_changes_visiblity(doc, visibility) + if doc and visible[doc] ~= visibility and doc.abs_filename then + visible[doc] = visibility + if visibility then check_prompt_reload(doc) end + get_project_doc_watch(doc):watch(doc.abs_filename, visibility) end end @@ -79,25 +58,32 @@ function dirwatch:check(change_callback, ...) on_check(self, function(dir) for _, doc in ipairs(core.docs) do if dir == common.dirname(doc.abs_filename) or dir == doc.abs_filename then - check_if_modified(doc) + local info = system.get_file_info(doc.filename or "") + if info and times[doc] ~= info.modified then + if not doc:is_dirty() and not config.plugins.autoreload.always_show_nagview then + reload_doc(doc) + else + doc.deferred_reload = true + if doc == core.active_view.doc then check_prompt_reload(doc) end + end + end end end change_callback(dir) end, ...) - check_prompt_reload() end local core_set_active_view = core.set_active_view function core.set_active_view(view) core_set_active_view(view) - doc_becomes_visible(view.doc) + doc_changes_visiblity(view.doc, true) end local node_set_active_view = Node.set_active_view function Node:set_active_view(view) - doc_becomes_invisible(self.active_view.doc) + if self.active_view then doc_changes_visiblity(self.active_view.doc, false) end node_set_active_view(self, view) - doc_becomes_visible(self.active_view.doc) + doc_changes_visiblity(self.active_view.doc, true) end core.add_thread(function() @@ -121,6 +107,8 @@ end Doc.save = function(self, ...) local res = save(self, ...) + -- if starting with an unsaved document with a filename. + if not times[self] then get_project_doc_watch(self):watch(self.abs_filename, true) end update_time(self) return res end diff --git a/src/api/dirmonitor/inotify.c b/src/api/dirmonitor/inotify.c index cb38c315..63e501a7 100644 --- a/src/api/dirmonitor/inotify.c +++ b/src/api/dirmonitor/inotify.c @@ -47,7 +47,7 @@ int translate_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buff int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) { - return inotify_add_watch(monitor->fd, path, IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO); + return inotify_add_watch(monitor->fd, path, IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MODIFY | IN_MOVED_TO); } From 4d3e8d8bd0a7de6cfcc478eb63dd9d5a5e2a17f9 Mon Sep 17 00:00:00 2001 From: jgmdev Date: Sun, 15 May 2022 16:10:57 -0400 Subject: [PATCH 15/16] command predicates: added support for strict matching by appending '!' on string predicates --- data/core/command.lua | 21 ++++++++++++++++----- data/core/object.lua | 5 +---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/data/core/command.lua b/data/core/command.lua index 39582de3..f54854a4 100644 --- a/data/core/command.lua +++ b/data/core/command.lua @@ -11,21 +11,32 @@ local always_true = function() return true end ---evaluation. --- ---If a string predicate is given it is treated as a require import that should ----return a valid object which is checked against the current active view, the ----sames applies if a table is given. A function that returns a boolean can be ----used instead to perform a custom evaluation, setting to nil means always ----evaluates to true. +---return a valid object which is checked against the current active view, +---eg: "core.docview" will match any view that inherits from DocView. Appending +---a `!` at the end of the string means we want to match the given object +---from the import strcitly eg: "core.docview!" only DocView is matched. +---A function that returns a boolean can be used instead to perform a custom +---evaluation, setting to nil means always evaluates to true. --- ---@param predicate string | table | function ---@return function function command.generate_predicate(predicate) predicate = predicate or always_true + local strict = false if type(predicate) == "string" then + if predicate:match("!$") then + strict = true + predicate = predicate:gsub("!$", "") + end predicate = require(predicate) end if type(predicate) == "table" then local class = predicate - predicate = function() return core.active_view:extends(class) end + if not strict then + predicate = function() return core.active_view:extends(class) end + else + predicate = function() return core.active_view:is(class) end + end end return predicate end diff --git a/data/core/object.lua b/data/core/object.lua index a57ac9e8..6a6ea490 100644 --- a/data/core/object.lua +++ b/data/core/object.lua @@ -21,10 +21,7 @@ end function Object:is(T) - if getmetatable(self) == T then - return true - end - return false + return getmetatable(self) == T end From 26e47f7583dfb753f365005b67e381b0fba0f1ea Mon Sep 17 00:00:00 2001 From: jgmdev Date: Sun, 15 May 2022 17:17:28 -0400 Subject: [PATCH 16/16] plugin contextmenu: simplify predicate --- data/plugins/contextmenu.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/plugins/contextmenu.lua b/data/plugins/contextmenu.lua index ac811b2a..918c6417 100644 --- a/data/plugins/contextmenu.lua +++ b/data/plugins/contextmenu.lua @@ -3,7 +3,6 @@ local core = require "core" local command = require "core.command" local keymap = require "core.keymap" local ContextMenu = require "core.contextmenu" -local DocView = require "core.docview" local RootView = require "core.rootview" local menu = ContextMenu() @@ -33,7 +32,7 @@ function RootView:draw(...) menu:draw() end -command.add(function() return getmetatable(core.active_view) == DocView end, { +command.add("core.docview!", { ["context:show"] = function() menu:show(core.active_view.position.x, core.active_view.position.y) end