aboutsummaryrefslogtreecommitdiff
path: root/data
diff options
context:
space:
mode:
authorjgmdev <jgmdev@gmail.com>2022-05-22 22:26:47 -0400
committerjgmdev <jgmdev@gmail.com>2022-05-22 22:26:47 -0400
commitb9bb64a2f08668b975b7615aa470e6089d9e5fb9 (patch)
treedc3194d3ddb6b4cb67690996b095bd8d9cac493a /data
parent36c4d5d9ed0e0be4df4d0b3260fe164a1ad75eab (diff)
parent26e47f7583dfb753f365005b67e381b0fba0f1ea (diff)
downloadlite-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.lua33
-rw-r--r--data/core/commands/doc.lua6
-rw-r--r--data/core/contextmenu.lua9
-rw-r--r--data/core/doc/init.lua12
-rw-r--r--data/core/keymap.lua142
-rw-r--r--data/core/object.lua5
-rw-r--r--data/core/statusview.lua17
-rw-r--r--data/core/tokenizer.lua22
-rw-r--r--data/core/utf8string.lua2
-rw-r--r--data/plugins/contextmenu.lua3
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