Manual sync with 2.1.3 tag
This commit is contained in:
parent
66fb996e76
commit
ad4c221dd8
52
README.md
52
README.md
|
@ -1,7 +1,7 @@
|
|||
# Lite XL
|
||||
|
||||
[![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]
|
||||
|
||||
|
@ -124,7 +124,6 @@ cd lite-xl
|
|||
```
|
||||
|
||||
To run lite-xl without installing:
|
||||
|
||||
```sh
|
||||
./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/
|
||||
```
|
||||
|
||||
#### 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:
|
||||
|
||||
```sh
|
||||
echo -e 'export PATH=$PATH:$HOME/.local/bin' >> $HOME/.bashrc
|
||||
```
|
||||
|
||||
Alternatively on recent versions of GNOME and KDE Plasma,
|
||||
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:
|
||||
To get the icon to show up in app launcher:
|
||||
|
||||
```sh
|
||||
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 Lite XL, run:
|
||||
To uninstall just run:
|
||||
|
||||
```sh
|
||||
rm -f $HOME/.local/bin/lite-xl
|
||||
rm -rf $HOME/.local/share/icons/hicolor/scalable/apps/lite-xl.svg \
|
||||
$HOME/.local/share/applications/com.lite_xl.LiteXL.desktop \
|
||||
$HOME/.local/share/metainfo/com.lite_xl.LiteXL.appdata.xml \
|
||||
$HOME/.local/share/applications/org.lite_xl.lite_xl.desktop \
|
||||
$HOME/.local/share/metainfo/org.lite_xl.lite_xl.appdata.xml \
|
||||
$HOME/.local/share/lite-xl
|
||||
```
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Any additional functionality that can be added through a plugin should be done
|
||||
|
|
|
@ -363,7 +363,7 @@ local commands = {
|
|||
["doc:select-lines"] = function(dv)
|
||||
for idx, line1, _, line2 in dv.doc:get_selections(true) do
|
||||
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,
|
||||
|
||||
|
@ -548,11 +548,6 @@ local commands = {
|
|||
dv.doc.crlf = not dv.doc.crlf
|
||||
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)
|
||||
local last_doc = core.last_active_view and core.last_active_view.doc
|
||||
local text
|
||||
|
|
|
@ -196,23 +196,6 @@ local function select_next(reverse)
|
|||
if l2 then doc():set_selection(l2, c2, l1, c1) 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, {
|
||||
["find-replace:select-next"] = select_next,
|
||||
["find-replace:select-previous"] = function() select_next(true) end,
|
||||
|
@ -229,11 +212,15 @@ command.add("core.docview!", {
|
|||
end,
|
||||
|
||||
["find-replace:replace"] = function()
|
||||
find_replace()
|
||||
end,
|
||||
|
||||
["find-replace:replace-in-selection"] = function()
|
||||
find_replace(true)
|
||||
local l1, c1, l2, c2 = doc():get_selection()
|
||||
local selected_text = doc():get_text(l1, c1, l2, c2)
|
||||
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,
|
||||
|
||||
["find-replace:replace-symbol"] = function()
|
||||
|
|
|
@ -4,7 +4,6 @@ local DocView = require "core.docview"
|
|||
local command = require "core.command"
|
||||
local common = require "core.common"
|
||||
local config = require "core.config"
|
||||
local Node = require "core.node"
|
||||
|
||||
|
||||
local t = {
|
||||
|
@ -30,6 +29,20 @@ local t = {
|
|||
core.confirm_close_docs(docs, core.root_view.close_all_docviews, core.root_view, true)
|
||||
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)
|
||||
local idx = node:get_view_idx(core.active_view)
|
||||
if idx > 1 then
|
||||
|
@ -104,7 +117,7 @@ end, t)
|
|||
|
||||
command.add(nil, {
|
||||
["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
|
||||
view.scroll.to.y = view.scroll.to.y + delta * -config.mouse_wheel_scroll
|
||||
return true
|
||||
|
@ -112,7 +125,7 @@ command.add(nil, {
|
|||
return false
|
||||
end,
|
||||
["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
|
||||
view.scroll.to.x = view.scroll.to.x + delta * -config.mouse_wheel_scroll
|
||||
return true
|
||||
|
@ -120,74 +133,3 @@ command.add(nil, {
|
|||
return false
|
||||
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
|
||||
})
|
||||
|
|
|
@ -84,11 +84,6 @@ function CommandView:get_line_screen_position(line, col)
|
|||
end
|
||||
|
||||
|
||||
function CommandView:supports_text_input()
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function CommandView:get_scrollable_size()
|
||||
return 0
|
||||
end
|
||||
|
|
|
@ -2,71 +2,15 @@ local common = require "core.common"
|
|||
|
||||
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
|
||||
|
||||
---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
|
||||
|
||||
---The timeout, in seconds, before a message dissapears from StatusView.
|
||||
---
|
||||
---Defaults to 5.
|
||||
---@type number
|
||||
config.message_timeout = 5
|
||||
|
||||
---The number of pixels scrolled per-step.
|
||||
---
|
||||
---Defaults to 50 * SCALE.
|
||||
---@type number
|
||||
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
|
||||
|
||||
---Enables/disables scrolling past the end of a document.
|
||||
---
|
||||
---Defaults to true.
|
||||
---@type boolean
|
||||
config.scroll_past_end = true
|
||||
|
||||
---@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
|
||||
---@type "expanded" | "contracted" | false @Force the scrollbar status of the DocView
|
||||
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
|
||||
|
||||
---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 = {
|
||||
-- folders
|
||||
"^%.svn/", "^%.git/", "^%.hg/", "^CVS/", "^%.Trash/", "^%.Trash%-.*/",
|
||||
|
@ -77,181 +21,40 @@ config.ignore_files = {
|
|||
"%.suo$", "%.pdb$", "%.idb$", "%.class$", "%.psd$", "%.db$",
|
||||
"^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_]*"
|
||||
|
||||
---A list of characters that delimits a word.
|
||||
---
|
||||
---The default is ``" \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"``
|
||||
---@type string
|
||||
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
|
||||
|
||||
---The maximum number of undo steps per-document.
|
||||
---
|
||||
---The default is 10000.
|
||||
---@type number
|
||||
config.max_undos = 10000
|
||||
|
||||
---The maximum number of tabs shown at a time.
|
||||
---
|
||||
---The default is 8.
|
||||
---@type number
|
||||
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
|
||||
|
||||
---@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
|
||||
-- Possible values: false, true, "no_selection"
|
||||
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
|
||||
|
||||
---The number of spaces each level of indentation represents.
|
||||
---
|
||||
---The default is 2.
|
||||
---@type number
|
||||
config.indent_size = 2
|
||||
|
||||
---The type of indentation.
|
||||
---
|
||||
---The default is "soft" (spaces).
|
||||
---@type "soft" | "hard"
|
||||
config.tab_type = "soft"
|
||||
|
||||
---Do not remove whitespaces when advancing to the next line.
|
||||
---
|
||||
---Defaults to false.
|
||||
---@type boolean
|
||||
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
|
||||
|
||||
---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
|
||||
|
||||
---Enables/disables all transitions.
|
||||
---
|
||||
---Defaults to true.
|
||||
---@type boolean
|
||||
config.transitions = true
|
||||
|
||||
---Enable/disable individual transitions.
|
||||
---These values are overriden by `config.transitions`.
|
||||
config.disabled_transitions = {
|
||||
---Disables scrolling transitions.
|
||||
scroll = false,
|
||||
---Disables transitions for CommandView's suggestions list.
|
||||
commandview = false,
|
||||
---Disables transitions for showing/hiding the context menu.
|
||||
contextmenu = false,
|
||||
---Disables transitions when clicking on log items in LogView.
|
||||
logview = false,
|
||||
---Disables transitions for showing/hiding the Nagbar.
|
||||
nagbar = false,
|
||||
---Disables transitions when scrolling the tab bar.
|
||||
tabs = false,
|
||||
---Disables transitions when a tab is being dragged.
|
||||
tab_drag = false,
|
||||
---Disables transitions when a notification is shown.
|
||||
statusbar = false,
|
||||
}
|
||||
|
||||
---The rate of all transitions.
|
||||
---
|
||||
---Defaults to 1.
|
||||
---@type number
|
||||
config.animation_rate = 1.0
|
||||
|
||||
---The caret's blinking period, in seconds.
|
||||
---
|
||||
---Defaults to 0.8.
|
||||
---@type number
|
||||
config.blink_period = 0.8
|
||||
|
||||
---Disables caret blinking.
|
||||
---
|
||||
---Defaults to false.
|
||||
---@type boolean
|
||||
config.disable_blink = false
|
||||
|
||||
---Draws whitespaces as dots.
|
||||
---This option is deprecated.
|
||||
---Please use the drawwhitespace plugin instead.
|
||||
---@deprecated
|
||||
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
|
||||
|
||||
---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
|
||||
|
||||
---Maximum number of clicks recognized by Lite XL.
|
||||
---
|
||||
---Defaults to 3.
|
||||
---@type number
|
||||
config.max_clicks = 3
|
||||
|
||||
---Disables plugin version checking.
|
||||
---Do not change this unless you know what you are doing.
|
||||
---
|
||||
---Defaults to false.
|
||||
---@type boolean
|
||||
-- set as true to be able to test non supported plugins
|
||||
config.skip_plugins_version = false
|
||||
|
||||
-- holds the plugins real config table
|
||||
|
|
|
@ -48,7 +48,7 @@ function Highlighter:start()
|
|||
self:update_notify(retokenized_from, max - retokenized_from)
|
||||
end
|
||||
core.redraw = true
|
||||
coroutine.yield(0)
|
||||
coroutine.yield()
|
||||
end
|
||||
self.max_wanted_line = 0
|
||||
self.running = false
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
local Object = require "core.object"
|
||||
local Highlighter = require "core.doc.highlighter"
|
||||
local translate = require "core.doc.translate"
|
||||
local core = require "core"
|
||||
local syntax = require "core.syntax"
|
||||
local config = require "core.config"
|
||||
|
@ -28,11 +27,9 @@ function Doc:new(filename, abs_filename, new_file)
|
|||
self:load(filename)
|
||||
end
|
||||
end
|
||||
if new_file then
|
||||
self.crlf = config.line_endings == "crlf"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:reset()
|
||||
self.lines = { "\n" }
|
||||
self.selections = { 1, 1, 1, 1 }
|
||||
|
@ -41,10 +38,10 @@ function Doc:reset()
|
|||
self.redo_stack = { idx = 1 }
|
||||
self.clean_change_id = 1
|
||||
self.highlighter = Highlighter(self)
|
||||
self.overwrite = false
|
||||
self:reset_syntax()
|
||||
end
|
||||
|
||||
|
||||
function Doc:reset_syntax()
|
||||
local header = self:get_text(1, 1, self:position_offset(1, 1, 128))
|
||||
local path = self.abs_filename
|
||||
|
@ -59,14 +56,16 @@ function Doc:reset_syntax()
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:set_filename(filename, abs_filename)
|
||||
self.filename = filename
|
||||
self.abs_filename = abs_filename
|
||||
self:reset_syntax()
|
||||
end
|
||||
|
||||
|
||||
function Doc:load(filename)
|
||||
local fp = assert(io.open(filename, "rb"))
|
||||
local fp = assert( io.open(filename, "rb") )
|
||||
self:reset()
|
||||
self.lines = {}
|
||||
local i = 1
|
||||
|
@ -86,6 +85,7 @@ function Doc:load(filename)
|
|||
self:reset_syntax()
|
||||
end
|
||||
|
||||
|
||||
function Doc:reload()
|
||||
if self.filename then
|
||||
local sel = { self:get_selection() }
|
||||
|
@ -95,6 +95,7 @@ function Doc:reload()
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:save(filename, abs_filename)
|
||||
if not filename then
|
||||
assert(self.filename, "no filename set to default to")
|
||||
|
@ -103,7 +104,7 @@ function Doc:save(filename, abs_filename)
|
|||
else
|
||||
assert(self.filename or abs_filename, "calling save on unnamed doc without absolute path")
|
||||
end
|
||||
local fp = assert(io.open(filename, "wb"))
|
||||
local fp = assert( io.open(filename, "wb") )
|
||||
for _, line in ipairs(self.lines) do
|
||||
if self.crlf then line = line:gsub("\n", "\r\n") end
|
||||
fp:write(line)
|
||||
|
@ -114,10 +115,12 @@ function Doc:save(filename, abs_filename)
|
|||
self:clean()
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_name()
|
||||
return self.filename or "unsaved"
|
||||
end
|
||||
|
||||
|
||||
function Doc:is_dirty()
|
||||
if self.new_file then
|
||||
if self.filename then return true end
|
||||
|
@ -127,10 +130,12 @@ function Doc:is_dirty()
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:clean()
|
||||
self.clean_change_id = self:get_change_id()
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_indent_info()
|
||||
if not self.indent_info then return config.tab_type, config.indent_size, false end
|
||||
return self.indent_info.type or config.tab_type,
|
||||
|
@ -138,6 +143,7 @@ function Doc:get_indent_info()
|
|||
self.indent_info.confirmed
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_change_id()
|
||||
return self.undo_stack.idx
|
||||
end
|
||||
|
@ -161,14 +167,13 @@ function Doc:get_selection(sort)
|
|||
return line1, col1, line2, col2, swap
|
||||
end
|
||||
|
||||
|
||||
---Get the selection specified by `idx`
|
||||
---@param idx integer @the index of the selection to retrieve
|
||||
---@param sort? boolean @whether to sort the selection returned
|
||||
---@return integer,integer,integer,integer,boolean? @line1, col1, line2, col2, was the selection sorted
|
||||
function Doc:get_selection_idx(idx, sort)
|
||||
local line1, col1, line2, col2 = self.selections[idx * 4 - 3], self.selections[idx * 4 - 2],
|
||||
self.selections[idx * 4 - 1],
|
||||
self.selections[idx * 4]
|
||||
local line1, col1, line2, col2 = self.selections[idx*4-3], self.selections[idx*4-2], self.selections[idx*4-1], self.selections[idx*4]
|
||||
if line1 and sort then
|
||||
return sort_positions(line1, col1, line2, col2)
|
||||
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
|
||||
line1, col1 = self:sanitize_position(line1, 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
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
|
||||
function Doc:remove_selection(idx)
|
||||
if self.last_selection >= idx then
|
||||
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)
|
||||
end
|
||||
|
||||
|
||||
function Doc:set_selection(line1, col1, line2, col2, swap)
|
||||
self.selections = {}
|
||||
self:set_selections(1, line1, col1, line2, col2, swap)
|
||||
|
@ -245,9 +252,9 @@ function Doc:merge_cursors(idx)
|
|||
for i = (idx or (#self.selections - 3)), (idx or 5), -4 do
|
||||
for j = 1, i - 4, 4 do
|
||||
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)
|
||||
if self.last_selection >= (i + 3) / 4 then
|
||||
if self.last_selection >= (i+3)/4 then
|
||||
self.last_selection = self.last_selection - 1
|
||||
end
|
||||
break
|
||||
|
@ -257,12 +264,12 @@ function Doc:merge_cursors(idx)
|
|||
end
|
||||
|
||||
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 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
|
||||
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
|
||||
|
||||
|
@ -270,9 +277,8 @@ end
|
|||
-- If a number, runs for exactly that iteration.
|
||||
function Doc:get_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 of cursor seciton.
|
||||
|
||||
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])
|
||||
end
|
||||
|
||||
|
||||
local function position_offset_func(self, line, col, fn, ...)
|
||||
line, col = self:sanitize_position(line, col)
|
||||
return fn(self, line, col, ...)
|
||||
|
@ -323,6 +330,7 @@ function Doc:position_offset(line, col, ...)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_text(line1, col1, line2, col2)
|
||||
line1, col1 = self:sanitize_position(line1, col1)
|
||||
line2, col2 = self:sanitize_position(line2, col2)
|
||||
|
@ -338,11 +346,13 @@ function Doc:get_text(line1, col1, line2, col2)
|
|||
return table.concat(lines)
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_char(line, col)
|
||||
line, col = self:sanitize_position(line, col)
|
||||
return self.lines[line]:sub(col, col)
|
||||
end
|
||||
|
||||
|
||||
local function push_undo(undo_stack, time, type, ...)
|
||||
undo_stack[undo_stack.idx] = { type = type, time = time, ... }
|
||||
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
|
||||
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
|
||||
self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition,
|
||||
ccol2 + column_addition)
|
||||
self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition, ccol2 + column_addition)
|
||||
end
|
||||
|
||||
-- push undo
|
||||
|
@ -417,6 +426,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
|
|||
self:sanitize_selection()
|
||||
end
|
||||
|
||||
|
||||
function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
|
||||
-- push undo
|
||||
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()
|
||||
end
|
||||
|
||||
|
||||
function Doc:insert(line, col, text)
|
||||
self.redo_stack = { idx = 1 }
|
||||
-- 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")
|
||||
end
|
||||
|
||||
|
||||
function Doc:remove(line1, col1, line2, col2)
|
||||
self.redo_stack = { idx = 1 }
|
||||
line1, col1 = self:sanitize_position(line1, col1)
|
||||
|
@ -495,34 +507,28 @@ function Doc:remove(line1, col1, line2, col2)
|
|||
self:on_text_change("remove")
|
||||
end
|
||||
|
||||
|
||||
function Doc:undo()
|
||||
pop_undo(self, self.undo_stack, self.redo_stack, false)
|
||||
end
|
||||
|
||||
|
||||
function Doc:redo()
|
||||
pop_undo(self, self.redo_stack, self.undo_stack, false)
|
||||
end
|
||||
|
||||
|
||||
function Doc:text_input(text, idx)
|
||||
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
|
||||
self:delete_to_cursor(sidx)
|
||||
had_selection = true
|
||||
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:move_to_cursor(sidx, #text)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:ime_text_editing(text, start, length, idx)
|
||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
|
@ -533,6 +539,7 @@ function Doc:ime_text_editing(text, start, length, idx)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
|
||||
local old_text = self:get_text(line1, col1, line2, col2)
|
||||
local new_text, res = fn(old_text)
|
||||
|
@ -548,7 +555,7 @@ function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
|
|||
end
|
||||
|
||||
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
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
results[idx] = self:replace_cursor(idx, line1, col1, line2, col2, fn)
|
||||
|
@ -562,6 +569,7 @@ function Doc:replace(fn)
|
|||
return results
|
||||
end
|
||||
|
||||
|
||||
function Doc:delete_to_cursor(idx, ...)
|
||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
|
@ -575,7 +583,6 @@ function Doc:delete_to_cursor(idx, ...)
|
|||
end
|
||||
self:merge_cursors(idx)
|
||||
end
|
||||
|
||||
function Doc:delete_to(...) return self:delete_to_cursor(nil, ...) end
|
||||
|
||||
function Doc:move_to_cursor(idx, ...)
|
||||
|
@ -584,9 +591,9 @@ function Doc:move_to_cursor(idx, ...)
|
|||
end
|
||||
self:merge_cursors(idx)
|
||||
end
|
||||
|
||||
function Doc:move_to(...) return self:move_to_cursor(nil, ...) end
|
||||
|
||||
|
||||
function Doc:select_to_cursor(idx, ...)
|
||||
for sidx, line, col, line2, col2 in self:get_selections(false, idx) do
|
||||
line, col = self:position_offset(line, col, ...)
|
||||
|
@ -594,9 +601,9 @@ function Doc:select_to_cursor(idx, ...)
|
|||
end
|
||||
self:merge_cursors(idx)
|
||||
end
|
||||
|
||||
function Doc:select_to(...) return self:select_to_cursor(nil, ...) end
|
||||
|
||||
|
||||
function Doc:get_indent_string()
|
||||
local indent_type, indent_size = self:get_indent_info()
|
||||
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 number = #indent / #soft_tab
|
||||
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
|
||||
|
||||
|
@ -667,4 +674,5 @@ function Doc:on_close()
|
|||
core.log_quiet("Closed doc \"%s\"", self:get_name())
|
||||
end
|
||||
|
||||
|
||||
return Doc
|
||||
|
|
|
@ -254,11 +254,6 @@ function DocView:scroll_to_line(line, ignore_if_visible, instant)
|
|||
end
|
||||
|
||||
|
||||
function DocView:supports_text_input()
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function DocView:scroll_to_make_visible(line, col)
|
||||
local _, oy = self:get_content_offset()
|
||||
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)
|
||||
line2, col2 = translate.end_of_word(doc, line2, col2)
|
||||
elseif snap_type == "lines" then
|
||||
col1, col2, line2 = 1, 1, line2 + 1
|
||||
col1, col2 = 1, math.huge
|
||||
end
|
||||
if swap then
|
||||
return line2, col2, line1, col1
|
||||
|
@ -460,13 +455,6 @@ function DocView:draw_line_text(line, x, y)
|
|||
return self:get_line_height()
|
||||
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)
|
||||
local lh = self:get_line_height()
|
||||
renderer.draw_rect(x, y, style.caret_width, lh, style.caret)
|
||||
|
@ -566,12 +554,7 @@ function DocView:draw_overlay()
|
|||
else
|
||||
if config.disable_blink
|
||||
or (core.blink_timer - core.blink_start) % T < T / 2 then
|
||||
local x, y = 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
|
||||
self:draw_caret(self:get_line_screen_position(line1, col1))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -668,9 +668,6 @@ end
|
|||
|
||||
|
||||
function core.init()
|
||||
core.log_items = {}
|
||||
core.log_quiet("Lite XL version %s - mod-version %s", VERSION, MOD_VERSION_STRING)
|
||||
|
||||
command = require "core.command"
|
||||
keymap = require "core.keymap"
|
||||
dirwatch = require "core.dirwatch"
|
||||
|
@ -724,6 +721,7 @@ function core.init()
|
|||
|
||||
core.frame_start = 0
|
||||
core.clip_rect_stack = {{ 0,0,0,0 }}
|
||||
core.log_items = {}
|
||||
core.docs = {}
|
||||
core.cursor_clipboard = {}
|
||||
core.cursor_clipboard_whole_line = {}
|
||||
|
@ -825,19 +823,15 @@ function core.init()
|
|||
local msg = {}
|
||||
for _, entry in pairs(plugins_refuse_list) do
|
||||
if #entry.plugins > 0 then
|
||||
local msg_list = {}
|
||||
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"))
|
||||
msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(entry.plugins, "\n"))
|
||||
end
|
||||
end
|
||||
core.nag_view:show(
|
||||
"Refused Plugins",
|
||||
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.",
|
||||
MOD_VERSION_STRING, table.concat(msg, ".\n\n")),
|
||||
table.concat(msg, ".\n\n")),
|
||||
opt, function(item)
|
||||
if item.text == "Exit" then os.exit(1) end
|
||||
end)
|
||||
|
@ -926,8 +920,6 @@ function core.restart()
|
|||
end
|
||||
|
||||
|
||||
local mod_version_regex =
|
||||
regex.compile([[--.*mod-version:(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:$|\s)]])
|
||||
local function get_plugin_details(filename)
|
||||
local info = system.get_file_info(filename)
|
||||
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
|
||||
local priority = false
|
||||
local version_match = false
|
||||
local major, minor, patch
|
||||
|
||||
for line in f:lines() do
|
||||
if not version_match then
|
||||
local _major, _minor, _patch = mod_version_regex:match(line)
|
||||
if _major then
|
||||
_major = tonumber(_major) or 0
|
||||
_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
|
||||
local mod_version = line:match('%-%-.*%f[%a]mod%-version%s*:%s*(%d+)')
|
||||
if mod_version then
|
||||
version_match = (mod_version == MOD_VERSION)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not priority then
|
||||
priority = line:match('%-%-.*%f[%a]priority%s*:%s*(%d+)')
|
||||
if priority then priority = tonumber(priority) end
|
||||
end
|
||||
|
||||
if version_match then
|
||||
break
|
||||
end
|
||||
|
@ -972,7 +949,6 @@ local function get_plugin_details(filename)
|
|||
f:close()
|
||||
return true, {
|
||||
version_match = version_match,
|
||||
version = major and {major, minor, patch} or {},
|
||||
priority = priority or 100
|
||||
}
|
||||
end
|
||||
|
@ -1008,8 +984,6 @@ function core.load_plugins()
|
|||
plugin.dir = dir
|
||||
plugin.priority = details and details.priority or 100
|
||||
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
|
||||
|
||||
-- sort by priority or name for plugins that have same priority
|
||||
|
@ -1025,35 +999,27 @@ function core.load_plugins()
|
|||
if plugin.valid then
|
||||
if not config.skip_plugins_version and not plugin.version_match then
|
||||
core.log_quiet(
|
||||
"Version mismatch for plugin %q[%s] from %s",
|
||||
"Version mismatch for plugin %q from %s",
|
||||
plugin.name,
|
||||
plugin.version_string,
|
||||
plugin.dir
|
||||
)
|
||||
local rlist = plugin.dir:find(USERDIR, 1, true) == 1
|
||||
and 'userdir' or 'datadir'
|
||||
local list = refused_list[rlist].plugins
|
||||
table.insert(list, plugin)
|
||||
table.insert(list, plugin.file)
|
||||
elseif config.plugins[plugin.name] ~= false then
|
||||
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
|
||||
local plugin_version = ""
|
||||
if plugin.version_string ~= MOD_VERSION_STRING then
|
||||
plugin_version = "["..plugin.version_string.."]"
|
||||
end
|
||||
core.log_quiet(
|
||||
"Loaded plugin %q%s from %s in %.1fms",
|
||||
"Loaded plugin %q from %s in %.1fms",
|
||||
plugin.name,
|
||||
plugin_version,
|
||||
plugin.dir,
|
||||
(system.get_time() - start) * 1000
|
||||
)
|
||||
end
|
||||
if not ok then
|
||||
no_errors = false
|
||||
elseif config.plugins[plugin.name].onload then
|
||||
core.try(config.plugins[plugin.name].onload, loaded_plugin)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1107,7 +1073,6 @@ function core.set_active_view(view)
|
|||
-- Reset the IME even if the focus didn't change
|
||||
ime.stop()
|
||||
if view ~= core.active_view then
|
||||
system.text_input(view:supports_text_input())
|
||||
if core.active_view and core.active_view.force_focus then
|
||||
core.next_active_view = view
|
||||
return
|
||||
|
@ -1296,12 +1261,6 @@ function core.on_event(type, ...)
|
|||
if not core.root_view:on_mouse_wheel(...) then
|
||||
did_keymap = keymap.on_mouse_wheel(...)
|
||||
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
|
||||
core.window_mode = system.get_window_mode()
|
||||
elseif type == "minimized" or type == "maximized" or type == "restored" then
|
||||
|
@ -1351,11 +1310,6 @@ function core.step()
|
|||
did_keymap = false
|
||||
elseif type == "mousemoved" then
|
||||
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
|
||||
local _, res = core.try(core.on_event, type, a, b, c, d)
|
||||
did_keymap = res or did_keymap
|
||||
|
@ -1510,16 +1464,4 @@ function core.on_error(err)
|
|||
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
|
||||
|
||||
|
|
|
@ -36,8 +36,6 @@ local function keymap_macos(keymap)
|
|||
["wheel"] = "root:scroll",
|
||||
["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+r"] = "find-replace:replace",
|
||||
|
|
|
@ -71,6 +71,7 @@ local function key_to_stroke(key)
|
|||
return normalize_stroke(table.concat(keys, "+"))
|
||||
end
|
||||
|
||||
|
||||
---Remove the given value from an array associated to a key in a table.
|
||||
---@param tbl table<string, string> The table containing the key
|
||||
---@param k string The key containing the array
|
||||
|
@ -124,6 +125,7 @@ end
|
|||
function keymap.add_direct(map)
|
||||
for stroke, commands in pairs(map) do
|
||||
stroke = normalize_stroke(stroke)
|
||||
|
||||
if type(commands) == "string" or type(commands) == "function" then
|
||||
commands = { commands }
|
||||
end
|
||||
|
@ -323,8 +325,6 @@ keymap.add_direct {
|
|||
["wheel"] = "root:scroll",
|
||||
["hwheel"] = "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+r"] = "find-replace:replace",
|
||||
|
@ -341,7 +341,6 @@ keymap.add_direct {
|
|||
["ctrl+x"] = "doc:cut",
|
||||
["ctrl+c"] = "doc:copy",
|
||||
["ctrl+v"] = "doc:paste",
|
||||
["insert"] = "doc:toggle-overwrite",
|
||||
["ctrl+insert"] = "doc:copy",
|
||||
["shift+insert"] = "doc:paste",
|
||||
["escape"] = { "command:escape", "doc:select-none", "dialog:select-no" },
|
||||
|
@ -391,7 +390,7 @@ keymap.add_direct {
|
|||
["shift+1lclick"] = "doc:select-to-cursor",
|
||||
["ctrl+1lclick"] = "doc:split-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",
|
||||
["shift+left"] = "doc:select-to-previous-char",
|
||||
["shift+right"] = "doc:select-to-next-char",
|
||||
|
@ -412,4 +411,3 @@ keymap.add_direct {
|
|||
}
|
||||
|
||||
return keymap
|
||||
|
||||
|
|
|
@ -7,10 +7,6 @@ modkeys.map = {
|
|||
["right shift"] = "shift",
|
||||
["left alt"] = "alt",
|
||||
["right alt"] = "altgr",
|
||||
["left gui"] = "super",
|
||||
["left windows"] = "super",
|
||||
["right gui"] = "super",
|
||||
["right windows"] = "super"
|
||||
}
|
||||
|
||||
modkeys.keys = { "ctrl", "shift", "alt", "altgr" }
|
||||
|
|
|
@ -24,7 +24,6 @@ function NagView:new()
|
|||
self.scrollable = true
|
||||
self.target_height = 0
|
||||
self.on_mouse_pressed_root = nil
|
||||
self.dim_alpha = 0
|
||||
end
|
||||
|
||||
function NagView:get_title()
|
||||
|
@ -69,9 +68,7 @@ function NagView:dim_window_content()
|
|||
oy = oy + self.show_height
|
||||
local w, h = core.root_view.size.x, core.root_view.size.y - oy
|
||||
core.root_view:defer_draw(function()
|
||||
local dim_color = { table.unpack(style.nagbar_dim) }
|
||||
dim_color[4] = style.nagbar_dim[4] * self.dim_alpha
|
||||
renderer.draw_rect(ox, oy, w, h, dim_color)
|
||||
renderer.draw_rect(ox, oy, w, h, style.nagbar_dim)
|
||||
end)
|
||||
end
|
||||
|
||||
|
@ -175,13 +172,10 @@ function NagView:update()
|
|||
NagView.super.update(self)
|
||||
|
||||
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", target_height, nil, "nagbar")
|
||||
self:move_towards(self, "show_height", self:get_target_height(), 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
|
||||
self:move_towards(self, "show_height", 0, nil, "nagbar")
|
||||
self:move_towards(self, "dim_alpha", 0, nil, "nagbar")
|
||||
if self.show_height <= 0 then
|
||||
self.title = nil
|
||||
self.message = nil
|
||||
|
|
|
@ -18,6 +18,7 @@ function Node:new(type)
|
|||
if self.type == "leaf" then
|
||||
self:add_view(EmptyView())
|
||||
end
|
||||
self.hovered = {x = -1, y = -1 }
|
||||
self.hovered_close = 0
|
||||
self.tab_shift = 0
|
||||
self.tab_offset = 1
|
||||
|
@ -32,10 +33,9 @@ function Node:propagate(fn, ...)
|
|||
end
|
||||
|
||||
|
||||
---@deprecated
|
||||
function Node:on_mouse_moved(x, y, ...)
|
||||
core.deprecation_log("Node:on_mouse_moved")
|
||||
if self.type == "leaf" then
|
||||
self.hovered.x, self.hovered.y = x, y
|
||||
self.active_view:on_mouse_moved(x, y, ...)
|
||||
else
|
||||
self:propagate("on_mouse_moved", x, y, ...)
|
||||
|
@ -43,9 +43,7 @@ function Node:on_mouse_moved(x, y, ...)
|
|||
end
|
||||
|
||||
|
||||
---@deprecated
|
||||
function Node:on_mouse_released(...)
|
||||
core.deprecation_log("Node:on_mouse_released")
|
||||
if self.type == "leaf" then
|
||||
self.active_view:on_mouse_released(...)
|
||||
else
|
||||
|
@ -54,9 +52,7 @@ function Node:on_mouse_released(...)
|
|||
end
|
||||
|
||||
|
||||
---@deprecated
|
||||
function Node:on_mouse_left()
|
||||
core.deprecation_log("Node:on_mouse_left")
|
||||
if self.type == "leaf" then
|
||||
self.active_view:on_mouse_left()
|
||||
else
|
||||
|
@ -65,17 +61,6 @@ function Node:on_mouse_left()
|
|||
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)
|
||||
for k, _ in pairs(self) do self[k] = nil 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")
|
||||
if self.views[1] and self.views[1]:is(EmptyView) then
|
||||
table.remove(self.views)
|
||||
if idx and idx > 1 then
|
||||
idx = idx - 1
|
||||
end
|
||||
end
|
||||
idx = common.clamp(idx or (#self.views + 1), 1, (#self.views + 1))
|
||||
table.insert(self.views, idx, view)
|
||||
table.insert(self.views, idx or (#self.views + 1), view)
|
||||
self:set_active_view(view)
|
||||
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
|
||||
self.hovered_close = tab_index
|
||||
end
|
||||
elseif #self.views > self:get_visible_tabs_number() then
|
||||
else
|
||||
self.hovered_scroll_button = self:get_scroll_button_index(px, py) or 0
|
||||
end
|
||||
end
|
||||
|
@ -338,17 +319,10 @@ function Node:get_child_overlapping_point(x, y)
|
|||
return child:get_child_overlapping_point(x, y)
|
||||
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)
|
||||
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)
|
||||
return x, self.position.y, w, h, pad
|
||||
end
|
||||
|
@ -359,8 +333,8 @@ function Node:get_tab_rect(idx)
|
|||
local x0 = self.position.x
|
||||
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 h, pad_y, margin_y = get_tab_y_sizes()
|
||||
return x1, self.position.y, x2 - x1, h, margin_y
|
||||
local h = style.font:get_height() + style.padding.y * 2
|
||||
return x1, self.position.y, x2 - x1, h
|
||||
end
|
||||
|
||||
|
||||
|
@ -508,7 +482,7 @@ function Node:update()
|
|||
for _, view in ipairs(self.views) do
|
||||
view:update()
|
||||
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()
|
||||
self:move_towards("tab_shift", tab_width * (self.tab_offset - 1), 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
|
||||
color = style.text
|
||||
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 - ds, y, ds, h, style.divider)
|
||||
end
|
||||
|
@ -559,8 +532,7 @@ function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalo
|
|||
end
|
||||
|
||||
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 + margin_y, w, h - margin_y, standalone)
|
||||
x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalone)
|
||||
-- Close button
|
||||
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)
|
||||
|
@ -636,13 +608,6 @@ function Node:is_empty()
|
|||
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)
|
||||
local node_active_view = self.active_view
|
||||
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)
|
||||
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
|
||||
-- use next tab
|
||||
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_x = tab_x - tab_w
|
||||
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
|
||||
|
||||
return Node
|
||||
|
|
|
@ -27,13 +27,6 @@ function Object:is(T)
|
|||
return getmetatable(self) == T
|
||||
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.
|
||||
---@param T any
|
||||
---@return boolean
|
||||
|
@ -48,22 +41,6 @@ function Object:extends(T)
|
|||
return false
|
||||
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.
|
||||
---@return string
|
||||
function Object:__tostring()
|
||||
|
|
|
@ -24,9 +24,6 @@ function RootView:new()
|
|||
base_color = 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.grab = nil -- = {view = nil, button = nil}
|
||||
self.overlapping_view = nil
|
||||
self.touched_view = nil
|
||||
end
|
||||
|
||||
|
||||
|
@ -119,31 +116,6 @@ function RootView:close_all_docviews(keep_active)
|
|||
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.
|
||||
---Do nothing by default.
|
||||
---@param button core.view.mousebutton
|
||||
|
@ -160,10 +132,6 @@ end
|
|||
---@param clicks integer
|
||||
---@return boolean
|
||||
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 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
|
||||
|
@ -188,7 +156,6 @@ function RootView:on_mouse_pressed(button, x, y, clicks)
|
|||
end
|
||||
elseif not self.dragged_node then -- avoid sending on_mouse_pressed events when dragging tabs
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
@ -221,21 +188,6 @@ end
|
|||
---@param x number
|
||||
---@param y number
|
||||
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
|
||||
self.dragged_divider = nil
|
||||
end
|
||||
|
@ -276,6 +228,8 @@ function RootView:on_mouse_released(button, x, y, ...)
|
|||
end
|
||||
self.dragged_node = nil
|
||||
end
|
||||
else -- avoid sending on_mouse_released events when dragging tabs
|
||||
self.root_node:on_mouse_released(button, x, y, ...)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -296,14 +250,6 @@ end
|
|||
---@param dx number
|
||||
---@param dy number
|
||||
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
|
||||
core.request_cursor("arrow")
|
||||
core.active_view:on_mouse_moved(x, y, dx, dy)
|
||||
|
@ -323,6 +269,8 @@ function RootView:on_mouse_moved(x, y, dx, dy)
|
|||
return
|
||||
end
|
||||
|
||||
self.mouse.x, self.mouse.y = x, y
|
||||
|
||||
local dn = self.dragged_node
|
||||
if dn and not dn.dragging then
|
||||
-- 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
|
||||
if dn then return end
|
||||
|
||||
local last_overlapping_view = self.overlapping_view
|
||||
local overlapping_node = self.root_node:get_child_overlapping_point(x, y)
|
||||
self.overlapping_view = overlapping_node and overlapping_node.active_view
|
||||
self.root_node:on_mouse_moved(x, y, dx, dy)
|
||||
|
||||
if last_overlapping_view and last_overlapping_view ~= self.overlapping_view then
|
||||
last_overlapping_view:on_mouse_left()
|
||||
local last_overlapping_node = self.overlapping_node
|
||||
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
|
||||
|
||||
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)
|
||||
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")
|
||||
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")
|
||||
elseif tab_index then
|
||||
core.request_cursor("arrow")
|
||||
elseif self.overlapping_node then
|
||||
core.request_cursor(self.overlapping_node.active_view.cursor)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function RootView:on_mouse_left()
|
||||
if self.overlapping_view then
|
||||
self.overlapping_view:on_mouse_left()
|
||||
if self.overlapping_node then
|
||||
self.overlapping_node:on_mouse_left()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -387,50 +334,6 @@ function RootView:on_text_input(...)
|
|||
core.active_view:on_text_input(...)
|
||||
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(...)
|
||||
core.active_view:on_ime_text_editing(...)
|
||||
end
|
||||
|
|
|
@ -58,9 +58,9 @@ function Scrollbar:new(options)
|
|||
---@type "expanded" | "contracted" | false @Force the scrollbar status
|
||||
self.force_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`
|
||||
self.contracted_size = options.contracted_size
|
||||
---@type number? @Override the default value specified by `style.scrollbar_size`
|
||||
self.expanded_size = options.expanded_size
|
||||
end
|
||||
|
||||
|
@ -121,7 +121,7 @@ function Scrollbar:_get_thumb_rect_normal()
|
|||
across_size = across_size + (expanded_scrollbar_size - scrollbar_size) * self.expand_percent
|
||||
return
|
||||
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,
|
||||
along_size
|
||||
end
|
||||
|
@ -238,8 +238,7 @@ end
|
|||
function Scrollbar:_on_mouse_moved_normal(x, y, dx, dy)
|
||||
if self.dragging then
|
||||
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 - along_size), 0, 1)
|
||||
return common.clamp((y - nr.along + self.drag_start_offset) / nr.along_size, 0, 1)
|
||||
end
|
||||
return self:_update_hover_status_normal(x, y)
|
||||
end
|
||||
|
@ -282,7 +281,7 @@ function Scrollbar:set_size(x, y, w, h, scrollable)
|
|||
end
|
||||
|
||||
---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)
|
||||
self.percent = percent
|
||||
end
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
-- this file is used by lite-xl to setup the Lua environment when starting
|
||||
VERSION = "2.1.2r1"
|
||||
MOD_VERSION_MAJOR = 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)
|
||||
VERSION = "2.1.3r1"
|
||||
MOD_VERSION = "3"
|
||||
|
||||
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)
|
||||
|
@ -25,7 +22,7 @@ package.path = DATADIR .. '/?/init.lua;' .. package.path
|
|||
package.path = USERDIR .. '/?.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 =
|
||||
USERDIR .. '/?.' .. ARCH .. "." .. suffix .. ";" ..
|
||||
USERDIR .. '/?/init.' .. ARCH .. "." .. suffix .. ";" ..
|
||||
|
|
|
@ -232,42 +232,15 @@ function StatusView:register_docview_items()
|
|||
return {
|
||||
style.text, line, ":",
|
||||
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,
|
||||
command = "doc:go-to-line",
|
||||
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({
|
||||
predicate = predicate_docview,
|
||||
name = "doc:indentation",
|
||||
|
@ -319,19 +292,6 @@ function StatusView:register_docview_items()
|
|||
end,
|
||||
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
|
||||
|
||||
|
||||
|
@ -1014,12 +974,6 @@ function StatusView:on_mouse_pressed(button, x, y, clicks)
|
|||
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)
|
||||
if not self.visible then return end
|
||||
StatusView.super.on_mouse_moved(self, x, y, dx, dy)
|
||||
|
|
|
@ -1,23 +1,13 @@
|
|||
local common = require "core.common"
|
||||
local style = {}
|
||||
|
||||
style.padding = { x = common.round(14 * SCALE), y = common.round(7 * SCALE) }
|
||||
style.divider_size = common.round(1 * SCALE)
|
||||
style.scrollbar_size = common.round(4 * SCALE)
|
||||
style.expanded_scrollbar_size = common.round(12 * SCALE)
|
||||
style.caret_width = common.round(2 * 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.
|
||||
-- It shoud be like the following:
|
||||
--
|
||||
|
@ -50,4 +40,3 @@ style.syntax_fonts = {}
|
|||
style.log = {}
|
||||
|
||||
return style
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ local common = require "core.common"
|
|||
local syntax = {}
|
||||
syntax.items = {}
|
||||
|
||||
syntax.plain_text_syntax = { name = "Plain Text", patterns = {}, symbols = {} }
|
||||
local plain_text_syntax = { name = "Plain Text", patterns = {}, symbols = {} }
|
||||
|
||||
|
||||
function syntax.add(t)
|
||||
|
@ -46,7 +46,7 @@ end
|
|||
function syntax.get(filename, header)
|
||||
return (filename and find(filename, "files"))
|
||||
or (header and find(header, "headers"))
|
||||
or syntax.plain_text_syntax
|
||||
or plain_text_syntax
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -112,12 +112,6 @@ function TitleView:on_mouse_pressed(button, x, y, clicks)
|
|||
end
|
||||
|
||||
|
||||
function TitleView:on_mouse_left()
|
||||
TitleView.super.on_mouse_left(self)
|
||||
self.hovered_item = nil
|
||||
end
|
||||
|
||||
|
||||
function TitleView:on_mouse_moved(px, py, ...)
|
||||
if self.size.y == 0 then return end
|
||||
TitleView.super.on_mouse_moved(self, px, py, ...)
|
||||
|
|
|
@ -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
|
||||
-- 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
|
||||
-- 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
|
||||
-- always follows a starting index after position 3 in the table.
|
||||
table.insert(find_results, find_results[2] + 1)
|
||||
|
@ -39,11 +42,9 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
|
|||
local fin = find_results[i + 1] - 1
|
||||
local type = pattern.type[i - 2]
|
||||
-- ↑ (i - 2) to convert from [3; n] to [1; n]
|
||||
if fin >= start then
|
||||
local text = full_text:usub(start, fin)
|
||||
push_token(t, syn.symbols[text] or type, text)
|
||||
end
|
||||
end
|
||||
else
|
||||
local start, fin = find_results[1], find_results[2]
|
||||
local text = full_text:usub(start, fin)
|
||||
|
@ -133,10 +134,8 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
local res
|
||||
local i = 1
|
||||
|
||||
state = state or string.char(0)
|
||||
|
||||
if #incoming_syntax.patterns == 0 then
|
||||
return { "normal", text }, state
|
||||
return { "normal", text }
|
||||
end
|
||||
|
||||
state = state or string.char(0)
|
||||
|
@ -243,7 +242,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
res[1] = char_pos_1
|
||||
res[2] = char_pos_2
|
||||
end
|
||||
if not res[1] then return end
|
||||
if res[1] and target[3] then
|
||||
-- Check to see if the escaped character is there,
|
||||
-- and if it is not itself escaped.
|
||||
|
@ -255,12 +253,12 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
if count % 2 == 0 then
|
||||
-- The match is not escaped, so confirm it
|
||||
break
|
||||
else
|
||||
-- The match is escaped, so avoid it
|
||||
res[1] = false
|
||||
elseif not close then
|
||||
-- The *open* match is escaped, so avoid it
|
||||
return
|
||||
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)
|
||||
end
|
||||
|
||||
|
@ -286,9 +284,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
if current_pattern_idx > 0 then
|
||||
local p = current_syntax.patterns[current_pattern_idx]
|
||||
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
|
||||
-- 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
|
||||
-- (as it's the syntax delimiter).
|
||||
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
|
||||
cont = false
|
||||
end
|
||||
|
@ -310,11 +305,11 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
-- continue on as normal.
|
||||
if cont 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)
|
||||
i = e + 1
|
||||
else
|
||||
push_token(res, token_type, text:usub(i))
|
||||
push_token(res, p.type, text:usub(i))
|
||||
break
|
||||
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
|
||||
-- just normally, upon finding a token.
|
||||
while subsyntax_info do
|
||||
local find_results = { find_text(text, subsyntax_info, i, true, true) }
|
||||
local s, e = find_results[1], find_results[2]
|
||||
local s, e = find_text(text, subsyntax_info, i, true, true)
|
||||
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.
|
||||
pop_subsyntax()
|
||||
i = e + 1
|
||||
|
|
|
@ -108,10 +108,6 @@ function View:get_h_scrollable_size()
|
|||
end
|
||||
|
||||
|
||||
function View:supports_text_input()
|
||||
return false
|
||||
end
|
||||
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@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)
|
||||
if result 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
|
||||
return true
|
||||
end
|
||||
result = self.h_scrollbar:on_mouse_pressed(button, x, y, clicks)
|
||||
if result 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
|
||||
return true
|
||||
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)
|
||||
if result 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
|
||||
self:clamp_scroll_position()
|
||||
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)
|
||||
if result 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
|
||||
self:clamp_scroll_position()
|
||||
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
|
||||
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 y
|
||||
|
@ -287,16 +266,12 @@ end
|
|||
function View:update_scrollbar()
|
||||
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)
|
||||
local v_percent = self.scroll.y/(v_scrollable - self.size.y)
|
||||
-- Avoid setting nan percent
|
||||
self.v_scrollbar:set_percent(v_percent == v_percent and v_percent or 0)
|
||||
self.v_scrollbar:set_percent(self.scroll.y/v_scrollable)
|
||||
self.v_scrollbar:update()
|
||||
|
||||
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)
|
||||
local h_percent = self.scroll.x/(h_scrollable - self.size.x)
|
||||
-- Avoid setting nan percent
|
||||
self.h_scrollbar:set_percent(h_percent == h_percent and h_percent or 0)
|
||||
self.h_scrollbar:set_percent(self.scroll.x/h_scrollable)
|
||||
self.h_scrollbar:update()
|
||||
end
|
||||
|
||||
|
|
|
@ -10,10 +10,6 @@ local RootView = require "core.rootview"
|
|||
local DocView = require "core.docview"
|
||||
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({
|
||||
-- Amount of characters that need to be written for autocomplete
|
||||
min_len = 3,
|
||||
|
@ -23,16 +19,8 @@ config.plugins.autocomplete = common.merge({
|
|||
max_suggestions = 100,
|
||||
-- Maximum amount of symbols to cache per document
|
||||
max_symbols = 4000,
|
||||
-- Which symbols to show on the suggestions list: global, local, related, none
|
||||
suggestions_scope = "global",
|
||||
-- Font size of the description box
|
||||
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
|
||||
config_spec = {
|
||||
name = "Autocomplete",
|
||||
|
@ -72,26 +60,6 @@ config.plugins.autocomplete = common.merge({
|
|||
min = 1000,
|
||||
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",
|
||||
description = "Font size of the description box.",
|
||||
|
@ -99,31 +67,6 @@ config.plugins.autocomplete = common.merge({
|
|||
type = "number",
|
||||
default = 12,
|
||||
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)
|
||||
|
@ -133,7 +76,6 @@ local autocomplete = {}
|
|||
autocomplete.map = {}
|
||||
autocomplete.map_manually = {}
|
||||
autocomplete.on_close = nil
|
||||
autocomplete.icons = {}
|
||||
|
||||
-- Flag that indicates if the autocomplete box was manually triggered
|
||||
-- with the autocomplete.complete() function to prevent the suggestions
|
||||
|
@ -153,7 +95,6 @@ function autocomplete.add(t, manually_triggered)
|
|||
{
|
||||
text = text,
|
||||
info = info.info,
|
||||
icon = info.icon, -- Name of icon to show
|
||||
desc = info.desc, -- Description shown on item selected
|
||||
onhover = info.onhover, -- A callback called once when item is hovered
|
||||
onselect = info.onselect, -- A callback called when item is selected
|
||||
|
@ -178,35 +119,28 @@ end
|
|||
--
|
||||
-- Thread that scans open document symbols and cache them
|
||||
--
|
||||
local global_symbols = {}
|
||||
local max_symbols = config.plugins.autocomplete.max_symbols
|
||||
|
||||
core.add_thread(function()
|
||||
local function load_syntax_symbols(doc)
|
||||
if doc.syntax and not autocomplete.map["language_"..doc.syntax.name] then
|
||||
local symbols = {
|
||||
name = "language_"..doc.syntax.name,
|
||||
files = doc.syntax.files,
|
||||
items = {}
|
||||
}
|
||||
for name, type in pairs(doc.syntax.symbols) do
|
||||
symbols.items[name] = type
|
||||
local cache = setmetatable({}, { __mode = "k" })
|
||||
|
||||
local function get_syntax_symbols(symbols, doc)
|
||||
if doc.syntax then
|
||||
for sym in pairs(doc.syntax.symbols) do
|
||||
symbols[sym] = true
|
||||
end
|
||||
autocomplete.add(symbols)
|
||||
return symbols.items
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
local function get_symbols(doc)
|
||||
local s = {}
|
||||
local syntax_symbols = load_syntax_symbols(doc)
|
||||
local max_symbols = config.plugins.autocomplete.max_symbols
|
||||
get_syntax_symbols(s, doc)
|
||||
if doc.disable_symbols then return s end
|
||||
local i = 1
|
||||
local symbols_count = 0
|
||||
while i <= #doc.lines 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
|
||||
if symbols_count > max_symbols then
|
||||
s = nil
|
||||
|
@ -252,18 +186,14 @@ core.add_thread(function()
|
|||
}
|
||||
end
|
||||
-- update symbol set with doc's symbol set
|
||||
if config.plugins.autocomplete.suggestions_scope == "global" then
|
||||
for sym in pairs(cache[doc].symbols) do
|
||||
symbols[sym] = true
|
||||
end
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
-- update global symbols list
|
||||
if config.plugins.autocomplete.suggestions_scope == "global" then
|
||||
global_symbols = symbols
|
||||
end
|
||||
-- update symbols list
|
||||
autocomplete.add { name = "open-docs", items = symbols }
|
||||
|
||||
-- wait for next scan
|
||||
local valid = true
|
||||
|
@ -310,50 +240,12 @@ local function update_suggestions()
|
|||
map = autocomplete.map_manually
|
||||
end
|
||||
|
||||
local assigned_sym = {}
|
||||
|
||||
-- get all relevant suggestions for given filename
|
||||
local items = {}
|
||||
for _, v in pairs(map) do
|
||||
if common.match_pattern(filename, v.files) then
|
||||
for _, item in pairs(v.items) do
|
||||
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
|
||||
|
@ -394,23 +286,13 @@ local function get_suggestions_rect(av)
|
|||
y = y + av:get_line_height() + style.padding.y
|
||||
local font = av:get_font()
|
||||
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
|
||||
for _, s in ipairs(suggestions) do
|
||||
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
|
||||
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)
|
||||
end
|
||||
|
||||
|
@ -437,8 +319,7 @@ local function get_suggestions_rect(av)
|
|||
x - style.padding.x,
|
||||
y - style.padding.y,
|
||||
max_width + style.padding.x * 2,
|
||||
max_items * (th + style.padding.y) + style.padding.y,
|
||||
has_icons
|
||||
max_items * (th + style.padding.y) + style.padding.y
|
||||
end
|
||||
|
||||
local function wrap_line(line, max_chars)
|
||||
|
@ -558,7 +439,7 @@ local function draw_suggestions_box(av)
|
|||
local ah = config.plugins.autocomplete.max_height
|
||||
|
||||
-- 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)
|
||||
|
||||
-- draw text
|
||||
|
@ -567,52 +448,17 @@ local function draw_suggestions_box(av)
|
|||
local y = ry + style.padding.y / 2
|
||||
local show_count = #suggestions <= ah and #suggestions or ah
|
||||
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
|
||||
if not suggestions[i] then
|
||||
break
|
||||
end
|
||||
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
|
||||
common.draw_text(
|
||||
font, color, s.text, "left",
|
||||
rx + icon_l_padding + style.padding.x, y, rw, lh
|
||||
)
|
||||
if s.info and not hide_info then
|
||||
common.draw_text(font, color, s.text, "left", rx + style.padding.x, y, rw, lh)
|
||||
if s.info then
|
||||
color = (i == suggestions_idx) and style.text or style.dim
|
||||
common.draw_text(
|
||||
style.font, color, s.info, "right",
|
||||
rx, y, rw - icon_r_padding - style.padding.x, lh
|
||||
)
|
||||
common.draw_text(style.font, color, s.info, "right", rx, y, rw - style.padding.x, lh)
|
||||
end
|
||||
y = y + lh
|
||||
if suggestions_idx == i then
|
||||
|
@ -773,31 +619,6 @@ function autocomplete.can_complete()
|
|||
return false
|
||||
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
|
||||
|
@ -811,6 +632,7 @@ command.add(predicate, {
|
|||
["autocomplete:complete"] = function(dv)
|
||||
local doc = dv.doc
|
||||
local item = suggestions[suggestions_idx]
|
||||
local text = item.text
|
||||
local inserted = false
|
||||
if item.onselect then
|
||||
inserted = item.onselect(suggestions_idx, item)
|
||||
|
|
|
@ -37,11 +37,7 @@ local function optimal_indent_from_stat(stat)
|
|||
elseif
|
||||
indent > stat[y]
|
||||
and
|
||||
(
|
||||
indent_occurrences_more_than_once(stat, y)
|
||||
or
|
||||
(y == count and stat[y] > 1)
|
||||
)
|
||||
then
|
||||
score = 0
|
||||
break
|
||||
|
@ -122,10 +118,10 @@ local function get_comment_patterns(syntax, _loop)
|
|||
end
|
||||
if type(pattern.regex) == "table" then
|
||||
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
|
||||
table.insert(comments, {"r", regex.compile(startp), r=startp})
|
||||
table.insert(comments, {"r", regex.compile(startp)})
|
||||
end
|
||||
end
|
||||
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]})
|
||||
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
|
||||
if #comments > 0 then
|
||||
return comments
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
-- mod-version:3
|
||||
|
||||
local core = require "core"
|
||||
local style = require "core.style"
|
||||
local DocView = require "core.docview"
|
||||
local common = require "core.common"
|
||||
|
@ -13,7 +12,6 @@ config.plugins.drawwhitespace = common.merge({
|
|||
show_leading = true,
|
||||
show_trailing = true,
|
||||
show_middle = true,
|
||||
show_selected_only = false,
|
||||
|
||||
show_middle_min = 1,
|
||||
|
||||
|
@ -67,13 +65,6 @@ config.plugins.drawwhitespace = common.merge({
|
|||
type = "toggle",
|
||||
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",
|
||||
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
|
||||
local tx = cache[i + 1] + x
|
||||
local tw = cache[i + 2]
|
||||
if tx <= x2 then
|
||||
local sub = cache[i]
|
||||
local color = cache[i + 3]
|
||||
local partials = {}
|
||||
if config.plugins.drawwhitespace.show_selected_only and self.doc:has_any_selection() then
|
||||
for _, l1, c1, l2, c2 in self.doc:get_selections(true) do
|
||||
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
|
||||
if tx + tw >= x1 then
|
||||
tx = renderer.draw_text(font, sub, tx, ty, color)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -128,7 +128,6 @@ syntax.add {
|
|||
{ pattern = { "```go", "```" }, type = "string", syntax = ".go" },
|
||||
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
|
||||
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
|
||||
{ pattern = { "```nix", "```" }, type = "string", syntax = ".nix" },
|
||||
{ pattern = { "```", "```" }, type = "string" },
|
||||
{ pattern = { "``", "``" }, type = "string" },
|
||||
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" },
|
||||
|
@ -267,4 +266,3 @@ core.add_thread(function()
|
|||
coroutine.yield(1)
|
||||
end
|
||||
end)
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ config.plugins.lineguide = common.merge({
|
|||
-- 120,
|
||||
config.line_limit
|
||||
},
|
||||
use_custom_color = false,
|
||||
custom_color = style.selection,
|
||||
-- The config specification used by gui generators
|
||||
config_spec = {
|
||||
name = "Line Guide",
|
||||
|
@ -65,21 +63,7 @@ config.plugins.lineguide = common.merge({
|
|||
end
|
||||
return new_rulers
|
||||
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)
|
||||
|
||||
|
@ -102,12 +86,10 @@ function DocView:draw_overlay(...)
|
|||
and
|
||||
self:is(DocView)
|
||||
then
|
||||
local conf = config.plugins.lineguide
|
||||
local line_x = self:get_line_screen_position(1)
|
||||
local character_width = self:get_font():get_width("n")
|
||||
local ruler_width = config.plugins.lineguide.width
|
||||
local ruler_color = conf.use_custom_color and conf.custom_color
|
||||
or (style.guide or style.selection)
|
||||
local ruler_color = style.guide or style.selection
|
||||
|
||||
for k,v in ipairs(config.plugins.lineguide.rulers) do
|
||||
local ruler = get_ruler(v)
|
||||
|
|
|
@ -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 })
|
||||
core.redraw = true
|
||||
end
|
||||
if n % 100 == 0 then coroutine.yield(0) end
|
||||
if n % 100 == 0 then coroutine.yield() end
|
||||
n = n + 1
|
||||
core.redraw = true
|
||||
end
|
||||
|
|
|
@ -48,7 +48,7 @@ end
|
|||
|
||||
function ToolbarView:get_icon_width()
|
||||
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
|
||||
end
|
||||
|
||||
|
@ -83,7 +83,7 @@ function ToolbarView:draw()
|
|||
|
||||
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
|
||||
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
|
||||
|
||||
|
@ -100,16 +100,6 @@ function ToolbarView:on_mouse_pressed(button, x, y, clicks)
|
|||
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, ...)
|
||||
if not self.visible then return end
|
||||
ToolbarView.super.on_mouse_moved(self, px, py, ...)
|
||||
|
|
|
@ -9,15 +9,10 @@ local View = require "core.view"
|
|||
local ContextMenu = require "core.contextmenu"
|
||||
local RootView = require "core.rootview"
|
||||
local CommandView = require "core.commandview"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
config.plugins.treeview = common.merge({
|
||||
-- Default treeview width
|
||||
size = 200 * SCALE,
|
||||
highlight_focused_file = true,
|
||||
expand_dirs_to_focused_file = false,
|
||||
scroll_to_focused_file = false,
|
||||
animate_scroll_to_focused_file = true
|
||||
size = 200 * SCALE
|
||||
}, config.plugins.treeview)
|
||||
|
||||
local tooltip_offset = style.font:get_height()
|
||||
|
@ -51,7 +46,7 @@ function TreeView:new()
|
|||
self.target_size = config.plugins.treeview.size
|
||||
self.cache = {}
|
||||
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_text_spacing = 0
|
||||
|
@ -174,71 +169,18 @@ function TreeView:each_item()
|
|||
end
|
||||
|
||||
|
||||
function TreeView:set_selection(selection, selection_y, center, instant)
|
||||
function TreeView:set_selection(selection, selection_y)
|
||||
self.selected_item = selection
|
||||
if selection and selection_y
|
||||
and (selection_y <= 0 or selection_y >= self.size.y) then
|
||||
|
||||
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
|
||||
end
|
||||
if center then
|
||||
selection_y = selection_y - (self.size.y - lh) / 2
|
||||
end
|
||||
local _, y = self:get_content_offset()
|
||||
self.scroll.to.y = 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
|
||||
self.scroll.to.y = selection and (selection_y - y)
|
||||
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
|
||||
|
||||
|
||||
|
@ -251,9 +193,10 @@ function TreeView:get_text_bounding_box(item, x, y, w, h)
|
|||
end
|
||||
|
||||
|
||||
|
||||
function TreeView:on_mouse_moved(px, py, ...)
|
||||
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
|
||||
-- mouse movement handled by the View (scrollbar)
|
||||
self.hovered_item = nil
|
||||
|
@ -280,12 +223,6 @@ function TreeView:on_mouse_moved(px, py, ...)
|
|||
end
|
||||
|
||||
|
||||
function TreeView:on_mouse_left()
|
||||
TreeView.super.on_mouse_left(self)
|
||||
self.hovered_item = nil
|
||||
end
|
||||
|
||||
|
||||
function TreeView:update()
|
||||
-- update width
|
||||
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")
|
||||
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
|
||||
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
|
||||
|
||||
-- this will make sure hovered_item is updated
|
||||
local dy = math.abs(self.last_scroll_y - self.scroll.y)
|
||||
if dy > 0 then
|
||||
self:on_mouse_moved(core.root_view.mouse.x, core.root_view.mouse.y, 0, 0)
|
||||
self.last_scroll_y = self.scroll.y
|
||||
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
|
||||
-- we don't want events when the thing is scrolling fast
|
||||
local dy = math.abs(self.scroll.to.y - self.scroll.y)
|
||||
if self.scroll.to.y ~= 0 and dy < self:get_item_height() then
|
||||
self:on_mouse_moved(self.cursor_pos.x, self.cursor_pos.y, 0, 0)
|
||||
end
|
||||
|
||||
TreeView.super.update(self)
|
||||
|
@ -505,8 +422,8 @@ function TreeView:get_previous(item)
|
|||
end
|
||||
|
||||
|
||||
function TreeView:toggle_expand(toggle, item)
|
||||
item = item or self.selected_item
|
||||
function TreeView:toggle_expand(toggle)
|
||||
local item = self.selected_item
|
||||
|
||||
if not item then return end
|
||||
|
||||
|
@ -524,11 +441,6 @@ function TreeView:toggle_expand(toggle, item)
|
|||
end
|
||||
|
||||
|
||||
function TreeView:open_doc(filename)
|
||||
core.root_view:open_doc(core.open_doc(filename))
|
||||
end
|
||||
|
||||
|
||||
-- init
|
||||
local view = TreeView()
|
||||
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
|
||||
core.set_active_view(core.last_active_view)
|
||||
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,
|
||||
|
@ -753,26 +666,6 @@ command.add(
|
|||
view:toggle_expand(true)
|
||||
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+")
|
||||
file:write("")
|
||||
file:close()
|
||||
view:open_doc(doc_filename)
|
||||
core.root_view:open_doc(core.open_doc(doc_filename))
|
||||
core.log("Created %s", doc_filename)
|
||||
end,
|
||||
suggest = function(text)
|
||||
|
@ -896,12 +789,10 @@ command.add(
|
|||
["treeview:open-in-system"] = function(item)
|
||||
if PLATFORM == "Windows" then
|
||||
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))
|
||||
elseif PLATFORM == "Linux" or string.find(PLATFORM, "BSD") then
|
||||
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
|
||||
})
|
||||
|
@ -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 {
|
||||
["ctrl+\\"] = "treeview:toggle",
|
||||
["up"] = "treeview:previous",
|
||||
|
@ -969,15 +841,6 @@ keymap.add {
|
|||
["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
|
||||
config.plugins.treeview.config_spec = {
|
||||
name = "Treeview",
|
||||
|
|
|
@ -208,10 +208,10 @@ else
|
|||
install_data('resources/icons/lite-xl.svg',
|
||||
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_data('resources/linux/com.lite_xl.LiteXL.appdata.xml',
|
||||
install_data('resources/linux/org.lite_xl.lite_xl.appdata.xml',
|
||||
install_dir : 'share/metainfo'
|
||||
)
|
||||
endif
|
||||
|
|
|
@ -138,7 +138,7 @@ generate_appimage() {
|
|||
mv AppRun LiteXL.AppDir/
|
||||
# These could be symlinks but it seems they doesn't work with AppimageLauncher
|
||||
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
|
||||
addons_download "${BUILD_DIR}"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#define MyAppName "Lite XL"
|
||||
#define MyAppVersion "@PROJECT_VERSION@"
|
||||
#define MyAppPublisher "Lite XL Team"
|
||||
#define MyAppURL "https://lite-xl.github.io"
|
||||
#define MyAppURL "https://lite-xl.com"
|
||||
#define MyAppExeName "lite-xl.exe"
|
||||
#define BuildDir "@PROJECT_BUILD_DIR@"
|
||||
#define SourceDir "@PROJECT_SOURCE_DIR@"
|
||||
|
@ -69,9 +69,7 @@ Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescrip
|
|||
Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked
|
||||
|
||||
[Files]
|
||||
Source: "{#BuildDir}/src/lite-xl.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#BuildDir}/mingwLibs{#Arch}/*"; DestDir: "{app}"; Flags: ignoreversion ; Check: DirExists(ExpandConstant('{#BuildDir}/mingwLibs{#Arch}'))
|
||||
Source: "{#SourceDir}/data/*"; DestDir: "{app}/data"; Flags: ignoreversion recursesubdirs
|
||||
Source: "{#SourceDir}/lite-xl/*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
|
||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||
|
||||
[Icons]
|
||||
|
|
|
@ -29,3 +29,4 @@ void api_load_libs(lua_State *L) {
|
|||
for (int i = 0; libs[i].name; i++)
|
||||
luaL_requiref(L, libs[i].name, libs[i].func, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -391,4 +391,3 @@ int luaopen_renderer(lua_State *L) {
|
|||
lua_setfield(L, -2, "font");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -369,21 +369,6 @@ top:
|
|||
lua_pushinteger(L, (lua_Integer)(e.tfinger.dy * h));
|
||||
lua_pushinteger(L, e.tfinger.fingerId);
|
||||
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:
|
||||
goto top;
|
||||
|
@ -1087,7 +1072,6 @@ static int f_path_compare(lua_State *L) {
|
|||
size_t offset = 0, i, j;
|
||||
for (i = 0; i < len1 && i < len2; i++) {
|
||||
if (path1[i] != path2[i]) break;
|
||||
if (isdigit(path1[i])) break;
|
||||
if (path1[i] == PATHSEP) {
|
||||
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[] = {
|
||||
{ "poll_event", f_poll_event },
|
||||
{ "wait_event", f_wait_event },
|
||||
|
@ -1200,7 +1175,6 @@ static const luaL_Reg lib[] = {
|
|||
{ "load_native_plugin", f_load_native_plugin },
|
||||
{ "path_compare", f_path_compare },
|
||||
{ "get_fs_type", f_get_fs_type },
|
||||
{ "text_input", f_text_input },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
@ -1212,4 +1186,3 @@ int luaopen_system(lua_State *L) {
|
|||
luaL_newlib(L, lib);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
18
src/main.c
18
src/main.c
|
@ -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
|
||||
#if defined(__x86_64__) || defined(_M_AMD64) || defined(__MINGW64__)
|
||||
#define ARCH_PROCESSOR "x86_64"
|
||||
#elif __arm__
|
||||
#define ARCH_PROCESSOR "arm"
|
||||
#elif defined(__amigaos4__) || defined(__morphos__)
|
||||
#define ARCH_PROCESSOR "ppc"
|
||||
#elif defined(__i386__) || defined(_M_IX86) || defined(__MINGW32__)
|
||||
|
@ -157,7 +155,6 @@ int main(int argc, char **argv) {
|
|||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||||
fprintf(stderr, "Error initializing sdl: %s", SDL_GetError());
|
||||
exit(1);
|
||||
|
@ -209,7 +206,7 @@ int main(int argc, char **argv) {
|
|||
fprintf(stderr, "Error creating lite-xl window: %s", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
window_renderer = ren_init(window);
|
||||
ren_init(window);
|
||||
|
||||
lua_State *L;
|
||||
init_lua:
|
||||
|
@ -269,23 +266,22 @@ init_lua:
|
|||
" core.init()\n"
|
||||
" core.run()\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(debug.traceback(nil, 2)..'\\n')\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"
|
||||
" 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(debug.traceback(nil, 2)..'\\n')\n"
|
||||
" fp:write(debug.traceback(nil, 4)..'\\n')\n"
|
||||
" fp:close()\n"
|
||||
" error_path = system.absolute_path(error_path)\n"
|
||||
" end\n"
|
||||
" system.show_fatal_error('Lite XL internal error',\n"
|
||||
" 'An internal error occurred in a critical part of the application.\\n\\n'..\n"
|
||||
" 'Error: '..tostring(err)..'\\n\\n'..\n"
|
||||
" 'Details can be found in \\\"'..error_path..'\\\"')\n"
|
||||
" 'Please verify the file \\\"error.txt\\\" in the directory '..error_dir)\n"
|
||||
" os.exit(1)\n"
|
||||
"end)\n"
|
||||
"return core and core.restart_request\n";
|
||||
|
|
|
@ -14,4 +14,3 @@ void rencache_begin_frame(RenWindow *window_renderer);
|
|||
void rencache_end_frame(RenWindow *window_renderer);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -515,7 +515,7 @@ void ren_init(SDL_Window *win) {
|
|||
int error = FT_Init_FreeType( &library );
|
||||
if ( error ) {
|
||||
fprintf(stderr, "internal font error when starting the application\n");
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
window_renderer.window = win;
|
||||
renwin_init_surface(&window_renderer);
|
||||
|
@ -523,23 +523,6 @@ void ren_init(SDL_Window *win) {
|
|||
renwin_clip_to_surface(&window_renderer);
|
||||
draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32,
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -149,4 +149,3 @@ void renwin_free(RenWindow *ren) {
|
|||
SDL_FreeSurface(ren->rensurface.surface);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
[wrap-file]
|
||||
directory = lua-5.4.6
|
||||
source_url = https://www.lua.org/ftp/lua-5.4.6.tar.gz
|
||||
source_filename = lua-5.4.6.tar.gz
|
||||
source_hash = 7d5ea1b9cb6aa0b59ca3dde1c6adcb57ef83a1ba8e5432c0ecd06bf439b3ad88
|
||||
patch_filename = lua_5.4.6-3_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.6-3/get_patch
|
||||
patch_hash = 9b72a95422fd47f79f969d9abdb589ee95712d5512a5246f94e7e4f63d2cb7b7
|
||||
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
|
||||
directory = lua-5.4.4
|
||||
source_url = https://www.lua.org/ftp/lua-5.4.4.tar.gz
|
||||
source_filename = lua-5.4.4.tar.gz
|
||||
source_hash = 164c7849653b80ae67bec4b7473b884bf5cc8d2dca05653475ec2ed27b9ebf61
|
||||
patch_filename = lua_5.4.4-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.4-1/get_patch
|
||||
patch_hash = e61cd965c629d6543176f41a9f1cb9050edfd1566cf00ce768ff211086e40bdc
|
||||
|
||||
[provide]
|
||||
lua-5.4 = lua_dep
|
||||
lua = lua_dep
|
||||
|
||||
|
|
Loading…
Reference in New Issue