diff options
| author | jgmdev <jgmdev@gmail.com> | 2022-05-22 22:26:47 -0400 |
|---|---|---|
| committer | jgmdev <jgmdev@gmail.com> | 2022-05-22 22:26:47 -0400 |
| commit | b9bb64a2f08668b975b7615aa470e6089d9e5fb9 (patch) | |
| tree | dc3194d3ddb6b4cb67690996b095bd8d9cac493a /data | |
| parent | 36c4d5d9ed0e0be4df4d0b3260fe164a1ad75eab (diff) | |
| parent | 26e47f7583dfb753f365005b67e381b0fba0f1ea (diff) | |
| download | lite-xl-master-2.1.tar.gz lite-xl-master-2.1.zip | |
Merge branch 'master' into master-2.1master-2.1
Diffstat (limited to 'data')
| -rw-r--r-- | data/core/command.lua | 33 | ||||
| -rw-r--r-- | data/core/commands/doc.lua | 6 | ||||
| -rw-r--r-- | data/core/contextmenu.lua | 9 | ||||
| -rw-r--r-- | data/core/doc/init.lua | 12 | ||||
| -rw-r--r-- | data/core/keymap.lua | 142 | ||||
| -rw-r--r-- | data/core/object.lua | 5 | ||||
| -rw-r--r-- | data/core/statusview.lua | 17 | ||||
| -rw-r--r-- | data/core/tokenizer.lua | 22 | ||||
| -rw-r--r-- | data/core/utf8string.lua | 2 | ||||
| -rw-r--r-- | data/plugins/contextmenu.lua | 3 |
10 files changed, 176 insertions, 75 deletions
diff --git a/data/core/command.lua b/data/core/command.lua index bdc1ed34..f54854a4 100644 --- a/data/core/command.lua +++ b/data/core/command.lua @@ -6,15 +6,44 @@ 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, +---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:is(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 + + +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/commands/doc.lua b/data/core/commands/doc.lua index cbecad23..5d953805 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() 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/doc/init.lua b/data/core/doc/init.lua index 0683b8b0..a2546246 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -458,7 +458,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) @@ -467,22 +467,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 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<keymap.shortcut,keymap.command|keymap.command[]> +---@alias keymap.rmap table<keymap.command, keymap.shortcut|keymap.shortcut[]> + +---Pressed status of mod keys. +---@type table<keymap.modkey, keymap.pressed> 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<keymap.modkey, keymap.modkey> 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<string, string> 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 +---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 -function keymap.unbind(key, cmd) - remove_only(keymap.map, key, cmd) - remove_only(keymap.reverse_map, cmd, key) +---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 -function keymap.get_binding(cmd) - return table.unpack(keymap.reverse_map[cmd] or {}) +---Returns all the shortcuts associated to a command packed in a table. +---@param cmd string +---@return table<integer, string> | 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) diff --git a/data/core/object.lua b/data/core/object.lua index 0941ce5d..6a6ea490 100644 --- a/data/core/object.lua +++ b/data/core/object.lua @@ -21,6 +21,11 @@ end function Object:is(T) + return getmetatable(self) == T +end + + +function Object:extends(T) local mt = getmetatable(self) while mt do if mt == T 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 ) 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 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/data/plugins/contextmenu.lua b/data/plugins/contextmenu.lua index 29e7c648..a5450056 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 |
