Manual sync with 2.1.3 tag

This commit is contained in:
George Sokianos 2024-02-11 17:51:12 +00:00
parent 66fb996e76
commit ad4c221dd8
44 changed files with 252 additions and 1363 deletions

View File

@ -1,7 +1,7 @@
# Lite XL # Lite XL
[![CI]](https://github.com/lite-xl/lite-xl/actions/workflows/build.yml) [![CI]](https://github.com/lite-xl/lite-xl/actions/workflows/build.yml)
[![Discord Badge Image]](https://discord.gg/UQKnzBhY5H) [![Discord Badge Image]](https://discord.gg/RWzqC3nx7K)
![screenshot-dark] ![screenshot-dark]
@ -124,7 +124,6 @@ cd lite-xl
``` ```
To run lite-xl without installing: To run lite-xl without installing:
```sh ```sh
./lite-xl ./lite-xl
``` ```
@ -137,68 +136,31 @@ mkdir -p $HOME/.local/bin && cp lite-xl $HOME/.local/bin/
mkdir -p $HOME/.local/share/lite-xl && cp -r data/* $HOME/.local/share/lite-xl/ mkdir -p $HOME/.local/share/lite-xl && cp -r data/* $HOME/.local/share/lite-xl/
``` ```
#### Add Lite XL to PATH
To run Lite XL from the command line, you must add it to PATH.
If `$HOME/.local/bin` is not in PATH: If `$HOME/.local/bin` is not in PATH:
```sh ```sh
echo -e 'export PATH=$PATH:$HOME/.local/bin' >> $HOME/.bashrc echo -e 'export PATH=$PATH:$HOME/.local/bin' >> $HOME/.bashrc
``` ```
Alternatively on recent versions of GNOME and KDE Plasma, To get the icon to show up in app launcher:
you can add `$HOME/.local/bin` to PATH via `~/.config/environment.d/envvars.conf`:
```ini
PATH=$HOME/.local/bin:$PATH
```
> **Note**
> Some systems might not load `.bashrc` when logging in.
> This can cause problems with launching applications from the desktop / menu.
#### Add Lite XL to application launchers
To get the icon to show up in app launcher, you need to create a desktop
entry and put it into `/usr/share/applications` or `~/.local/share/applications`.
Here is an example for a desktop entry in `~/.local/share/applications/com.lite_xl.LiteXL.desktop`,
assuming Lite XL is in PATH:
```ini
[Desktop Entry]
Type=Application
Name=Lite XL
Comment=A lightweight text editor written in Lua
Exec=lite-xl %F
Icon=lite-xl
Terminal=false
StartupWMClass=lite-xl
Categories=Development;IDE;
MimeType=text/plain;inode/directory;
```
To get the icon to show up in app launcher immediately, run:
```sh ```sh
xdg-desktop-menu forceupdate xdg-desktop-menu forceupdate
``` ```
Alternatively, you may log out and log in again. You may need to logout and login again to see icon in app launcher.
#### Uninstall To uninstall just run:
To uninstall Lite XL, run:
```sh ```sh
rm -f $HOME/.local/bin/lite-xl rm -f $HOME/.local/bin/lite-xl
rm -rf $HOME/.local/share/icons/hicolor/scalable/apps/lite-xl.svg \ rm -rf $HOME/.local/share/icons/hicolor/scalable/apps/lite-xl.svg \
$HOME/.local/share/applications/com.lite_xl.LiteXL.desktop \ $HOME/.local/share/applications/org.lite_xl.lite_xl.desktop \
$HOME/.local/share/metainfo/com.lite_xl.LiteXL.appdata.xml \ $HOME/.local/share/metainfo/org.lite_xl.lite_xl.appdata.xml \
$HOME/.local/share/lite-xl $HOME/.local/share/lite-xl
``` ```
## Contributing ## Contributing
Any additional functionality that can be added through a plugin should be done Any additional functionality that can be added through a plugin should be done

View File

@ -363,7 +363,7 @@ local commands = {
["doc:select-lines"] = function(dv) ["doc:select-lines"] = function(dv)
for idx, line1, _, line2 in dv.doc:get_selections(true) do for idx, line1, _, line2 in dv.doc:get_selections(true) do
append_line_if_last_line(line2) append_line_if_last_line(line2)
dv.doc:set_selections(idx, line2 + 1, 1, line1, 1) dv.doc:set_selections(idx, line1, 1, line2 + 1, 1)
end end
end, end,
@ -548,11 +548,6 @@ local commands = {
dv.doc.crlf = not dv.doc.crlf dv.doc.crlf = not dv.doc.crlf
end, end,
["doc:toggle-overwrite"] = function(dv)
dv.doc.overwrite = not dv.doc.overwrite
core.blink_reset() -- to show the cursor has changed edit modes
end,
["doc:save-as"] = function(dv) ["doc:save-as"] = function(dv)
local last_doc = core.last_active_view and core.last_active_view.doc local last_doc = core.last_active_view and core.last_active_view.doc
local text local text

View File

@ -196,23 +196,6 @@ local function select_next(reverse)
if l2 then doc():set_selection(l2, c2, l1, c1) end if l2 then doc():set_selection(l2, c2, l1, c1) end
end end
---@param in_selection? boolean whether to replace in the selections only, or in the whole file.
local function find_replace(in_selection)
local l1, c1, l2, c2 = doc():get_selection()
local selected_text = ""
if not in_selection then
selected_text = doc():get_text(l1, c1, l2, c2)
doc():set_selection(l2, c2, l2, c2)
end
replace("Text", l1 == l2 and selected_text or "", function(text, old, new)
if not find_regex then
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
end
local result, matches = regex.gsub(regex.compile(old, "m"), text, new)
return result, matches
end)
end
command.add(has_unique_selection, { command.add(has_unique_selection, {
["find-replace:select-next"] = select_next, ["find-replace:select-next"] = select_next,
["find-replace:select-previous"] = function() select_next(true) end, ["find-replace:select-previous"] = function() select_next(true) end,
@ -229,11 +212,15 @@ command.add("core.docview!", {
end, end,
["find-replace:replace"] = function() ["find-replace:replace"] = function()
find_replace() local l1, c1, l2, c2 = doc():get_selection()
end, local selected_text = doc():get_text(l1, c1, l2, c2)
replace("Text", l1 == l2 and selected_text or "", function(text, old, new)
["find-replace:replace-in-selection"] = function() if not find_regex then
find_replace(true) return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
end
local result, matches = regex.gsub(regex.compile(old, "m"), text, new)
return result, matches
end)
end, end,
["find-replace:replace-symbol"] = function() ["find-replace:replace-symbol"] = function()

View File

@ -4,7 +4,6 @@ local DocView = require "core.docview"
local command = require "core.command" local command = require "core.command"
local common = require "core.common" local common = require "core.common"
local config = require "core.config" local config = require "core.config"
local Node = require "core.node"
local t = { local t = {
@ -30,6 +29,20 @@ local t = {
core.confirm_close_docs(docs, core.root_view.close_all_docviews, core.root_view, true) core.confirm_close_docs(docs, core.root_view.close_all_docviews, core.root_view, true)
end, end,
["root:switch-to-previous-tab"] = function(node)
local idx = node:get_view_idx(core.active_view)
idx = idx - 1
if idx < 1 then idx = #node.views end
node:set_active_view(node.views[idx])
end,
["root:switch-to-next-tab"] = function(node)
local idx = node:get_view_idx(core.active_view)
idx = idx + 1
if idx > #node.views then idx = 1 end
node:set_active_view(node.views[idx])
end,
["root:move-tab-left"] = function(node) ["root:move-tab-left"] = function(node)
local idx = node:get_view_idx(core.active_view) local idx = node:get_view_idx(core.active_view)
if idx > 1 then if idx > 1 then
@ -104,7 +117,7 @@ end, t)
command.add(nil, { command.add(nil, {
["root:scroll"] = function(delta) ["root:scroll"] = function(delta)
local view = core.root_view.overlapping_view or core.active_view local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view
if view and view.scrollable then if view and view.scrollable then
view.scroll.to.y = view.scroll.to.y + delta * -config.mouse_wheel_scroll view.scroll.to.y = view.scroll.to.y + delta * -config.mouse_wheel_scroll
return true return true
@ -112,7 +125,7 @@ command.add(nil, {
return false return false
end, end,
["root:horizontal-scroll"] = function(delta) ["root:horizontal-scroll"] = function(delta)
local view = core.root_view.overlapping_view or core.active_view local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view
if view and view.scrollable then if view and view.scrollable then
view.scroll.to.x = view.scroll.to.x + delta * -config.mouse_wheel_scroll view.scroll.to.x = view.scroll.to.x + delta * -config.mouse_wheel_scroll
return true return true
@ -120,74 +133,3 @@ command.add(nil, {
return false return false
end end
}) })
command.add(function(node)
if not Node:is_extended_by(node) then node = nil end
-- No node was specified, use the active one
node = node or core.root_view:get_active_node()
if not node then return false end
return true, node
end,
{
["root:switch-to-previous-tab"] = function(node)
local idx = node:get_view_idx(node.active_view)
idx = idx - 1
if idx < 1 then idx = #node.views end
node:set_active_view(node.views[idx])
end,
["root:switch-to-next-tab"] = function(node)
local idx = node:get_view_idx(node.active_view)
idx = idx + 1
if idx > #node.views then idx = 1 end
node:set_active_view(node.views[idx])
end,
["root:scroll-tabs-backward"] = function(node)
node:scroll_tabs(1)
end,
["root:scroll-tabs-forward"] = function(node)
node:scroll_tabs(2)
end
}
)
command.add(function()
local node = core.root_view.root_node:get_child_overlapping_point(core.root_view.mouse.x, core.root_view.mouse.y)
if not node then return false end
return (node.hovered_tab or node.hovered_scroll_button > 0) and true, node
end,
{
["root:switch-to-hovered-previous-tab"] = function(node)
command.perform("root:switch-to-previous-tab", node)
end,
["root:switch-to-hovered-next-tab"] = function(node)
command.perform("root:switch-to-next-tab", node)
end,
["root:scroll-hovered-tabs-backward"] = function(node)
command.perform("root:scroll-tabs-backward", node)
end,
["root:scroll-hovered-tabs-forward"] = function(node)
command.perform("root:scroll-tabs-forward", node)
end
}
)
-- double clicking the tab bar, or on the emptyview should open a new doc
command.add(function(x, y)
local node = x and y and core.root_view.root_node:get_child_overlapping_point(x, y)
return node and node:is_in_tab_area(x, y)
end, {
["tabbar:new-doc"] = function()
command.perform("core:new-doc")
end
})
command.add("core.emptyview", {
["emptyview:new-doc"] = function()
command.perform("core:new-doc")
end
})

View File

@ -84,11 +84,6 @@ function CommandView:get_line_screen_position(line, col)
end end
function CommandView:supports_text_input()
return true
end
function CommandView:get_scrollable_size() function CommandView:get_scrollable_size()
return 0 return 0
end end

View File

@ -2,71 +2,15 @@ local common = require "core.common"
local config = {} local config = {}
---The frame rate of Lite XL.
---Note that setting this value to the screen's refresh rate
---does not eliminate screen tearing.
---
---Defaults to 60.
---@type number
config.fps = 60 config.fps = 60
---Maximum number of log items that will be stored.
---When the number of log items exceed this value, old items will be discarded.
---
---Defaults to 800.
---@type number
config.max_log_items = 800 config.max_log_items = 800
---The timeout, in seconds, before a message dissapears from StatusView.
---
---Defaults to 5.
---@type number
config.message_timeout = 5 config.message_timeout = 5
---The number of pixels scrolled per-step.
---
---Defaults to 50 * SCALE.
---@type number
config.mouse_wheel_scroll = 50 * SCALE config.mouse_wheel_scroll = 50 * SCALE
---Enables/disables transitions when scrolling with the scrollbar.
---When enabled, the scrollbar will have inertia and slowly move towards the cursor.
---Otherwise, the scrollbar will immediately follow the cursor.
---
---Defaults to false.
---@type boolean
config.animate_drag_scroll = false config.animate_drag_scroll = false
---Enables/disables scrolling past the end of a document.
---
---Defaults to true.
---@type boolean
config.scroll_past_end = true config.scroll_past_end = true
---@type "expanded" | "contracted" | false @Force the scrollbar status of the DocView
---@alias config.scrollbartype
---| "expanded" # A thicker scrollbar is shown at all times.
---| "contracted" # A thinner scrollbar is shown at all times.
---| false # The scrollbar expands when the cursor hovers over it.
---Controls whether the DocView scrollbar is always shown or hidden.
---This option does not affect other View's scrollbars.
---
---Defaults to false.
---@type config.scrollbartype
config.force_scrollbar_status = false config.force_scrollbar_status = false
---The file size limit, in megabytes.
---Files larger than this size will not be shown in the file picker.
---
---Defaults to 10.
---@type number
config.file_size_limit = 10 config.file_size_limit = 10
---A list of files and directories to ignore.
---Each element is a Lua pattern, where patterns ending with a forward slash
---are recognized as directories while patterns ending with an anchor ("$") are
---recognized as files.
---@type string[]
config.ignore_files = { config.ignore_files = {
-- folders -- folders
"^%.svn/", "^%.git/", "^%.hg/", "^CVS/", "^%.Trash/", "^%.Trash%-.*/", "^%.svn/", "^%.git/", "^%.hg/", "^CVS/", "^%.Trash/", "^%.Trash%-.*/",
@ -77,181 +21,40 @@ config.ignore_files = {
"%.suo$", "%.pdb$", "%.idb$", "%.class$", "%.psd$", "%.db$", "%.suo$", "%.pdb$", "%.idb$", "%.class$", "%.psd$", "%.db$",
"^desktop%.ini$", "^%.DS_Store$", "^%.directory$", "^desktop%.ini$", "^%.DS_Store$", "^%.directory$",
} }
---Lua pattern used to find symbols when advanced syntax highlighting
---is not available.
---This pattern is also used for navigation, e.g. move to next word.
---
---The default pattern matches all letters, followed by any number
---of letters and digits.
---@type string
config.symbol_pattern = "[%a_][%w_]*" config.symbol_pattern = "[%a_][%w_]*"
---A list of characters that delimits a word.
---
---The default is ``" \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"``
---@type string
config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-" config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
---The timeout, in seconds, before several consecutive actions
---are merged as a single undo step.
---
---The default is 0.3 seconds.
---@type number
config.undo_merge_timeout = 0.3 config.undo_merge_timeout = 0.3
---The maximum number of undo steps per-document.
---
---The default is 10000.
---@type number
config.max_undos = 10000 config.max_undos = 10000
---The maximum number of tabs shown at a time.
---
---The default is 8.
---@type number
config.max_tabs = 8 config.max_tabs = 8
---Shows/hides the tab bar when there is only one tab open.
---
---The tab bar is always shown by default.
---@type boolean
config.always_show_tabs = true config.always_show_tabs = true
-- Possible values: false, true, "no_selection"
---@alias config.highlightlinetype
---| true # Always highlight the current line.
---| false # Never highlight the current line.
---| "no_selection" # Highlight the current line if no text is selected.
---Highlights the current line.
---
---The default is true.
---@type config.highlightlinetype
config.highlight_current_line = true config.highlight_current_line = true
---The spacing between each line of text.
---
---The default is 120% of the height of the text (1.2).
---@type number
config.line_height = 1.2 config.line_height = 1.2
---The number of spaces each level of indentation represents.
---
---The default is 2.
---@type number
config.indent_size = 2 config.indent_size = 2
---The type of indentation.
---
---The default is "soft" (spaces).
---@type "soft" | "hard"
config.tab_type = "soft" config.tab_type = "soft"
---Do not remove whitespaces when advancing to the next line.
---
---Defaults to false.
---@type boolean
config.keep_newline_whitespace = false config.keep_newline_whitespace = false
---Default line endings for new files.
---
---Defaults to `crlf` (`\r\n`) on Windows and `lf` (`\n`) on everything else.
---@type "crlf" | "lf"
config.line_endings = PLATFORM == "Windows" and "crlf" or "lf"
---Maximum number of characters per-line for the line guide.
---
---Defaults to 80.
---@type number
config.line_limit = 80 config.line_limit = 80
---Maximum number of project files to keep track of.
---If the number of files in the project exceeds this number,
---Lite XL will not be able to keep track of them.
---They will be not be searched when searching for files or text.
---
---Defaults to 2000.
---@type number
config.max_project_files = 2000 config.max_project_files = 2000
---Enables/disables all transitions.
---
---Defaults to true.
---@type boolean
config.transitions = true config.transitions = true
---Enable/disable individual transitions.
---These values are overriden by `config.transitions`.
config.disabled_transitions = { config.disabled_transitions = {
---Disables scrolling transitions.
scroll = false, scroll = false,
---Disables transitions for CommandView's suggestions list.
commandview = false, commandview = false,
---Disables transitions for showing/hiding the context menu.
contextmenu = false, contextmenu = false,
---Disables transitions when clicking on log items in LogView.
logview = false, logview = false,
---Disables transitions for showing/hiding the Nagbar.
nagbar = false, nagbar = false,
---Disables transitions when scrolling the tab bar.
tabs = false, tabs = false,
---Disables transitions when a tab is being dragged.
tab_drag = false, tab_drag = false,
---Disables transitions when a notification is shown.
statusbar = false, statusbar = false,
} }
---The rate of all transitions.
---
---Defaults to 1.
---@type number
config.animation_rate = 1.0 config.animation_rate = 1.0
---The caret's blinking period, in seconds.
---
---Defaults to 0.8.
---@type number
config.blink_period = 0.8 config.blink_period = 0.8
---Disables caret blinking.
---
---Defaults to false.
---@type boolean
config.disable_blink = false config.disable_blink = false
---Draws whitespaces as dots.
---This option is deprecated.
---Please use the drawwhitespace plugin instead.
---@deprecated
config.draw_whitespace = false config.draw_whitespace = false
---Disables system-drawn window borders.
---
---When set to true, Lite XL draws its own window decorations,
---which can be useful for certain setups.
---
---Defaults to false.
---@type boolean
config.borderless = false config.borderless = false
---Shows/hides the close buttons on tabs.
---When hidden, users can close tabs via keyboard shortcuts or commands.
---
---Defaults to true.
---@type boolean
config.tab_close_button = true config.tab_close_button = true
---Maximum number of clicks recognized by Lite XL.
---
---Defaults to 3.
---@type number
config.max_clicks = 3 config.max_clicks = 3
---Disables plugin version checking. -- set as true to be able to test non supported plugins
---Do not change this unless you know what you are doing.
---
---Defaults to false.
---@type boolean
config.skip_plugins_version = false config.skip_plugins_version = false
-- holds the plugins real config table -- holds the plugins real config table

View File

@ -48,7 +48,7 @@ function Highlighter:start()
self:update_notify(retokenized_from, max - retokenized_from) self:update_notify(retokenized_from, max - retokenized_from)
end end
core.redraw = true core.redraw = true
coroutine.yield(0) coroutine.yield()
end end
self.max_wanted_line = 0 self.max_wanted_line = 0
self.running = false self.running = false

View File

@ -1,6 +1,5 @@
local Object = require "core.object" local Object = require "core.object"
local Highlighter = require "core.doc.highlighter" local Highlighter = require "core.doc.highlighter"
local translate = require "core.doc.translate"
local core = require "core" local core = require "core"
local syntax = require "core.syntax" local syntax = require "core.syntax"
local config = require "core.config" local config = require "core.config"
@ -28,11 +27,9 @@ function Doc:new(filename, abs_filename, new_file)
self:load(filename) self:load(filename)
end end
end end
if new_file then
self.crlf = config.line_endings == "crlf"
end
end end
function Doc:reset() function Doc:reset()
self.lines = { "\n" } self.lines = { "\n" }
self.selections = { 1, 1, 1, 1 } self.selections = { 1, 1, 1, 1 }
@ -41,10 +38,10 @@ function Doc:reset()
self.redo_stack = { idx = 1 } self.redo_stack = { idx = 1 }
self.clean_change_id = 1 self.clean_change_id = 1
self.highlighter = Highlighter(self) self.highlighter = Highlighter(self)
self.overwrite = false
self:reset_syntax() self:reset_syntax()
end end
function Doc:reset_syntax() function Doc:reset_syntax()
local header = self:get_text(1, 1, self:position_offset(1, 1, 128)) local header = self:get_text(1, 1, self:position_offset(1, 1, 128))
local path = self.abs_filename local path = self.abs_filename
@ -59,14 +56,16 @@ function Doc:reset_syntax()
end end
end end
function Doc:set_filename(filename, abs_filename) function Doc:set_filename(filename, abs_filename)
self.filename = filename self.filename = filename
self.abs_filename = abs_filename self.abs_filename = abs_filename
self:reset_syntax() self:reset_syntax()
end end
function Doc:load(filename) function Doc:load(filename)
local fp = assert(io.open(filename, "rb")) local fp = assert( io.open(filename, "rb") )
self:reset() self:reset()
self.lines = {} self.lines = {}
local i = 1 local i = 1
@ -86,6 +85,7 @@ function Doc:load(filename)
self:reset_syntax() self:reset_syntax()
end end
function Doc:reload() function Doc:reload()
if self.filename then if self.filename then
local sel = { self:get_selection() } local sel = { self:get_selection() }
@ -95,6 +95,7 @@ function Doc:reload()
end end
end end
function Doc:save(filename, abs_filename) function Doc:save(filename, abs_filename)
if not filename then if not filename then
assert(self.filename, "no filename set to default to") assert(self.filename, "no filename set to default to")
@ -103,7 +104,7 @@ function Doc:save(filename, abs_filename)
else else
assert(self.filename or abs_filename, "calling save on unnamed doc without absolute path") assert(self.filename or abs_filename, "calling save on unnamed doc without absolute path")
end end
local fp = assert(io.open(filename, "wb")) local fp = assert( io.open(filename, "wb") )
for _, line in ipairs(self.lines) do for _, line in ipairs(self.lines) do
if self.crlf then line = line:gsub("\n", "\r\n") end if self.crlf then line = line:gsub("\n", "\r\n") end
fp:write(line) fp:write(line)
@ -114,10 +115,12 @@ function Doc:save(filename, abs_filename)
self:clean() self:clean()
end end
function Doc:get_name() function Doc:get_name()
return self.filename or "unsaved" return self.filename or "unsaved"
end end
function Doc:is_dirty() function Doc:is_dirty()
if self.new_file then if self.new_file then
if self.filename then return true end if self.filename then return true end
@ -127,17 +130,20 @@ function Doc:is_dirty()
end end
end end
function Doc:clean() function Doc:clean()
self.clean_change_id = self:get_change_id() self.clean_change_id = self:get_change_id()
end end
function Doc:get_indent_info() function Doc:get_indent_info()
if not self.indent_info then return config.tab_type, config.indent_size, false end if not self.indent_info then return config.tab_type, config.indent_size, false end
return self.indent_info.type or config.tab_type, return self.indent_info.type or config.tab_type,
self.indent_info.size or config.indent_size, self.indent_info.size or config.indent_size,
self.indent_info.confirmed self.indent_info.confirmed
end end
function Doc:get_change_id() function Doc:get_change_id()
return self.undo_stack.idx return self.undo_stack.idx
end end
@ -161,14 +167,13 @@ function Doc:get_selection(sort)
return line1, col1, line2, col2, swap return line1, col1, line2, col2, swap
end end
---Get the selection specified by `idx` ---Get the selection specified by `idx`
---@param idx integer @the index of the selection to retrieve ---@param idx integer @the index of the selection to retrieve
---@param sort? boolean @whether to sort the selection returned ---@param sort? boolean @whether to sort the selection returned
---@return integer,integer,integer,integer,boolean? @line1, col1, line2, col2, was the selection sorted ---@return integer,integer,integer,integer,boolean? @line1, col1, line2, col2, was the selection sorted
function Doc:get_selection_idx(idx, sort) function Doc:get_selection_idx(idx, sort)
local line1, col1, line2, col2 = self.selections[idx * 4 - 3], self.selections[idx * 4 - 2], local line1, col1, line2, col2 = self.selections[idx*4-3], self.selections[idx*4-2], self.selections[idx*4-1], self.selections[idx*4]
self.selections[idx * 4 - 1],
self.selections[idx * 4]
if line1 and sort then if line1 and sort then
return sort_positions(line1, col1, line2, col2) return sort_positions(line1, col1, line2, col2)
else else
@ -212,7 +217,7 @@ function Doc:set_selections(idx, line1, col1, line2, col2, swap, rm)
if swap then line1, col1, line2, col2 = line2, col2, line1, col1 end if swap then line1, col1, line2, col2 = line2, col2, line1, col1 end
line1, col1 = self:sanitize_position(line1, col1) line1, col1 = self:sanitize_position(line1, col1)
line2, col2 = self:sanitize_position(line2 or line1, col2 or col1) line2, col2 = self:sanitize_position(line2 or line1, col2 or col1)
common.splice(self.selections, (idx - 1) * 4 + 1, rm == nil and 4 or rm, { line1, col1, line2, col2 }) common.splice(self.selections, (idx - 1)*4 + 1, rm == nil and 4 or rm, { line1, col1, line2, col2 })
end end
function Doc:add_selection(line1, col1, line2, col2, swap) function Doc:add_selection(line1, col1, line2, col2, swap)
@ -228,6 +233,7 @@ function Doc:add_selection(line1, col1, line2, col2, swap)
self.last_selection = target self.last_selection = target
end end
function Doc:remove_selection(idx) function Doc:remove_selection(idx)
if self.last_selection >= idx then if self.last_selection >= idx then
self.last_selection = self.last_selection - 1 self.last_selection = self.last_selection - 1
@ -235,6 +241,7 @@ function Doc:remove_selection(idx)
common.splice(self.selections, (idx - 1) * 4 + 1, 4) common.splice(self.selections, (idx - 1) * 4 + 1, 4)
end end
function Doc:set_selection(line1, col1, line2, col2, swap) function Doc:set_selection(line1, col1, line2, col2, swap)
self.selections = {} self.selections = {}
self:set_selections(1, line1, col1, line2, col2, swap) self:set_selections(1, line1, col1, line2, col2, swap)
@ -245,24 +252,24 @@ function Doc:merge_cursors(idx)
for i = (idx or (#self.selections - 3)), (idx or 5), -4 do for i = (idx or (#self.selections - 3)), (idx or 5), -4 do
for j = 1, i - 4, 4 do for j = 1, i - 4, 4 do
if self.selections[i] == self.selections[j] and if self.selections[i] == self.selections[j] and
self.selections[i + 1] == self.selections[j + 1] then self.selections[i+1] == self.selections[j+1] then
common.splice(self.selections, i, 4) common.splice(self.selections, i, 4)
if self.last_selection >= (i + 3) / 4 then if self.last_selection >= (i+3)/4 then
self.last_selection = self.last_selection - 1 self.last_selection = self.last_selection - 1
end end
break break
end end
end end
end end
end end
local function selection_iterator(invariant, idx) local function selection_iterator(invariant, idx)
local target = invariant[3] and (idx * 4 - 7) or (idx * 4 + 1) local target = invariant[3] and (idx*4 - 7) or (idx*4 + 1)
if target > #invariant[1] or target <= 0 or (type(invariant[3]) == "number" and invariant[3] ~= idx - 1) then return end if target > #invariant[1] or target <= 0 or (type(invariant[3]) == "number" and invariant[3] ~= idx - 1) then return end
if invariant[2] then if invariant[2] then
return idx + (invariant[3] and -1 or 1), sort_positions(table.unpack(invariant[1], target, target + 4)) return idx+(invariant[3] and -1 or 1), sort_positions(table.unpack(invariant[1], target, target+4))
else else
return idx + (invariant[3] and -1 or 1), table.unpack(invariant[1], target, target + 4) return idx+(invariant[3] and -1 or 1), table.unpack(invariant[1], target, target+4)
end end
end end
@ -270,9 +277,8 @@ end
-- If a number, runs for exactly that iteration. -- If a number, runs for exactly that iteration.
function Doc:get_selections(sort_intra, idx_reverse) function Doc:get_selections(sort_intra, idx_reverse)
return selection_iterator, { self.selections, sort_intra, idx_reverse }, return selection_iterator, { self.selections, sort_intra, idx_reverse },
idx_reverse == true and ((#self.selections / 4) + 1) or ((idx_reverse or -1) + 1) idx_reverse == true and ((#self.selections / 4) + 1) or ((idx_reverse or -1)+1)
end end
-- End of cursor seciton. -- End of cursor seciton.
function Doc:sanitize_position(line, col) function Doc:sanitize_position(line, col)
@ -285,6 +291,7 @@ function Doc:sanitize_position(line, col)
return line, common.clamp(col, 1, #self.lines[line]) return line, common.clamp(col, 1, #self.lines[line])
end end
local function position_offset_func(self, line, col, fn, ...) local function position_offset_func(self, line, col, fn, ...)
line, col = self:sanitize_position(line, col) line, col = self:sanitize_position(line, col)
return fn(self, line, col, ...) return fn(self, line, col, ...)
@ -323,6 +330,7 @@ function Doc:position_offset(line, col, ...)
end end
end end
function Doc:get_text(line1, col1, line2, col2) function Doc:get_text(line1, col1, line2, col2)
line1, col1 = self:sanitize_position(line1, col1) line1, col1 = self:sanitize_position(line1, col1)
line2, col2 = self:sanitize_position(line2, col2) line2, col2 = self:sanitize_position(line2, col2)
@ -338,11 +346,13 @@ function Doc:get_text(line1, col1, line2, col2)
return table.concat(lines) return table.concat(lines)
end end
function Doc:get_char(line, col) function Doc:get_char(line, col)
line, col = self:sanitize_position(line, col) line, col = self:sanitize_position(line, col)
return self.lines[line]:sub(col, col) return self.lines[line]:sub(col, col)
end end
local function push_undo(undo_stack, time, type, ...) local function push_undo(undo_stack, time, type, ...)
undo_stack[undo_stack.idx] = { type = type, time = time, ... } undo_stack[undo_stack.idx] = { type = type, time = time, ... }
undo_stack[undo_stack.idx - config.max_undos] = nil undo_stack[undo_stack.idx - config.max_undos] = nil
@ -403,8 +413,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
if cline1 < line then break end if cline1 < line then break end
local line_addition = (line < cline1 or col < ccol1) and #lines - 1 or 0 local line_addition = (line < cline1 or col < ccol1) and #lines - 1 or 0
local column_addition = line == cline1 and ccol1 > col and len or 0 local column_addition = line == cline1 and ccol1 > col and len or 0
self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition, self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition, ccol2 + column_addition)
ccol2 + column_addition)
end end
-- push undo -- push undo
@ -417,6 +426,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
self:sanitize_selection() self:sanitize_selection()
end end
function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time) function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
-- push undo -- push undo
local text = self:get_text(line1, col1, line2, col2) local text = self:get_text(line1, col1, line2, col2)
@ -475,6 +485,7 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
self:sanitize_selection() self:sanitize_selection()
end end
function Doc:insert(line, col, text) function Doc:insert(line, col, text)
self.redo_stack = { idx = 1 } self.redo_stack = { idx = 1 }
-- Reset the clean id when we're pushing something new before it -- Reset the clean id when we're pushing something new before it
@ -486,6 +497,7 @@ function Doc:insert(line, col, text)
self:on_text_change("insert") self:on_text_change("insert")
end end
function Doc:remove(line1, col1, line2, col2) function Doc:remove(line1, col1, line2, col2)
self.redo_stack = { idx = 1 } self.redo_stack = { idx = 1 }
line1, col1 = self:sanitize_position(line1, col1) line1, col1 = self:sanitize_position(line1, col1)
@ -495,34 +507,28 @@ function Doc:remove(line1, col1, line2, col2)
self:on_text_change("remove") self:on_text_change("remove")
end end
function Doc:undo() function Doc:undo()
pop_undo(self, self.undo_stack, self.redo_stack, false) pop_undo(self, self.undo_stack, self.redo_stack, false)
end end
function Doc:redo() function Doc:redo()
pop_undo(self, self.redo_stack, self.undo_stack, false) pop_undo(self, self.redo_stack, self.undo_stack, false)
end end
function Doc:text_input(text, idx) function Doc:text_input(text, idx)
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
local had_selection = false
if line1 ~= line2 or col1 ~= col2 then if line1 ~= line2 or col1 ~= col2 then
self:delete_to_cursor(sidx) self:delete_to_cursor(sidx)
had_selection = true
end end
if self.overwrite
and not had_selection
and col1 < #self.lines[line1]
and text:ulen() == 1 then
self:remove(line1, col1, translate.next_char(self, line1, col1))
end
self:insert(line1, col1, text) self:insert(line1, col1, text)
self:move_to_cursor(sidx, #text) self:move_to_cursor(sidx, #text)
end end
end end
function Doc:ime_text_editing(text, start, length, idx) function Doc:ime_text_editing(text, start, length, idx)
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
if line1 ~= line2 or col1 ~= col2 then if line1 ~= line2 or col1 ~= col2 then
@ -533,6 +539,7 @@ function Doc:ime_text_editing(text, start, length, idx)
end end
end end
function Doc:replace_cursor(idx, line1, col1, line2, col2, fn) function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
local old_text = self:get_text(line1, col1, line2, col2) local old_text = self:get_text(line1, col1, line2, col2)
local new_text, res = fn(old_text) local new_text, res = fn(old_text)
@ -548,7 +555,7 @@ function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
end end
function Doc:replace(fn) function Doc:replace(fn)
local has_selection, results = false, {} local has_selection, results = false, { }
for idx, line1, col1, line2, col2 in self:get_selections(true) do for idx, line1, col1, line2, col2 in self:get_selections(true) do
if line1 ~= line2 or col1 ~= col2 then if line1 ~= line2 or col1 ~= col2 then
results[idx] = self:replace_cursor(idx, line1, col1, line2, col2, fn) results[idx] = self:replace_cursor(idx, line1, col1, line2, col2, fn)
@ -562,6 +569,7 @@ function Doc:replace(fn)
return results return results
end end
function Doc:delete_to_cursor(idx, ...) function Doc:delete_to_cursor(idx, ...)
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do
if line1 ~= line2 or col1 ~= col2 then if line1 ~= line2 or col1 ~= col2 then
@ -575,7 +583,6 @@ function Doc:delete_to_cursor(idx, ...)
end end
self:merge_cursors(idx) self:merge_cursors(idx)
end end
function Doc:delete_to(...) return self:delete_to_cursor(nil, ...) end function Doc:delete_to(...) return self:delete_to_cursor(nil, ...) end
function Doc:move_to_cursor(idx, ...) function Doc:move_to_cursor(idx, ...)
@ -584,9 +591,9 @@ function Doc:move_to_cursor(idx, ...)
end end
self:merge_cursors(idx) self:merge_cursors(idx)
end end
function Doc:move_to(...) return self:move_to_cursor(nil, ...) end function Doc:move_to(...) return self:move_to_cursor(nil, ...) end
function Doc:select_to_cursor(idx, ...) function Doc:select_to_cursor(idx, ...)
for sidx, line, col, line2, col2 in self:get_selections(false, idx) do for sidx, line, col, line2, col2 in self:get_selections(false, idx) do
line, col = self:position_offset(line, col, ...) line, col = self:position_offset(line, col, ...)
@ -594,9 +601,9 @@ function Doc:select_to_cursor(idx, ...)
end end
self:merge_cursors(idx) self:merge_cursors(idx)
end end
function Doc:select_to(...) return self:select_to_cursor(nil, ...) end function Doc:select_to(...) return self:select_to_cursor(nil, ...) end
function Doc:get_indent_string() function Doc:get_indent_string()
local indent_type, indent_size = self:get_indent_info() local indent_type, indent_size = self:get_indent_info()
if indent_type == "hard" then if indent_type == "hard" then
@ -618,7 +625,7 @@ function Doc:get_line_indent(line, rnd_up)
local indent = e and line:sub(1, e):gsub("\t", soft_tab) or "" local indent = e and line:sub(1, e):gsub("\t", soft_tab) or ""
local number = #indent / #soft_tab local number = #indent / #soft_tab
return e, indent:sub(1, return e, indent:sub(1,
(rnd_up and math.ceil(number) or math.floor(number)) * #soft_tab) (rnd_up and math.ceil(number) or math.floor(number))*#soft_tab)
end end
end end
@ -667,4 +674,5 @@ function Doc:on_close()
core.log_quiet("Closed doc \"%s\"", self:get_name()) core.log_quiet("Closed doc \"%s\"", self:get_name())
end end
return Doc return Doc

View File

@ -254,11 +254,6 @@ function DocView:scroll_to_line(line, ignore_if_visible, instant)
end end
function DocView:supports_text_input()
return true
end
function DocView:scroll_to_make_visible(line, col) function DocView:scroll_to_make_visible(line, col)
local _, oy = self:get_content_offset() local _, oy = self:get_content_offset()
local _, ly = self:get_line_screen_position(line, col) local _, ly = self:get_line_screen_position(line, col)
@ -322,7 +317,7 @@ function DocView:mouse_selection(doc, snap_type, line1, col1, line2, col2)
line1, col1 = translate.start_of_word(doc, line1, col1) line1, col1 = translate.start_of_word(doc, line1, col1)
line2, col2 = translate.end_of_word(doc, line2, col2) line2, col2 = translate.end_of_word(doc, line2, col2)
elseif snap_type == "lines" then elseif snap_type == "lines" then
col1, col2, line2 = 1, 1, line2 + 1 col1, col2 = 1, math.huge
end end
if swap then if swap then
return line2, col2, line1, col1 return line2, col2, line1, col1
@ -460,16 +455,9 @@ function DocView:draw_line_text(line, x, y)
return self:get_line_height() return self:get_line_height()
end end
function DocView:draw_overwrite_caret(x, y, width)
local lh = self:get_line_height()
renderer.draw_rect(x, y + lh - style.caret_width, width, style.caret_width, style.caret)
end
function DocView:draw_caret(x, y) function DocView:draw_caret(x, y)
local lh = self:get_line_height() local lh = self:get_line_height()
renderer.draw_rect(x, y, style.caret_width, lh, style.caret) renderer.draw_rect(x, y, style.caret_width, lh, style.caret)
end end
function DocView:draw_line_body(line, x, y) function DocView:draw_line_body(line, x, y)
@ -566,12 +554,7 @@ function DocView:draw_overlay()
else else
if config.disable_blink if config.disable_blink
or (core.blink_timer - core.blink_start) % T < T / 2 then or (core.blink_timer - core.blink_start) % T < T / 2 then
local x, y = self:get_line_screen_position(line1, col1) self:draw_caret(self:get_line_screen_position(line1, col1))
if self.doc.overwrite then
self:draw_overwrite_caret(x, y, self:get_font():get_width(self.doc:get_char(line1, col1)))
else
self:draw_caret(x, y)
end
end end
end end
end end

View File

@ -668,9 +668,6 @@ end
function core.init() function core.init()
core.log_items = {}
core.log_quiet("Lite XL version %s - mod-version %s", VERSION, MOD_VERSION_STRING)
command = require "core.command" command = require "core.command"
keymap = require "core.keymap" keymap = require "core.keymap"
dirwatch = require "core.dirwatch" dirwatch = require "core.dirwatch"
@ -724,6 +721,7 @@ function core.init()
core.frame_start = 0 core.frame_start = 0
core.clip_rect_stack = {{ 0,0,0,0 }} core.clip_rect_stack = {{ 0,0,0,0 }}
core.log_items = {}
core.docs = {} core.docs = {}
core.cursor_clipboard = {} core.cursor_clipboard = {}
core.cursor_clipboard_whole_line = {} core.cursor_clipboard_whole_line = {}
@ -825,19 +823,15 @@ function core.init()
local msg = {} local msg = {}
for _, entry in pairs(plugins_refuse_list) do for _, entry in pairs(plugins_refuse_list) do
if #entry.plugins > 0 then if #entry.plugins > 0 then
local msg_list = {} msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(entry.plugins, "\n"))
for _, p in pairs(entry.plugins) do
table.insert(msg_list, string.format("%s[%s]", p.file, p.version_string))
end
msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(msg_list, "\n"))
end end
end end
core.nag_view:show( core.nag_view:show(
"Refused Plugins", "Refused Plugins",
string.format( string.format(
"Some plugins are not loaded due to version mismatch. Expected version %s.\n\n%s.\n\n" .. "Some plugins are not loaded due to version mismatch.\n\n%s.\n\n" ..
"Please download a recent version from https://github.com/lite-xl/lite-xl-plugins.", "Please download a recent version from https://github.com/lite-xl/lite-xl-plugins.",
MOD_VERSION_STRING, table.concat(msg, ".\n\n")), table.concat(msg, ".\n\n")),
opt, function(item) opt, function(item)
if item.text == "Exit" then os.exit(1) end if item.text == "Exit" then os.exit(1) end
end) end)
@ -926,8 +920,6 @@ function core.restart()
end end
local mod_version_regex =
regex.compile([[--.*mod-version:(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:$|\s)]])
local function get_plugin_details(filename) local function get_plugin_details(filename)
local info = system.get_file_info(filename) local info = system.get_file_info(filename)
if info ~= nil and info.type == "dir" then if info ~= nil and info.type == "dir" then
@ -939,32 +931,17 @@ local function get_plugin_details(filename)
if not f then return false end if not f then return false end
local priority = false local priority = false
local version_match = false local version_match = false
local major, minor, patch
for line in f:lines() do for line in f:lines() do
if not version_match then if not version_match then
local _major, _minor, _patch = mod_version_regex:match(line) local mod_version = line:match('%-%-.*%f[%a]mod%-version%s*:%s*(%d+)')
if _major then if mod_version then
_major = tonumber(_major) or 0 version_match = (mod_version == MOD_VERSION)
_minor = tonumber(_minor) or 0
_patch = tonumber(_patch) or 0
major, minor, patch = _major, _minor, _patch
version_match = major == MOD_VERSION_MAJOR
if version_match then
version_match = minor <= MOD_VERSION_MINOR
end
if version_match then
version_match = patch <= MOD_VERSION_PATCH
end
end end
end end
if not priority then if not priority then
priority = line:match('%-%-.*%f[%a]priority%s*:%s*(%d+)') priority = line:match('%-%-.*%f[%a]priority%s*:%s*(%d+)')
if priority then priority = tonumber(priority) end if priority then priority = tonumber(priority) end
end end
if version_match then if version_match then
break break
end end
@ -972,7 +949,6 @@ local function get_plugin_details(filename)
f:close() f:close()
return true, { return true, {
version_match = version_match, version_match = version_match,
version = major and {major, minor, patch} or {},
priority = priority or 100 priority = priority or 100
} }
end end
@ -1008,8 +984,6 @@ function core.load_plugins()
plugin.dir = dir plugin.dir = dir
plugin.priority = details and details.priority or 100 plugin.priority = details and details.priority or 100
plugin.version_match = details and details.version_match or false plugin.version_match = details and details.version_match or false
plugin.version = details and details.version or {}
plugin.version_string = #plugin.version > 0 and table.concat(plugin.version, ".") or "unknown"
end end
-- sort by priority or name for plugins that have same priority -- sort by priority or name for plugins that have same priority
@ -1025,35 +999,27 @@ function core.load_plugins()
if plugin.valid then if plugin.valid then
if not config.skip_plugins_version and not plugin.version_match then if not config.skip_plugins_version and not plugin.version_match then
core.log_quiet( core.log_quiet(
"Version mismatch for plugin %q[%s] from %s", "Version mismatch for plugin %q from %s",
plugin.name, plugin.name,
plugin.version_string,
plugin.dir plugin.dir
) )
local rlist = plugin.dir:find(USERDIR, 1, true) == 1 local rlist = plugin.dir:find(USERDIR, 1, true) == 1
and 'userdir' or 'datadir' and 'userdir' or 'datadir'
local list = refused_list[rlist].plugins local list = refused_list[rlist].plugins
table.insert(list, plugin) table.insert(list, plugin.file)
elseif config.plugins[plugin.name] ~= false then elseif config.plugins[plugin.name] ~= false then
local start = system.get_time() local start = system.get_time()
local ok, loaded_plugin = core.try(require, "plugins." .. plugin.name) local ok = core.try(require, "plugins." .. plugin.name)
if ok then if ok then
local plugin_version = ""
if plugin.version_string ~= MOD_VERSION_STRING then
plugin_version = "["..plugin.version_string.."]"
end
core.log_quiet( core.log_quiet(
"Loaded plugin %q%s from %s in %.1fms", "Loaded plugin %q from %s in %.1fms",
plugin.name, plugin.name,
plugin_version,
plugin.dir, plugin.dir,
(system.get_time() - start) * 1000 (system.get_time() - start) * 1000
) )
end end
if not ok then if not ok then
no_errors = false no_errors = false
elseif config.plugins[plugin.name].onload then
core.try(config.plugins[plugin.name].onload, loaded_plugin)
end end
end end
end end
@ -1107,7 +1073,6 @@ function core.set_active_view(view)
-- Reset the IME even if the focus didn't change -- Reset the IME even if the focus didn't change
ime.stop() ime.stop()
if view ~= core.active_view then if view ~= core.active_view then
system.text_input(view:supports_text_input())
if core.active_view and core.active_view.force_focus then if core.active_view and core.active_view.force_focus then
core.next_active_view = view core.next_active_view = view
return return
@ -1296,12 +1261,6 @@ function core.on_event(type, ...)
if not core.root_view:on_mouse_wheel(...) then if not core.root_view:on_mouse_wheel(...) then
did_keymap = keymap.on_mouse_wheel(...) did_keymap = keymap.on_mouse_wheel(...)
end end
elseif type == "touchpressed" then
core.root_view:on_touch_pressed(...)
elseif type == "touchreleased" then
core.root_view:on_touch_released(...)
elseif type == "touchmoved" then
core.root_view:on_touch_moved(...)
elseif type == "resized" then elseif type == "resized" then
core.window_mode = system.get_window_mode() core.window_mode = system.get_window_mode()
elseif type == "minimized" or type == "maximized" or type == "restored" then elseif type == "minimized" or type == "maximized" or type == "restored" then
@ -1351,11 +1310,6 @@ function core.step()
did_keymap = false did_keymap = false
elseif type == "mousemoved" then elseif type == "mousemoved" then
core.try(core.on_event, type, a, b, c, d) core.try(core.on_event, type, a, b, c, d)
elseif type == "enteringforeground" then
-- to break our frame refresh in two if we get entering/entered at the same time.
-- required to avoid flashing and refresh issues on mobile
core.redraw = true
break
else else
local _, res = core.try(core.on_event, type, a, b, c, d) local _, res = core.try(core.on_event, type, a, b, c, d)
did_keymap = res or did_keymap did_keymap = res or did_keymap
@ -1510,16 +1464,4 @@ function core.on_error(err)
end end
local alerted_deprecations = {}
---Show deprecation notice once per `kind`.
---
---@param kind string
function core.deprecation_log(kind)
if alerted_deprecations[kind] then return end
alerted_deprecations[kind] = true
core.warn("Used deprecated functionality [%s]. Check if your plugins are up to date.", kind)
end
return core return core

View File

@ -36,8 +36,6 @@ local function keymap_macos(keymap)
["wheel"] = "root:scroll", ["wheel"] = "root:scroll",
["hwheel"] = "root:horizontal-scroll", ["hwheel"] = "root:horizontal-scroll",
["shift+hwheel"] = "root:horizontal-scroll", ["shift+hwheel"] = "root:horizontal-scroll",
["wheelup"] = "root:scroll-hovered-tabs-backward",
["wheeldown"] = "root:scroll-hovered-tabs-forward",
["cmd+f"] = "find-replace:find", ["cmd+f"] = "find-replace:find",
["cmd+r"] = "find-replace:replace", ["cmd+r"] = "find-replace:replace",

View File

@ -71,6 +71,7 @@ local function key_to_stroke(key)
return normalize_stroke(table.concat(keys, "+")) return normalize_stroke(table.concat(keys, "+"))
end end
---Remove the given value from an array associated to a key in a table. ---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 tbl table<string, string> The table containing the key
---@param k string The key containing the array ---@param k string The key containing the array
@ -124,6 +125,7 @@ end
function keymap.add_direct(map) function keymap.add_direct(map)
for stroke, commands in pairs(map) do for stroke, commands in pairs(map) do
stroke = normalize_stroke(stroke) stroke = normalize_stroke(stroke)
if type(commands) == "string" or type(commands) == "function" then if type(commands) == "string" or type(commands) == "function" then
commands = { commands } commands = { commands }
end end
@ -323,8 +325,6 @@ keymap.add_direct {
["wheel"] = "root:scroll", ["wheel"] = "root:scroll",
["hwheel"] = "root:horizontal-scroll", ["hwheel"] = "root:horizontal-scroll",
["shift+wheel"] = "root:horizontal-scroll", ["shift+wheel"] = "root:horizontal-scroll",
["wheelup"] = "root:scroll-hovered-tabs-backward",
["wheeldown"] = "root:scroll-hovered-tabs-forward",
["ctrl+f"] = "find-replace:find", ["ctrl+f"] = "find-replace:find",
["ctrl+r"] = "find-replace:replace", ["ctrl+r"] = "find-replace:replace",
@ -341,7 +341,6 @@ keymap.add_direct {
["ctrl+x"] = "doc:cut", ["ctrl+x"] = "doc:cut",
["ctrl+c"] = "doc:copy", ["ctrl+c"] = "doc:copy",
["ctrl+v"] = "doc:paste", ["ctrl+v"] = "doc:paste",
["insert"] = "doc:toggle-overwrite",
["ctrl+insert"] = "doc:copy", ["ctrl+insert"] = "doc:copy",
["shift+insert"] = "doc:paste", ["shift+insert"] = "doc:paste",
["escape"] = { "command:escape", "doc:select-none", "dialog:select-no" }, ["escape"] = { "command:escape", "doc:select-none", "dialog:select-no" },
@ -391,7 +390,7 @@ keymap.add_direct {
["shift+1lclick"] = "doc:select-to-cursor", ["shift+1lclick"] = "doc:select-to-cursor",
["ctrl+1lclick"] = "doc:split-cursor", ["ctrl+1lclick"] = "doc:split-cursor",
["1lclick"] = "doc:set-cursor", ["1lclick"] = "doc:set-cursor",
["2lclick"] = { "doc:set-cursor-word", "emptyview:new-doc", "tabbar:new-doc" }, ["2lclick"] = "doc:set-cursor-word",
["3lclick"] = "doc:set-cursor-line", ["3lclick"] = "doc:set-cursor-line",
["shift+left"] = "doc:select-to-previous-char", ["shift+left"] = "doc:select-to-previous-char",
["shift+right"] = "doc:select-to-next-char", ["shift+right"] = "doc:select-to-next-char",
@ -412,4 +411,3 @@ keymap.add_direct {
} }
return keymap return keymap

View File

@ -7,10 +7,6 @@ modkeys.map = {
["right shift"] = "shift", ["right shift"] = "shift",
["left alt"] = "alt", ["left alt"] = "alt",
["right alt"] = "altgr", ["right alt"] = "altgr",
["left gui"] = "super",
["left windows"] = "super",
["right gui"] = "super",
["right windows"] = "super"
} }
modkeys.keys = { "ctrl", "shift", "alt", "altgr" } modkeys.keys = { "ctrl", "shift", "alt", "altgr" }

View File

@ -24,7 +24,6 @@ function NagView:new()
self.scrollable = true self.scrollable = true
self.target_height = 0 self.target_height = 0
self.on_mouse_pressed_root = nil self.on_mouse_pressed_root = nil
self.dim_alpha = 0
end end
function NagView:get_title() function NagView:get_title()
@ -69,9 +68,7 @@ function NagView:dim_window_content()
oy = oy + self.show_height oy = oy + self.show_height
local w, h = core.root_view.size.x, core.root_view.size.y - oy local w, h = core.root_view.size.x, core.root_view.size.y - oy
core.root_view:defer_draw(function() core.root_view:defer_draw(function()
local dim_color = { table.unpack(style.nagbar_dim) } renderer.draw_rect(ox, oy, w, h, style.nagbar_dim)
dim_color[4] = style.nagbar_dim[4] * self.dim_alpha
renderer.draw_rect(ox, oy, w, h, dim_color)
end) end)
end end
@ -175,13 +172,10 @@ function NagView:update()
NagView.super.update(self) NagView.super.update(self)
if self.visible and core.active_view == self and self.title then if self.visible and core.active_view == self and self.title then
local target_height = self:get_target_height() self:move_towards(self, "show_height", self:get_target_height(), nil, "nagbar")
self:move_towards(self, "show_height", target_height, nil, "nagbar")
self:move_towards(self, "underline_progress", 1, nil, "nagbar") self:move_towards(self, "underline_progress", 1, nil, "nagbar")
self:move_towards(self, "dim_alpha", self.show_height / target_height, nil, "nagbar")
else else
self:move_towards(self, "show_height", 0, nil, "nagbar") self:move_towards(self, "show_height", 0, nil, "nagbar")
self:move_towards(self, "dim_alpha", 0, nil, "nagbar")
if self.show_height <= 0 then if self.show_height <= 0 then
self.title = nil self.title = nil
self.message = nil self.message = nil

View File

@ -18,6 +18,7 @@ function Node:new(type)
if self.type == "leaf" then if self.type == "leaf" then
self:add_view(EmptyView()) self:add_view(EmptyView())
end end
self.hovered = {x = -1, y = -1 }
self.hovered_close = 0 self.hovered_close = 0
self.tab_shift = 0 self.tab_shift = 0
self.tab_offset = 1 self.tab_offset = 1
@ -32,10 +33,9 @@ function Node:propagate(fn, ...)
end end
---@deprecated
function Node:on_mouse_moved(x, y, ...) function Node:on_mouse_moved(x, y, ...)
core.deprecation_log("Node:on_mouse_moved")
if self.type == "leaf" then if self.type == "leaf" then
self.hovered.x, self.hovered.y = x, y
self.active_view:on_mouse_moved(x, y, ...) self.active_view:on_mouse_moved(x, y, ...)
else else
self:propagate("on_mouse_moved", x, y, ...) self:propagate("on_mouse_moved", x, y, ...)
@ -43,9 +43,7 @@ function Node:on_mouse_moved(x, y, ...)
end end
---@deprecated
function Node:on_mouse_released(...) function Node:on_mouse_released(...)
core.deprecation_log("Node:on_mouse_released")
if self.type == "leaf" then if self.type == "leaf" then
self.active_view:on_mouse_released(...) self.active_view:on_mouse_released(...)
else else
@ -54,9 +52,7 @@ function Node:on_mouse_released(...)
end end
---@deprecated
function Node:on_mouse_left() function Node:on_mouse_left()
core.deprecation_log("Node:on_mouse_left")
if self.type == "leaf" then if self.type == "leaf" then
self.active_view:on_mouse_left() self.active_view:on_mouse_left()
else else
@ -65,17 +61,6 @@ function Node:on_mouse_left()
end end
---@deprecated
function Node:on_touch_moved(...)
core.deprecation_log("Node:on_touch_moved")
if self.type == "leaf" then
self.active_view:on_touch_moved(...)
else
self:propagate("on_touch_moved", ...)
end
end
function Node:consume(node) function Node:consume(node)
for k, _ in pairs(self) do self[k] = nil end for k, _ in pairs(self) do self[k] = nil end
for k, v in pairs(node) do self[k] = v end for k, v in pairs(node) do self[k] = v end
@ -177,12 +162,8 @@ function Node:add_view(view, idx)
assert(not self.locked, "Tried to add view to locked node") assert(not self.locked, "Tried to add view to locked node")
if self.views[1] and self.views[1]:is(EmptyView) then if self.views[1] and self.views[1]:is(EmptyView) then
table.remove(self.views) table.remove(self.views)
if idx and idx > 1 then
idx = idx - 1
end
end end
idx = common.clamp(idx or (#self.views + 1), 1, (#self.views + 1)) table.insert(self.views, idx or (#self.views + 1), view)
table.insert(self.views, idx, view)
self:set_active_view(view) self:set_active_view(view)
end end
@ -320,7 +301,7 @@ function Node:tab_hovered_update(px, py)
if px >= cx and px < cx + cw and py >= y and py < y + h and config.tab_close_button then if px >= cx and px < cx + cw and py >= y and py < y + h and config.tab_close_button then
self.hovered_close = tab_index self.hovered_close = tab_index
end end
elseif #self.views > self:get_visible_tabs_number() then else
self.hovered_scroll_button = self:get_scroll_button_index(px, py) or 0 self.hovered_scroll_button = self:get_scroll_button_index(px, py) or 0
end end
end end
@ -338,17 +319,10 @@ function Node:get_child_overlapping_point(x, y)
return child:get_child_overlapping_point(x, y) return child:get_child_overlapping_point(x, y)
end end
-- returns: total height, text padding, top margin
local function get_tab_y_sizes()
local height = style.font:get_height()
local padding = style.padding.y
local margin = style.margin.tab.top
return height + (padding * 2) + margin, padding, margin
end
function Node:get_scroll_button_rect(index) function Node:get_scroll_button_rect(index)
local w, pad = get_scroll_button_width() local w, pad = get_scroll_button_width()
local h = get_tab_y_sizes() local h = style.font:get_height() + style.padding.y * 2
local x = self.position.x + (index == 1 and self.size.x - w * 2 or self.size.x - w) local x = self.position.x + (index == 1 and self.size.x - w * 2 or self.size.x - w)
return x, self.position.y, w, h, pad return x, self.position.y, w, h, pad
end end
@ -359,8 +333,8 @@ function Node:get_tab_rect(idx)
local x0 = self.position.x local x0 = self.position.x
local x1 = x0 + common.clamp(self.tab_width * (idx - 1) - self.tab_shift, 0, maxw) local x1 = x0 + common.clamp(self.tab_width * (idx - 1) - self.tab_shift, 0, maxw)
local x2 = x0 + common.clamp(self.tab_width * idx - self.tab_shift, 0, maxw) local x2 = x0 + common.clamp(self.tab_width * idx - self.tab_shift, 0, maxw)
local h, pad_y, margin_y = get_tab_y_sizes() local h = style.font:get_height() + style.padding.y * 2
return x1, self.position.y, x2 - x1, h, margin_y return x1, self.position.y, x2 - x1, h
end end
@ -508,7 +482,7 @@ function Node:update()
for _, view in ipairs(self.views) do for _, view in ipairs(self.views) do
view:update() view:update()
end end
self:tab_hovered_update(core.root_view.mouse.x, core.root_view.mouse.y) self:tab_hovered_update(self.hovered.x, self.hovered.y)
local tab_width = self:target_tab_width() local tab_width = self:target_tab_width()
self:move_towards("tab_shift", tab_width * (self.tab_offset - 1), nil, "tabs") self:move_towards("tab_shift", tab_width * (self.tab_offset - 1), nil, "tabs")
self:move_towards("tab_width", tab_width, nil, "tabs") self:move_towards("tab_width", tab_width, nil, "tabs")
@ -551,7 +525,6 @@ function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalo
if is_active then if is_active then
color = style.text color = style.text
renderer.draw_rect(x, y, w, h, style.background) renderer.draw_rect(x, y, w, h, style.background)
renderer.draw_rect(x, y, w, ds, style.divider)
renderer.draw_rect(x + w, y, ds, h, style.divider) renderer.draw_rect(x + w, y, ds, h, style.divider)
renderer.draw_rect(x - ds, y, ds, h, style.divider) renderer.draw_rect(x - ds, y, ds, h, style.divider)
end end
@ -559,8 +532,7 @@ function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalo
end end
function Node:draw_tab(view, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone) function Node:draw_tab(view, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
local _, padding_y, margin_y = get_tab_y_sizes() x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalone)
x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y + margin_y, w, h - margin_y, standalone)
-- Close button -- Close button
local cx, cw, cpad = close_button_location(x, w) local cx, cw, cpad = close_button_location(x, w)
local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button) local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button)
@ -636,13 +608,6 @@ function Node:is_empty()
end end
function Node:is_in_tab_area(x, y)
if not self:should_show_tabs() then return false end
local _, ty, _, th = self:get_scroll_button_rect(1)
return y >= ty and y < ty + th
end
function Node:close_all_docviews(keep_active) function Node:close_all_docviews(keep_active)
local node_active_view = self.active_view local node_active_view = self.active_view
local lost_active_view = false local lost_active_view = false
@ -781,7 +746,7 @@ function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
tab_index = self:get_visible_tabs_number() + (self.tab_offset - 1 or 0) tab_index = self:get_visible_tabs_number() + (self.tab_offset - 1 or 0)
end end
end end
local tab_x, tab_y, tab_w, tab_h, margin_y = self:get_tab_rect(tab_index) local tab_x, tab_y, tab_w, tab_h = self:get_tab_rect(tab_index)
if x > tab_x + tab_w / 2 and tab_index <= #self.views then if x > tab_x + tab_w / 2 and tab_index <= #self.views then
-- use next tab -- use next tab
tab_x = tab_x + tab_w tab_x = tab_x + tab_w
@ -792,7 +757,7 @@ function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
tab_index = tab_index - 1 tab_index = tab_index - 1
tab_x = tab_x - tab_w tab_x = tab_x - tab_w
end end
return tab_index, tab_x, tab_y + margin_y, tab_w, tab_h - margin_y return tab_index, tab_x, tab_y, tab_w, tab_h
end end
return Node return Node

View File

@ -27,13 +27,6 @@ function Object:is(T)
return getmetatable(self) == T return getmetatable(self) == T
end end
---Check if the parameter is strictly of the object type.
---@param T any
---@return boolean
function Object:is_class_of(T)
return getmetatable(T) == self
end
---Check if the object inherits from the given type. ---Check if the object inherits from the given type.
---@param T any ---@param T any
---@return boolean ---@return boolean
@ -48,22 +41,6 @@ function Object:extends(T)
return false return false
end end
---Check if the parameter inherits from the object.
---@param T any
---@return boolean
function Object:is_extended_by(T)
local mt = getmetatable(T)
while mt do
if mt == self then
return true
end
local _mt = getmetatable(T)
if mt == _mt then break end
mt = _mt
end
return false
end
---Metamethod to get a string representation of an object. ---Metamethod to get a string representation of an object.
---@return string ---@return string
function Object:__tostring() function Object:__tostring()

View File

@ -24,9 +24,6 @@ function RootView:new()
base_color = style.drag_overlay_tab, base_color = style.drag_overlay_tab,
color = { table.unpack(style.drag_overlay_tab) } } color = { table.unpack(style.drag_overlay_tab) } }
self.drag_overlay_tab.to = { x = 0, y = 0, w = 0, h = 0 } self.drag_overlay_tab.to = { x = 0, y = 0, w = 0, h = 0 }
self.grab = nil -- = {view = nil, button = nil}
self.overlapping_view = nil
self.touched_view = nil
end end
@ -119,31 +116,6 @@ function RootView:close_all_docviews(keep_active)
end end
---Obtain mouse grab.
---
---This means that mouse movements will be sent to the specified view, even when
---those occur outside of it.
---There can't be multiple mouse grabs, even for different buttons.
---@see RootView:ungrab_mouse
---@param button core.view.mousebutton
---@param view core.view
function RootView:grab_mouse(button, view)
assert(self.grab == nil)
self.grab = {view = view, button = button}
end
---Release mouse grab.
---
---The specified button *must* be the last button that grabbed the mouse.
---@see RootView:grab_mouse
---@param button core.view.mousebutton
function RootView:ungrab_mouse(button)
assert(self.grab and self.grab.button == button)
self.grab = nil
end
---Function to intercept mouse pressed events on the active view. ---Function to intercept mouse pressed events on the active view.
---Do nothing by default. ---Do nothing by default.
---@param button core.view.mousebutton ---@param button core.view.mousebutton
@ -160,10 +132,6 @@ end
---@param clicks integer ---@param clicks integer
---@return boolean ---@return boolean
function RootView:on_mouse_pressed(button, x, y, clicks) function RootView:on_mouse_pressed(button, x, y, clicks)
-- If there is a grab, release it first
if self.grab then
self:on_mouse_released(self.grab.button, x, y)
end
local div = self.root_node:get_divider_overlapping_point(x, y) local div = self.root_node:get_divider_overlapping_point(x, y)
local node = self.root_node:get_child_overlapping_point(x, y) local node = self.root_node:get_child_overlapping_point(x, y)
if div and (node and not node.active_view:scrollbar_overlaps_point(x, y)) then if div and (node and not node.active_view:scrollbar_overlaps_point(x, y)) then
@ -188,7 +156,6 @@ function RootView:on_mouse_pressed(button, x, y, clicks)
end end
elseif not self.dragged_node then -- avoid sending on_mouse_pressed events when dragging tabs elseif not self.dragged_node then -- avoid sending on_mouse_pressed events when dragging tabs
core.set_active_view(node.active_view) core.set_active_view(node.active_view)
self:grab_mouse(button, node.active_view)
return self.on_view_mouse_pressed(button, x, y, clicks) or node.active_view:on_mouse_pressed(button, x, y, clicks) return self.on_view_mouse_pressed(button, x, y, clicks) or node.active_view:on_mouse_pressed(button, x, y, clicks)
end end
end end
@ -221,21 +188,6 @@ end
---@param x number ---@param x number
---@param y number ---@param y number
function RootView:on_mouse_released(button, x, y, ...) function RootView:on_mouse_released(button, x, y, ...)
if self.grab then
if self.grab.button == button then
local grabbed_view = self.grab.view
grabbed_view:on_mouse_released(button, x, y, ...)
self:ungrab_mouse(button)
-- If the mouse was released over a different view, send it the mouse position
local hovered_view = self.root_node:get_child_overlapping_point(x, y)
if grabbed_view ~= hovered_view then
self:on_mouse_moved(x, y, 0, 0)
end
end
return
end
if self.dragged_divider then if self.dragged_divider then
self.dragged_divider = nil self.dragged_divider = nil
end end
@ -276,6 +228,8 @@ function RootView:on_mouse_released(button, x, y, ...)
end end
self.dragged_node = nil self.dragged_node = nil
end end
else -- avoid sending on_mouse_released events when dragging tabs
self.root_node:on_mouse_released(button, x, y, ...)
end end
end end
@ -296,14 +250,6 @@ end
---@param dx number ---@param dx number
---@param dy number ---@param dy number
function RootView:on_mouse_moved(x, y, dx, dy) function RootView:on_mouse_moved(x, y, dx, dy)
self.mouse.x, self.mouse.y = x, y
if self.grab then
self.grab.view:on_mouse_moved(x, y, dx, dy)
core.request_cursor(self.grab.view.cursor)
return
end
if core.active_view == core.nag_view then if core.active_view == core.nag_view then
core.request_cursor("arrow") core.request_cursor("arrow")
core.active_view:on_mouse_moved(x, y, dx, dy) core.active_view:on_mouse_moved(x, y, dx, dy)
@ -323,6 +269,8 @@ function RootView:on_mouse_moved(x, y, dx, dy)
return return
end end
self.mouse.x, self.mouse.y = x, y
local dn = self.dragged_node local dn = self.dragged_node
if dn and not dn.dragging then if dn and not dn.dragging then
-- start dragging only after enough movement -- start dragging only after enough movement
@ -335,33 +283,32 @@ function RootView:on_mouse_moved(x, y, dx, dy)
-- avoid sending on_mouse_moved events when dragging tabs -- avoid sending on_mouse_moved events when dragging tabs
if dn then return end if dn then return end
local last_overlapping_view = self.overlapping_view self.root_node:on_mouse_moved(x, y, dx, dy)
local overlapping_node = self.root_node:get_child_overlapping_point(x, y)
self.overlapping_view = overlapping_node and overlapping_node.active_view
if last_overlapping_view and last_overlapping_view ~= self.overlapping_view then local last_overlapping_node = self.overlapping_node
last_overlapping_view:on_mouse_left() self.overlapping_node = self.root_node:get_child_overlapping_point(x, y)
if last_overlapping_node and last_overlapping_node ~= self.overlapping_node then
last_overlapping_node:on_mouse_left()
end end
if not self.overlapping_view then return end
self.overlapping_view:on_mouse_moved(x, y, dx, dy)
core.request_cursor(self.overlapping_view.cursor)
if not overlapping_node then return end
local div = self.root_node:get_divider_overlapping_point(x, y) local div = self.root_node:get_divider_overlapping_point(x, y)
if overlapping_node:get_scroll_button_index(x, y) or overlapping_node:is_in_tab_area(x, y) then local tab_index = self.overlapping_node and self.overlapping_node:get_tab_overlapping_point(x, y)
if self.overlapping_node and self.overlapping_node:get_scroll_button_index(x, y) then
core.request_cursor("arrow") core.request_cursor("arrow")
elseif div and not self.overlapping_view:scrollbar_overlaps_point(x, y) then elseif div and (self.overlapping_node and not self.overlapping_node.active_view:scrollbar_overlaps_point(x, y)) then
core.request_cursor(div.type == "hsplit" and "sizeh" or "sizev") core.request_cursor(div.type == "hsplit" and "sizeh" or "sizev")
elseif tab_index then
core.request_cursor("arrow")
elseif self.overlapping_node then
core.request_cursor(self.overlapping_node.active_view.cursor)
end end
end end
function RootView:on_mouse_left() function RootView:on_mouse_left()
if self.overlapping_view then if self.overlapping_node then
self.overlapping_view:on_mouse_left() self.overlapping_node:on_mouse_left()
end end
end end
@ -387,50 +334,6 @@ function RootView:on_text_input(...)
core.active_view:on_text_input(...) core.active_view:on_text_input(...)
end end
function RootView:on_touch_pressed(x, y, ...)
local touched_node = self.root_node:get_child_overlapping_point(x, y)
self.touched_view = touched_node and touched_node.active_view
end
function RootView:on_touch_released(x, y, ...)
self.touched_view = nil
end
function RootView:on_touch_moved(x, y, dx, dy, ...)
if not self.touched_view then return end
if core.active_view == core.nag_view then
core.active_view:on_touch_moved(x, y, dx, dy, ...)
return
end
if self.dragged_divider then
local node = self.dragged_divider
if node.type == "hsplit" then
x = common.clamp(x - node.position.x, 0, self.root_node.size.x * 0.95)
resize_child_node(node, "x", x, dx)
elseif node.type == "vsplit" then
y = common.clamp(y - node.position.y, 0, self.root_node.size.y * 0.95)
resize_child_node(node, "y", y, dy)
end
node.divider = common.clamp(node.divider, 0.01, 0.99)
return
end
local dn = self.dragged_node
if dn and not dn.dragging then
-- start dragging only after enough movement
dn.dragging = common.distance(x, y, dn.drag_start_x, dn.drag_start_y) > style.tab_width * .05
if dn.dragging then
core.request_cursor("hand")
end
end
-- avoid sending on_touch_moved events when dragging tabs
if dn then return end
self.touched_view:on_touch_moved(x, y, dx, dy, ...)
end
function RootView:on_ime_text_editing(...) function RootView:on_ime_text_editing(...)
core.active_view:on_ime_text_editing(...) core.active_view:on_ime_text_editing(...)
end end

View File

@ -58,9 +58,9 @@ function Scrollbar:new(options)
---@type "expanded" | "contracted" | false @Force the scrollbar status ---@type "expanded" | "contracted" | false @Force the scrollbar status
self.force_status = options.force_status self.force_status = options.force_status
self:set_forced_status(options.force_status) self:set_forced_status(options.force_status)
---@type number? @Override the default value specified by `style.scrollbar_size`
self.contracted_size = options.contracted_size
---@type number? @Override the default value specified by `style.expanded_scrollbar_size` ---@type number? @Override the default value specified by `style.expanded_scrollbar_size`
self.contracted_size = options.contracted_size
---@type number? @Override the default value specified by `style.scrollbar_size`
self.expanded_size = options.expanded_size self.expanded_size = options.expanded_size
end end
@ -121,7 +121,7 @@ function Scrollbar:_get_thumb_rect_normal()
across_size = across_size + (expanded_scrollbar_size - scrollbar_size) * self.expand_percent across_size = across_size + (expanded_scrollbar_size - scrollbar_size) * self.expand_percent
return return
nr.across + nr.across_size - across_size, nr.across + nr.across_size - across_size,
nr.along + self.percent * (nr.along_size - along_size), nr.along + self.percent * nr.scrollable * (nr.along_size - along_size) / (sz - nr.along_size),
across_size, across_size,
along_size along_size
end end
@ -238,8 +238,7 @@ end
function Scrollbar:_on_mouse_moved_normal(x, y, dx, dy) function Scrollbar:_on_mouse_moved_normal(x, y, dx, dy)
if self.dragging then if self.dragging then
local nr = self.normal_rect local nr = self.normal_rect
local _, _, _, along_size = self:_get_thumb_rect_normal() return common.clamp((y - nr.along + self.drag_start_offset) / nr.along_size, 0, 1)
return common.clamp((y - nr.along + self.drag_start_offset) / (nr.along_size - along_size), 0, 1)
end end
return self:_update_hover_status_normal(x, y) return self:_update_hover_status_normal(x, y)
end end
@ -282,7 +281,7 @@ function Scrollbar:set_size(x, y, w, h, scrollable)
end end
---Updates the scrollbar location ---Updates the scrollbar location
---@param percent number @number between 0 and 1 where 0 means thumb at the top and 1 at the bottom ---@param percent number @number between 0 and 1 representing the position of the middle part of the thumb
function Scrollbar:set_percent(percent) function Scrollbar:set_percent(percent)
self.percent = percent self.percent = percent
end end

View File

@ -1,9 +1,6 @@
-- this file is used by lite-xl to setup the Lua environment when starting -- this file is used by lite-xl to setup the Lua environment when starting
VERSION = "2.1.2r1" VERSION = "2.1.3r1"
MOD_VERSION_MAJOR = 3 MOD_VERSION = "3"
MOD_VERSION_MINOR = 0
MOD_VERSION_PATCH = 0
MOD_VERSION_STRING = string.format("%d.%d.%d", MOD_VERSION_MAJOR, MOD_VERSION_MINOR, MOD_VERSION_PATCH)
SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or 1 SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or 1
PATHSEP = package.config:sub(1, 1) PATHSEP = package.config:sub(1, 1)
@ -25,7 +22,7 @@ package.path = DATADIR .. '/?/init.lua;' .. package.path
package.path = USERDIR .. '/?.lua;' .. package.path package.path = USERDIR .. '/?.lua;' .. package.path
package.path = USERDIR .. '/?/init.lua;' .. package.path package.path = USERDIR .. '/?/init.lua;' .. package.path
local suffix = PLATFORM == "Windows" and 'dll' or 'so' local suffix = PLATFORM == "Mac OS X" and 'lib' or (PLATFORM == "Windows" and 'dll' or 'so')
package.cpath = package.cpath =
USERDIR .. '/?.' .. ARCH .. "." .. suffix .. ";" .. USERDIR .. '/?.' .. ARCH .. "." .. suffix .. ";" ..
USERDIR .. '/?/init.' .. ARCH .. "." .. suffix .. ";" .. USERDIR .. '/?/init.' .. ARCH .. "." .. suffix .. ";" ..

View File

@ -232,42 +232,15 @@ function StatusView:register_docview_items()
return { return {
style.text, line, ":", style.text, line, ":",
col > config.line_limit and style.accent or style.text, col, col > config.line_limit and style.accent or style.text, col,
style.text style.text,
self.separator,
string.format("%.f%%", line / #dv.doc.lines * 100)
} }
end, end,
command = "doc:go-to-line", command = "doc:go-to-line",
tooltip = "line : column" tooltip = "line : column"
}) })
self:add_item({
predicate = predicate_docview,
name = "doc:position-percent",
alignment = StatusView.Item.LEFT,
get_item = function()
local dv = core.active_view
local line = dv.doc:get_selection()
return {
string.format("%.f%%", line / #dv.doc.lines * 100)
}
end,
tooltip = "caret position"
})
self:add_item({
predicate = predicate_docview,
name = "doc:selections",
alignment = StatusView.Item.LEFT,
get_item = function()
local dv = core.active_view
local nsel = math.floor(#dv.doc.selections / 4)
if nsel > 1 then
return { style.text, nsel, " selections" }
end
return {}
end
})
self:add_item({ self:add_item({
predicate = predicate_docview, predicate = predicate_docview,
name = "doc:indentation", name = "doc:indentation",
@ -319,19 +292,6 @@ function StatusView:register_docview_items()
end, end,
command = "doc:toggle-line-ending" command = "doc:toggle-line-ending"
}) })
self:add_item {
predicate = predicate_docview,
name = "doc:overwrite-mode",
alignment = StatusView.Item.RIGHT,
get_item = function()
return {
style.text, core.active_view.doc.overwrite and "OVR" or "INS"
}
end,
command = "doc:toggle-overwrite",
separator = StatusView.separator2
}
end end
@ -1014,12 +974,6 @@ function StatusView:on_mouse_pressed(button, x, y, clicks)
end end
function StatusView:on_mouse_left()
StatusView.super.on_mouse_left(self)
self.hovered_item = {}
end
function StatusView:on_mouse_moved(x, y, dx, dy) function StatusView:on_mouse_moved(x, y, dx, dy)
if not self.visible then return end if not self.visible then return end
StatusView.super.on_mouse_moved(self, x, y, dx, dy) StatusView.super.on_mouse_moved(self, x, y, dx, dy)

View File

@ -1,23 +1,13 @@
local common = require "core.common" local common = require "core.common"
local style = {} local style = {}
style.padding = { x = common.round(14 * SCALE), y = common.round(7 * SCALE) }
style.divider_size = common.round(1 * SCALE) style.divider_size = common.round(1 * SCALE)
style.scrollbar_size = common.round(4 * SCALE) style.scrollbar_size = common.round(4 * SCALE)
style.expanded_scrollbar_size = common.round(12 * SCALE) style.expanded_scrollbar_size = common.round(12 * SCALE)
style.caret_width = common.round(2 * SCALE) style.caret_width = common.round(2 * SCALE)
style.tab_width = common.round(170 * SCALE) style.tab_width = common.round(170 * SCALE)
style.padding = {
x = common.round(14 * SCALE),
y = common.round(7 * SCALE),
}
style.margin = {
tab = {
top = common.round(-style.divider_size * SCALE)
}
}
-- The function renderer.font.load can accept an option table as a second optional argument. -- The function renderer.font.load can accept an option table as a second optional argument.
-- It shoud be like the following: -- It shoud be like the following:
-- --
@ -50,4 +40,3 @@ style.syntax_fonts = {}
style.log = {} style.log = {}
return style return style

View File

@ -3,7 +3,7 @@ local common = require "core.common"
local syntax = {} local syntax = {}
syntax.items = {} syntax.items = {}
syntax.plain_text_syntax = { name = "Plain Text", patterns = {}, symbols = {} } local plain_text_syntax = { name = "Plain Text", patterns = {}, symbols = {} }
function syntax.add(t) function syntax.add(t)
@ -46,7 +46,7 @@ end
function syntax.get(filename, header) function syntax.get(filename, header)
return (filename and find(filename, "files")) return (filename and find(filename, "files"))
or (header and find(header, "headers")) or (header and find(header, "headers"))
or syntax.plain_text_syntax or plain_text_syntax
end end

View File

@ -112,12 +112,6 @@ function TitleView:on_mouse_pressed(button, x, y, clicks)
end end
function TitleView:on_mouse_left()
TitleView.super.on_mouse_left(self)
self.hovered_item = nil
end
function TitleView:on_mouse_moved(px, py, ...) function TitleView:on_mouse_moved(px, py, ...)
if self.size.y == 0 then return end if self.size.y == 0 then return end
TitleView.super.on_mouse_moved(self, px, py, ...) TitleView.super.on_mouse_moved(self, px, py, ...)

View File

@ -28,8 +28,11 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
-- Each position spans characters from i_n to ((i_n+1) - 1), to form -- Each position spans characters from i_n to ((i_n+1) - 1), to form
-- consecutive spans of text. -- consecutive spans of text.
-- --
-- Insert the start index at i_1 to make iterating easier -- If i_1 is not equal to start, start is automatically inserted at
table.insert(find_results, 3, find_results[1]) -- that index.
if find_results[3] ~= find_results[1] then
table.insert(find_results, 3, find_results[1])
end
-- Copy the ending index to the end of the table, so that an ending index -- Copy the ending index to the end of the table, so that an ending index
-- always follows a starting index after position 3 in the table. -- always follows a starting index after position 3 in the table.
table.insert(find_results, find_results[2] + 1) table.insert(find_results, find_results[2] + 1)
@ -39,10 +42,8 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
local fin = find_results[i + 1] - 1 local fin = find_results[i + 1] - 1
local type = pattern.type[i - 2] local type = pattern.type[i - 2]
-- ↑ (i - 2) to convert from [3; n] to [1; n] -- ↑ (i - 2) to convert from [3; n] to [1; n]
if fin >= start then local text = full_text:usub(start, fin)
local text = full_text:usub(start, fin) push_token(t, syn.symbols[text] or type, text)
push_token(t, syn.symbols[text] or type, text)
end
end end
else else
local start, fin = find_results[1], find_results[2] local start, fin = find_results[1], find_results[2]
@ -133,10 +134,8 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
local res local res
local i = 1 local i = 1
state = state or string.char(0)
if #incoming_syntax.patterns == 0 then if #incoming_syntax.patterns == 0 then
return { "normal", text }, state return { "normal", text }
end end
state = state or string.char(0) state = state or string.char(0)
@ -243,7 +242,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
res[1] = char_pos_1 res[1] = char_pos_1
res[2] = char_pos_2 res[2] = char_pos_2
end end
if not res[1] then return end
if res[1] and target[3] then if res[1] and target[3] then
-- Check to see if the escaped character is there, -- Check to see if the escaped character is there,
-- and if it is not itself escaped. -- and if it is not itself escaped.
@ -255,12 +253,12 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
if count % 2 == 0 then if count % 2 == 0 then
-- The match is not escaped, so confirm it -- The match is not escaped, so confirm it
break break
else elseif not close then
-- The match is escaped, so avoid it -- The *open* match is escaped, so avoid it
res[1] = false return
end end
end end
until at_start or not close or not target[3] until not res[1] or not close or not target[3]
return table.unpack(res) return table.unpack(res)
end end
@ -286,9 +284,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
if current_pattern_idx > 0 then if current_pattern_idx > 0 then
local p = current_syntax.patterns[current_pattern_idx] local p = current_syntax.patterns[current_pattern_idx]
local s, e = find_text(text, p, i, false, true) local s, e = find_text(text, p, i, false, true)
-- Use the first token type specified in the type table for the "middle"
-- part of the subsyntax.
local token_type = type(p.type) == "table" and p.type[1] or p.type
local cont = true local cont = true
-- If we're in subsyntax mode, always check to see if we end our syntax -- If we're in subsyntax mode, always check to see if we end our syntax
@ -301,7 +296,7 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
-- treat the bit after as a token to be normally parsed -- treat the bit after as a token to be normally parsed
-- (as it's the syntax delimiter). -- (as it's the syntax delimiter).
if ss and (s == nil or ss < s) then if ss and (s == nil or ss < s) then
push_token(res, token_type, text:usub(i, ss - 1)) push_token(res, p.type, text:usub(i, ss - 1))
i = ss i = ss
cont = false cont = false
end end
@ -310,11 +305,11 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
-- continue on as normal. -- continue on as normal.
if cont then if cont then
if s then if s then
push_token(res, token_type, text:usub(i, e)) push_token(res, p.type, text:usub(i, e))
set_subsyntax_pattern_idx(0) set_subsyntax_pattern_idx(0)
i = e + 1 i = e + 1
else else
push_token(res, token_type, text:usub(i)) push_token(res, p.type, text:usub(i))
break break
end end
end end
@ -323,10 +318,9 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
-- we're ending early in the middle of a delimiter, or -- we're ending early in the middle of a delimiter, or
-- just normally, upon finding a token. -- just normally, upon finding a token.
while subsyntax_info do while subsyntax_info do
local find_results = { find_text(text, subsyntax_info, i, true, true) } local s, e = find_text(text, subsyntax_info, i, true, true)
local s, e = find_results[1], find_results[2]
if s then if s then
push_tokens(res, current_syntax, subsyntax_info, text, find_results) push_token(res, subsyntax_info.type, text:usub(i, e))
-- On finding unescaped delimiter, pop it. -- On finding unescaped delimiter, pop it.
pop_subsyntax() pop_subsyntax()
i = e + 1 i = e + 1

View File

@ -108,10 +108,6 @@ function View:get_h_scrollable_size()
end end
function View:supports_text_input()
return false
end
---@param x number ---@param x number
---@param y number ---@param y number
---@return boolean ---@return boolean
@ -142,14 +138,14 @@ function View:on_mouse_pressed(button, x, y, clicks)
local result = self.v_scrollbar:on_mouse_pressed(button, x, y, clicks) local result = self.v_scrollbar:on_mouse_pressed(button, x, y, clicks)
if result then if result then
if result ~= true then if result ~= true then
self.scroll.to.y = result * (self:get_scrollable_size() - self.size.y) self.scroll.to.y = result * self:get_scrollable_size()
end end
return true return true
end end
result = self.h_scrollbar:on_mouse_pressed(button, x, y, clicks) result = self.h_scrollbar:on_mouse_pressed(button, x, y, clicks)
if result then if result then
if result ~= true then if result ~= true then
self.scroll.to.x = result * (self:get_h_scrollable_size() - self.size.x) self.scroll.to.x = result * self:get_h_scrollable_size()
end end
return true return true
end end
@ -177,7 +173,7 @@ function View:on_mouse_moved(x, y, dx, dy)
result = self.v_scrollbar:on_mouse_moved(x, y, dx, dy) result = self.v_scrollbar:on_mouse_moved(x, y, dx, dy)
if result then if result then
if result ~= true then if result ~= true then
self.scroll.to.y = result * (self:get_scrollable_size() - self.size.y) self.scroll.to.y = result * self:get_scrollable_size()
if not config.animate_drag_scroll then if not config.animate_drag_scroll then
self:clamp_scroll_position() self:clamp_scroll_position()
self.scroll.y = self.scroll.to.y self.scroll.y = self.scroll.to.y
@ -191,7 +187,7 @@ function View:on_mouse_moved(x, y, dx, dy)
result = self.h_scrollbar:on_mouse_moved(x, y, dx, dy) result = self.h_scrollbar:on_mouse_moved(x, y, dx, dy)
if result then if result then
if result ~= true then if result ~= true then
self.scroll.to.x = result * (self:get_h_scrollable_size() - self.size.x) self.scroll.to.x = result * self:get_h_scrollable_size()
if not config.animate_drag_scroll then if not config.animate_drag_scroll then
self:clamp_scroll_position() self:clamp_scroll_position()
self.scroll.x = self.scroll.to.x self.scroll.x = self.scroll.to.x
@ -248,23 +244,6 @@ function View:get_content_bounds()
return x, y, x + self.size.x, y + self.size.y return x, y, x + self.size.x, y + self.size.y
end end
---@param x number
---@param y number
---@param dx number
---@param dy number
---@param i number
function View:on_touch_moved(x, y, dx, dy, i)
if not self.scrollable then return end
if self.dragging_scrollbar then
local delta = self:get_scrollable_size() / self.size.y * dy
self.scroll.to.y = self.scroll.to.y + delta
end
self.hovered_scrollbar = self:scrollbar_overlaps_point(x, y)
self.scroll.to.y = self.scroll.to.y + -dy
self.scroll.to.x = self.scroll.to.x + -dx
end
---@return number x ---@return number x
---@return number y ---@return number y
@ -287,16 +266,12 @@ end
function View:update_scrollbar() function View:update_scrollbar()
local v_scrollable = self:get_scrollable_size() local v_scrollable = self:get_scrollable_size()
self.v_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, v_scrollable) self.v_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, v_scrollable)
local v_percent = self.scroll.y/(v_scrollable - self.size.y) self.v_scrollbar:set_percent(self.scroll.y/v_scrollable)
-- Avoid setting nan percent
self.v_scrollbar:set_percent(v_percent == v_percent and v_percent or 0)
self.v_scrollbar:update() self.v_scrollbar:update()
local h_scrollable = self:get_h_scrollable_size() local h_scrollable = self:get_h_scrollable_size()
self.h_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, h_scrollable) self.h_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, h_scrollable)
local h_percent = self.scroll.x/(h_scrollable - self.size.x) self.h_scrollbar:set_percent(self.scroll.x/h_scrollable)
-- Avoid setting nan percent
self.h_scrollbar:set_percent(h_percent == h_percent and h_percent or 0)
self.h_scrollbar:update() self.h_scrollbar:update()
end end

View File

@ -10,10 +10,6 @@ local RootView = require "core.rootview"
local DocView = require "core.docview" local DocView = require "core.docview"
local Doc = require "core.doc" local Doc = require "core.doc"
---Symbols cache of all open documents
---@type table<core.doc, table>
local cache = setmetatable({}, { __mode = "k" })
config.plugins.autocomplete = common.merge({ config.plugins.autocomplete = common.merge({
-- Amount of characters that need to be written for autocomplete -- Amount of characters that need to be written for autocomplete
min_len = 3, min_len = 3,
@ -23,16 +19,8 @@ config.plugins.autocomplete = common.merge({
max_suggestions = 100, max_suggestions = 100,
-- Maximum amount of symbols to cache per document -- Maximum amount of symbols to cache per document
max_symbols = 4000, max_symbols = 4000,
-- Which symbols to show on the suggestions list: global, local, related, none
suggestions_scope = "global",
-- Font size of the description box -- Font size of the description box
desc_font_size = 12, desc_font_size = 12,
-- Do not show the icons associated to the suggestions
hide_icons = false,
-- Position where icons will be displayed on the suggestions list
icon_position = "left",
-- Do not show the additional information related to a suggestion
hide_info = false,
-- The config specification used by gui generators -- The config specification used by gui generators
config_spec = { config_spec = {
name = "Autocomplete", name = "Autocomplete",
@ -72,26 +60,6 @@ config.plugins.autocomplete = common.merge({
min = 1000, min = 1000,
max = 10000 max = 10000
}, },
{
label = "Suggestions Scope",
description = "Which symbols to show on the suggestions list.",
path = "suggestions_scope",
type = "selection",
default = "global",
values = {
{"All Documents", "global"},
{"Current Document", "local"},
{"Related Documents", "related"},
{"Known Symbols", "none"}
},
on_apply = function(value)
if value == "global" then
for _, doc in ipairs(core.docs) do
if cache[doc] then cache[doc] = nil end
end
end
end
},
{ {
label = "Description Font Size", label = "Description Font Size",
description = "Font size of the description box.", description = "Font size of the description box.",
@ -99,31 +67,6 @@ config.plugins.autocomplete = common.merge({
type = "number", type = "number",
default = 12, default = 12,
min = 8 min = 8
},
{
label = "Hide Icons",
description = "Do not show icons on the suggestions list.",
path = "hide_icons",
type = "toggle",
default = false
},
{
label = "Icons Position",
description = "Position to display icons on the suggestions list.",
path = "icon_position",
type = "selection",
default = "left",
values = {
{"Left", "left"},
{"Right", "Right"}
}
},
{
label = "Hide Items Info",
description = "Do not show the additional info related to each suggestion.",
path = "hide_info",
type = "toggle",
default = false
} }
} }
}, config.plugins.autocomplete) }, config.plugins.autocomplete)
@ -133,7 +76,6 @@ local autocomplete = {}
autocomplete.map = {} autocomplete.map = {}
autocomplete.map_manually = {} autocomplete.map_manually = {}
autocomplete.on_close = nil autocomplete.on_close = nil
autocomplete.icons = {}
-- Flag that indicates if the autocomplete box was manually triggered -- Flag that indicates if the autocomplete box was manually triggered
-- with the autocomplete.complete() function to prevent the suggestions -- with the autocomplete.complete() function to prevent the suggestions
@ -153,7 +95,6 @@ function autocomplete.add(t, manually_triggered)
{ {
text = text, text = text,
info = info.info, info = info.info,
icon = info.icon, -- Name of icon to show
desc = info.desc, -- Description shown on item selected desc = info.desc, -- Description shown on item selected
onhover = info.onhover, -- A callback called once when item is hovered onhover = info.onhover, -- A callback called once when item is hovered
onselect = info.onselect, -- A callback called when item is selected onselect = info.onselect, -- A callback called when item is selected
@ -178,35 +119,28 @@ end
-- --
-- Thread that scans open document symbols and cache them -- Thread that scans open document symbols and cache them
-- --
local global_symbols = {} local max_symbols = config.plugins.autocomplete.max_symbols
core.add_thread(function() core.add_thread(function()
local function load_syntax_symbols(doc) local cache = setmetatable({}, { __mode = "k" })
if doc.syntax and not autocomplete.map["language_"..doc.syntax.name] then
local symbols = { local function get_syntax_symbols(symbols, doc)
name = "language_"..doc.syntax.name, if doc.syntax then
files = doc.syntax.files, for sym in pairs(doc.syntax.symbols) do
items = {} symbols[sym] = true
}
for name, type in pairs(doc.syntax.symbols) do
symbols.items[name] = type
end end
autocomplete.add(symbols)
return symbols.items
end end
return {}
end end
local function get_symbols(doc) local function get_symbols(doc)
local s = {} local s = {}
local syntax_symbols = load_syntax_symbols(doc) get_syntax_symbols(s, doc)
local max_symbols = config.plugins.autocomplete.max_symbols
if doc.disable_symbols then return s end if doc.disable_symbols then return s end
local i = 1 local i = 1
local symbols_count = 0 local symbols_count = 0
while i <= #doc.lines do while i <= #doc.lines do
for sym in doc.lines[i]:gmatch(config.symbol_pattern) do for sym in doc.lines[i]:gmatch(config.symbol_pattern) do
if not s[sym] and not syntax_symbols[sym] then if not s[sym] then
symbols_count = symbols_count + 1 symbols_count = symbols_count + 1
if symbols_count > max_symbols then if symbols_count > max_symbols then
s = nil s = nil
@ -252,18 +186,14 @@ core.add_thread(function()
} }
end end
-- update symbol set with doc's symbol set -- update symbol set with doc's symbol set
if config.plugins.autocomplete.suggestions_scope == "global" then for sym in pairs(cache[doc].symbols) do
for sym in pairs(cache[doc].symbols) do symbols[sym] = true
symbols[sym] = true
end
end end
coroutine.yield() coroutine.yield()
end end
-- update global symbols list -- update symbols list
if config.plugins.autocomplete.suggestions_scope == "global" then autocomplete.add { name = "open-docs", items = symbols }
global_symbols = symbols
end
-- wait for next scan -- wait for next scan
local valid = true local valid = true
@ -310,50 +240,12 @@ local function update_suggestions()
map = autocomplete.map_manually map = autocomplete.map_manually
end end
local assigned_sym = {}
-- get all relevant suggestions for given filename -- get all relevant suggestions for given filename
local items = {} local items = {}
for _, v in pairs(map) do for _, v in pairs(map) do
if common.match_pattern(filename, v.files) then if common.match_pattern(filename, v.files) then
for _, item in pairs(v.items) do for _, item in pairs(v.items) do
table.insert(items, item) table.insert(items, item)
assigned_sym[item.text] = true
end
end
end
-- Append the global, local or related text symbols if applicable
local scope = config.plugins.autocomplete.suggestions_scope
if not triggered_manually then
local text_symbols = nil
if scope == "global" then
text_symbols = global_symbols
elseif scope == "local" and cache[doc] and cache[doc].symbols then
text_symbols = cache[doc].symbols
elseif scope == "related" then
for _, d in ipairs(core.docs) do
if doc.syntax == d.syntax then
if cache[d].symbols then
for name in pairs(cache[d].symbols) do
if not assigned_sym[name] then
table.insert(items, setmetatable(
{text = name, info = "normal"}, mt
))
end
end
end
end
end
end
if text_symbols then
for name in pairs(text_symbols) do
if not assigned_sym[name] then
table.insert(items, setmetatable({text = name, info = "normal"}, mt))
end
end end
end end
end end
@ -394,23 +286,13 @@ local function get_suggestions_rect(av)
y = y + av:get_line_height() + style.padding.y y = y + av:get_line_height() + style.padding.y
local font = av:get_font() local font = av:get_font()
local th = font:get_height() local th = font:get_height()
local has_icons = false
local hide_info = config.plugins.autocomplete.hide_info
local hide_icons = config.plugins.autocomplete.hide_icons
local max_width = 0 local max_width = 0
for _, s in ipairs(suggestions) do for _, s in ipairs(suggestions) do
local w = font:get_width(s.text) local w = font:get_width(s.text)
if s.info and not hide_info then if s.info then
w = w + style.font:get_width(s.info) + style.padding.x w = w + style.font:get_width(s.info) + style.padding.x
end end
local icon = s.icon or s.info
if not hide_icons and icon and autocomplete.icons[icon] then
w = w + autocomplete.icons[icon].font:get_width(
autocomplete.icons[icon].char
) + (style.padding.x / 2)
has_icons = true
end
max_width = math.max(max_width, w) max_width = math.max(max_width, w)
end end
@ -437,8 +319,7 @@ local function get_suggestions_rect(av)
x - style.padding.x, x - style.padding.x,
y - style.padding.y, y - style.padding.y,
max_width + style.padding.x * 2, max_width + style.padding.x * 2,
max_items * (th + style.padding.y) + style.padding.y, max_items * (th + style.padding.y) + style.padding.y
has_icons
end end
local function wrap_line(line, max_chars) local function wrap_line(line, max_chars)
@ -558,7 +439,7 @@ local function draw_suggestions_box(av)
local ah = config.plugins.autocomplete.max_height local ah = config.plugins.autocomplete.max_height
-- draw background rect -- draw background rect
local rx, ry, rw, rh, has_icons = get_suggestions_rect(av) local rx, ry, rw, rh = get_suggestions_rect(av)
renderer.draw_rect(rx, ry, rw, rh, style.background3) renderer.draw_rect(rx, ry, rw, rh, style.background3)
-- draw text -- draw text
@ -567,52 +448,17 @@ local function draw_suggestions_box(av)
local y = ry + style.padding.y / 2 local y = ry + style.padding.y / 2
local show_count = #suggestions <= ah and #suggestions or ah local show_count = #suggestions <= ah and #suggestions or ah
local start_index = suggestions_idx > ah and (suggestions_idx-(ah-1)) or 1 local start_index = suggestions_idx > ah and (suggestions_idx-(ah-1)) or 1
local hide_info = config.plugins.autocomplete.hide_info
for i=start_index, start_index+show_count-1, 1 do for i=start_index, start_index+show_count-1, 1 do
if not suggestions[i] then if not suggestions[i] then
break break
end end
local s = suggestions[i] local s = suggestions[i]
local icon_l_padding, icon_r_padding = 0, 0
if has_icons then
local icon = s.icon or s.info
if icon and autocomplete.icons[icon] then
local ifont = autocomplete.icons[icon].font
local itext = autocomplete.icons[icon].char
local icolor = autocomplete.icons[icon].color
if i == suggestions_idx then
icolor = style.accent
elseif type(icolor) == "string" then
icolor = style.syntax[icolor]
end
if config.plugins.autocomplete.icon_position == "left" then
common.draw_text(
ifont, icolor, itext, "left", rx + style.padding.x, y, rw, lh
)
icon_l_padding = ifont:get_width(itext) + (style.padding.x / 2)
else
common.draw_text(
ifont, icolor, itext, "right", rx, y, rw - style.padding.x, lh
)
icon_r_padding = ifont:get_width(itext) + (style.padding.x / 2)
end
end
end
local color = (i == suggestions_idx) and style.accent or style.text local color = (i == suggestions_idx) and style.accent or style.text
common.draw_text( common.draw_text(font, color, s.text, "left", rx + style.padding.x, y, rw, lh)
font, color, s.text, "left", if s.info then
rx + icon_l_padding + style.padding.x, y, rw, lh
)
if s.info and not hide_info then
color = (i == suggestions_idx) and style.text or style.dim color = (i == suggestions_idx) and style.text or style.dim
common.draw_text( common.draw_text(style.font, color, s.info, "right", rx, y, rw - style.padding.x, lh)
style.font, color, s.info, "right",
rx, y, rw - icon_r_padding - style.padding.x, lh
)
end end
y = y + lh y = y + lh
if suggestions_idx == i then if suggestions_idx == i then
@ -773,31 +619,6 @@ function autocomplete.can_complete()
return false return false
end end
---Register a font icon that can be assigned to completion items.
---@param name string
---@param character string
---@param font? renderer.font
---@param color? string | renderer.color A style.syntax[] name or specific color
function autocomplete.add_icon(name, character, font, color)
local color_type = type(color)
assert(
not color or color_type == "table"
or (color_type == "string" and style.syntax[color]),
"invalid icon color given"
)
autocomplete.icons[name] = {
char = character,
font = font or style.code_font,
color = color or "keyword"
}
end
--
-- Register built-in syntax symbol types icon
--
for name, _ in pairs(style.syntax) do
autocomplete.add_icon(name, "M", style.icon_font, name)
end
-- --
-- Commands -- Commands
@ -811,6 +632,7 @@ command.add(predicate, {
["autocomplete:complete"] = function(dv) ["autocomplete:complete"] = function(dv)
local doc = dv.doc local doc = dv.doc
local item = suggestions[suggestions_idx] local item = suggestions[suggestions_idx]
local text = item.text
local inserted = false local inserted = false
if item.onselect then if item.onselect then
inserted = item.onselect(suggestions_idx, item) inserted = item.onselect(suggestions_idx, item)

View File

@ -37,11 +37,7 @@ local function optimal_indent_from_stat(stat)
elseif elseif
indent > stat[y] indent > stat[y]
and and
( indent_occurrences_more_than_once(stat, y)
indent_occurrences_more_than_once(stat, y)
or
(y == count and stat[y] > 1)
)
then then
score = 0 score = 0
break break
@ -122,10 +118,10 @@ local function get_comment_patterns(syntax, _loop)
end end
if type(pattern.regex) == "table" then if type(pattern.regex) == "table" then
table.insert(comments, { table.insert(comments, {
"r", regex.compile(startp), regex.compile(pattern.regex[2]), r=startp "r", regex.compile(startp), regex.compile(pattern.regex[2])
}) })
elseif not_is_string then elseif not_is_string then
table.insert(comments, {"r", regex.compile(startp), r=startp}) table.insert(comments, {"r", regex.compile(startp)})
end end
end end
elseif pattern.syntax then elseif pattern.syntax then
@ -156,25 +152,6 @@ local function get_comment_patterns(syntax, _loop)
table.insert(comments, {"p", "^%s*" .. block_comment[1], block_comment[2]}) table.insert(comments, {"p", "^%s*" .. block_comment[1], block_comment[2]})
end end
end end
-- Put comments first and strings last
table.sort(comments, function(c1, c2)
local comment1, comment2 = false, false
if
(c1[1] == "p" and string.find(c1[2], "^%s*", 1, true))
or
(c1[1] == "r" and string.find(c1["r"], "^\\s*", 1, true))
then
comment1 = true
end
if
(c2[1] == "p" and string.find(c2[2], "^%s*", 1, true))
or
(c2[1] == "r" and string.find(c2["r"], "^\\s*", 1, true))
then
comment2 = true
end
return comment1 and not comment2
end)
comments_cache[syntax] = comments comments_cache[syntax] = comments
if #comments > 0 then if #comments > 0 then
return comments return comments

View File

@ -1,6 +1,5 @@
-- mod-version:3 -- mod-version:3
local core = require "core"
local style = require "core.style" local style = require "core.style"
local DocView = require "core.docview" local DocView = require "core.docview"
local common = require "core.common" local common = require "core.common"
@ -13,7 +12,6 @@ config.plugins.drawwhitespace = common.merge({
show_leading = true, show_leading = true,
show_trailing = true, show_trailing = true,
show_middle = true, show_middle = true,
show_selected_only = false,
show_middle_min = 1, show_middle_min = 1,
@ -67,13 +65,6 @@ config.plugins.drawwhitespace = common.merge({
type = "toggle", type = "toggle",
default = true, default = true,
}, },
{
label = "Show Selected Only",
description = "Only draw whitespaces if it is within a selection.",
path = "show_selected_only",
type = "toggle",
default = false,
},
{ {
label = "Show Trailing as Error", label = "Show Trailing as Error",
description = "Uses an error square to spot them easily, requires 'Show Trailing' enabled.", description = "Uses an error square to spot them easily, requires 'Show Trailing' enabled.",
@ -302,41 +293,11 @@ function DocView:draw_line_text(idx, x, y)
for i=1,#cache,4 do for i=1,#cache,4 do
local tx = cache[i + 1] + x local tx = cache[i + 1] + x
local tw = cache[i + 2] local tw = cache[i + 2]
local sub = cache[i] if tx <= x2 then
local color = cache[i + 3] local sub = cache[i]
local partials = {} local color = cache[i + 3]
if config.plugins.drawwhitespace.show_selected_only and self.doc:has_any_selection() then if tx + tw >= x1 then
for _, l1, c1, l2, c2 in self.doc:get_selections(true) do tx = renderer.draw_text(font, sub, tx, ty, color)
if idx > l1 and idx < l2 then
-- Between selection lines, so everything is selected
table.insert(partials, false)
elseif idx == l1 and idx == l2 then
-- Both ends of the selection are on the same line
local _x1 = math.max(cache[i + 1], self:get_col_x_offset(idx, c1))
local _x2 = math.min((cache[i + 1] + tw), self:get_col_x_offset(idx, c2))
if _x1 < _x2 then
table.insert(partials, {_x1 + x, 0, _x2 - _x1, math.huge})
end
elseif idx >= l1 and idx <= l2 then
-- On one of the selection ends
if idx == l1 then -- Start of the selection
local _x = math.max(cache[i + 1], self:get_col_x_offset(idx, c1))
table.insert(partials, {_x + x, 0, math.huge, math.huge})
else -- End of the selection
local _x = math.min((cache[i + 1] + tw), self:get_col_x_offset(idx, c2))
table.insert(partials, {0, 0, _x + x, math.huge})
end
end
end
end
if #partials == 0 and not config.plugins.drawwhitespace.show_selected_only then
renderer.draw_text(font, sub, tx, ty, color)
else
for _, p in pairs(partials) do
if p then core.push_clip_rect(table.unpack(p)) end
renderer.draw_text(font, sub, tx, ty, color)
if p then core.pop_clip_rect() end
end end
end end
end end

View File

@ -128,7 +128,6 @@ syntax.add {
{ pattern = { "```go", "```" }, type = "string", syntax = ".go" }, { pattern = { "```go", "```" }, type = "string", syntax = ".go" },
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" }, { pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" }, { pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
{ pattern = { "```nix", "```" }, type = "string", syntax = ".nix" },
{ pattern = { "```", "```" }, type = "string" }, { pattern = { "```", "```" }, type = "string" },
{ pattern = { "``", "``" }, type = "string" }, { pattern = { "``", "``" }, type = "string" },
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" }, { pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" },
@ -267,4 +266,3 @@ core.add_thread(function()
coroutine.yield(1) coroutine.yield(1)
end end
end) end)

View File

@ -15,8 +15,6 @@ config.plugins.lineguide = common.merge({
-- 120, -- 120,
config.line_limit config.line_limit
}, },
use_custom_color = false,
custom_color = style.selection,
-- The config specification used by gui generators -- The config specification used by gui generators
config_spec = { config_spec = {
name = "Line Guide", name = "Line Guide",
@ -65,21 +63,7 @@ config.plugins.lineguide = common.merge({
end end
return new_rulers return new_rulers
end end
}, }
{
label = "Use Custom Color",
description = "Enable the utilization of a custom line color.",
path = "use_custom_color",
type = "toggle",
default = false
},
{
label = "Custom Color",
description = "Applied when the above toggle is enabled.",
path = "custom_color",
type = "color",
default = style.selection
},
} }
}, config.plugins.lineguide) }, config.plugins.lineguide)
@ -102,12 +86,10 @@ function DocView:draw_overlay(...)
and and
self:is(DocView) self:is(DocView)
then then
local conf = config.plugins.lineguide
local line_x = self:get_line_screen_position(1) local line_x = self:get_line_screen_position(1)
local character_width = self:get_font():get_width("n") local character_width = self:get_font():get_width("n")
local ruler_width = config.plugins.lineguide.width local ruler_width = config.plugins.lineguide.width
local ruler_color = conf.use_custom_color and conf.custom_color local ruler_color = style.guide or style.selection
or (style.guide or style.selection)
for k,v in ipairs(config.plugins.lineguide.rulers) do for k,v in ipairs(config.plugins.lineguide.rulers) do
local ruler = get_ruler(v) local ruler = get_ruler(v)

View File

@ -37,7 +37,7 @@ local function find_all_matches_in_file(t, filename, fn)
table.insert(t, { file = filename, text = (start_index > 1 and "..." or "") .. line:sub(start_index, 256 + start_index), line = n, col = s }) table.insert(t, { file = filename, text = (start_index > 1 and "..." or "") .. line:sub(start_index, 256 + start_index), line = n, col = s })
core.redraw = true core.redraw = true
end end
if n % 100 == 0 then coroutine.yield(0) end if n % 100 == 0 then coroutine.yield() end
n = n + 1 n = n + 1
core.redraw = true core.redraw = true
end end

View File

@ -48,7 +48,7 @@ end
function ToolbarView:get_icon_width() function ToolbarView:get_icon_width()
local max_width = 0 local max_width = 0
for i,v in ipairs(self.toolbar_commands) do max_width = math.max(max_width, (v.font or self.toolbar_font):get_width(v.symbol)) end for i,v in ipairs(self.toolbar_commands) do max_width = math.max(max_width, self.toolbar_font:get_width(v.symbol)) end
return max_width return max_width
end end
@ -83,7 +83,7 @@ function ToolbarView:draw()
for item, x, y, w, h in self:each_item() do for item, x, y, w, h in self:each_item() do
local color = item == self.hovered_item and command.is_valid(item.command) and style.text or style.dim local color = item == self.hovered_item and command.is_valid(item.command) and style.text or style.dim
common.draw_text(item.font or self.toolbar_font, color, item.symbol, nil, x, y, 0, h) common.draw_text(self.toolbar_font, color, item.symbol, nil, x, y, 0, h)
end end
end end
@ -100,16 +100,6 @@ function ToolbarView:on_mouse_pressed(button, x, y, clicks)
end end
function ToolbarView:on_mouse_left()
ToolbarView.super.on_mouse_left(self)
if self.tooltip then
core.status_view:remove_tooltip()
self.tooltip = false
end
self.hovered_item = nil
end
function ToolbarView:on_mouse_moved(px, py, ...) function ToolbarView:on_mouse_moved(px, py, ...)
if not self.visible then return end if not self.visible then return end
ToolbarView.super.on_mouse_moved(self, px, py, ...) ToolbarView.super.on_mouse_moved(self, px, py, ...)

View File

@ -9,15 +9,10 @@ local View = require "core.view"
local ContextMenu = require "core.contextmenu" local ContextMenu = require "core.contextmenu"
local RootView = require "core.rootview" local RootView = require "core.rootview"
local CommandView = require "core.commandview" local CommandView = require "core.commandview"
local DocView = require "core.docview"
config.plugins.treeview = common.merge({ config.plugins.treeview = common.merge({
-- Default treeview width -- Default treeview width
size = 200 * SCALE, size = 200 * SCALE
highlight_focused_file = true,
expand_dirs_to_focused_file = false,
scroll_to_focused_file = false,
animate_scroll_to_focused_file = true
}, config.plugins.treeview) }, config.plugins.treeview)
local tooltip_offset = style.font:get_height() local tooltip_offset = style.font:get_height()
@ -51,7 +46,7 @@ function TreeView:new()
self.target_size = config.plugins.treeview.size self.target_size = config.plugins.treeview.size
self.cache = {} self.cache = {}
self.tooltip = { x = 0, y = 0, begin = 0, alpha = 0 } self.tooltip = { x = 0, y = 0, begin = 0, alpha = 0 }
self.last_scroll_y = 0 self.cursor_pos = { x = 0, y = 0 }
self.item_icon_width = 0 self.item_icon_width = 0
self.item_text_spacing = 0 self.item_text_spacing = 0
@ -174,73 +169,20 @@ function TreeView:each_item()
end end
function TreeView:set_selection(selection, selection_y, center, instant) function TreeView:set_selection(selection, selection_y)
self.selected_item = selection self.selected_item = selection
if selection and selection_y if selection and selection_y
and (selection_y <= 0 or selection_y >= self.size.y) then and (selection_y <= 0 or selection_y >= self.size.y) then
local lh = self:get_item_height() local lh = self:get_item_height()
if not center and selection_y >= self.size.y - lh then if selection_y >= self.size.y - lh then
selection_y = selection_y - self.size.y + lh selection_y = selection_y - self.size.y + lh
end end
if center then
selection_y = selection_y - (self.size.y - lh) / 2
end
local _, y = self:get_content_offset() local _, y = self:get_content_offset()
self.scroll.to.y = selection_y - y self.scroll.to.y = selection and (selection_y - y)
self.scroll.to.y = common.clamp(self.scroll.to.y, 0, self:get_scrollable_size() - self.size.y)
if instant then
self.scroll.y = self.scroll.to.y
end
end end
end end
---Sets the selection to the file with the specified path.
---
---@param path string #Absolute path of item to select
---@param expand boolean #Expand dirs leading to the item
---@param scroll_to boolean #Scroll to make the item visible
---@param instant boolean #Don't animate the scroll
---@return table? #The selected item
function TreeView:set_selection_to_path(path, expand, scroll_to, instant)
local to_select, to_select_y
local let_it_finish, done
::restart::
for item, x,y,w,h in self:each_item() do
if not done then
if item.type == "dir" then
local _, to = string.find(path, item.abs_filename..PATHSEP, 1, true)
if to and to == #item.abs_filename + #PATHSEP then
to_select, to_select_y = item, y
if expand and not item.expanded then
-- Use TreeView:toggle_expand to update the directory structure.
-- Directly using item.expanded doesn't update the cached tree.
self:toggle_expand(true, item)
-- Because we altered the size of the TreeView
-- and because TreeView:get_scrollable_size uses self.count_lines
-- which gets updated only when TreeView:each_item finishes,
-- we can't stop here or we risk that the scroll
-- gets clamped by View:clamp_scroll_position.
let_it_finish = true
-- We need to restart the process because if TreeView:toggle_expand
-- altered the cache, TreeView:each_item risks looping indefinitely.
goto restart
end
end
else
if item.abs_filename == path then
to_select, to_select_y = item, y
done = true
if not let_it_finish then break end
end
end
end
end
if to_select then
self:set_selection(to_select, scroll_to and to_select_y, true, instant)
end
return to_select
end
function TreeView:get_text_bounding_box(item, x, y, w, h) function TreeView:get_text_bounding_box(item, x, y, w, h)
local icon_width = style.icon_font:get_width("D") local icon_width = style.icon_font:get_width("D")
@ -251,9 +193,10 @@ function TreeView:get_text_bounding_box(item, x, y, w, h)
end end
function TreeView:on_mouse_moved(px, py, ...) function TreeView:on_mouse_moved(px, py, ...)
if not self.visible then return end if not self.visible then return end
self.cursor_pos.x = px
self.cursor_pos.y = py
if TreeView.super.on_mouse_moved(self, px, py, ...) then if TreeView.super.on_mouse_moved(self, px, py, ...) then
-- mouse movement handled by the View (scrollbar) -- mouse movement handled by the View (scrollbar)
self.hovered_item = nil self.hovered_item = nil
@ -280,12 +223,6 @@ function TreeView:on_mouse_moved(px, py, ...)
end end
function TreeView:on_mouse_left()
TreeView.super.on_mouse_left(self)
self.hovered_item = nil
end
function TreeView:update() function TreeView:update()
-- update width -- update width
local dest = self.visible and self.target_size or 0 local dest = self.visible and self.target_size or 0
@ -296,7 +233,7 @@ function TreeView:update()
self:move_towards(self.size, "x", dest, nil, "treeview") self:move_towards(self.size, "x", dest, nil, "treeview")
end end
if self.size.x == 0 or self.size.y == 0 or not self.visible then return end if not self.visible then return end
local duration = system.get_time() - self.tooltip.begin local duration = system.get_time() - self.tooltip.begin
if self.hovered_item and self.tooltip.x and duration > tooltip_delay then if self.hovered_item and self.tooltip.x and duration > tooltip_delay then
@ -309,30 +246,10 @@ function TreeView:update()
self.item_text_spacing = style.icon_font:get_width("f") / 2 self.item_text_spacing = style.icon_font:get_width("f") / 2
-- this will make sure hovered_item is updated -- this will make sure hovered_item is updated
local dy = math.abs(self.last_scroll_y - self.scroll.y) -- we don't want events when the thing is scrolling fast
if dy > 0 then local dy = math.abs(self.scroll.to.y - self.scroll.y)
self:on_mouse_moved(core.root_view.mouse.x, core.root_view.mouse.y, 0, 0) if self.scroll.to.y ~= 0 and dy < self:get_item_height() then
self.last_scroll_y = self.scroll.y self:on_mouse_moved(self.cursor_pos.x, self.cursor_pos.y, 0, 0)
end
local config = config.plugins.treeview
if config.highlight_focused_file then
-- Try to only highlight when we actually change tabs
local current_node = core.root_view:get_active_node()
local current_active_view = core.active_view
if current_node and not current_node.locked
and current_active_view ~= self and current_active_view ~= self.last_active_view then
self.selected_item = nil
self.last_active_view = current_active_view
if DocView:is_extended_by(current_active_view) then
local abs_filename = current_active_view.doc
and current_active_view.doc.abs_filename or ""
self:set_selection_to_path(abs_filename,
config.expand_dirs_to_focused_file,
config.scroll_to_focused_file,
not config.animate_scroll_to_focused_file)
end
end
end end
TreeView.super.update(self) TreeView.super.update(self)
@ -505,8 +422,8 @@ function TreeView:get_previous(item)
end end
function TreeView:toggle_expand(toggle, item) function TreeView:toggle_expand(toggle)
item = item or self.selected_item local item = self.selected_item
if not item then return end if not item then return end
@ -524,11 +441,6 @@ function TreeView:toggle_expand(toggle, item)
end end
function TreeView:open_doc(filename)
core.root_view:open_doc(core.open_doc(filename))
end
-- init -- init
local view = TreeView() local view = TreeView()
local node = core.root_view:get_active_node() local node = core.root_view:get_active_node()
@ -707,7 +619,8 @@ command.add(
if core.last_active_view and core.active_view == view then if core.last_active_view and core.active_view == view then
core.set_active_view(core.last_active_view) core.set_active_view(core.last_active_view)
end end
view:open_doc(core.normalize_to_project_dir(item.abs_filename)) local doc_filename = core.normalize_to_project_dir(item.abs_filename)
core.root_view:open_doc(core.open_doc(doc_filename))
end) end)
end end
end, end,
@ -753,26 +666,6 @@ command.add(
view:toggle_expand(true) view:toggle_expand(true)
end end
end, end,
["treeview-context:show"] = function()
if view.hovered_item then
menu:show(core.root_view.mouse.x, core.root_view.mouse.y)
return
end
local item = view.selected_item
if not item then return end
local x, y
for _i, _x, _y, _w, _h in view:each_item() do
if _i == item then
x = _x + _w / 2
y = _y + _h / 2
break
end
end
menu:show(x, y)
end
}) })
@ -866,7 +759,7 @@ command.add(
local file = io.open(doc_filename, "a+") local file = io.open(doc_filename, "a+")
file:write("") file:write("")
file:close() file:close()
view:open_doc(doc_filename) core.root_view:open_doc(core.open_doc(doc_filename))
core.log("Created %s", doc_filename) core.log("Created %s", doc_filename)
end, end,
suggest = function(text) suggest = function(text)
@ -896,12 +789,10 @@ command.add(
["treeview:open-in-system"] = function(item) ["treeview:open-in-system"] = function(item)
if PLATFORM == "Windows" then if PLATFORM == "Windows" then
system.exec(string.format("start \"\" %q", item.abs_filename)) system.exec(string.format("start \"\" %q", item.abs_filename))
elseif string.find(PLATFORM, "Mac") or PLATFORM == "MorphOS" then elseif string.find(PLATFORM, "Mac") then
system.exec(string.format("open %q", item.abs_filename)) system.exec(string.format("open %q", item.abs_filename))
elseif PLATFORM == "Linux" or string.find(PLATFORM, "BSD") then elseif PLATFORM == "Linux" or string.find(PLATFORM, "BSD") then
system.exec(string.format("xdg-open %q", item.abs_filename)) system.exec(string.format("xdg-open %q", item.abs_filename))
elseif PLATFORM == "AmigaOS 4" then
system.exec(string.format("WBRUN %q SHOW=all VIEWBY=name", item.abs_filename))
end end
end end
}) })
@ -911,8 +802,8 @@ if projectsearch then
menu:register(function() menu:register(function()
local item = treeitem() local item = treeitem()
return item and item.type == "dir" return item and item.type == "dir"
end, { end, {
{ text = "Find in directory", command = "treeview:search-in-directory" } { text = "Find in directory", command = "treeview:search-in-directory" }
}) })
command.add(function() command.add(function()
return view.hovered_item and view.hovered_item.type == "dir" return view.hovered_item and view.hovered_item.type == "dir"
@ -935,25 +826,6 @@ command.add(function()
}) })
command.add(
function()
return menu.show_context_menu == true and core.active_view:is(TreeView)
end, {
["treeview-context:focus-previous"] = function()
menu:focus_previous()
end,
["treeview-context:focus-next"] = function()
menu:focus_next()
end,
["treeview-context:hide"] = function()
menu:hide()
end,
["treeview-context:on-selected"] = function()
menu:call_selected_item()
end,
})
keymap.add { keymap.add {
["ctrl+\\"] = "treeview:toggle", ["ctrl+\\"] = "treeview:toggle",
["up"] = "treeview:previous", ["up"] = "treeview:previous",
@ -969,15 +841,6 @@ keymap.add {
["ctrl+lclick"] = "treeview:new-folder" ["ctrl+lclick"] = "treeview:new-folder"
} }
keymap.add {
["menu"] = "treeview-context:show",
["return"] = "treeview-context:on-selected",
["up"] = "treeview-context:focus-previous",
["down"] = "treeview-context:focus-next",
["escape"] = "treeview-context:hide"
}
-- The config specification used by gui generators -- The config specification used by gui generators
config.plugins.treeview.config_spec = { config.plugins.treeview.config_spec = {
name = "Treeview", name = "Treeview",

View File

@ -208,10 +208,10 @@ else
install_data('resources/icons/lite-xl.svg', install_data('resources/icons/lite-xl.svg',
install_dir : 'share/icons/hicolor/scalable/apps' install_dir : 'share/icons/hicolor/scalable/apps'
) )
install_data('resources/linux/com.lite_xl.LiteXL.desktop', install_data('resources/linux/org.lite_xl.lite_xl.desktop',
install_dir : 'share/applications' install_dir : 'share/applications'
) )
install_data('resources/linux/com.lite_xl.LiteXL.appdata.xml', install_data('resources/linux/org.lite_xl.lite_xl.appdata.xml',
install_dir : 'share/metainfo' install_dir : 'share/metainfo'
) )
endif endif

View File

@ -138,7 +138,7 @@ generate_appimage() {
mv AppRun LiteXL.AppDir/ mv AppRun LiteXL.AppDir/
# These could be symlinks but it seems they doesn't work with AppimageLauncher # These could be symlinks but it seems they doesn't work with AppimageLauncher
cp resources/icons/lite-xl.svg LiteXL.AppDir/ cp resources/icons/lite-xl.svg LiteXL.AppDir/
cp resources/linux/com.lite_xl.LiteXL.desktop LiteXL.AppDir/ cp resources/linux/org.lite_xl.lite_xl.desktop LiteXL.AppDir/
if [[ $ADDONS == true ]]; then if [[ $ADDONS == true ]]; then
addons_download "${BUILD_DIR}" addons_download "${BUILD_DIR}"

View File

@ -1,7 +1,7 @@
#define MyAppName "Lite XL" #define MyAppName "Lite XL"
#define MyAppVersion "@PROJECT_VERSION@" #define MyAppVersion "@PROJECT_VERSION@"
#define MyAppPublisher "Lite XL Team" #define MyAppPublisher "Lite XL Team"
#define MyAppURL "https://lite-xl.github.io" #define MyAppURL "https://lite-xl.com"
#define MyAppExeName "lite-xl.exe" #define MyAppExeName "lite-xl.exe"
#define BuildDir "@PROJECT_BUILD_DIR@" #define BuildDir "@PROJECT_BUILD_DIR@"
#define SourceDir "@PROJECT_SOURCE_DIR@" #define SourceDir "@PROJECT_SOURCE_DIR@"
@ -69,9 +69,7 @@ Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescrip
Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked
[Files] [Files]
Source: "{#BuildDir}/src/lite-xl.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "{#SourceDir}/lite-xl/*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
Source: "{#BuildDir}/mingwLibs{#Arch}/*"; DestDir: "{app}"; Flags: ignoreversion ; Check: DirExists(ExpandConstant('{#BuildDir}/mingwLibs{#Arch}'))
Source: "{#SourceDir}/data/*"; DestDir: "{app}/data"; Flags: ignoreversion recursesubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons] [Icons]

View File

@ -29,3 +29,4 @@ void api_load_libs(lua_State *L) {
for (int i = 0; libs[i].name; i++) for (int i = 0; libs[i].name; i++)
luaL_requiref(L, libs[i].name, libs[i].func, 1); luaL_requiref(L, libs[i].name, libs[i].func, 1);
} }

View File

@ -391,4 +391,3 @@ int luaopen_renderer(lua_State *L) {
lua_setfield(L, -2, "font"); lua_setfield(L, -2, "font");
return 1; return 1;
} }

View File

@ -369,21 +369,6 @@ top:
lua_pushinteger(L, (lua_Integer)(e.tfinger.dy * h)); lua_pushinteger(L, (lua_Integer)(e.tfinger.dy * h));
lua_pushinteger(L, e.tfinger.fingerId); lua_pushinteger(L, e.tfinger.fingerId);
return 6; return 6;
case SDL_APP_WILLENTERFOREGROUND:
case SDL_APP_DIDENTERFOREGROUND:
#ifdef LITE_USE_SDL_RENDERER
rencache_invalidate();
#else
SDL_UpdateWindowSurface(window_renderer->window);
#endif
lua_pushstring(L, e.type == SDL_APP_WILLENTERFOREGROUND ? "enteringforeground" : "enteredforeground");
return 1;
case SDL_APP_WILLENTERBACKGROUND:
lua_pushstring(L, "enteringbackground");
return 1;
case SDL_APP_DIDENTERBACKGROUND:
lua_pushstring(L, "enteredbackground");
return 1;
default: default:
goto top; goto top;
@ -1021,7 +1006,7 @@ static int f_library_gc(lua_State *L) {
lua_getfield(L, 1, "handle"); lua_getfield(L, 1, "handle");
void* handle = lua_touserdata(L, -1); void* handle = lua_touserdata(L, -1);
SDL_UnloadObject(handle); SDL_UnloadObject(handle);
return 0; return 0;
} }
@ -1087,7 +1072,6 @@ static int f_path_compare(lua_State *L) {
size_t offset = 0, i, j; size_t offset = 0, i, j;
for (i = 0; i < len1 && i < len2; i++) { for (i = 0; i < len1 && i < len2; i++) {
if (path1[i] != path2[i]) break; if (path1[i] != path2[i]) break;
if (isdigit(path1[i])) break;
if (path1[i] == PATHSEP) { if (path1[i] == PATHSEP) {
offset = i + 1; offset = i + 1;
} }
@ -1158,15 +1142,6 @@ static int f_path_compare(lua_State *L) {
} }
static int f_text_input(lua_State* L) {
if (lua_toboolean(L, 1))
SDL_StartTextInput();
else
SDL_StopTextInput();
return 0;
}
static const luaL_Reg lib[] = { static const luaL_Reg lib[] = {
{ "poll_event", f_poll_event }, { "poll_event", f_poll_event },
{ "wait_event", f_wait_event }, { "wait_event", f_wait_event },
@ -1200,16 +1175,14 @@ static const luaL_Reg lib[] = {
{ "load_native_plugin", f_load_native_plugin }, { "load_native_plugin", f_load_native_plugin },
{ "path_compare", f_path_compare }, { "path_compare", f_path_compare },
{ "get_fs_type", f_get_fs_type }, { "get_fs_type", f_get_fs_type },
{ "text_input", f_text_input },
{ NULL, NULL } { NULL, NULL }
}; };
int luaopen_system(lua_State *L) { int luaopen_system(lua_State *L) {
luaL_newmetatable(L, API_TYPE_NATIVE_PLUGIN); luaL_newmetatable(L, API_TYPE_NATIVE_PLUGIN);
lua_pushcfunction(L, f_library_gc); lua_pushcfunction(L, f_library_gc);
lua_setfield(L, -2, "__gc"); lua_setfield(L, -2, "__gc");
luaL_newlib(L, lib); luaL_newlib(L, lib);
return 1; return 1;
} }

View File

@ -111,8 +111,6 @@ void set_macos_bundle_resources(lua_State *L);
// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-140 // https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-140
#if defined(__x86_64__) || defined(_M_AMD64) || defined(__MINGW64__) #if defined(__x86_64__) || defined(_M_AMD64) || defined(__MINGW64__)
#define ARCH_PROCESSOR "x86_64" #define ARCH_PROCESSOR "x86_64"
#elif __arm__
#define ARCH_PROCESSOR "arm"
#elif defined(__amigaos4__) || defined(__morphos__) #elif defined(__amigaos4__) || defined(__morphos__)
#define ARCH_PROCESSOR "ppc" #define ARCH_PROCESSOR "ppc"
#elif defined(__i386__) || defined(_M_IX86) || defined(__MINGW32__) #elif defined(__i386__) || defined(_M_IX86) || defined(__MINGW32__)
@ -157,7 +155,6 @@ int main(int argc, char **argv) {
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
#endif #endif
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
fprintf(stderr, "Error initializing sdl: %s", SDL_GetError()); fprintf(stderr, "Error initializing sdl: %s", SDL_GetError());
exit(1); exit(1);
@ -209,7 +206,7 @@ int main(int argc, char **argv) {
fprintf(stderr, "Error creating lite-xl window: %s", SDL_GetError()); fprintf(stderr, "Error creating lite-xl window: %s", SDL_GetError());
exit(1); exit(1);
} }
window_renderer = ren_init(window); ren_init(window);
lua_State *L; lua_State *L;
init_lua: init_lua:
@ -269,23 +266,22 @@ init_lua:
" core.init()\n" " core.init()\n"
" core.run()\n" " core.run()\n"
"end, function(err)\n" "end, function(err)\n"
" local error_path = 'error.txt'\n" " local error_dir\n"
" io.stdout:write('Error: '..tostring(err)..'\\n')\n" " io.stdout:write('Error: '..tostring(err)..'\\n')\n"
" io.stdout:write(debug.traceback(nil, 2)..'\\n')\n" " io.stdout:write(debug.traceback(nil, 2)..'\\n')\n"
" if core and core.on_error then\n" " if core and core.on_error then\n"
" error_path = USERDIR .. PATHSEP .. error_path\n" " error_dir=USERDIR\n"
" pcall(core.on_error, err)\n" " pcall(core.on_error, err)\n"
" else\n" " else\n"
" local fp = io.open(error_path, 'wb')\n" " error_dir=system.absolute_path('.')\n"
" local fp = io.open('error.txt', 'wb')\n"
" fp:write('Error: ' .. tostring(err) .. '\\n')\n" " fp:write('Error: ' .. tostring(err) .. '\\n')\n"
" fp:write(debug.traceback(nil, 2)..'\\n')\n" " fp:write(debug.traceback(nil, 4)..'\\n')\n"
" fp:close()\n" " fp:close()\n"
" error_path = system.absolute_path(error_path)\n"
" end\n" " end\n"
" system.show_fatal_error('Lite XL internal error',\n" " system.show_fatal_error('Lite XL internal error',\n"
" 'An internal error occurred in a critical part of the application.\\n\\n'..\n" " 'An internal error occurred in a critical part of the application.\\n\\n'..\n"
" 'Error: '..tostring(err)..'\\n\\n'..\n" " 'Please verify the file \\\"error.txt\\\" in the directory '..error_dir)\n"
" 'Details can be found in \\\"'..error_path..'\\\"')\n"
" os.exit(1)\n" " os.exit(1)\n"
"end)\n" "end)\n"
"return core and core.restart_request\n"; "return core and core.restart_request\n";

View File

@ -14,4 +14,3 @@ void rencache_begin_frame(RenWindow *window_renderer);
void rencache_end_frame(RenWindow *window_renderer); void rencache_end_frame(RenWindow *window_renderer);
#endif #endif

View File

@ -236,7 +236,7 @@ static void font_file_close(FT_Stream stream) {
RenFont* ren_font_load(RenWindow *window_renderer, const char* path, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style) { RenFont* ren_font_load(RenWindow *window_renderer, const char* path, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style) {
RenFont *font = NULL; RenFont *font = NULL;
FT_Face face = NULL; FT_Face face = NULL;
SDL_RWops *file = SDL_RWFromFile(path, "rb"); SDL_RWops *file = SDL_RWFromFile(path, "rb");
if (!file) if (!file)
goto rwops_failure; goto rwops_failure;
@ -515,7 +515,7 @@ void ren_init(SDL_Window *win) {
int error = FT_Init_FreeType( &library ); int error = FT_Init_FreeType( &library );
if ( error ) { if ( error ) {
fprintf(stderr, "internal font error when starting the application\n"); fprintf(stderr, "internal font error when starting the application\n");
return NULL; return;
} }
window_renderer.window = win; window_renderer.window = win;
renwin_init_surface(&window_renderer); renwin_init_surface(&window_renderer);
@ -523,23 +523,6 @@ void ren_init(SDL_Window *win) {
renwin_clip_to_surface(&window_renderer); renwin_clip_to_surface(&window_renderer);
draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32, draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32,
0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
return window_renderer;
}
void ren_free(RenWindow* window_renderer) {
assert(window_renderer);
renwin_free(window_renderer);
SDL_FreeSurface(draw_rect_surface);
free(window_renderer->command_buf);
window_renderer->command_buf = NULL;
window_renderer->command_buf_size = 0;
free(window_renderer);
}
void ren_resize_window(RenWindow *window_renderer) {
renwin_resize_surface(window_renderer);
renwin_update_scale(window_renderer);
} }

View File

@ -149,4 +149,3 @@ void renwin_free(RenWindow *ren) {
SDL_FreeSurface(ren->rensurface.surface); SDL_FreeSurface(ren->rensurface.surface);
#endif #endif
} }

View File

@ -1,14 +1,12 @@
[wrap-file] [wrap-file]
directory = lua-5.4.6 directory = lua-5.4.4
source_url = https://www.lua.org/ftp/lua-5.4.6.tar.gz source_url = https://www.lua.org/ftp/lua-5.4.4.tar.gz
source_filename = lua-5.4.6.tar.gz source_filename = lua-5.4.4.tar.gz
source_hash = 7d5ea1b9cb6aa0b59ca3dde1c6adcb57ef83a1ba8e5432c0ecd06bf439b3ad88 source_hash = 164c7849653b80ae67bec4b7473b884bf5cc8d2dca05653475ec2ed27b9ebf61
patch_filename = lua_5.4.6-3_patch.zip patch_filename = lua_5.4.4-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.6-3/get_patch patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.4-1/get_patch
patch_hash = 9b72a95422fd47f79f969d9abdb589ee95712d5512a5246f94e7e4f63d2cb7b7 patch_hash = e61cd965c629d6543176f41a9f1cb9050edfd1566cf00ce768ff211086e40bdc
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/lua_5.4.6-3/lua-5.4.6.tar.gz
wrapdb_version = 5.4.6-3
[provide] [provide]
lua-5.4 = lua_dep lua-5.4 = lua_dep
lua = lua_dep