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
|
# Lite XL
|
||||||
|
|
||||||
[![CI]](https://github.com/lite-xl/lite-xl/actions/workflows/build.yml)
|
[![CI]](https://github.com/lite-xl/lite-xl/actions/workflows/build.yml)
|
||||||
[![Discord Badge Image]](https://discord.gg/UQKnzBhY5H)
|
[![Discord Badge Image]](https://discord.gg/RWzqC3nx7K)
|
||||||
|
|
||||||
![screenshot-dark]
|
![screenshot-dark]
|
||||||
|
|
||||||
|
@ -124,7 +124,6 @@ cd lite-xl
|
||||||
```
|
```
|
||||||
|
|
||||||
To run lite-xl without installing:
|
To run lite-xl without installing:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./lite-xl
|
./lite-xl
|
||||||
```
|
```
|
||||||
|
@ -137,68 +136,31 @@ mkdir -p $HOME/.local/bin && cp lite-xl $HOME/.local/bin/
|
||||||
mkdir -p $HOME/.local/share/lite-xl && cp -r data/* $HOME/.local/share/lite-xl/
|
mkdir -p $HOME/.local/share/lite-xl && cp -r data/* $HOME/.local/share/lite-xl/
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Add Lite XL to PATH
|
|
||||||
|
|
||||||
To run Lite XL from the command line, you must add it to PATH.
|
|
||||||
|
|
||||||
If `$HOME/.local/bin` is not in PATH:
|
If `$HOME/.local/bin` is not in PATH:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
echo -e 'export PATH=$PATH:$HOME/.local/bin' >> $HOME/.bashrc
|
echo -e 'export PATH=$PATH:$HOME/.local/bin' >> $HOME/.bashrc
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively on recent versions of GNOME and KDE Plasma,
|
To get the icon to show up in app launcher:
|
||||||
you can add `$HOME/.local/bin` to PATH via `~/.config/environment.d/envvars.conf`:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
PATH=$HOME/.local/bin:$PATH
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
> Some systems might not load `.bashrc` when logging in.
|
|
||||||
> This can cause problems with launching applications from the desktop / menu.
|
|
||||||
|
|
||||||
#### Add Lite XL to application launchers
|
|
||||||
|
|
||||||
To get the icon to show up in app launcher, you need to create a desktop
|
|
||||||
entry and put it into `/usr/share/applications` or `~/.local/share/applications`.
|
|
||||||
|
|
||||||
Here is an example for a desktop entry in `~/.local/share/applications/com.lite_xl.LiteXL.desktop`,
|
|
||||||
assuming Lite XL is in PATH:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Name=Lite XL
|
|
||||||
Comment=A lightweight text editor written in Lua
|
|
||||||
Exec=lite-xl %F
|
|
||||||
Icon=lite-xl
|
|
||||||
Terminal=false
|
|
||||||
StartupWMClass=lite-xl
|
|
||||||
Categories=Development;IDE;
|
|
||||||
MimeType=text/plain;inode/directory;
|
|
||||||
```
|
|
||||||
|
|
||||||
To get the icon to show up in app launcher immediately, run:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
xdg-desktop-menu forceupdate
|
xdg-desktop-menu forceupdate
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, you may log out and log in again.
|
You may need to logout and login again to see icon in app launcher.
|
||||||
|
|
||||||
#### Uninstall
|
To uninstall just run:
|
||||||
|
|
||||||
To uninstall Lite XL, run:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
rm -f $HOME/.local/bin/lite-xl
|
rm -f $HOME/.local/bin/lite-xl
|
||||||
rm -rf $HOME/.local/share/icons/hicolor/scalable/apps/lite-xl.svg \
|
rm -rf $HOME/.local/share/icons/hicolor/scalable/apps/lite-xl.svg \
|
||||||
$HOME/.local/share/applications/com.lite_xl.LiteXL.desktop \
|
$HOME/.local/share/applications/org.lite_xl.lite_xl.desktop \
|
||||||
$HOME/.local/share/metainfo/com.lite_xl.LiteXL.appdata.xml \
|
$HOME/.local/share/metainfo/org.lite_xl.lite_xl.appdata.xml \
|
||||||
$HOME/.local/share/lite-xl
|
$HOME/.local/share/lite-xl
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Any additional functionality that can be added through a plugin should be done
|
Any additional functionality that can be added through a plugin should be done
|
||||||
|
|
|
@ -363,7 +363,7 @@ local commands = {
|
||||||
["doc:select-lines"] = function(dv)
|
["doc:select-lines"] = function(dv)
|
||||||
for idx, line1, _, line2 in dv.doc:get_selections(true) do
|
for idx, line1, _, line2 in dv.doc:get_selections(true) do
|
||||||
append_line_if_last_line(line2)
|
append_line_if_last_line(line2)
|
||||||
dv.doc:set_selections(idx, line2 + 1, 1, line1, 1)
|
dv.doc:set_selections(idx, line1, 1, line2 + 1, 1)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
@ -548,11 +548,6 @@ local commands = {
|
||||||
dv.doc.crlf = not dv.doc.crlf
|
dv.doc.crlf = not dv.doc.crlf
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["doc:toggle-overwrite"] = function(dv)
|
|
||||||
dv.doc.overwrite = not dv.doc.overwrite
|
|
||||||
core.blink_reset() -- to show the cursor has changed edit modes
|
|
||||||
end,
|
|
||||||
|
|
||||||
["doc:save-as"] = function(dv)
|
["doc:save-as"] = function(dv)
|
||||||
local last_doc = core.last_active_view and core.last_active_view.doc
|
local last_doc = core.last_active_view and core.last_active_view.doc
|
||||||
local text
|
local text
|
||||||
|
|
|
@ -196,23 +196,6 @@ local function select_next(reverse)
|
||||||
if l2 then doc():set_selection(l2, c2, l1, c1) end
|
if l2 then doc():set_selection(l2, c2, l1, c1) end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param in_selection? boolean whether to replace in the selections only, or in the whole file.
|
|
||||||
local function find_replace(in_selection)
|
|
||||||
local l1, c1, l2, c2 = doc():get_selection()
|
|
||||||
local selected_text = ""
|
|
||||||
if not in_selection then
|
|
||||||
selected_text = doc():get_text(l1, c1, l2, c2)
|
|
||||||
doc():set_selection(l2, c2, l2, c2)
|
|
||||||
end
|
|
||||||
replace("Text", l1 == l2 and selected_text or "", function(text, old, new)
|
|
||||||
if not find_regex then
|
|
||||||
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
|
|
||||||
end
|
|
||||||
local result, matches = regex.gsub(regex.compile(old, "m"), text, new)
|
|
||||||
return result, matches
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
command.add(has_unique_selection, {
|
command.add(has_unique_selection, {
|
||||||
["find-replace:select-next"] = select_next,
|
["find-replace:select-next"] = select_next,
|
||||||
["find-replace:select-previous"] = function() select_next(true) end,
|
["find-replace:select-previous"] = function() select_next(true) end,
|
||||||
|
@ -229,11 +212,15 @@ command.add("core.docview!", {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["find-replace:replace"] = function()
|
["find-replace:replace"] = function()
|
||||||
find_replace()
|
local l1, c1, l2, c2 = doc():get_selection()
|
||||||
end,
|
local selected_text = doc():get_text(l1, c1, l2, c2)
|
||||||
|
replace("Text", l1 == l2 and selected_text or "", function(text, old, new)
|
||||||
["find-replace:replace-in-selection"] = function()
|
if not find_regex then
|
||||||
find_replace(true)
|
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
|
||||||
|
end
|
||||||
|
local result, matches = regex.gsub(regex.compile(old, "m"), text, new)
|
||||||
|
return result, matches
|
||||||
|
end)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["find-replace:replace-symbol"] = function()
|
["find-replace:replace-symbol"] = function()
|
||||||
|
|
|
@ -4,7 +4,6 @@ local DocView = require "core.docview"
|
||||||
local command = require "core.command"
|
local command = require "core.command"
|
||||||
local common = require "core.common"
|
local common = require "core.common"
|
||||||
local config = require "core.config"
|
local config = require "core.config"
|
||||||
local Node = require "core.node"
|
|
||||||
|
|
||||||
|
|
||||||
local t = {
|
local t = {
|
||||||
|
@ -30,6 +29,20 @@ local t = {
|
||||||
core.confirm_close_docs(docs, core.root_view.close_all_docviews, core.root_view, true)
|
core.confirm_close_docs(docs, core.root_view.close_all_docviews, core.root_view, true)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
["root:switch-to-previous-tab"] = function(node)
|
||||||
|
local idx = node:get_view_idx(core.active_view)
|
||||||
|
idx = idx - 1
|
||||||
|
if idx < 1 then idx = #node.views end
|
||||||
|
node:set_active_view(node.views[idx])
|
||||||
|
end,
|
||||||
|
|
||||||
|
["root:switch-to-next-tab"] = function(node)
|
||||||
|
local idx = node:get_view_idx(core.active_view)
|
||||||
|
idx = idx + 1
|
||||||
|
if idx > #node.views then idx = 1 end
|
||||||
|
node:set_active_view(node.views[idx])
|
||||||
|
end,
|
||||||
|
|
||||||
["root:move-tab-left"] = function(node)
|
["root:move-tab-left"] = function(node)
|
||||||
local idx = node:get_view_idx(core.active_view)
|
local idx = node:get_view_idx(core.active_view)
|
||||||
if idx > 1 then
|
if idx > 1 then
|
||||||
|
@ -104,7 +117,7 @@ end, t)
|
||||||
|
|
||||||
command.add(nil, {
|
command.add(nil, {
|
||||||
["root:scroll"] = function(delta)
|
["root:scroll"] = function(delta)
|
||||||
local view = core.root_view.overlapping_view or core.active_view
|
local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view
|
||||||
if view and view.scrollable then
|
if view and view.scrollable then
|
||||||
view.scroll.to.y = view.scroll.to.y + delta * -config.mouse_wheel_scroll
|
view.scroll.to.y = view.scroll.to.y + delta * -config.mouse_wheel_scroll
|
||||||
return true
|
return true
|
||||||
|
@ -112,7 +125,7 @@ command.add(nil, {
|
||||||
return false
|
return false
|
||||||
end,
|
end,
|
||||||
["root:horizontal-scroll"] = function(delta)
|
["root:horizontal-scroll"] = function(delta)
|
||||||
local view = core.root_view.overlapping_view or core.active_view
|
local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view
|
||||||
if view and view.scrollable then
|
if view and view.scrollable then
|
||||||
view.scroll.to.x = view.scroll.to.x + delta * -config.mouse_wheel_scroll
|
view.scroll.to.x = view.scroll.to.x + delta * -config.mouse_wheel_scroll
|
||||||
return true
|
return true
|
||||||
|
@ -120,74 +133,3 @@ command.add(nil, {
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
command.add(function(node)
|
|
||||||
if not Node:is_extended_by(node) then node = nil end
|
|
||||||
-- No node was specified, use the active one
|
|
||||||
node = node or core.root_view:get_active_node()
|
|
||||||
if not node then return false end
|
|
||||||
return true, node
|
|
||||||
end,
|
|
||||||
{
|
|
||||||
["root:switch-to-previous-tab"] = function(node)
|
|
||||||
local idx = node:get_view_idx(node.active_view)
|
|
||||||
idx = idx - 1
|
|
||||||
if idx < 1 then idx = #node.views end
|
|
||||||
node:set_active_view(node.views[idx])
|
|
||||||
end,
|
|
||||||
|
|
||||||
["root:switch-to-next-tab"] = function(node)
|
|
||||||
local idx = node:get_view_idx(node.active_view)
|
|
||||||
idx = idx + 1
|
|
||||||
if idx > #node.views then idx = 1 end
|
|
||||||
node:set_active_view(node.views[idx])
|
|
||||||
end,
|
|
||||||
|
|
||||||
["root:scroll-tabs-backward"] = function(node)
|
|
||||||
node:scroll_tabs(1)
|
|
||||||
end,
|
|
||||||
|
|
||||||
["root:scroll-tabs-forward"] = function(node)
|
|
||||||
node:scroll_tabs(2)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
command.add(function()
|
|
||||||
local node = core.root_view.root_node:get_child_overlapping_point(core.root_view.mouse.x, core.root_view.mouse.y)
|
|
||||||
if not node then return false end
|
|
||||||
return (node.hovered_tab or node.hovered_scroll_button > 0) and true, node
|
|
||||||
end,
|
|
||||||
{
|
|
||||||
["root:switch-to-hovered-previous-tab"] = function(node)
|
|
||||||
command.perform("root:switch-to-previous-tab", node)
|
|
||||||
end,
|
|
||||||
|
|
||||||
["root:switch-to-hovered-next-tab"] = function(node)
|
|
||||||
command.perform("root:switch-to-next-tab", node)
|
|
||||||
end,
|
|
||||||
|
|
||||||
["root:scroll-hovered-tabs-backward"] = function(node)
|
|
||||||
command.perform("root:scroll-tabs-backward", node)
|
|
||||||
end,
|
|
||||||
|
|
||||||
["root:scroll-hovered-tabs-forward"] = function(node)
|
|
||||||
command.perform("root:scroll-tabs-forward", node)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
-- double clicking the tab bar, or on the emptyview should open a new doc
|
|
||||||
command.add(function(x, y)
|
|
||||||
local node = x and y and core.root_view.root_node:get_child_overlapping_point(x, y)
|
|
||||||
return node and node:is_in_tab_area(x, y)
|
|
||||||
end, {
|
|
||||||
["tabbar:new-doc"] = function()
|
|
||||||
command.perform("core:new-doc")
|
|
||||||
end
|
|
||||||
})
|
|
||||||
command.add("core.emptyview", {
|
|
||||||
["emptyview:new-doc"] = function()
|
|
||||||
command.perform("core:new-doc")
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
|
@ -84,11 +84,6 @@ function CommandView:get_line_screen_position(line, col)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function CommandView:supports_text_input()
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function CommandView:get_scrollable_size()
|
function CommandView:get_scrollable_size()
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,71 +2,15 @@ local common = require "core.common"
|
||||||
|
|
||||||
local config = {}
|
local config = {}
|
||||||
|
|
||||||
---The frame rate of Lite XL.
|
|
||||||
---Note that setting this value to the screen's refresh rate
|
|
||||||
---does not eliminate screen tearing.
|
|
||||||
---
|
|
||||||
---Defaults to 60.
|
|
||||||
---@type number
|
|
||||||
config.fps = 60
|
config.fps = 60
|
||||||
|
|
||||||
---Maximum number of log items that will be stored.
|
|
||||||
---When the number of log items exceed this value, old items will be discarded.
|
|
||||||
---
|
|
||||||
---Defaults to 800.
|
|
||||||
---@type number
|
|
||||||
config.max_log_items = 800
|
config.max_log_items = 800
|
||||||
|
|
||||||
---The timeout, in seconds, before a message dissapears from StatusView.
|
|
||||||
---
|
|
||||||
---Defaults to 5.
|
|
||||||
---@type number
|
|
||||||
config.message_timeout = 5
|
config.message_timeout = 5
|
||||||
|
|
||||||
---The number of pixels scrolled per-step.
|
|
||||||
---
|
|
||||||
---Defaults to 50 * SCALE.
|
|
||||||
---@type number
|
|
||||||
config.mouse_wheel_scroll = 50 * SCALE
|
config.mouse_wheel_scroll = 50 * SCALE
|
||||||
|
|
||||||
---Enables/disables transitions when scrolling with the scrollbar.
|
|
||||||
---When enabled, the scrollbar will have inertia and slowly move towards the cursor.
|
|
||||||
---Otherwise, the scrollbar will immediately follow the cursor.
|
|
||||||
---
|
|
||||||
---Defaults to false.
|
|
||||||
---@type boolean
|
|
||||||
config.animate_drag_scroll = false
|
config.animate_drag_scroll = false
|
||||||
|
|
||||||
---Enables/disables scrolling past the end of a document.
|
|
||||||
---
|
|
||||||
---Defaults to true.
|
|
||||||
---@type boolean
|
|
||||||
config.scroll_past_end = true
|
config.scroll_past_end = true
|
||||||
|
---@type "expanded" | "contracted" | false @Force the scrollbar status of the DocView
|
||||||
---@alias config.scrollbartype
|
|
||||||
---| "expanded" # A thicker scrollbar is shown at all times.
|
|
||||||
---| "contracted" # A thinner scrollbar is shown at all times.
|
|
||||||
---| false # The scrollbar expands when the cursor hovers over it.
|
|
||||||
|
|
||||||
---Controls whether the DocView scrollbar is always shown or hidden.
|
|
||||||
---This option does not affect other View's scrollbars.
|
|
||||||
---
|
|
||||||
---Defaults to false.
|
|
||||||
---@type config.scrollbartype
|
|
||||||
config.force_scrollbar_status = false
|
config.force_scrollbar_status = false
|
||||||
|
|
||||||
---The file size limit, in megabytes.
|
|
||||||
---Files larger than this size will not be shown in the file picker.
|
|
||||||
---
|
|
||||||
---Defaults to 10.
|
|
||||||
---@type number
|
|
||||||
config.file_size_limit = 10
|
config.file_size_limit = 10
|
||||||
|
|
||||||
---A list of files and directories to ignore.
|
|
||||||
---Each element is a Lua pattern, where patterns ending with a forward slash
|
|
||||||
---are recognized as directories while patterns ending with an anchor ("$") are
|
|
||||||
---recognized as files.
|
|
||||||
---@type string[]
|
|
||||||
config.ignore_files = {
|
config.ignore_files = {
|
||||||
-- folders
|
-- folders
|
||||||
"^%.svn/", "^%.git/", "^%.hg/", "^CVS/", "^%.Trash/", "^%.Trash%-.*/",
|
"^%.svn/", "^%.git/", "^%.hg/", "^CVS/", "^%.Trash/", "^%.Trash%-.*/",
|
||||||
|
@ -77,181 +21,40 @@ config.ignore_files = {
|
||||||
"%.suo$", "%.pdb$", "%.idb$", "%.class$", "%.psd$", "%.db$",
|
"%.suo$", "%.pdb$", "%.idb$", "%.class$", "%.psd$", "%.db$",
|
||||||
"^desktop%.ini$", "^%.DS_Store$", "^%.directory$",
|
"^desktop%.ini$", "^%.DS_Store$", "^%.directory$",
|
||||||
}
|
}
|
||||||
|
|
||||||
---Lua pattern used to find symbols when advanced syntax highlighting
|
|
||||||
---is not available.
|
|
||||||
---This pattern is also used for navigation, e.g. move to next word.
|
|
||||||
---
|
|
||||||
---The default pattern matches all letters, followed by any number
|
|
||||||
---of letters and digits.
|
|
||||||
---@type string
|
|
||||||
config.symbol_pattern = "[%a_][%w_]*"
|
config.symbol_pattern = "[%a_][%w_]*"
|
||||||
|
|
||||||
---A list of characters that delimits a word.
|
|
||||||
---
|
|
||||||
---The default is ``" \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"``
|
|
||||||
---@type string
|
|
||||||
config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
|
config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
|
||||||
|
|
||||||
---The timeout, in seconds, before several consecutive actions
|
|
||||||
---are merged as a single undo step.
|
|
||||||
---
|
|
||||||
---The default is 0.3 seconds.
|
|
||||||
---@type number
|
|
||||||
config.undo_merge_timeout = 0.3
|
config.undo_merge_timeout = 0.3
|
||||||
|
|
||||||
---The maximum number of undo steps per-document.
|
|
||||||
---
|
|
||||||
---The default is 10000.
|
|
||||||
---@type number
|
|
||||||
config.max_undos = 10000
|
config.max_undos = 10000
|
||||||
|
|
||||||
---The maximum number of tabs shown at a time.
|
|
||||||
---
|
|
||||||
---The default is 8.
|
|
||||||
---@type number
|
|
||||||
config.max_tabs = 8
|
config.max_tabs = 8
|
||||||
|
|
||||||
---Shows/hides the tab bar when there is only one tab open.
|
|
||||||
---
|
|
||||||
---The tab bar is always shown by default.
|
|
||||||
---@type boolean
|
|
||||||
config.always_show_tabs = true
|
config.always_show_tabs = true
|
||||||
|
-- Possible values: false, true, "no_selection"
|
||||||
---@alias config.highlightlinetype
|
|
||||||
---| true # Always highlight the current line.
|
|
||||||
---| false # Never highlight the current line.
|
|
||||||
---| "no_selection" # Highlight the current line if no text is selected.
|
|
||||||
|
|
||||||
---Highlights the current line.
|
|
||||||
---
|
|
||||||
---The default is true.
|
|
||||||
---@type config.highlightlinetype
|
|
||||||
config.highlight_current_line = true
|
config.highlight_current_line = true
|
||||||
|
|
||||||
---The spacing between each line of text.
|
|
||||||
---
|
|
||||||
---The default is 120% of the height of the text (1.2).
|
|
||||||
---@type number
|
|
||||||
config.line_height = 1.2
|
config.line_height = 1.2
|
||||||
|
|
||||||
---The number of spaces each level of indentation represents.
|
|
||||||
---
|
|
||||||
---The default is 2.
|
|
||||||
---@type number
|
|
||||||
config.indent_size = 2
|
config.indent_size = 2
|
||||||
|
|
||||||
---The type of indentation.
|
|
||||||
---
|
|
||||||
---The default is "soft" (spaces).
|
|
||||||
---@type "soft" | "hard"
|
|
||||||
config.tab_type = "soft"
|
config.tab_type = "soft"
|
||||||
|
|
||||||
---Do not remove whitespaces when advancing to the next line.
|
|
||||||
---
|
|
||||||
---Defaults to false.
|
|
||||||
---@type boolean
|
|
||||||
config.keep_newline_whitespace = false
|
config.keep_newline_whitespace = false
|
||||||
|
|
||||||
---Default line endings for new files.
|
|
||||||
---
|
|
||||||
---Defaults to `crlf` (`\r\n`) on Windows and `lf` (`\n`) on everything else.
|
|
||||||
---@type "crlf" | "lf"
|
|
||||||
config.line_endings = PLATFORM == "Windows" and "crlf" or "lf"
|
|
||||||
|
|
||||||
---Maximum number of characters per-line for the line guide.
|
|
||||||
---
|
|
||||||
---Defaults to 80.
|
|
||||||
---@type number
|
|
||||||
config.line_limit = 80
|
config.line_limit = 80
|
||||||
|
|
||||||
---Maximum number of project files to keep track of.
|
|
||||||
---If the number of files in the project exceeds this number,
|
|
||||||
---Lite XL will not be able to keep track of them.
|
|
||||||
---They will be not be searched when searching for files or text.
|
|
||||||
---
|
|
||||||
---Defaults to 2000.
|
|
||||||
---@type number
|
|
||||||
config.max_project_files = 2000
|
config.max_project_files = 2000
|
||||||
|
|
||||||
---Enables/disables all transitions.
|
|
||||||
---
|
|
||||||
---Defaults to true.
|
|
||||||
---@type boolean
|
|
||||||
config.transitions = true
|
config.transitions = true
|
||||||
|
|
||||||
---Enable/disable individual transitions.
|
|
||||||
---These values are overriden by `config.transitions`.
|
|
||||||
config.disabled_transitions = {
|
config.disabled_transitions = {
|
||||||
---Disables scrolling transitions.
|
|
||||||
scroll = false,
|
scroll = false,
|
||||||
---Disables transitions for CommandView's suggestions list.
|
|
||||||
commandview = false,
|
commandview = false,
|
||||||
---Disables transitions for showing/hiding the context menu.
|
|
||||||
contextmenu = false,
|
contextmenu = false,
|
||||||
---Disables transitions when clicking on log items in LogView.
|
|
||||||
logview = false,
|
logview = false,
|
||||||
---Disables transitions for showing/hiding the Nagbar.
|
|
||||||
nagbar = false,
|
nagbar = false,
|
||||||
---Disables transitions when scrolling the tab bar.
|
|
||||||
tabs = false,
|
tabs = false,
|
||||||
---Disables transitions when a tab is being dragged.
|
|
||||||
tab_drag = false,
|
tab_drag = false,
|
||||||
---Disables transitions when a notification is shown.
|
|
||||||
statusbar = false,
|
statusbar = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
---The rate of all transitions.
|
|
||||||
---
|
|
||||||
---Defaults to 1.
|
|
||||||
---@type number
|
|
||||||
config.animation_rate = 1.0
|
config.animation_rate = 1.0
|
||||||
|
|
||||||
---The caret's blinking period, in seconds.
|
|
||||||
---
|
|
||||||
---Defaults to 0.8.
|
|
||||||
---@type number
|
|
||||||
config.blink_period = 0.8
|
config.blink_period = 0.8
|
||||||
|
|
||||||
---Disables caret blinking.
|
|
||||||
---
|
|
||||||
---Defaults to false.
|
|
||||||
---@type boolean
|
|
||||||
config.disable_blink = false
|
config.disable_blink = false
|
||||||
|
|
||||||
---Draws whitespaces as dots.
|
|
||||||
---This option is deprecated.
|
|
||||||
---Please use the drawwhitespace plugin instead.
|
|
||||||
---@deprecated
|
|
||||||
config.draw_whitespace = false
|
config.draw_whitespace = false
|
||||||
|
|
||||||
---Disables system-drawn window borders.
|
|
||||||
---
|
|
||||||
---When set to true, Lite XL draws its own window decorations,
|
|
||||||
---which can be useful for certain setups.
|
|
||||||
---
|
|
||||||
---Defaults to false.
|
|
||||||
---@type boolean
|
|
||||||
config.borderless = false
|
config.borderless = false
|
||||||
|
|
||||||
---Shows/hides the close buttons on tabs.
|
|
||||||
---When hidden, users can close tabs via keyboard shortcuts or commands.
|
|
||||||
---
|
|
||||||
---Defaults to true.
|
|
||||||
---@type boolean
|
|
||||||
config.tab_close_button = true
|
config.tab_close_button = true
|
||||||
|
|
||||||
---Maximum number of clicks recognized by Lite XL.
|
|
||||||
---
|
|
||||||
---Defaults to 3.
|
|
||||||
---@type number
|
|
||||||
config.max_clicks = 3
|
config.max_clicks = 3
|
||||||
|
|
||||||
---Disables plugin version checking.
|
-- set as true to be able to test non supported plugins
|
||||||
---Do not change this unless you know what you are doing.
|
|
||||||
---
|
|
||||||
---Defaults to false.
|
|
||||||
---@type boolean
|
|
||||||
config.skip_plugins_version = false
|
config.skip_plugins_version = false
|
||||||
|
|
||||||
-- holds the plugins real config table
|
-- holds the plugins real config table
|
||||||
|
|
|
@ -48,7 +48,7 @@ function Highlighter:start()
|
||||||
self:update_notify(retokenized_from, max - retokenized_from)
|
self:update_notify(retokenized_from, max - retokenized_from)
|
||||||
end
|
end
|
||||||
core.redraw = true
|
core.redraw = true
|
||||||
coroutine.yield(0)
|
coroutine.yield()
|
||||||
end
|
end
|
||||||
self.max_wanted_line = 0
|
self.max_wanted_line = 0
|
||||||
self.running = false
|
self.running = false
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
local Object = require "core.object"
|
local Object = require "core.object"
|
||||||
local Highlighter = require "core.doc.highlighter"
|
local Highlighter = require "core.doc.highlighter"
|
||||||
local translate = require "core.doc.translate"
|
|
||||||
local core = require "core"
|
local core = require "core"
|
||||||
local syntax = require "core.syntax"
|
local syntax = require "core.syntax"
|
||||||
local config = require "core.config"
|
local config = require "core.config"
|
||||||
|
@ -28,11 +27,9 @@ function Doc:new(filename, abs_filename, new_file)
|
||||||
self:load(filename)
|
self:load(filename)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if new_file then
|
|
||||||
self.crlf = config.line_endings == "crlf"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:reset()
|
function Doc:reset()
|
||||||
self.lines = { "\n" }
|
self.lines = { "\n" }
|
||||||
self.selections = { 1, 1, 1, 1 }
|
self.selections = { 1, 1, 1, 1 }
|
||||||
|
@ -41,10 +38,10 @@ function Doc:reset()
|
||||||
self.redo_stack = { idx = 1 }
|
self.redo_stack = { idx = 1 }
|
||||||
self.clean_change_id = 1
|
self.clean_change_id = 1
|
||||||
self.highlighter = Highlighter(self)
|
self.highlighter = Highlighter(self)
|
||||||
self.overwrite = false
|
|
||||||
self:reset_syntax()
|
self:reset_syntax()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:reset_syntax()
|
function Doc:reset_syntax()
|
||||||
local header = self:get_text(1, 1, self:position_offset(1, 1, 128))
|
local header = self:get_text(1, 1, self:position_offset(1, 1, 128))
|
||||||
local path = self.abs_filename
|
local path = self.abs_filename
|
||||||
|
@ -59,12 +56,14 @@ function Doc:reset_syntax()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:set_filename(filename, abs_filename)
|
function Doc:set_filename(filename, abs_filename)
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.abs_filename = abs_filename
|
self.abs_filename = abs_filename
|
||||||
self:reset_syntax()
|
self:reset_syntax()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:load(filename)
|
function Doc:load(filename)
|
||||||
local fp = assert( io.open(filename, "rb") )
|
local fp = assert( io.open(filename, "rb") )
|
||||||
self:reset()
|
self:reset()
|
||||||
|
@ -86,6 +85,7 @@ function Doc:load(filename)
|
||||||
self:reset_syntax()
|
self:reset_syntax()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:reload()
|
function Doc:reload()
|
||||||
if self.filename then
|
if self.filename then
|
||||||
local sel = { self:get_selection() }
|
local sel = { self:get_selection() }
|
||||||
|
@ -95,6 +95,7 @@ function Doc:reload()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:save(filename, abs_filename)
|
function Doc:save(filename, abs_filename)
|
||||||
if not filename then
|
if not filename then
|
||||||
assert(self.filename, "no filename set to default to")
|
assert(self.filename, "no filename set to default to")
|
||||||
|
@ -114,10 +115,12 @@ function Doc:save(filename, abs_filename)
|
||||||
self:clean()
|
self:clean()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:get_name()
|
function Doc:get_name()
|
||||||
return self.filename or "unsaved"
|
return self.filename or "unsaved"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:is_dirty()
|
function Doc:is_dirty()
|
||||||
if self.new_file then
|
if self.new_file then
|
||||||
if self.filename then return true end
|
if self.filename then return true end
|
||||||
|
@ -127,10 +130,12 @@ function Doc:is_dirty()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:clean()
|
function Doc:clean()
|
||||||
self.clean_change_id = self:get_change_id()
|
self.clean_change_id = self:get_change_id()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:get_indent_info()
|
function Doc:get_indent_info()
|
||||||
if not self.indent_info then return config.tab_type, config.indent_size, false end
|
if not self.indent_info then return config.tab_type, config.indent_size, false end
|
||||||
return self.indent_info.type or config.tab_type,
|
return self.indent_info.type or config.tab_type,
|
||||||
|
@ -138,6 +143,7 @@ function Doc:get_indent_info()
|
||||||
self.indent_info.confirmed
|
self.indent_info.confirmed
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:get_change_id()
|
function Doc:get_change_id()
|
||||||
return self.undo_stack.idx
|
return self.undo_stack.idx
|
||||||
end
|
end
|
||||||
|
@ -161,14 +167,13 @@ function Doc:get_selection(sort)
|
||||||
return line1, col1, line2, col2, swap
|
return line1, col1, line2, col2, swap
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---Get the selection specified by `idx`
|
---Get the selection specified by `idx`
|
||||||
---@param idx integer @the index of the selection to retrieve
|
---@param idx integer @the index of the selection to retrieve
|
||||||
---@param sort? boolean @whether to sort the selection returned
|
---@param sort? boolean @whether to sort the selection returned
|
||||||
---@return integer,integer,integer,integer,boolean? @line1, col1, line2, col2, was the selection sorted
|
---@return integer,integer,integer,integer,boolean? @line1, col1, line2, col2, was the selection sorted
|
||||||
function Doc:get_selection_idx(idx, sort)
|
function Doc:get_selection_idx(idx, sort)
|
||||||
local line1, col1, line2, col2 = self.selections[idx * 4 - 3], self.selections[idx * 4 - 2],
|
local line1, col1, line2, col2 = self.selections[idx*4-3], self.selections[idx*4-2], self.selections[idx*4-1], self.selections[idx*4]
|
||||||
self.selections[idx * 4 - 1],
|
|
||||||
self.selections[idx * 4]
|
|
||||||
if line1 and sort then
|
if line1 and sort then
|
||||||
return sort_positions(line1, col1, line2, col2)
|
return sort_positions(line1, col1, line2, col2)
|
||||||
else
|
else
|
||||||
|
@ -228,6 +233,7 @@ function Doc:add_selection(line1, col1, line2, col2, swap)
|
||||||
self.last_selection = target
|
self.last_selection = target
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:remove_selection(idx)
|
function Doc:remove_selection(idx)
|
||||||
if self.last_selection >= idx then
|
if self.last_selection >= idx then
|
||||||
self.last_selection = self.last_selection - 1
|
self.last_selection = self.last_selection - 1
|
||||||
|
@ -235,6 +241,7 @@ function Doc:remove_selection(idx)
|
||||||
common.splice(self.selections, (idx - 1) * 4 + 1, 4)
|
common.splice(self.selections, (idx - 1) * 4 + 1, 4)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:set_selection(line1, col1, line2, col2, swap)
|
function Doc:set_selection(line1, col1, line2, col2, swap)
|
||||||
self.selections = {}
|
self.selections = {}
|
||||||
self:set_selections(1, line1, col1, line2, col2, swap)
|
self:set_selections(1, line1, col1, line2, col2, swap)
|
||||||
|
@ -272,7 +279,6 @@ function Doc:get_selections(sort_intra, idx_reverse)
|
||||||
return selection_iterator, { self.selections, sort_intra, idx_reverse },
|
return selection_iterator, { self.selections, sort_intra, idx_reverse },
|
||||||
idx_reverse == true and ((#self.selections / 4) + 1) or ((idx_reverse or -1)+1)
|
idx_reverse == true and ((#self.selections / 4) + 1) or ((idx_reverse or -1)+1)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- End of cursor seciton.
|
-- End of cursor seciton.
|
||||||
|
|
||||||
function Doc:sanitize_position(line, col)
|
function Doc:sanitize_position(line, col)
|
||||||
|
@ -285,6 +291,7 @@ function Doc:sanitize_position(line, col)
|
||||||
return line, common.clamp(col, 1, #self.lines[line])
|
return line, common.clamp(col, 1, #self.lines[line])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function position_offset_func(self, line, col, fn, ...)
|
local function position_offset_func(self, line, col, fn, ...)
|
||||||
line, col = self:sanitize_position(line, col)
|
line, col = self:sanitize_position(line, col)
|
||||||
return fn(self, line, col, ...)
|
return fn(self, line, col, ...)
|
||||||
|
@ -323,6 +330,7 @@ function Doc:position_offset(line, col, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:get_text(line1, col1, line2, col2)
|
function Doc:get_text(line1, col1, line2, col2)
|
||||||
line1, col1 = self:sanitize_position(line1, col1)
|
line1, col1 = self:sanitize_position(line1, col1)
|
||||||
line2, col2 = self:sanitize_position(line2, col2)
|
line2, col2 = self:sanitize_position(line2, col2)
|
||||||
|
@ -338,11 +346,13 @@ function Doc:get_text(line1, col1, line2, col2)
|
||||||
return table.concat(lines)
|
return table.concat(lines)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:get_char(line, col)
|
function Doc:get_char(line, col)
|
||||||
line, col = self:sanitize_position(line, col)
|
line, col = self:sanitize_position(line, col)
|
||||||
return self.lines[line]:sub(col, col)
|
return self.lines[line]:sub(col, col)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function push_undo(undo_stack, time, type, ...)
|
local function push_undo(undo_stack, time, type, ...)
|
||||||
undo_stack[undo_stack.idx] = { type = type, time = time, ... }
|
undo_stack[undo_stack.idx] = { type = type, time = time, ... }
|
||||||
undo_stack[undo_stack.idx - config.max_undos] = nil
|
undo_stack[undo_stack.idx - config.max_undos] = nil
|
||||||
|
@ -403,8 +413,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
|
||||||
if cline1 < line then break end
|
if cline1 < line then break end
|
||||||
local line_addition = (line < cline1 or col < ccol1) and #lines - 1 or 0
|
local line_addition = (line < cline1 or col < ccol1) and #lines - 1 or 0
|
||||||
local column_addition = line == cline1 and ccol1 > col and len or 0
|
local column_addition = line == cline1 and ccol1 > col and len or 0
|
||||||
self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition,
|
self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition, ccol2 + column_addition)
|
||||||
ccol2 + column_addition)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- push undo
|
-- push undo
|
||||||
|
@ -417,6 +426,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
|
||||||
self:sanitize_selection()
|
self:sanitize_selection()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
|
function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
|
||||||
-- push undo
|
-- push undo
|
||||||
local text = self:get_text(line1, col1, line2, col2)
|
local text = self:get_text(line1, col1, line2, col2)
|
||||||
|
@ -475,6 +485,7 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
|
||||||
self:sanitize_selection()
|
self:sanitize_selection()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:insert(line, col, text)
|
function Doc:insert(line, col, text)
|
||||||
self.redo_stack = { idx = 1 }
|
self.redo_stack = { idx = 1 }
|
||||||
-- Reset the clean id when we're pushing something new before it
|
-- Reset the clean id when we're pushing something new before it
|
||||||
|
@ -486,6 +497,7 @@ function Doc:insert(line, col, text)
|
||||||
self:on_text_change("insert")
|
self:on_text_change("insert")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:remove(line1, col1, line2, col2)
|
function Doc:remove(line1, col1, line2, col2)
|
||||||
self.redo_stack = { idx = 1 }
|
self.redo_stack = { idx = 1 }
|
||||||
line1, col1 = self:sanitize_position(line1, col1)
|
line1, col1 = self:sanitize_position(line1, col1)
|
||||||
|
@ -495,34 +507,28 @@ function Doc:remove(line1, col1, line2, col2)
|
||||||
self:on_text_change("remove")
|
self:on_text_change("remove")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:undo()
|
function Doc:undo()
|
||||||
pop_undo(self, self.undo_stack, self.redo_stack, false)
|
pop_undo(self, self.undo_stack, self.redo_stack, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:redo()
|
function Doc:redo()
|
||||||
pop_undo(self, self.redo_stack, self.undo_stack, false)
|
pop_undo(self, self.redo_stack, self.undo_stack, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:text_input(text, idx)
|
function Doc:text_input(text, idx)
|
||||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
|
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
|
||||||
local had_selection = false
|
|
||||||
if line1 ~= line2 or col1 ~= col2 then
|
if line1 ~= line2 or col1 ~= col2 then
|
||||||
self:delete_to_cursor(sidx)
|
self:delete_to_cursor(sidx)
|
||||||
had_selection = true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.overwrite
|
|
||||||
and not had_selection
|
|
||||||
and col1 < #self.lines[line1]
|
|
||||||
and text:ulen() == 1 then
|
|
||||||
self:remove(line1, col1, translate.next_char(self, line1, col1))
|
|
||||||
end
|
|
||||||
|
|
||||||
self:insert(line1, col1, text)
|
self:insert(line1, col1, text)
|
||||||
self:move_to_cursor(sidx, #text)
|
self:move_to_cursor(sidx, #text)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:ime_text_editing(text, start, length, idx)
|
function Doc:ime_text_editing(text, start, length, idx)
|
||||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
|
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
|
||||||
if line1 ~= line2 or col1 ~= col2 then
|
if line1 ~= line2 or col1 ~= col2 then
|
||||||
|
@ -533,6 +539,7 @@ function Doc:ime_text_editing(text, start, length, idx)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
|
function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
|
||||||
local old_text = self:get_text(line1, col1, line2, col2)
|
local old_text = self:get_text(line1, col1, line2, col2)
|
||||||
local new_text, res = fn(old_text)
|
local new_text, res = fn(old_text)
|
||||||
|
@ -562,6 +569,7 @@ function Doc:replace(fn)
|
||||||
return results
|
return results
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:delete_to_cursor(idx, ...)
|
function Doc:delete_to_cursor(idx, ...)
|
||||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do
|
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do
|
||||||
if line1 ~= line2 or col1 ~= col2 then
|
if line1 ~= line2 or col1 ~= col2 then
|
||||||
|
@ -575,7 +583,6 @@ function Doc:delete_to_cursor(idx, ...)
|
||||||
end
|
end
|
||||||
self:merge_cursors(idx)
|
self:merge_cursors(idx)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Doc:delete_to(...) return self:delete_to_cursor(nil, ...) end
|
function Doc:delete_to(...) return self:delete_to_cursor(nil, ...) end
|
||||||
|
|
||||||
function Doc:move_to_cursor(idx, ...)
|
function Doc:move_to_cursor(idx, ...)
|
||||||
|
@ -584,9 +591,9 @@ function Doc:move_to_cursor(idx, ...)
|
||||||
end
|
end
|
||||||
self:merge_cursors(idx)
|
self:merge_cursors(idx)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Doc:move_to(...) return self:move_to_cursor(nil, ...) end
|
function Doc:move_to(...) return self:move_to_cursor(nil, ...) end
|
||||||
|
|
||||||
|
|
||||||
function Doc:select_to_cursor(idx, ...)
|
function Doc:select_to_cursor(idx, ...)
|
||||||
for sidx, line, col, line2, col2 in self:get_selections(false, idx) do
|
for sidx, line, col, line2, col2 in self:get_selections(false, idx) do
|
||||||
line, col = self:position_offset(line, col, ...)
|
line, col = self:position_offset(line, col, ...)
|
||||||
|
@ -594,9 +601,9 @@ function Doc:select_to_cursor(idx, ...)
|
||||||
end
|
end
|
||||||
self:merge_cursors(idx)
|
self:merge_cursors(idx)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Doc:select_to(...) return self:select_to_cursor(nil, ...) end
|
function Doc:select_to(...) return self:select_to_cursor(nil, ...) end
|
||||||
|
|
||||||
|
|
||||||
function Doc:get_indent_string()
|
function Doc:get_indent_string()
|
||||||
local indent_type, indent_size = self:get_indent_info()
|
local indent_type, indent_size = self:get_indent_info()
|
||||||
if indent_type == "hard" then
|
if indent_type == "hard" then
|
||||||
|
@ -667,4 +674,5 @@ function Doc:on_close()
|
||||||
core.log_quiet("Closed doc \"%s\"", self:get_name())
|
core.log_quiet("Closed doc \"%s\"", self:get_name())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return Doc
|
return Doc
|
||||||
|
|
|
@ -254,11 +254,6 @@ function DocView:scroll_to_line(line, ignore_if_visible, instant)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function DocView:supports_text_input()
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function DocView:scroll_to_make_visible(line, col)
|
function DocView:scroll_to_make_visible(line, col)
|
||||||
local _, oy = self:get_content_offset()
|
local _, oy = self:get_content_offset()
|
||||||
local _, ly = self:get_line_screen_position(line, col)
|
local _, ly = self:get_line_screen_position(line, col)
|
||||||
|
@ -322,7 +317,7 @@ function DocView:mouse_selection(doc, snap_type, line1, col1, line2, col2)
|
||||||
line1, col1 = translate.start_of_word(doc, line1, col1)
|
line1, col1 = translate.start_of_word(doc, line1, col1)
|
||||||
line2, col2 = translate.end_of_word(doc, line2, col2)
|
line2, col2 = translate.end_of_word(doc, line2, col2)
|
||||||
elseif snap_type == "lines" then
|
elseif snap_type == "lines" then
|
||||||
col1, col2, line2 = 1, 1, line2 + 1
|
col1, col2 = 1, math.huge
|
||||||
end
|
end
|
||||||
if swap then
|
if swap then
|
||||||
return line2, col2, line1, col1
|
return line2, col2, line1, col1
|
||||||
|
@ -460,13 +455,6 @@ function DocView:draw_line_text(line, x, y)
|
||||||
return self:get_line_height()
|
return self:get_line_height()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function DocView:draw_overwrite_caret(x, y, width)
|
|
||||||
local lh = self:get_line_height()
|
|
||||||
renderer.draw_rect(x, y + lh - style.caret_width, width, style.caret_width, style.caret)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function DocView:draw_caret(x, y)
|
function DocView:draw_caret(x, y)
|
||||||
local lh = self:get_line_height()
|
local lh = self:get_line_height()
|
||||||
renderer.draw_rect(x, y, style.caret_width, lh, style.caret)
|
renderer.draw_rect(x, y, style.caret_width, lh, style.caret)
|
||||||
|
@ -566,12 +554,7 @@ function DocView:draw_overlay()
|
||||||
else
|
else
|
||||||
if config.disable_blink
|
if config.disable_blink
|
||||||
or (core.blink_timer - core.blink_start) % T < T / 2 then
|
or (core.blink_timer - core.blink_start) % T < T / 2 then
|
||||||
local x, y = self:get_line_screen_position(line1, col1)
|
self:draw_caret(self:get_line_screen_position(line1, col1))
|
||||||
if self.doc.overwrite then
|
|
||||||
self:draw_overwrite_caret(x, y, self:get_font():get_width(self.doc:get_char(line1, col1)))
|
|
||||||
else
|
|
||||||
self:draw_caret(x, y)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -668,9 +668,6 @@ end
|
||||||
|
|
||||||
|
|
||||||
function core.init()
|
function core.init()
|
||||||
core.log_items = {}
|
|
||||||
core.log_quiet("Lite XL version %s - mod-version %s", VERSION, MOD_VERSION_STRING)
|
|
||||||
|
|
||||||
command = require "core.command"
|
command = require "core.command"
|
||||||
keymap = require "core.keymap"
|
keymap = require "core.keymap"
|
||||||
dirwatch = require "core.dirwatch"
|
dirwatch = require "core.dirwatch"
|
||||||
|
@ -724,6 +721,7 @@ function core.init()
|
||||||
|
|
||||||
core.frame_start = 0
|
core.frame_start = 0
|
||||||
core.clip_rect_stack = {{ 0,0,0,0 }}
|
core.clip_rect_stack = {{ 0,0,0,0 }}
|
||||||
|
core.log_items = {}
|
||||||
core.docs = {}
|
core.docs = {}
|
||||||
core.cursor_clipboard = {}
|
core.cursor_clipboard = {}
|
||||||
core.cursor_clipboard_whole_line = {}
|
core.cursor_clipboard_whole_line = {}
|
||||||
|
@ -825,19 +823,15 @@ function core.init()
|
||||||
local msg = {}
|
local msg = {}
|
||||||
for _, entry in pairs(plugins_refuse_list) do
|
for _, entry in pairs(plugins_refuse_list) do
|
||||||
if #entry.plugins > 0 then
|
if #entry.plugins > 0 then
|
||||||
local msg_list = {}
|
msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(entry.plugins, "\n"))
|
||||||
for _, p in pairs(entry.plugins) do
|
|
||||||
table.insert(msg_list, string.format("%s[%s]", p.file, p.version_string))
|
|
||||||
end
|
|
||||||
msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(msg_list, "\n"))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
core.nag_view:show(
|
core.nag_view:show(
|
||||||
"Refused Plugins",
|
"Refused Plugins",
|
||||||
string.format(
|
string.format(
|
||||||
"Some plugins are not loaded due to version mismatch. Expected version %s.\n\n%s.\n\n" ..
|
"Some plugins are not loaded due to version mismatch.\n\n%s.\n\n" ..
|
||||||
"Please download a recent version from https://github.com/lite-xl/lite-xl-plugins.",
|
"Please download a recent version from https://github.com/lite-xl/lite-xl-plugins.",
|
||||||
MOD_VERSION_STRING, table.concat(msg, ".\n\n")),
|
table.concat(msg, ".\n\n")),
|
||||||
opt, function(item)
|
opt, function(item)
|
||||||
if item.text == "Exit" then os.exit(1) end
|
if item.text == "Exit" then os.exit(1) end
|
||||||
end)
|
end)
|
||||||
|
@ -926,8 +920,6 @@ function core.restart()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local mod_version_regex =
|
|
||||||
regex.compile([[--.*mod-version:(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:$|\s)]])
|
|
||||||
local function get_plugin_details(filename)
|
local function get_plugin_details(filename)
|
||||||
local info = system.get_file_info(filename)
|
local info = system.get_file_info(filename)
|
||||||
if info ~= nil and info.type == "dir" then
|
if info ~= nil and info.type == "dir" then
|
||||||
|
@ -939,32 +931,17 @@ local function get_plugin_details(filename)
|
||||||
if not f then return false end
|
if not f then return false end
|
||||||
local priority = false
|
local priority = false
|
||||||
local version_match = false
|
local version_match = false
|
||||||
local major, minor, patch
|
|
||||||
|
|
||||||
for line in f:lines() do
|
for line in f:lines() do
|
||||||
if not version_match then
|
if not version_match then
|
||||||
local _major, _minor, _patch = mod_version_regex:match(line)
|
local mod_version = line:match('%-%-.*%f[%a]mod%-version%s*:%s*(%d+)')
|
||||||
if _major then
|
if mod_version then
|
||||||
_major = tonumber(_major) or 0
|
version_match = (mod_version == MOD_VERSION)
|
||||||
_minor = tonumber(_minor) or 0
|
|
||||||
_patch = tonumber(_patch) or 0
|
|
||||||
major, minor, patch = _major, _minor, _patch
|
|
||||||
|
|
||||||
version_match = major == MOD_VERSION_MAJOR
|
|
||||||
if version_match then
|
|
||||||
version_match = minor <= MOD_VERSION_MINOR
|
|
||||||
end
|
|
||||||
if version_match then
|
|
||||||
version_match = patch <= MOD_VERSION_PATCH
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if not priority then
|
if not priority then
|
||||||
priority = line:match('%-%-.*%f[%a]priority%s*:%s*(%d+)')
|
priority = line:match('%-%-.*%f[%a]priority%s*:%s*(%d+)')
|
||||||
if priority then priority = tonumber(priority) end
|
if priority then priority = tonumber(priority) end
|
||||||
end
|
end
|
||||||
|
|
||||||
if version_match then
|
if version_match then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
@ -972,7 +949,6 @@ local function get_plugin_details(filename)
|
||||||
f:close()
|
f:close()
|
||||||
return true, {
|
return true, {
|
||||||
version_match = version_match,
|
version_match = version_match,
|
||||||
version = major and {major, minor, patch} or {},
|
|
||||||
priority = priority or 100
|
priority = priority or 100
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -1008,8 +984,6 @@ function core.load_plugins()
|
||||||
plugin.dir = dir
|
plugin.dir = dir
|
||||||
plugin.priority = details and details.priority or 100
|
plugin.priority = details and details.priority or 100
|
||||||
plugin.version_match = details and details.version_match or false
|
plugin.version_match = details and details.version_match or false
|
||||||
plugin.version = details and details.version or {}
|
|
||||||
plugin.version_string = #plugin.version > 0 and table.concat(plugin.version, ".") or "unknown"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- sort by priority or name for plugins that have same priority
|
-- sort by priority or name for plugins that have same priority
|
||||||
|
@ -1025,35 +999,27 @@ function core.load_plugins()
|
||||||
if plugin.valid then
|
if plugin.valid then
|
||||||
if not config.skip_plugins_version and not plugin.version_match then
|
if not config.skip_plugins_version and not plugin.version_match then
|
||||||
core.log_quiet(
|
core.log_quiet(
|
||||||
"Version mismatch for plugin %q[%s] from %s",
|
"Version mismatch for plugin %q from %s",
|
||||||
plugin.name,
|
plugin.name,
|
||||||
plugin.version_string,
|
|
||||||
plugin.dir
|
plugin.dir
|
||||||
)
|
)
|
||||||
local rlist = plugin.dir:find(USERDIR, 1, true) == 1
|
local rlist = plugin.dir:find(USERDIR, 1, true) == 1
|
||||||
and 'userdir' or 'datadir'
|
and 'userdir' or 'datadir'
|
||||||
local list = refused_list[rlist].plugins
|
local list = refused_list[rlist].plugins
|
||||||
table.insert(list, plugin)
|
table.insert(list, plugin.file)
|
||||||
elseif config.plugins[plugin.name] ~= false then
|
elseif config.plugins[plugin.name] ~= false then
|
||||||
local start = system.get_time()
|
local start = system.get_time()
|
||||||
local ok, loaded_plugin = core.try(require, "plugins." .. plugin.name)
|
local ok = core.try(require, "plugins." .. plugin.name)
|
||||||
if ok then
|
if ok then
|
||||||
local plugin_version = ""
|
|
||||||
if plugin.version_string ~= MOD_VERSION_STRING then
|
|
||||||
plugin_version = "["..plugin.version_string.."]"
|
|
||||||
end
|
|
||||||
core.log_quiet(
|
core.log_quiet(
|
||||||
"Loaded plugin %q%s from %s in %.1fms",
|
"Loaded plugin %q from %s in %.1fms",
|
||||||
plugin.name,
|
plugin.name,
|
||||||
plugin_version,
|
|
||||||
plugin.dir,
|
plugin.dir,
|
||||||
(system.get_time() - start) * 1000
|
(system.get_time() - start) * 1000
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
if not ok then
|
if not ok then
|
||||||
no_errors = false
|
no_errors = false
|
||||||
elseif config.plugins[plugin.name].onload then
|
|
||||||
core.try(config.plugins[plugin.name].onload, loaded_plugin)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1107,7 +1073,6 @@ function core.set_active_view(view)
|
||||||
-- Reset the IME even if the focus didn't change
|
-- Reset the IME even if the focus didn't change
|
||||||
ime.stop()
|
ime.stop()
|
||||||
if view ~= core.active_view then
|
if view ~= core.active_view then
|
||||||
system.text_input(view:supports_text_input())
|
|
||||||
if core.active_view and core.active_view.force_focus then
|
if core.active_view and core.active_view.force_focus then
|
||||||
core.next_active_view = view
|
core.next_active_view = view
|
||||||
return
|
return
|
||||||
|
@ -1296,12 +1261,6 @@ function core.on_event(type, ...)
|
||||||
if not core.root_view:on_mouse_wheel(...) then
|
if not core.root_view:on_mouse_wheel(...) then
|
||||||
did_keymap = keymap.on_mouse_wheel(...)
|
did_keymap = keymap.on_mouse_wheel(...)
|
||||||
end
|
end
|
||||||
elseif type == "touchpressed" then
|
|
||||||
core.root_view:on_touch_pressed(...)
|
|
||||||
elseif type == "touchreleased" then
|
|
||||||
core.root_view:on_touch_released(...)
|
|
||||||
elseif type == "touchmoved" then
|
|
||||||
core.root_view:on_touch_moved(...)
|
|
||||||
elseif type == "resized" then
|
elseif type == "resized" then
|
||||||
core.window_mode = system.get_window_mode()
|
core.window_mode = system.get_window_mode()
|
||||||
elseif type == "minimized" or type == "maximized" or type == "restored" then
|
elseif type == "minimized" or type == "maximized" or type == "restored" then
|
||||||
|
@ -1351,11 +1310,6 @@ function core.step()
|
||||||
did_keymap = false
|
did_keymap = false
|
||||||
elseif type == "mousemoved" then
|
elseif type == "mousemoved" then
|
||||||
core.try(core.on_event, type, a, b, c, d)
|
core.try(core.on_event, type, a, b, c, d)
|
||||||
elseif type == "enteringforeground" then
|
|
||||||
-- to break our frame refresh in two if we get entering/entered at the same time.
|
|
||||||
-- required to avoid flashing and refresh issues on mobile
|
|
||||||
core.redraw = true
|
|
||||||
break
|
|
||||||
else
|
else
|
||||||
local _, res = core.try(core.on_event, type, a, b, c, d)
|
local _, res = core.try(core.on_event, type, a, b, c, d)
|
||||||
did_keymap = res or did_keymap
|
did_keymap = res or did_keymap
|
||||||
|
@ -1510,16 +1464,4 @@ function core.on_error(err)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local alerted_deprecations = {}
|
|
||||||
---Show deprecation notice once per `kind`.
|
|
||||||
---
|
|
||||||
---@param kind string
|
|
||||||
function core.deprecation_log(kind)
|
|
||||||
if alerted_deprecations[kind] then return end
|
|
||||||
alerted_deprecations[kind] = true
|
|
||||||
core.warn("Used deprecated functionality [%s]. Check if your plugins are up to date.", kind)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
return core
|
return core
|
||||||
|
|
||||||
|
|
|
@ -36,8 +36,6 @@ local function keymap_macos(keymap)
|
||||||
["wheel"] = "root:scroll",
|
["wheel"] = "root:scroll",
|
||||||
["hwheel"] = "root:horizontal-scroll",
|
["hwheel"] = "root:horizontal-scroll",
|
||||||
["shift+hwheel"] = "root:horizontal-scroll",
|
["shift+hwheel"] = "root:horizontal-scroll",
|
||||||
["wheelup"] = "root:scroll-hovered-tabs-backward",
|
|
||||||
["wheeldown"] = "root:scroll-hovered-tabs-forward",
|
|
||||||
|
|
||||||
["cmd+f"] = "find-replace:find",
|
["cmd+f"] = "find-replace:find",
|
||||||
["cmd+r"] = "find-replace:replace",
|
["cmd+r"] = "find-replace:replace",
|
||||||
|
|
|
@ -71,6 +71,7 @@ local function key_to_stroke(key)
|
||||||
return normalize_stroke(table.concat(keys, "+"))
|
return normalize_stroke(table.concat(keys, "+"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---Remove the given value from an array associated to a key in a table.
|
---Remove the given value from an array associated to a key in a table.
|
||||||
---@param tbl table<string, string> The table containing the key
|
---@param tbl table<string, string> The table containing the key
|
||||||
---@param k string The key containing the array
|
---@param k string The key containing the array
|
||||||
|
@ -124,6 +125,7 @@ end
|
||||||
function keymap.add_direct(map)
|
function keymap.add_direct(map)
|
||||||
for stroke, commands in pairs(map) do
|
for stroke, commands in pairs(map) do
|
||||||
stroke = normalize_stroke(stroke)
|
stroke = normalize_stroke(stroke)
|
||||||
|
|
||||||
if type(commands) == "string" or type(commands) == "function" then
|
if type(commands) == "string" or type(commands) == "function" then
|
||||||
commands = { commands }
|
commands = { commands }
|
||||||
end
|
end
|
||||||
|
@ -323,8 +325,6 @@ keymap.add_direct {
|
||||||
["wheel"] = "root:scroll",
|
["wheel"] = "root:scroll",
|
||||||
["hwheel"] = "root:horizontal-scroll",
|
["hwheel"] = "root:horizontal-scroll",
|
||||||
["shift+wheel"] = "root:horizontal-scroll",
|
["shift+wheel"] = "root:horizontal-scroll",
|
||||||
["wheelup"] = "root:scroll-hovered-tabs-backward",
|
|
||||||
["wheeldown"] = "root:scroll-hovered-tabs-forward",
|
|
||||||
|
|
||||||
["ctrl+f"] = "find-replace:find",
|
["ctrl+f"] = "find-replace:find",
|
||||||
["ctrl+r"] = "find-replace:replace",
|
["ctrl+r"] = "find-replace:replace",
|
||||||
|
@ -341,7 +341,6 @@ keymap.add_direct {
|
||||||
["ctrl+x"] = "doc:cut",
|
["ctrl+x"] = "doc:cut",
|
||||||
["ctrl+c"] = "doc:copy",
|
["ctrl+c"] = "doc:copy",
|
||||||
["ctrl+v"] = "doc:paste",
|
["ctrl+v"] = "doc:paste",
|
||||||
["insert"] = "doc:toggle-overwrite",
|
|
||||||
["ctrl+insert"] = "doc:copy",
|
["ctrl+insert"] = "doc:copy",
|
||||||
["shift+insert"] = "doc:paste",
|
["shift+insert"] = "doc:paste",
|
||||||
["escape"] = { "command:escape", "doc:select-none", "dialog:select-no" },
|
["escape"] = { "command:escape", "doc:select-none", "dialog:select-no" },
|
||||||
|
@ -391,7 +390,7 @@ keymap.add_direct {
|
||||||
["shift+1lclick"] = "doc:select-to-cursor",
|
["shift+1lclick"] = "doc:select-to-cursor",
|
||||||
["ctrl+1lclick"] = "doc:split-cursor",
|
["ctrl+1lclick"] = "doc:split-cursor",
|
||||||
["1lclick"] = "doc:set-cursor",
|
["1lclick"] = "doc:set-cursor",
|
||||||
["2lclick"] = { "doc:set-cursor-word", "emptyview:new-doc", "tabbar:new-doc" },
|
["2lclick"] = "doc:set-cursor-word",
|
||||||
["3lclick"] = "doc:set-cursor-line",
|
["3lclick"] = "doc:set-cursor-line",
|
||||||
["shift+left"] = "doc:select-to-previous-char",
|
["shift+left"] = "doc:select-to-previous-char",
|
||||||
["shift+right"] = "doc:select-to-next-char",
|
["shift+right"] = "doc:select-to-next-char",
|
||||||
|
@ -412,4 +411,3 @@ keymap.add_direct {
|
||||||
}
|
}
|
||||||
|
|
||||||
return keymap
|
return keymap
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,6 @@ modkeys.map = {
|
||||||
["right shift"] = "shift",
|
["right shift"] = "shift",
|
||||||
["left alt"] = "alt",
|
["left alt"] = "alt",
|
||||||
["right alt"] = "altgr",
|
["right alt"] = "altgr",
|
||||||
["left gui"] = "super",
|
|
||||||
["left windows"] = "super",
|
|
||||||
["right gui"] = "super",
|
|
||||||
["right windows"] = "super"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
modkeys.keys = { "ctrl", "shift", "alt", "altgr" }
|
modkeys.keys = { "ctrl", "shift", "alt", "altgr" }
|
||||||
|
|
|
@ -24,7 +24,6 @@ function NagView:new()
|
||||||
self.scrollable = true
|
self.scrollable = true
|
||||||
self.target_height = 0
|
self.target_height = 0
|
||||||
self.on_mouse_pressed_root = nil
|
self.on_mouse_pressed_root = nil
|
||||||
self.dim_alpha = 0
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function NagView:get_title()
|
function NagView:get_title()
|
||||||
|
@ -69,9 +68,7 @@ function NagView:dim_window_content()
|
||||||
oy = oy + self.show_height
|
oy = oy + self.show_height
|
||||||
local w, h = core.root_view.size.x, core.root_view.size.y - oy
|
local w, h = core.root_view.size.x, core.root_view.size.y - oy
|
||||||
core.root_view:defer_draw(function()
|
core.root_view:defer_draw(function()
|
||||||
local dim_color = { table.unpack(style.nagbar_dim) }
|
renderer.draw_rect(ox, oy, w, h, style.nagbar_dim)
|
||||||
dim_color[4] = style.nagbar_dim[4] * self.dim_alpha
|
|
||||||
renderer.draw_rect(ox, oy, w, h, dim_color)
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -175,13 +172,10 @@ function NagView:update()
|
||||||
NagView.super.update(self)
|
NagView.super.update(self)
|
||||||
|
|
||||||
if self.visible and core.active_view == self and self.title then
|
if self.visible and core.active_view == self and self.title then
|
||||||
local target_height = self:get_target_height()
|
self:move_towards(self, "show_height", self:get_target_height(), nil, "nagbar")
|
||||||
self:move_towards(self, "show_height", target_height, nil, "nagbar")
|
|
||||||
self:move_towards(self, "underline_progress", 1, nil, "nagbar")
|
self:move_towards(self, "underline_progress", 1, nil, "nagbar")
|
||||||
self:move_towards(self, "dim_alpha", self.show_height / target_height, nil, "nagbar")
|
|
||||||
else
|
else
|
||||||
self:move_towards(self, "show_height", 0, nil, "nagbar")
|
self:move_towards(self, "show_height", 0, nil, "nagbar")
|
||||||
self:move_towards(self, "dim_alpha", 0, nil, "nagbar")
|
|
||||||
if self.show_height <= 0 then
|
if self.show_height <= 0 then
|
||||||
self.title = nil
|
self.title = nil
|
||||||
self.message = nil
|
self.message = nil
|
||||||
|
|
|
@ -18,6 +18,7 @@ function Node:new(type)
|
||||||
if self.type == "leaf" then
|
if self.type == "leaf" then
|
||||||
self:add_view(EmptyView())
|
self:add_view(EmptyView())
|
||||||
end
|
end
|
||||||
|
self.hovered = {x = -1, y = -1 }
|
||||||
self.hovered_close = 0
|
self.hovered_close = 0
|
||||||
self.tab_shift = 0
|
self.tab_shift = 0
|
||||||
self.tab_offset = 1
|
self.tab_offset = 1
|
||||||
|
@ -32,10 +33,9 @@ function Node:propagate(fn, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---@deprecated
|
|
||||||
function Node:on_mouse_moved(x, y, ...)
|
function Node:on_mouse_moved(x, y, ...)
|
||||||
core.deprecation_log("Node:on_mouse_moved")
|
|
||||||
if self.type == "leaf" then
|
if self.type == "leaf" then
|
||||||
|
self.hovered.x, self.hovered.y = x, y
|
||||||
self.active_view:on_mouse_moved(x, y, ...)
|
self.active_view:on_mouse_moved(x, y, ...)
|
||||||
else
|
else
|
||||||
self:propagate("on_mouse_moved", x, y, ...)
|
self:propagate("on_mouse_moved", x, y, ...)
|
||||||
|
@ -43,9 +43,7 @@ function Node:on_mouse_moved(x, y, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---@deprecated
|
|
||||||
function Node:on_mouse_released(...)
|
function Node:on_mouse_released(...)
|
||||||
core.deprecation_log("Node:on_mouse_released")
|
|
||||||
if self.type == "leaf" then
|
if self.type == "leaf" then
|
||||||
self.active_view:on_mouse_released(...)
|
self.active_view:on_mouse_released(...)
|
||||||
else
|
else
|
||||||
|
@ -54,9 +52,7 @@ function Node:on_mouse_released(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---@deprecated
|
|
||||||
function Node:on_mouse_left()
|
function Node:on_mouse_left()
|
||||||
core.deprecation_log("Node:on_mouse_left")
|
|
||||||
if self.type == "leaf" then
|
if self.type == "leaf" then
|
||||||
self.active_view:on_mouse_left()
|
self.active_view:on_mouse_left()
|
||||||
else
|
else
|
||||||
|
@ -65,17 +61,6 @@ function Node:on_mouse_left()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---@deprecated
|
|
||||||
function Node:on_touch_moved(...)
|
|
||||||
core.deprecation_log("Node:on_touch_moved")
|
|
||||||
if self.type == "leaf" then
|
|
||||||
self.active_view:on_touch_moved(...)
|
|
||||||
else
|
|
||||||
self:propagate("on_touch_moved", ...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function Node:consume(node)
|
function Node:consume(node)
|
||||||
for k, _ in pairs(self) do self[k] = nil end
|
for k, _ in pairs(self) do self[k] = nil end
|
||||||
for k, v in pairs(node) do self[k] = v end
|
for k, v in pairs(node) do self[k] = v end
|
||||||
|
@ -177,12 +162,8 @@ function Node:add_view(view, idx)
|
||||||
assert(not self.locked, "Tried to add view to locked node")
|
assert(not self.locked, "Tried to add view to locked node")
|
||||||
if self.views[1] and self.views[1]:is(EmptyView) then
|
if self.views[1] and self.views[1]:is(EmptyView) then
|
||||||
table.remove(self.views)
|
table.remove(self.views)
|
||||||
if idx and idx > 1 then
|
|
||||||
idx = idx - 1
|
|
||||||
end
|
end
|
||||||
end
|
table.insert(self.views, idx or (#self.views + 1), view)
|
||||||
idx = common.clamp(idx or (#self.views + 1), 1, (#self.views + 1))
|
|
||||||
table.insert(self.views, idx, view)
|
|
||||||
self:set_active_view(view)
|
self:set_active_view(view)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -320,7 +301,7 @@ function Node:tab_hovered_update(px, py)
|
||||||
if px >= cx and px < cx + cw and py >= y and py < y + h and config.tab_close_button then
|
if px >= cx and px < cx + cw and py >= y and py < y + h and config.tab_close_button then
|
||||||
self.hovered_close = tab_index
|
self.hovered_close = tab_index
|
||||||
end
|
end
|
||||||
elseif #self.views > self:get_visible_tabs_number() then
|
else
|
||||||
self.hovered_scroll_button = self:get_scroll_button_index(px, py) or 0
|
self.hovered_scroll_button = self:get_scroll_button_index(px, py) or 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -338,17 +319,10 @@ function Node:get_child_overlapping_point(x, y)
|
||||||
return child:get_child_overlapping_point(x, y)
|
return child:get_child_overlapping_point(x, y)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- returns: total height, text padding, top margin
|
|
||||||
local function get_tab_y_sizes()
|
|
||||||
local height = style.font:get_height()
|
|
||||||
local padding = style.padding.y
|
|
||||||
local margin = style.margin.tab.top
|
|
||||||
return height + (padding * 2) + margin, padding, margin
|
|
||||||
end
|
|
||||||
|
|
||||||
function Node:get_scroll_button_rect(index)
|
function Node:get_scroll_button_rect(index)
|
||||||
local w, pad = get_scroll_button_width()
|
local w, pad = get_scroll_button_width()
|
||||||
local h = get_tab_y_sizes()
|
local h = style.font:get_height() + style.padding.y * 2
|
||||||
local x = self.position.x + (index == 1 and self.size.x - w * 2 or self.size.x - w)
|
local x = self.position.x + (index == 1 and self.size.x - w * 2 or self.size.x - w)
|
||||||
return x, self.position.y, w, h, pad
|
return x, self.position.y, w, h, pad
|
||||||
end
|
end
|
||||||
|
@ -359,8 +333,8 @@ function Node:get_tab_rect(idx)
|
||||||
local x0 = self.position.x
|
local x0 = self.position.x
|
||||||
local x1 = x0 + common.clamp(self.tab_width * (idx - 1) - self.tab_shift, 0, maxw)
|
local x1 = x0 + common.clamp(self.tab_width * (idx - 1) - self.tab_shift, 0, maxw)
|
||||||
local x2 = x0 + common.clamp(self.tab_width * idx - self.tab_shift, 0, maxw)
|
local x2 = x0 + common.clamp(self.tab_width * idx - self.tab_shift, 0, maxw)
|
||||||
local h, pad_y, margin_y = get_tab_y_sizes()
|
local h = style.font:get_height() + style.padding.y * 2
|
||||||
return x1, self.position.y, x2 - x1, h, margin_y
|
return x1, self.position.y, x2 - x1, h
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -508,7 +482,7 @@ function Node:update()
|
||||||
for _, view in ipairs(self.views) do
|
for _, view in ipairs(self.views) do
|
||||||
view:update()
|
view:update()
|
||||||
end
|
end
|
||||||
self:tab_hovered_update(core.root_view.mouse.x, core.root_view.mouse.y)
|
self:tab_hovered_update(self.hovered.x, self.hovered.y)
|
||||||
local tab_width = self:target_tab_width()
|
local tab_width = self:target_tab_width()
|
||||||
self:move_towards("tab_shift", tab_width * (self.tab_offset - 1), nil, "tabs")
|
self:move_towards("tab_shift", tab_width * (self.tab_offset - 1), nil, "tabs")
|
||||||
self:move_towards("tab_width", tab_width, nil, "tabs")
|
self:move_towards("tab_width", tab_width, nil, "tabs")
|
||||||
|
@ -551,7 +525,6 @@ function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalo
|
||||||
if is_active then
|
if is_active then
|
||||||
color = style.text
|
color = style.text
|
||||||
renderer.draw_rect(x, y, w, h, style.background)
|
renderer.draw_rect(x, y, w, h, style.background)
|
||||||
renderer.draw_rect(x, y, w, ds, style.divider)
|
|
||||||
renderer.draw_rect(x + w, y, ds, h, style.divider)
|
renderer.draw_rect(x + w, y, ds, h, style.divider)
|
||||||
renderer.draw_rect(x - ds, y, ds, h, style.divider)
|
renderer.draw_rect(x - ds, y, ds, h, style.divider)
|
||||||
end
|
end
|
||||||
|
@ -559,8 +532,7 @@ function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalo
|
||||||
end
|
end
|
||||||
|
|
||||||
function Node:draw_tab(view, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
|
function Node:draw_tab(view, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
|
||||||
local _, padding_y, margin_y = get_tab_y_sizes()
|
x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalone)
|
||||||
x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y + margin_y, w, h - margin_y, standalone)
|
|
||||||
-- Close button
|
-- Close button
|
||||||
local cx, cw, cpad = close_button_location(x, w)
|
local cx, cw, cpad = close_button_location(x, w)
|
||||||
local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button)
|
local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button)
|
||||||
|
@ -636,13 +608,6 @@ function Node:is_empty()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Node:is_in_tab_area(x, y)
|
|
||||||
if not self:should_show_tabs() then return false end
|
|
||||||
local _, ty, _, th = self:get_scroll_button_rect(1)
|
|
||||||
return y >= ty and y < ty + th
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function Node:close_all_docviews(keep_active)
|
function Node:close_all_docviews(keep_active)
|
||||||
local node_active_view = self.active_view
|
local node_active_view = self.active_view
|
||||||
local lost_active_view = false
|
local lost_active_view = false
|
||||||
|
@ -781,7 +746,7 @@ function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
|
||||||
tab_index = self:get_visible_tabs_number() + (self.tab_offset - 1 or 0)
|
tab_index = self:get_visible_tabs_number() + (self.tab_offset - 1 or 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local tab_x, tab_y, tab_w, tab_h, margin_y = self:get_tab_rect(tab_index)
|
local tab_x, tab_y, tab_w, tab_h = self:get_tab_rect(tab_index)
|
||||||
if x > tab_x + tab_w / 2 and tab_index <= #self.views then
|
if x > tab_x + tab_w / 2 and tab_index <= #self.views then
|
||||||
-- use next tab
|
-- use next tab
|
||||||
tab_x = tab_x + tab_w
|
tab_x = tab_x + tab_w
|
||||||
|
@ -792,7 +757,7 @@ function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
|
||||||
tab_index = tab_index - 1
|
tab_index = tab_index - 1
|
||||||
tab_x = tab_x - tab_w
|
tab_x = tab_x - tab_w
|
||||||
end
|
end
|
||||||
return tab_index, tab_x, tab_y + margin_y, tab_w, tab_h - margin_y
|
return tab_index, tab_x, tab_y, tab_w, tab_h
|
||||||
end
|
end
|
||||||
|
|
||||||
return Node
|
return Node
|
||||||
|
|
|
@ -27,13 +27,6 @@ function Object:is(T)
|
||||||
return getmetatable(self) == T
|
return getmetatable(self) == T
|
||||||
end
|
end
|
||||||
|
|
||||||
---Check if the parameter is strictly of the object type.
|
|
||||||
---@param T any
|
|
||||||
---@return boolean
|
|
||||||
function Object:is_class_of(T)
|
|
||||||
return getmetatable(T) == self
|
|
||||||
end
|
|
||||||
|
|
||||||
---Check if the object inherits from the given type.
|
---Check if the object inherits from the given type.
|
||||||
---@param T any
|
---@param T any
|
||||||
---@return boolean
|
---@return boolean
|
||||||
|
@ -48,22 +41,6 @@ function Object:extends(T)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
---Check if the parameter inherits from the object.
|
|
||||||
---@param T any
|
|
||||||
---@return boolean
|
|
||||||
function Object:is_extended_by(T)
|
|
||||||
local mt = getmetatable(T)
|
|
||||||
while mt do
|
|
||||||
if mt == self then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
local _mt = getmetatable(T)
|
|
||||||
if mt == _mt then break end
|
|
||||||
mt = _mt
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
---Metamethod to get a string representation of an object.
|
---Metamethod to get a string representation of an object.
|
||||||
---@return string
|
---@return string
|
||||||
function Object:__tostring()
|
function Object:__tostring()
|
||||||
|
|
|
@ -24,9 +24,6 @@ function RootView:new()
|
||||||
base_color = style.drag_overlay_tab,
|
base_color = style.drag_overlay_tab,
|
||||||
color = { table.unpack(style.drag_overlay_tab) } }
|
color = { table.unpack(style.drag_overlay_tab) } }
|
||||||
self.drag_overlay_tab.to = { x = 0, y = 0, w = 0, h = 0 }
|
self.drag_overlay_tab.to = { x = 0, y = 0, w = 0, h = 0 }
|
||||||
self.grab = nil -- = {view = nil, button = nil}
|
|
||||||
self.overlapping_view = nil
|
|
||||||
self.touched_view = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -119,31 +116,6 @@ function RootView:close_all_docviews(keep_active)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
---Obtain mouse grab.
|
|
||||||
---
|
|
||||||
---This means that mouse movements will be sent to the specified view, even when
|
|
||||||
---those occur outside of it.
|
|
||||||
---There can't be multiple mouse grabs, even for different buttons.
|
|
||||||
---@see RootView:ungrab_mouse
|
|
||||||
---@param button core.view.mousebutton
|
|
||||||
---@param view core.view
|
|
||||||
function RootView:grab_mouse(button, view)
|
|
||||||
assert(self.grab == nil)
|
|
||||||
self.grab = {view = view, button = button}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
---Release mouse grab.
|
|
||||||
---
|
|
||||||
---The specified button *must* be the last button that grabbed the mouse.
|
|
||||||
---@see RootView:grab_mouse
|
|
||||||
---@param button core.view.mousebutton
|
|
||||||
function RootView:ungrab_mouse(button)
|
|
||||||
assert(self.grab and self.grab.button == button)
|
|
||||||
self.grab = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
---Function to intercept mouse pressed events on the active view.
|
---Function to intercept mouse pressed events on the active view.
|
||||||
---Do nothing by default.
|
---Do nothing by default.
|
||||||
---@param button core.view.mousebutton
|
---@param button core.view.mousebutton
|
||||||
|
@ -160,10 +132,6 @@ end
|
||||||
---@param clicks integer
|
---@param clicks integer
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function RootView:on_mouse_pressed(button, x, y, clicks)
|
function RootView:on_mouse_pressed(button, x, y, clicks)
|
||||||
-- If there is a grab, release it first
|
|
||||||
if self.grab then
|
|
||||||
self:on_mouse_released(self.grab.button, x, y)
|
|
||||||
end
|
|
||||||
local div = self.root_node:get_divider_overlapping_point(x, y)
|
local div = self.root_node:get_divider_overlapping_point(x, y)
|
||||||
local node = self.root_node:get_child_overlapping_point(x, y)
|
local node = self.root_node:get_child_overlapping_point(x, y)
|
||||||
if div and (node and not node.active_view:scrollbar_overlaps_point(x, y)) then
|
if div and (node and not node.active_view:scrollbar_overlaps_point(x, y)) then
|
||||||
|
@ -188,7 +156,6 @@ function RootView:on_mouse_pressed(button, x, y, clicks)
|
||||||
end
|
end
|
||||||
elseif not self.dragged_node then -- avoid sending on_mouse_pressed events when dragging tabs
|
elseif not self.dragged_node then -- avoid sending on_mouse_pressed events when dragging tabs
|
||||||
core.set_active_view(node.active_view)
|
core.set_active_view(node.active_view)
|
||||||
self:grab_mouse(button, node.active_view)
|
|
||||||
return self.on_view_mouse_pressed(button, x, y, clicks) or node.active_view:on_mouse_pressed(button, x, y, clicks)
|
return self.on_view_mouse_pressed(button, x, y, clicks) or node.active_view:on_mouse_pressed(button, x, y, clicks)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -221,21 +188,6 @@ end
|
||||||
---@param x number
|
---@param x number
|
||||||
---@param y number
|
---@param y number
|
||||||
function RootView:on_mouse_released(button, x, y, ...)
|
function RootView:on_mouse_released(button, x, y, ...)
|
||||||
if self.grab then
|
|
||||||
if self.grab.button == button then
|
|
||||||
local grabbed_view = self.grab.view
|
|
||||||
grabbed_view:on_mouse_released(button, x, y, ...)
|
|
||||||
self:ungrab_mouse(button)
|
|
||||||
|
|
||||||
-- If the mouse was released over a different view, send it the mouse position
|
|
||||||
local hovered_view = self.root_node:get_child_overlapping_point(x, y)
|
|
||||||
if grabbed_view ~= hovered_view then
|
|
||||||
self:on_mouse_moved(x, y, 0, 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.dragged_divider then
|
if self.dragged_divider then
|
||||||
self.dragged_divider = nil
|
self.dragged_divider = nil
|
||||||
end
|
end
|
||||||
|
@ -276,6 +228,8 @@ function RootView:on_mouse_released(button, x, y, ...)
|
||||||
end
|
end
|
||||||
self.dragged_node = nil
|
self.dragged_node = nil
|
||||||
end
|
end
|
||||||
|
else -- avoid sending on_mouse_released events when dragging tabs
|
||||||
|
self.root_node:on_mouse_released(button, x, y, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -296,14 +250,6 @@ end
|
||||||
---@param dx number
|
---@param dx number
|
||||||
---@param dy number
|
---@param dy number
|
||||||
function RootView:on_mouse_moved(x, y, dx, dy)
|
function RootView:on_mouse_moved(x, y, dx, dy)
|
||||||
self.mouse.x, self.mouse.y = x, y
|
|
||||||
|
|
||||||
if self.grab then
|
|
||||||
self.grab.view:on_mouse_moved(x, y, dx, dy)
|
|
||||||
core.request_cursor(self.grab.view.cursor)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if core.active_view == core.nag_view then
|
if core.active_view == core.nag_view then
|
||||||
core.request_cursor("arrow")
|
core.request_cursor("arrow")
|
||||||
core.active_view:on_mouse_moved(x, y, dx, dy)
|
core.active_view:on_mouse_moved(x, y, dx, dy)
|
||||||
|
@ -323,6 +269,8 @@ function RootView:on_mouse_moved(x, y, dx, dy)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self.mouse.x, self.mouse.y = x, y
|
||||||
|
|
||||||
local dn = self.dragged_node
|
local dn = self.dragged_node
|
||||||
if dn and not dn.dragging then
|
if dn and not dn.dragging then
|
||||||
-- start dragging only after enough movement
|
-- start dragging only after enough movement
|
||||||
|
@ -335,33 +283,32 @@ function RootView:on_mouse_moved(x, y, dx, dy)
|
||||||
-- avoid sending on_mouse_moved events when dragging tabs
|
-- avoid sending on_mouse_moved events when dragging tabs
|
||||||
if dn then return end
|
if dn then return end
|
||||||
|
|
||||||
local last_overlapping_view = self.overlapping_view
|
self.root_node:on_mouse_moved(x, y, dx, dy)
|
||||||
local overlapping_node = self.root_node:get_child_overlapping_point(x, y)
|
|
||||||
self.overlapping_view = overlapping_node and overlapping_node.active_view
|
|
||||||
|
|
||||||
if last_overlapping_view and last_overlapping_view ~= self.overlapping_view then
|
local last_overlapping_node = self.overlapping_node
|
||||||
last_overlapping_view:on_mouse_left()
|
self.overlapping_node = self.root_node:get_child_overlapping_point(x, y)
|
||||||
|
|
||||||
|
if last_overlapping_node and last_overlapping_node ~= self.overlapping_node then
|
||||||
|
last_overlapping_node:on_mouse_left()
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self.overlapping_view then return end
|
|
||||||
|
|
||||||
self.overlapping_view:on_mouse_moved(x, y, dx, dy)
|
|
||||||
core.request_cursor(self.overlapping_view.cursor)
|
|
||||||
|
|
||||||
if not overlapping_node then return end
|
|
||||||
|
|
||||||
local div = self.root_node:get_divider_overlapping_point(x, y)
|
local div = self.root_node:get_divider_overlapping_point(x, y)
|
||||||
if overlapping_node:get_scroll_button_index(x, y) or overlapping_node:is_in_tab_area(x, y) then
|
local tab_index = self.overlapping_node and self.overlapping_node:get_tab_overlapping_point(x, y)
|
||||||
|
if self.overlapping_node and self.overlapping_node:get_scroll_button_index(x, y) then
|
||||||
core.request_cursor("arrow")
|
core.request_cursor("arrow")
|
||||||
elseif div and not self.overlapping_view:scrollbar_overlaps_point(x, y) then
|
elseif div and (self.overlapping_node and not self.overlapping_node.active_view:scrollbar_overlaps_point(x, y)) then
|
||||||
core.request_cursor(div.type == "hsplit" and "sizeh" or "sizev")
|
core.request_cursor(div.type == "hsplit" and "sizeh" or "sizev")
|
||||||
|
elseif tab_index then
|
||||||
|
core.request_cursor("arrow")
|
||||||
|
elseif self.overlapping_node then
|
||||||
|
core.request_cursor(self.overlapping_node.active_view.cursor)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function RootView:on_mouse_left()
|
function RootView:on_mouse_left()
|
||||||
if self.overlapping_view then
|
if self.overlapping_node then
|
||||||
self.overlapping_view:on_mouse_left()
|
self.overlapping_node:on_mouse_left()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -387,50 +334,6 @@ function RootView:on_text_input(...)
|
||||||
core.active_view:on_text_input(...)
|
core.active_view:on_text_input(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
function RootView:on_touch_pressed(x, y, ...)
|
|
||||||
local touched_node = self.root_node:get_child_overlapping_point(x, y)
|
|
||||||
self.touched_view = touched_node and touched_node.active_view
|
|
||||||
end
|
|
||||||
|
|
||||||
function RootView:on_touch_released(x, y, ...)
|
|
||||||
self.touched_view = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function RootView:on_touch_moved(x, y, dx, dy, ...)
|
|
||||||
if not self.touched_view then return end
|
|
||||||
if core.active_view == core.nag_view then
|
|
||||||
core.active_view:on_touch_moved(x, y, dx, dy, ...)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.dragged_divider then
|
|
||||||
local node = self.dragged_divider
|
|
||||||
if node.type == "hsplit" then
|
|
||||||
x = common.clamp(x - node.position.x, 0, self.root_node.size.x * 0.95)
|
|
||||||
resize_child_node(node, "x", x, dx)
|
|
||||||
elseif node.type == "vsplit" then
|
|
||||||
y = common.clamp(y - node.position.y, 0, self.root_node.size.y * 0.95)
|
|
||||||
resize_child_node(node, "y", y, dy)
|
|
||||||
end
|
|
||||||
node.divider = common.clamp(node.divider, 0.01, 0.99)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local dn = self.dragged_node
|
|
||||||
if dn and not dn.dragging then
|
|
||||||
-- start dragging only after enough movement
|
|
||||||
dn.dragging = common.distance(x, y, dn.drag_start_x, dn.drag_start_y) > style.tab_width * .05
|
|
||||||
if dn.dragging then
|
|
||||||
core.request_cursor("hand")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- avoid sending on_touch_moved events when dragging tabs
|
|
||||||
if dn then return end
|
|
||||||
|
|
||||||
self.touched_view:on_touch_moved(x, y, dx, dy, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
function RootView:on_ime_text_editing(...)
|
function RootView:on_ime_text_editing(...)
|
||||||
core.active_view:on_ime_text_editing(...)
|
core.active_view:on_ime_text_editing(...)
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,9 +58,9 @@ function Scrollbar:new(options)
|
||||||
---@type "expanded" | "contracted" | false @Force the scrollbar status
|
---@type "expanded" | "contracted" | false @Force the scrollbar status
|
||||||
self.force_status = options.force_status
|
self.force_status = options.force_status
|
||||||
self:set_forced_status(options.force_status)
|
self:set_forced_status(options.force_status)
|
||||||
---@type number? @Override the default value specified by `style.scrollbar_size`
|
|
||||||
self.contracted_size = options.contracted_size
|
|
||||||
---@type number? @Override the default value specified by `style.expanded_scrollbar_size`
|
---@type number? @Override the default value specified by `style.expanded_scrollbar_size`
|
||||||
|
self.contracted_size = options.contracted_size
|
||||||
|
---@type number? @Override the default value specified by `style.scrollbar_size`
|
||||||
self.expanded_size = options.expanded_size
|
self.expanded_size = options.expanded_size
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ function Scrollbar:_get_thumb_rect_normal()
|
||||||
across_size = across_size + (expanded_scrollbar_size - scrollbar_size) * self.expand_percent
|
across_size = across_size + (expanded_scrollbar_size - scrollbar_size) * self.expand_percent
|
||||||
return
|
return
|
||||||
nr.across + nr.across_size - across_size,
|
nr.across + nr.across_size - across_size,
|
||||||
nr.along + self.percent * (nr.along_size - along_size),
|
nr.along + self.percent * nr.scrollable * (nr.along_size - along_size) / (sz - nr.along_size),
|
||||||
across_size,
|
across_size,
|
||||||
along_size
|
along_size
|
||||||
end
|
end
|
||||||
|
@ -238,8 +238,7 @@ end
|
||||||
function Scrollbar:_on_mouse_moved_normal(x, y, dx, dy)
|
function Scrollbar:_on_mouse_moved_normal(x, y, dx, dy)
|
||||||
if self.dragging then
|
if self.dragging then
|
||||||
local nr = self.normal_rect
|
local nr = self.normal_rect
|
||||||
local _, _, _, along_size = self:_get_thumb_rect_normal()
|
return common.clamp((y - nr.along + self.drag_start_offset) / nr.along_size, 0, 1)
|
||||||
return common.clamp((y - nr.along + self.drag_start_offset) / (nr.along_size - along_size), 0, 1)
|
|
||||||
end
|
end
|
||||||
return self:_update_hover_status_normal(x, y)
|
return self:_update_hover_status_normal(x, y)
|
||||||
end
|
end
|
||||||
|
@ -282,7 +281,7 @@ function Scrollbar:set_size(x, y, w, h, scrollable)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Updates the scrollbar location
|
---Updates the scrollbar location
|
||||||
---@param percent number @number between 0 and 1 where 0 means thumb at the top and 1 at the bottom
|
---@param percent number @number between 0 and 1 representing the position of the middle part of the thumb
|
||||||
function Scrollbar:set_percent(percent)
|
function Scrollbar:set_percent(percent)
|
||||||
self.percent = percent
|
self.percent = percent
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
-- this file is used by lite-xl to setup the Lua environment when starting
|
-- this file is used by lite-xl to setup the Lua environment when starting
|
||||||
VERSION = "2.1.2r1"
|
VERSION = "2.1.3r1"
|
||||||
MOD_VERSION_MAJOR = 3
|
MOD_VERSION = "3"
|
||||||
MOD_VERSION_MINOR = 0
|
|
||||||
MOD_VERSION_PATCH = 0
|
|
||||||
MOD_VERSION_STRING = string.format("%d.%d.%d", MOD_VERSION_MAJOR, MOD_VERSION_MINOR, MOD_VERSION_PATCH)
|
|
||||||
|
|
||||||
SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or 1
|
SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or 1
|
||||||
PATHSEP = package.config:sub(1, 1)
|
PATHSEP = package.config:sub(1, 1)
|
||||||
|
@ -25,7 +22,7 @@ package.path = DATADIR .. '/?/init.lua;' .. package.path
|
||||||
package.path = USERDIR .. '/?.lua;' .. package.path
|
package.path = USERDIR .. '/?.lua;' .. package.path
|
||||||
package.path = USERDIR .. '/?/init.lua;' .. package.path
|
package.path = USERDIR .. '/?/init.lua;' .. package.path
|
||||||
|
|
||||||
local suffix = PLATFORM == "Windows" and 'dll' or 'so'
|
local suffix = PLATFORM == "Mac OS X" and 'lib' or (PLATFORM == "Windows" and 'dll' or 'so')
|
||||||
package.cpath =
|
package.cpath =
|
||||||
USERDIR .. '/?.' .. ARCH .. "." .. suffix .. ";" ..
|
USERDIR .. '/?.' .. ARCH .. "." .. suffix .. ";" ..
|
||||||
USERDIR .. '/?/init.' .. ARCH .. "." .. suffix .. ";" ..
|
USERDIR .. '/?/init.' .. ARCH .. "." .. suffix .. ";" ..
|
||||||
|
|
|
@ -232,42 +232,15 @@ function StatusView:register_docview_items()
|
||||||
return {
|
return {
|
||||||
style.text, line, ":",
|
style.text, line, ":",
|
||||||
col > config.line_limit and style.accent or style.text, col,
|
col > config.line_limit and style.accent or style.text, col,
|
||||||
style.text
|
style.text,
|
||||||
|
self.separator,
|
||||||
|
string.format("%.f%%", line / #dv.doc.lines * 100)
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
command = "doc:go-to-line",
|
command = "doc:go-to-line",
|
||||||
tooltip = "line : column"
|
tooltip = "line : column"
|
||||||
})
|
})
|
||||||
|
|
||||||
self:add_item({
|
|
||||||
predicate = predicate_docview,
|
|
||||||
name = "doc:position-percent",
|
|
||||||
alignment = StatusView.Item.LEFT,
|
|
||||||
get_item = function()
|
|
||||||
local dv = core.active_view
|
|
||||||
local line = dv.doc:get_selection()
|
|
||||||
return {
|
|
||||||
string.format("%.f%%", line / #dv.doc.lines * 100)
|
|
||||||
}
|
|
||||||
end,
|
|
||||||
tooltip = "caret position"
|
|
||||||
})
|
|
||||||
|
|
||||||
self:add_item({
|
|
||||||
predicate = predicate_docview,
|
|
||||||
name = "doc:selections",
|
|
||||||
alignment = StatusView.Item.LEFT,
|
|
||||||
get_item = function()
|
|
||||||
local dv = core.active_view
|
|
||||||
local nsel = math.floor(#dv.doc.selections / 4)
|
|
||||||
if nsel > 1 then
|
|
||||||
return { style.text, nsel, " selections" }
|
|
||||||
end
|
|
||||||
|
|
||||||
return {}
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
self:add_item({
|
self:add_item({
|
||||||
predicate = predicate_docview,
|
predicate = predicate_docview,
|
||||||
name = "doc:indentation",
|
name = "doc:indentation",
|
||||||
|
@ -319,19 +292,6 @@ function StatusView:register_docview_items()
|
||||||
end,
|
end,
|
||||||
command = "doc:toggle-line-ending"
|
command = "doc:toggle-line-ending"
|
||||||
})
|
})
|
||||||
|
|
||||||
self:add_item {
|
|
||||||
predicate = predicate_docview,
|
|
||||||
name = "doc:overwrite-mode",
|
|
||||||
alignment = StatusView.Item.RIGHT,
|
|
||||||
get_item = function()
|
|
||||||
return {
|
|
||||||
style.text, core.active_view.doc.overwrite and "OVR" or "INS"
|
|
||||||
}
|
|
||||||
end,
|
|
||||||
command = "doc:toggle-overwrite",
|
|
||||||
separator = StatusView.separator2
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -1014,12 +974,6 @@ function StatusView:on_mouse_pressed(button, x, y, clicks)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function StatusView:on_mouse_left()
|
|
||||||
StatusView.super.on_mouse_left(self)
|
|
||||||
self.hovered_item = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function StatusView:on_mouse_moved(x, y, dx, dy)
|
function StatusView:on_mouse_moved(x, y, dx, dy)
|
||||||
if not self.visible then return end
|
if not self.visible then return end
|
||||||
StatusView.super.on_mouse_moved(self, x, y, dx, dy)
|
StatusView.super.on_mouse_moved(self, x, y, dx, dy)
|
||||||
|
|
|
@ -1,23 +1,13 @@
|
||||||
local common = require "core.common"
|
local common = require "core.common"
|
||||||
local style = {}
|
local style = {}
|
||||||
|
|
||||||
|
style.padding = { x = common.round(14 * SCALE), y = common.round(7 * SCALE) }
|
||||||
style.divider_size = common.round(1 * SCALE)
|
style.divider_size = common.round(1 * SCALE)
|
||||||
style.scrollbar_size = common.round(4 * SCALE)
|
style.scrollbar_size = common.round(4 * SCALE)
|
||||||
style.expanded_scrollbar_size = common.round(12 * SCALE)
|
style.expanded_scrollbar_size = common.round(12 * SCALE)
|
||||||
style.caret_width = common.round(2 * SCALE)
|
style.caret_width = common.round(2 * SCALE)
|
||||||
style.tab_width = common.round(170 * SCALE)
|
style.tab_width = common.round(170 * SCALE)
|
||||||
|
|
||||||
style.padding = {
|
|
||||||
x = common.round(14 * SCALE),
|
|
||||||
y = common.round(7 * SCALE),
|
|
||||||
}
|
|
||||||
|
|
||||||
style.margin = {
|
|
||||||
tab = {
|
|
||||||
top = common.round(-style.divider_size * SCALE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-- The function renderer.font.load can accept an option table as a second optional argument.
|
-- The function renderer.font.load can accept an option table as a second optional argument.
|
||||||
-- It shoud be like the following:
|
-- It shoud be like the following:
|
||||||
--
|
--
|
||||||
|
@ -50,4 +40,3 @@ style.syntax_fonts = {}
|
||||||
style.log = {}
|
style.log = {}
|
||||||
|
|
||||||
return style
|
return style
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ local common = require "core.common"
|
||||||
local syntax = {}
|
local syntax = {}
|
||||||
syntax.items = {}
|
syntax.items = {}
|
||||||
|
|
||||||
syntax.plain_text_syntax = { name = "Plain Text", patterns = {}, symbols = {} }
|
local plain_text_syntax = { name = "Plain Text", patterns = {}, symbols = {} }
|
||||||
|
|
||||||
|
|
||||||
function syntax.add(t)
|
function syntax.add(t)
|
||||||
|
@ -46,7 +46,7 @@ end
|
||||||
function syntax.get(filename, header)
|
function syntax.get(filename, header)
|
||||||
return (filename and find(filename, "files"))
|
return (filename and find(filename, "files"))
|
||||||
or (header and find(header, "headers"))
|
or (header and find(header, "headers"))
|
||||||
or syntax.plain_text_syntax
|
or plain_text_syntax
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -112,12 +112,6 @@ function TitleView:on_mouse_pressed(button, x, y, clicks)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function TitleView:on_mouse_left()
|
|
||||||
TitleView.super.on_mouse_left(self)
|
|
||||||
self.hovered_item = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TitleView:on_mouse_moved(px, py, ...)
|
function TitleView:on_mouse_moved(px, py, ...)
|
||||||
if self.size.y == 0 then return end
|
if self.size.y == 0 then return end
|
||||||
TitleView.super.on_mouse_moved(self, px, py, ...)
|
TitleView.super.on_mouse_moved(self, px, py, ...)
|
||||||
|
|
|
@ -28,8 +28,11 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
|
||||||
-- Each position spans characters from i_n to ((i_n+1) - 1), to form
|
-- Each position spans characters from i_n to ((i_n+1) - 1), to form
|
||||||
-- consecutive spans of text.
|
-- consecutive spans of text.
|
||||||
--
|
--
|
||||||
-- Insert the start index at i_1 to make iterating easier
|
-- If i_1 is not equal to start, start is automatically inserted at
|
||||||
|
-- that index.
|
||||||
|
if find_results[3] ~= find_results[1] then
|
||||||
table.insert(find_results, 3, find_results[1])
|
table.insert(find_results, 3, find_results[1])
|
||||||
|
end
|
||||||
-- Copy the ending index to the end of the table, so that an ending index
|
-- Copy the ending index to the end of the table, so that an ending index
|
||||||
-- always follows a starting index after position 3 in the table.
|
-- always follows a starting index after position 3 in the table.
|
||||||
table.insert(find_results, find_results[2] + 1)
|
table.insert(find_results, find_results[2] + 1)
|
||||||
|
@ -39,11 +42,9 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
|
||||||
local fin = find_results[i + 1] - 1
|
local fin = find_results[i + 1] - 1
|
||||||
local type = pattern.type[i - 2]
|
local type = pattern.type[i - 2]
|
||||||
-- ↑ (i - 2) to convert from [3; n] to [1; n]
|
-- ↑ (i - 2) to convert from [3; n] to [1; n]
|
||||||
if fin >= start then
|
|
||||||
local text = full_text:usub(start, fin)
|
local text = full_text:usub(start, fin)
|
||||||
push_token(t, syn.symbols[text] or type, text)
|
push_token(t, syn.symbols[text] or type, text)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
else
|
else
|
||||||
local start, fin = find_results[1], find_results[2]
|
local start, fin = find_results[1], find_results[2]
|
||||||
local text = full_text:usub(start, fin)
|
local text = full_text:usub(start, fin)
|
||||||
|
@ -133,10 +134,8 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
||||||
local res
|
local res
|
||||||
local i = 1
|
local i = 1
|
||||||
|
|
||||||
state = state or string.char(0)
|
|
||||||
|
|
||||||
if #incoming_syntax.patterns == 0 then
|
if #incoming_syntax.patterns == 0 then
|
||||||
return { "normal", text }, state
|
return { "normal", text }
|
||||||
end
|
end
|
||||||
|
|
||||||
state = state or string.char(0)
|
state = state or string.char(0)
|
||||||
|
@ -243,7 +242,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
||||||
res[1] = char_pos_1
|
res[1] = char_pos_1
|
||||||
res[2] = char_pos_2
|
res[2] = char_pos_2
|
||||||
end
|
end
|
||||||
if not res[1] then return end
|
|
||||||
if res[1] and target[3] then
|
if res[1] and target[3] then
|
||||||
-- Check to see if the escaped character is there,
|
-- Check to see if the escaped character is there,
|
||||||
-- and if it is not itself escaped.
|
-- and if it is not itself escaped.
|
||||||
|
@ -255,12 +253,12 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
||||||
if count % 2 == 0 then
|
if count % 2 == 0 then
|
||||||
-- The match is not escaped, so confirm it
|
-- The match is not escaped, so confirm it
|
||||||
break
|
break
|
||||||
else
|
elseif not close then
|
||||||
-- The match is escaped, so avoid it
|
-- The *open* match is escaped, so avoid it
|
||||||
res[1] = false
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
until at_start or not close or not target[3]
|
until not res[1] or not close or not target[3]
|
||||||
return table.unpack(res)
|
return table.unpack(res)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -286,9 +284,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
||||||
if current_pattern_idx > 0 then
|
if current_pattern_idx > 0 then
|
||||||
local p = current_syntax.patterns[current_pattern_idx]
|
local p = current_syntax.patterns[current_pattern_idx]
|
||||||
local s, e = find_text(text, p, i, false, true)
|
local s, e = find_text(text, p, i, false, true)
|
||||||
-- Use the first token type specified in the type table for the "middle"
|
|
||||||
-- part of the subsyntax.
|
|
||||||
local token_type = type(p.type) == "table" and p.type[1] or p.type
|
|
||||||
|
|
||||||
local cont = true
|
local cont = true
|
||||||
-- If we're in subsyntax mode, always check to see if we end our syntax
|
-- If we're in subsyntax mode, always check to see if we end our syntax
|
||||||
|
@ -301,7 +296,7 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
||||||
-- treat the bit after as a token to be normally parsed
|
-- treat the bit after as a token to be normally parsed
|
||||||
-- (as it's the syntax delimiter).
|
-- (as it's the syntax delimiter).
|
||||||
if ss and (s == nil or ss < s) then
|
if ss and (s == nil or ss < s) then
|
||||||
push_token(res, token_type, text:usub(i, ss - 1))
|
push_token(res, p.type, text:usub(i, ss - 1))
|
||||||
i = ss
|
i = ss
|
||||||
cont = false
|
cont = false
|
||||||
end
|
end
|
||||||
|
@ -310,11 +305,11 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
||||||
-- continue on as normal.
|
-- continue on as normal.
|
||||||
if cont then
|
if cont then
|
||||||
if s then
|
if s then
|
||||||
push_token(res, token_type, text:usub(i, e))
|
push_token(res, p.type, text:usub(i, e))
|
||||||
set_subsyntax_pattern_idx(0)
|
set_subsyntax_pattern_idx(0)
|
||||||
i = e + 1
|
i = e + 1
|
||||||
else
|
else
|
||||||
push_token(res, token_type, text:usub(i))
|
push_token(res, p.type, text:usub(i))
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -323,10 +318,9 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
||||||
-- we're ending early in the middle of a delimiter, or
|
-- we're ending early in the middle of a delimiter, or
|
||||||
-- just normally, upon finding a token.
|
-- just normally, upon finding a token.
|
||||||
while subsyntax_info do
|
while subsyntax_info do
|
||||||
local find_results = { find_text(text, subsyntax_info, i, true, true) }
|
local s, e = find_text(text, subsyntax_info, i, true, true)
|
||||||
local s, e = find_results[1], find_results[2]
|
|
||||||
if s then
|
if s then
|
||||||
push_tokens(res, current_syntax, subsyntax_info, text, find_results)
|
push_token(res, subsyntax_info.type, text:usub(i, e))
|
||||||
-- On finding unescaped delimiter, pop it.
|
-- On finding unescaped delimiter, pop it.
|
||||||
pop_subsyntax()
|
pop_subsyntax()
|
||||||
i = e + 1
|
i = e + 1
|
||||||
|
|
|
@ -108,10 +108,6 @@ function View:get_h_scrollable_size()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function View:supports_text_input()
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param x number
|
---@param x number
|
||||||
---@param y number
|
---@param y number
|
||||||
---@return boolean
|
---@return boolean
|
||||||
|
@ -142,14 +138,14 @@ function View:on_mouse_pressed(button, x, y, clicks)
|
||||||
local result = self.v_scrollbar:on_mouse_pressed(button, x, y, clicks)
|
local result = self.v_scrollbar:on_mouse_pressed(button, x, y, clicks)
|
||||||
if result then
|
if result then
|
||||||
if result ~= true then
|
if result ~= true then
|
||||||
self.scroll.to.y = result * (self:get_scrollable_size() - self.size.y)
|
self.scroll.to.y = result * self:get_scrollable_size()
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
result = self.h_scrollbar:on_mouse_pressed(button, x, y, clicks)
|
result = self.h_scrollbar:on_mouse_pressed(button, x, y, clicks)
|
||||||
if result then
|
if result then
|
||||||
if result ~= true then
|
if result ~= true then
|
||||||
self.scroll.to.x = result * (self:get_h_scrollable_size() - self.size.x)
|
self.scroll.to.x = result * self:get_h_scrollable_size()
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -177,7 +173,7 @@ function View:on_mouse_moved(x, y, dx, dy)
|
||||||
result = self.v_scrollbar:on_mouse_moved(x, y, dx, dy)
|
result = self.v_scrollbar:on_mouse_moved(x, y, dx, dy)
|
||||||
if result then
|
if result then
|
||||||
if result ~= true then
|
if result ~= true then
|
||||||
self.scroll.to.y = result * (self:get_scrollable_size() - self.size.y)
|
self.scroll.to.y = result * self:get_scrollable_size()
|
||||||
if not config.animate_drag_scroll then
|
if not config.animate_drag_scroll then
|
||||||
self:clamp_scroll_position()
|
self:clamp_scroll_position()
|
||||||
self.scroll.y = self.scroll.to.y
|
self.scroll.y = self.scroll.to.y
|
||||||
|
@ -191,7 +187,7 @@ function View:on_mouse_moved(x, y, dx, dy)
|
||||||
result = self.h_scrollbar:on_mouse_moved(x, y, dx, dy)
|
result = self.h_scrollbar:on_mouse_moved(x, y, dx, dy)
|
||||||
if result then
|
if result then
|
||||||
if result ~= true then
|
if result ~= true then
|
||||||
self.scroll.to.x = result * (self:get_h_scrollable_size() - self.size.x)
|
self.scroll.to.x = result * self:get_h_scrollable_size()
|
||||||
if not config.animate_drag_scroll then
|
if not config.animate_drag_scroll then
|
||||||
self:clamp_scroll_position()
|
self:clamp_scroll_position()
|
||||||
self.scroll.x = self.scroll.to.x
|
self.scroll.x = self.scroll.to.x
|
||||||
|
@ -248,23 +244,6 @@ function View:get_content_bounds()
|
||||||
return x, y, x + self.size.x, y + self.size.y
|
return x, y, x + self.size.x, y + self.size.y
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param x number
|
|
||||||
---@param y number
|
|
||||||
---@param dx number
|
|
||||||
---@param dy number
|
|
||||||
---@param i number
|
|
||||||
function View:on_touch_moved(x, y, dx, dy, i)
|
|
||||||
if not self.scrollable then return end
|
|
||||||
if self.dragging_scrollbar then
|
|
||||||
local delta = self:get_scrollable_size() / self.size.y * dy
|
|
||||||
self.scroll.to.y = self.scroll.to.y + delta
|
|
||||||
end
|
|
||||||
self.hovered_scrollbar = self:scrollbar_overlaps_point(x, y)
|
|
||||||
|
|
||||||
self.scroll.to.y = self.scroll.to.y + -dy
|
|
||||||
self.scroll.to.x = self.scroll.to.x + -dx
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
---@return number x
|
---@return number x
|
||||||
---@return number y
|
---@return number y
|
||||||
|
@ -287,16 +266,12 @@ end
|
||||||
function View:update_scrollbar()
|
function View:update_scrollbar()
|
||||||
local v_scrollable = self:get_scrollable_size()
|
local v_scrollable = self:get_scrollable_size()
|
||||||
self.v_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, v_scrollable)
|
self.v_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, v_scrollable)
|
||||||
local v_percent = self.scroll.y/(v_scrollable - self.size.y)
|
self.v_scrollbar:set_percent(self.scroll.y/v_scrollable)
|
||||||
-- Avoid setting nan percent
|
|
||||||
self.v_scrollbar:set_percent(v_percent == v_percent and v_percent or 0)
|
|
||||||
self.v_scrollbar:update()
|
self.v_scrollbar:update()
|
||||||
|
|
||||||
local h_scrollable = self:get_h_scrollable_size()
|
local h_scrollable = self:get_h_scrollable_size()
|
||||||
self.h_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, h_scrollable)
|
self.h_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, h_scrollable)
|
||||||
local h_percent = self.scroll.x/(h_scrollable - self.size.x)
|
self.h_scrollbar:set_percent(self.scroll.x/h_scrollable)
|
||||||
-- Avoid setting nan percent
|
|
||||||
self.h_scrollbar:set_percent(h_percent == h_percent and h_percent or 0)
|
|
||||||
self.h_scrollbar:update()
|
self.h_scrollbar:update()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,6 @@ local RootView = require "core.rootview"
|
||||||
local DocView = require "core.docview"
|
local DocView = require "core.docview"
|
||||||
local Doc = require "core.doc"
|
local Doc = require "core.doc"
|
||||||
|
|
||||||
---Symbols cache of all open documents
|
|
||||||
---@type table<core.doc, table>
|
|
||||||
local cache = setmetatable({}, { __mode = "k" })
|
|
||||||
|
|
||||||
config.plugins.autocomplete = common.merge({
|
config.plugins.autocomplete = common.merge({
|
||||||
-- Amount of characters that need to be written for autocomplete
|
-- Amount of characters that need to be written for autocomplete
|
||||||
min_len = 3,
|
min_len = 3,
|
||||||
|
@ -23,16 +19,8 @@ config.plugins.autocomplete = common.merge({
|
||||||
max_suggestions = 100,
|
max_suggestions = 100,
|
||||||
-- Maximum amount of symbols to cache per document
|
-- Maximum amount of symbols to cache per document
|
||||||
max_symbols = 4000,
|
max_symbols = 4000,
|
||||||
-- Which symbols to show on the suggestions list: global, local, related, none
|
|
||||||
suggestions_scope = "global",
|
|
||||||
-- Font size of the description box
|
-- Font size of the description box
|
||||||
desc_font_size = 12,
|
desc_font_size = 12,
|
||||||
-- Do not show the icons associated to the suggestions
|
|
||||||
hide_icons = false,
|
|
||||||
-- Position where icons will be displayed on the suggestions list
|
|
||||||
icon_position = "left",
|
|
||||||
-- Do not show the additional information related to a suggestion
|
|
||||||
hide_info = false,
|
|
||||||
-- The config specification used by gui generators
|
-- The config specification used by gui generators
|
||||||
config_spec = {
|
config_spec = {
|
||||||
name = "Autocomplete",
|
name = "Autocomplete",
|
||||||
|
@ -72,26 +60,6 @@ config.plugins.autocomplete = common.merge({
|
||||||
min = 1000,
|
min = 1000,
|
||||||
max = 10000
|
max = 10000
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label = "Suggestions Scope",
|
|
||||||
description = "Which symbols to show on the suggestions list.",
|
|
||||||
path = "suggestions_scope",
|
|
||||||
type = "selection",
|
|
||||||
default = "global",
|
|
||||||
values = {
|
|
||||||
{"All Documents", "global"},
|
|
||||||
{"Current Document", "local"},
|
|
||||||
{"Related Documents", "related"},
|
|
||||||
{"Known Symbols", "none"}
|
|
||||||
},
|
|
||||||
on_apply = function(value)
|
|
||||||
if value == "global" then
|
|
||||||
for _, doc in ipairs(core.docs) do
|
|
||||||
if cache[doc] then cache[doc] = nil end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label = "Description Font Size",
|
label = "Description Font Size",
|
||||||
description = "Font size of the description box.",
|
description = "Font size of the description box.",
|
||||||
|
@ -99,31 +67,6 @@ config.plugins.autocomplete = common.merge({
|
||||||
type = "number",
|
type = "number",
|
||||||
default = 12,
|
default = 12,
|
||||||
min = 8
|
min = 8
|
||||||
},
|
|
||||||
{
|
|
||||||
label = "Hide Icons",
|
|
||||||
description = "Do not show icons on the suggestions list.",
|
|
||||||
path = "hide_icons",
|
|
||||||
type = "toggle",
|
|
||||||
default = false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label = "Icons Position",
|
|
||||||
description = "Position to display icons on the suggestions list.",
|
|
||||||
path = "icon_position",
|
|
||||||
type = "selection",
|
|
||||||
default = "left",
|
|
||||||
values = {
|
|
||||||
{"Left", "left"},
|
|
||||||
{"Right", "Right"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label = "Hide Items Info",
|
|
||||||
description = "Do not show the additional info related to each suggestion.",
|
|
||||||
path = "hide_info",
|
|
||||||
type = "toggle",
|
|
||||||
default = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, config.plugins.autocomplete)
|
}, config.plugins.autocomplete)
|
||||||
|
@ -133,7 +76,6 @@ local autocomplete = {}
|
||||||
autocomplete.map = {}
|
autocomplete.map = {}
|
||||||
autocomplete.map_manually = {}
|
autocomplete.map_manually = {}
|
||||||
autocomplete.on_close = nil
|
autocomplete.on_close = nil
|
||||||
autocomplete.icons = {}
|
|
||||||
|
|
||||||
-- Flag that indicates if the autocomplete box was manually triggered
|
-- Flag that indicates if the autocomplete box was manually triggered
|
||||||
-- with the autocomplete.complete() function to prevent the suggestions
|
-- with the autocomplete.complete() function to prevent the suggestions
|
||||||
|
@ -153,7 +95,6 @@ function autocomplete.add(t, manually_triggered)
|
||||||
{
|
{
|
||||||
text = text,
|
text = text,
|
||||||
info = info.info,
|
info = info.info,
|
||||||
icon = info.icon, -- Name of icon to show
|
|
||||||
desc = info.desc, -- Description shown on item selected
|
desc = info.desc, -- Description shown on item selected
|
||||||
onhover = info.onhover, -- A callback called once when item is hovered
|
onhover = info.onhover, -- A callback called once when item is hovered
|
||||||
onselect = info.onselect, -- A callback called when item is selected
|
onselect = info.onselect, -- A callback called when item is selected
|
||||||
|
@ -178,35 +119,28 @@ end
|
||||||
--
|
--
|
||||||
-- Thread that scans open document symbols and cache them
|
-- Thread that scans open document symbols and cache them
|
||||||
--
|
--
|
||||||
local global_symbols = {}
|
local max_symbols = config.plugins.autocomplete.max_symbols
|
||||||
|
|
||||||
core.add_thread(function()
|
core.add_thread(function()
|
||||||
local function load_syntax_symbols(doc)
|
local cache = setmetatable({}, { __mode = "k" })
|
||||||
if doc.syntax and not autocomplete.map["language_"..doc.syntax.name] then
|
|
||||||
local symbols = {
|
local function get_syntax_symbols(symbols, doc)
|
||||||
name = "language_"..doc.syntax.name,
|
if doc.syntax then
|
||||||
files = doc.syntax.files,
|
for sym in pairs(doc.syntax.symbols) do
|
||||||
items = {}
|
symbols[sym] = true
|
||||||
}
|
|
||||||
for name, type in pairs(doc.syntax.symbols) do
|
|
||||||
symbols.items[name] = type
|
|
||||||
end
|
end
|
||||||
autocomplete.add(symbols)
|
|
||||||
return symbols.items
|
|
||||||
end
|
end
|
||||||
return {}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_symbols(doc)
|
local function get_symbols(doc)
|
||||||
local s = {}
|
local s = {}
|
||||||
local syntax_symbols = load_syntax_symbols(doc)
|
get_syntax_symbols(s, doc)
|
||||||
local max_symbols = config.plugins.autocomplete.max_symbols
|
|
||||||
if doc.disable_symbols then return s end
|
if doc.disable_symbols then return s end
|
||||||
local i = 1
|
local i = 1
|
||||||
local symbols_count = 0
|
local symbols_count = 0
|
||||||
while i <= #doc.lines do
|
while i <= #doc.lines do
|
||||||
for sym in doc.lines[i]:gmatch(config.symbol_pattern) do
|
for sym in doc.lines[i]:gmatch(config.symbol_pattern) do
|
||||||
if not s[sym] and not syntax_symbols[sym] then
|
if not s[sym] then
|
||||||
symbols_count = symbols_count + 1
|
symbols_count = symbols_count + 1
|
||||||
if symbols_count > max_symbols then
|
if symbols_count > max_symbols then
|
||||||
s = nil
|
s = nil
|
||||||
|
@ -252,18 +186,14 @@ core.add_thread(function()
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
-- update symbol set with doc's symbol set
|
-- update symbol set with doc's symbol set
|
||||||
if config.plugins.autocomplete.suggestions_scope == "global" then
|
|
||||||
for sym in pairs(cache[doc].symbols) do
|
for sym in pairs(cache[doc].symbols) do
|
||||||
symbols[sym] = true
|
symbols[sym] = true
|
||||||
end
|
end
|
||||||
end
|
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- update global symbols list
|
-- update symbols list
|
||||||
if config.plugins.autocomplete.suggestions_scope == "global" then
|
autocomplete.add { name = "open-docs", items = symbols }
|
||||||
global_symbols = symbols
|
|
||||||
end
|
|
||||||
|
|
||||||
-- wait for next scan
|
-- wait for next scan
|
||||||
local valid = true
|
local valid = true
|
||||||
|
@ -310,50 +240,12 @@ local function update_suggestions()
|
||||||
map = autocomplete.map_manually
|
map = autocomplete.map_manually
|
||||||
end
|
end
|
||||||
|
|
||||||
local assigned_sym = {}
|
|
||||||
|
|
||||||
-- get all relevant suggestions for given filename
|
-- get all relevant suggestions for given filename
|
||||||
local items = {}
|
local items = {}
|
||||||
for _, v in pairs(map) do
|
for _, v in pairs(map) do
|
||||||
if common.match_pattern(filename, v.files) then
|
if common.match_pattern(filename, v.files) then
|
||||||
for _, item in pairs(v.items) do
|
for _, item in pairs(v.items) do
|
||||||
table.insert(items, item)
|
table.insert(items, item)
|
||||||
assigned_sym[item.text] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Append the global, local or related text symbols if applicable
|
|
||||||
local scope = config.plugins.autocomplete.suggestions_scope
|
|
||||||
|
|
||||||
if not triggered_manually then
|
|
||||||
local text_symbols = nil
|
|
||||||
|
|
||||||
if scope == "global" then
|
|
||||||
text_symbols = global_symbols
|
|
||||||
elseif scope == "local" and cache[doc] and cache[doc].symbols then
|
|
||||||
text_symbols = cache[doc].symbols
|
|
||||||
elseif scope == "related" then
|
|
||||||
for _, d in ipairs(core.docs) do
|
|
||||||
if doc.syntax == d.syntax then
|
|
||||||
if cache[d].symbols then
|
|
||||||
for name in pairs(cache[d].symbols) do
|
|
||||||
if not assigned_sym[name] then
|
|
||||||
table.insert(items, setmetatable(
|
|
||||||
{text = name, info = "normal"}, mt
|
|
||||||
))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if text_symbols then
|
|
||||||
for name in pairs(text_symbols) do
|
|
||||||
if not assigned_sym[name] then
|
|
||||||
table.insert(items, setmetatable({text = name, info = "normal"}, mt))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -394,23 +286,13 @@ local function get_suggestions_rect(av)
|
||||||
y = y + av:get_line_height() + style.padding.y
|
y = y + av:get_line_height() + style.padding.y
|
||||||
local font = av:get_font()
|
local font = av:get_font()
|
||||||
local th = font:get_height()
|
local th = font:get_height()
|
||||||
local has_icons = false
|
|
||||||
local hide_info = config.plugins.autocomplete.hide_info
|
|
||||||
local hide_icons = config.plugins.autocomplete.hide_icons
|
|
||||||
|
|
||||||
local max_width = 0
|
local max_width = 0
|
||||||
for _, s in ipairs(suggestions) do
|
for _, s in ipairs(suggestions) do
|
||||||
local w = font:get_width(s.text)
|
local w = font:get_width(s.text)
|
||||||
if s.info and not hide_info then
|
if s.info then
|
||||||
w = w + style.font:get_width(s.info) + style.padding.x
|
w = w + style.font:get_width(s.info) + style.padding.x
|
||||||
end
|
end
|
||||||
local icon = s.icon or s.info
|
|
||||||
if not hide_icons and icon and autocomplete.icons[icon] then
|
|
||||||
w = w + autocomplete.icons[icon].font:get_width(
|
|
||||||
autocomplete.icons[icon].char
|
|
||||||
) + (style.padding.x / 2)
|
|
||||||
has_icons = true
|
|
||||||
end
|
|
||||||
max_width = math.max(max_width, w)
|
max_width = math.max(max_width, w)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -437,8 +319,7 @@ local function get_suggestions_rect(av)
|
||||||
x - style.padding.x,
|
x - style.padding.x,
|
||||||
y - style.padding.y,
|
y - style.padding.y,
|
||||||
max_width + style.padding.x * 2,
|
max_width + style.padding.x * 2,
|
||||||
max_items * (th + style.padding.y) + style.padding.y,
|
max_items * (th + style.padding.y) + style.padding.y
|
||||||
has_icons
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function wrap_line(line, max_chars)
|
local function wrap_line(line, max_chars)
|
||||||
|
@ -558,7 +439,7 @@ local function draw_suggestions_box(av)
|
||||||
local ah = config.plugins.autocomplete.max_height
|
local ah = config.plugins.autocomplete.max_height
|
||||||
|
|
||||||
-- draw background rect
|
-- draw background rect
|
||||||
local rx, ry, rw, rh, has_icons = get_suggestions_rect(av)
|
local rx, ry, rw, rh = get_suggestions_rect(av)
|
||||||
renderer.draw_rect(rx, ry, rw, rh, style.background3)
|
renderer.draw_rect(rx, ry, rw, rh, style.background3)
|
||||||
|
|
||||||
-- draw text
|
-- draw text
|
||||||
|
@ -567,52 +448,17 @@ local function draw_suggestions_box(av)
|
||||||
local y = ry + style.padding.y / 2
|
local y = ry + style.padding.y / 2
|
||||||
local show_count = #suggestions <= ah and #suggestions or ah
|
local show_count = #suggestions <= ah and #suggestions or ah
|
||||||
local start_index = suggestions_idx > ah and (suggestions_idx-(ah-1)) or 1
|
local start_index = suggestions_idx > ah and (suggestions_idx-(ah-1)) or 1
|
||||||
local hide_info = config.plugins.autocomplete.hide_info
|
|
||||||
|
|
||||||
for i=start_index, start_index+show_count-1, 1 do
|
for i=start_index, start_index+show_count-1, 1 do
|
||||||
if not suggestions[i] then
|
if not suggestions[i] then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
local s = suggestions[i]
|
local s = suggestions[i]
|
||||||
|
|
||||||
local icon_l_padding, icon_r_padding = 0, 0
|
|
||||||
|
|
||||||
if has_icons then
|
|
||||||
local icon = s.icon or s.info
|
|
||||||
if icon and autocomplete.icons[icon] then
|
|
||||||
local ifont = autocomplete.icons[icon].font
|
|
||||||
local itext = autocomplete.icons[icon].char
|
|
||||||
local icolor = autocomplete.icons[icon].color
|
|
||||||
if i == suggestions_idx then
|
|
||||||
icolor = style.accent
|
|
||||||
elseif type(icolor) == "string" then
|
|
||||||
icolor = style.syntax[icolor]
|
|
||||||
end
|
|
||||||
if config.plugins.autocomplete.icon_position == "left" then
|
|
||||||
common.draw_text(
|
|
||||||
ifont, icolor, itext, "left", rx + style.padding.x, y, rw, lh
|
|
||||||
)
|
|
||||||
icon_l_padding = ifont:get_width(itext) + (style.padding.x / 2)
|
|
||||||
else
|
|
||||||
common.draw_text(
|
|
||||||
ifont, icolor, itext, "right", rx, y, rw - style.padding.x, lh
|
|
||||||
)
|
|
||||||
icon_r_padding = ifont:get_width(itext) + (style.padding.x / 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local color = (i == suggestions_idx) and style.accent or style.text
|
local color = (i == suggestions_idx) and style.accent or style.text
|
||||||
common.draw_text(
|
common.draw_text(font, color, s.text, "left", rx + style.padding.x, y, rw, lh)
|
||||||
font, color, s.text, "left",
|
if s.info then
|
||||||
rx + icon_l_padding + style.padding.x, y, rw, lh
|
|
||||||
)
|
|
||||||
if s.info and not hide_info then
|
|
||||||
color = (i == suggestions_idx) and style.text or style.dim
|
color = (i == suggestions_idx) and style.text or style.dim
|
||||||
common.draw_text(
|
common.draw_text(style.font, color, s.info, "right", rx, y, rw - style.padding.x, lh)
|
||||||
style.font, color, s.info, "right",
|
|
||||||
rx, y, rw - icon_r_padding - style.padding.x, lh
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
y = y + lh
|
y = y + lh
|
||||||
if suggestions_idx == i then
|
if suggestions_idx == i then
|
||||||
|
@ -773,31 +619,6 @@ function autocomplete.can_complete()
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
---Register a font icon that can be assigned to completion items.
|
|
||||||
---@param name string
|
|
||||||
---@param character string
|
|
||||||
---@param font? renderer.font
|
|
||||||
---@param color? string | renderer.color A style.syntax[] name or specific color
|
|
||||||
function autocomplete.add_icon(name, character, font, color)
|
|
||||||
local color_type = type(color)
|
|
||||||
assert(
|
|
||||||
not color or color_type == "table"
|
|
||||||
or (color_type == "string" and style.syntax[color]),
|
|
||||||
"invalid icon color given"
|
|
||||||
)
|
|
||||||
autocomplete.icons[name] = {
|
|
||||||
char = character,
|
|
||||||
font = font or style.code_font,
|
|
||||||
color = color or "keyword"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Register built-in syntax symbol types icon
|
|
||||||
--
|
|
||||||
for name, _ in pairs(style.syntax) do
|
|
||||||
autocomplete.add_icon(name, "M", style.icon_font, name)
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Commands
|
-- Commands
|
||||||
|
@ -811,6 +632,7 @@ command.add(predicate, {
|
||||||
["autocomplete:complete"] = function(dv)
|
["autocomplete:complete"] = function(dv)
|
||||||
local doc = dv.doc
|
local doc = dv.doc
|
||||||
local item = suggestions[suggestions_idx]
|
local item = suggestions[suggestions_idx]
|
||||||
|
local text = item.text
|
||||||
local inserted = false
|
local inserted = false
|
||||||
if item.onselect then
|
if item.onselect then
|
||||||
inserted = item.onselect(suggestions_idx, item)
|
inserted = item.onselect(suggestions_idx, item)
|
||||||
|
|
|
@ -37,11 +37,7 @@ local function optimal_indent_from_stat(stat)
|
||||||
elseif
|
elseif
|
||||||
indent > stat[y]
|
indent > stat[y]
|
||||||
and
|
and
|
||||||
(
|
|
||||||
indent_occurrences_more_than_once(stat, y)
|
indent_occurrences_more_than_once(stat, y)
|
||||||
or
|
|
||||||
(y == count and stat[y] > 1)
|
|
||||||
)
|
|
||||||
then
|
then
|
||||||
score = 0
|
score = 0
|
||||||
break
|
break
|
||||||
|
@ -122,10 +118,10 @@ local function get_comment_patterns(syntax, _loop)
|
||||||
end
|
end
|
||||||
if type(pattern.regex) == "table" then
|
if type(pattern.regex) == "table" then
|
||||||
table.insert(comments, {
|
table.insert(comments, {
|
||||||
"r", regex.compile(startp), regex.compile(pattern.regex[2]), r=startp
|
"r", regex.compile(startp), regex.compile(pattern.regex[2])
|
||||||
})
|
})
|
||||||
elseif not_is_string then
|
elseif not_is_string then
|
||||||
table.insert(comments, {"r", regex.compile(startp), r=startp})
|
table.insert(comments, {"r", regex.compile(startp)})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif pattern.syntax then
|
elseif pattern.syntax then
|
||||||
|
@ -156,25 +152,6 @@ local function get_comment_patterns(syntax, _loop)
|
||||||
table.insert(comments, {"p", "^%s*" .. block_comment[1], block_comment[2]})
|
table.insert(comments, {"p", "^%s*" .. block_comment[1], block_comment[2]})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Put comments first and strings last
|
|
||||||
table.sort(comments, function(c1, c2)
|
|
||||||
local comment1, comment2 = false, false
|
|
||||||
if
|
|
||||||
(c1[1] == "p" and string.find(c1[2], "^%s*", 1, true))
|
|
||||||
or
|
|
||||||
(c1[1] == "r" and string.find(c1["r"], "^\\s*", 1, true))
|
|
||||||
then
|
|
||||||
comment1 = true
|
|
||||||
end
|
|
||||||
if
|
|
||||||
(c2[1] == "p" and string.find(c2[2], "^%s*", 1, true))
|
|
||||||
or
|
|
||||||
(c2[1] == "r" and string.find(c2["r"], "^\\s*", 1, true))
|
|
||||||
then
|
|
||||||
comment2 = true
|
|
||||||
end
|
|
||||||
return comment1 and not comment2
|
|
||||||
end)
|
|
||||||
comments_cache[syntax] = comments
|
comments_cache[syntax] = comments
|
||||||
if #comments > 0 then
|
if #comments > 0 then
|
||||||
return comments
|
return comments
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
-- mod-version:3
|
-- mod-version:3
|
||||||
|
|
||||||
local core = require "core"
|
|
||||||
local style = require "core.style"
|
local style = require "core.style"
|
||||||
local DocView = require "core.docview"
|
local DocView = require "core.docview"
|
||||||
local common = require "core.common"
|
local common = require "core.common"
|
||||||
|
@ -13,7 +12,6 @@ config.plugins.drawwhitespace = common.merge({
|
||||||
show_leading = true,
|
show_leading = true,
|
||||||
show_trailing = true,
|
show_trailing = true,
|
||||||
show_middle = true,
|
show_middle = true,
|
||||||
show_selected_only = false,
|
|
||||||
|
|
||||||
show_middle_min = 1,
|
show_middle_min = 1,
|
||||||
|
|
||||||
|
@ -67,13 +65,6 @@ config.plugins.drawwhitespace = common.merge({
|
||||||
type = "toggle",
|
type = "toggle",
|
||||||
default = true,
|
default = true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label = "Show Selected Only",
|
|
||||||
description = "Only draw whitespaces if it is within a selection.",
|
|
||||||
path = "show_selected_only",
|
|
||||||
type = "toggle",
|
|
||||||
default = false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label = "Show Trailing as Error",
|
label = "Show Trailing as Error",
|
||||||
description = "Uses an error square to spot them easily, requires 'Show Trailing' enabled.",
|
description = "Uses an error square to spot them easily, requires 'Show Trailing' enabled.",
|
||||||
|
@ -302,41 +293,11 @@ function DocView:draw_line_text(idx, x, y)
|
||||||
for i=1,#cache,4 do
|
for i=1,#cache,4 do
|
||||||
local tx = cache[i + 1] + x
|
local tx = cache[i + 1] + x
|
||||||
local tw = cache[i + 2]
|
local tw = cache[i + 2]
|
||||||
|
if tx <= x2 then
|
||||||
local sub = cache[i]
|
local sub = cache[i]
|
||||||
local color = cache[i + 3]
|
local color = cache[i + 3]
|
||||||
local partials = {}
|
if tx + tw >= x1 then
|
||||||
if config.plugins.drawwhitespace.show_selected_only and self.doc:has_any_selection() then
|
tx = renderer.draw_text(font, sub, tx, ty, color)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -128,7 +128,6 @@ syntax.add {
|
||||||
{ pattern = { "```go", "```" }, type = "string", syntax = ".go" },
|
{ pattern = { "```go", "```" }, type = "string", syntax = ".go" },
|
||||||
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
|
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
|
||||||
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
|
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
|
||||||
{ pattern = { "```nix", "```" }, type = "string", syntax = ".nix" },
|
|
||||||
{ pattern = { "```", "```" }, type = "string" },
|
{ pattern = { "```", "```" }, type = "string" },
|
||||||
{ pattern = { "``", "``" }, type = "string" },
|
{ pattern = { "``", "``" }, type = "string" },
|
||||||
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" },
|
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" },
|
||||||
|
@ -267,4 +266,3 @@ core.add_thread(function()
|
||||||
coroutine.yield(1)
|
coroutine.yield(1)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,6 @@ config.plugins.lineguide = common.merge({
|
||||||
-- 120,
|
-- 120,
|
||||||
config.line_limit
|
config.line_limit
|
||||||
},
|
},
|
||||||
use_custom_color = false,
|
|
||||||
custom_color = style.selection,
|
|
||||||
-- The config specification used by gui generators
|
-- The config specification used by gui generators
|
||||||
config_spec = {
|
config_spec = {
|
||||||
name = "Line Guide",
|
name = "Line Guide",
|
||||||
|
@ -65,21 +63,7 @@ config.plugins.lineguide = common.merge({
|
||||||
end
|
end
|
||||||
return new_rulers
|
return new_rulers
|
||||||
end
|
end
|
||||||
},
|
}
|
||||||
{
|
|
||||||
label = "Use Custom Color",
|
|
||||||
description = "Enable the utilization of a custom line color.",
|
|
||||||
path = "use_custom_color",
|
|
||||||
type = "toggle",
|
|
||||||
default = false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label = "Custom Color",
|
|
||||||
description = "Applied when the above toggle is enabled.",
|
|
||||||
path = "custom_color",
|
|
||||||
type = "color",
|
|
||||||
default = style.selection
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}, config.plugins.lineguide)
|
}, config.plugins.lineguide)
|
||||||
|
|
||||||
|
@ -102,12 +86,10 @@ function DocView:draw_overlay(...)
|
||||||
and
|
and
|
||||||
self:is(DocView)
|
self:is(DocView)
|
||||||
then
|
then
|
||||||
local conf = config.plugins.lineguide
|
|
||||||
local line_x = self:get_line_screen_position(1)
|
local line_x = self:get_line_screen_position(1)
|
||||||
local character_width = self:get_font():get_width("n")
|
local character_width = self:get_font():get_width("n")
|
||||||
local ruler_width = config.plugins.lineguide.width
|
local ruler_width = config.plugins.lineguide.width
|
||||||
local ruler_color = conf.use_custom_color and conf.custom_color
|
local ruler_color = style.guide or style.selection
|
||||||
or (style.guide or style.selection)
|
|
||||||
|
|
||||||
for k,v in ipairs(config.plugins.lineguide.rulers) do
|
for k,v in ipairs(config.plugins.lineguide.rulers) do
|
||||||
local ruler = get_ruler(v)
|
local ruler = get_ruler(v)
|
||||||
|
|
|
@ -37,7 +37,7 @@ local function find_all_matches_in_file(t, filename, fn)
|
||||||
table.insert(t, { file = filename, text = (start_index > 1 and "..." or "") .. line:sub(start_index, 256 + start_index), line = n, col = s })
|
table.insert(t, { file = filename, text = (start_index > 1 and "..." or "") .. line:sub(start_index, 256 + start_index), line = n, col = s })
|
||||||
core.redraw = true
|
core.redraw = true
|
||||||
end
|
end
|
||||||
if n % 100 == 0 then coroutine.yield(0) end
|
if n % 100 == 0 then coroutine.yield() end
|
||||||
n = n + 1
|
n = n + 1
|
||||||
core.redraw = true
|
core.redraw = true
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,7 +48,7 @@ end
|
||||||
|
|
||||||
function ToolbarView:get_icon_width()
|
function ToolbarView:get_icon_width()
|
||||||
local max_width = 0
|
local max_width = 0
|
||||||
for i,v in ipairs(self.toolbar_commands) do max_width = math.max(max_width, (v.font or self.toolbar_font):get_width(v.symbol)) end
|
for i,v in ipairs(self.toolbar_commands) do max_width = math.max(max_width, self.toolbar_font:get_width(v.symbol)) end
|
||||||
return max_width
|
return max_width
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ function ToolbarView:draw()
|
||||||
|
|
||||||
for item, x, y, w, h in self:each_item() do
|
for item, x, y, w, h in self:each_item() do
|
||||||
local color = item == self.hovered_item and command.is_valid(item.command) and style.text or style.dim
|
local color = item == self.hovered_item and command.is_valid(item.command) and style.text or style.dim
|
||||||
common.draw_text(item.font or self.toolbar_font, color, item.symbol, nil, x, y, 0, h)
|
common.draw_text(self.toolbar_font, color, item.symbol, nil, x, y, 0, h)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -100,16 +100,6 @@ function ToolbarView:on_mouse_pressed(button, x, y, clicks)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function ToolbarView:on_mouse_left()
|
|
||||||
ToolbarView.super.on_mouse_left(self)
|
|
||||||
if self.tooltip then
|
|
||||||
core.status_view:remove_tooltip()
|
|
||||||
self.tooltip = false
|
|
||||||
end
|
|
||||||
self.hovered_item = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function ToolbarView:on_mouse_moved(px, py, ...)
|
function ToolbarView:on_mouse_moved(px, py, ...)
|
||||||
if not self.visible then return end
|
if not self.visible then return end
|
||||||
ToolbarView.super.on_mouse_moved(self, px, py, ...)
|
ToolbarView.super.on_mouse_moved(self, px, py, ...)
|
||||||
|
|
|
@ -9,15 +9,10 @@ local View = require "core.view"
|
||||||
local ContextMenu = require "core.contextmenu"
|
local ContextMenu = require "core.contextmenu"
|
||||||
local RootView = require "core.rootview"
|
local RootView = require "core.rootview"
|
||||||
local CommandView = require "core.commandview"
|
local CommandView = require "core.commandview"
|
||||||
local DocView = require "core.docview"
|
|
||||||
|
|
||||||
config.plugins.treeview = common.merge({
|
config.plugins.treeview = common.merge({
|
||||||
-- Default treeview width
|
-- Default treeview width
|
||||||
size = 200 * SCALE,
|
size = 200 * SCALE
|
||||||
highlight_focused_file = true,
|
|
||||||
expand_dirs_to_focused_file = false,
|
|
||||||
scroll_to_focused_file = false,
|
|
||||||
animate_scroll_to_focused_file = true
|
|
||||||
}, config.plugins.treeview)
|
}, config.plugins.treeview)
|
||||||
|
|
||||||
local tooltip_offset = style.font:get_height()
|
local tooltip_offset = style.font:get_height()
|
||||||
|
@ -51,7 +46,7 @@ function TreeView:new()
|
||||||
self.target_size = config.plugins.treeview.size
|
self.target_size = config.plugins.treeview.size
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
self.tooltip = { x = 0, y = 0, begin = 0, alpha = 0 }
|
self.tooltip = { x = 0, y = 0, begin = 0, alpha = 0 }
|
||||||
self.last_scroll_y = 0
|
self.cursor_pos = { x = 0, y = 0 }
|
||||||
|
|
||||||
self.item_icon_width = 0
|
self.item_icon_width = 0
|
||||||
self.item_text_spacing = 0
|
self.item_text_spacing = 0
|
||||||
|
@ -174,72 +169,19 @@ function TreeView:each_item()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function TreeView:set_selection(selection, selection_y, center, instant)
|
function TreeView:set_selection(selection, selection_y)
|
||||||
self.selected_item = selection
|
self.selected_item = selection
|
||||||
if selection and selection_y
|
if selection and selection_y
|
||||||
and (selection_y <= 0 or selection_y >= self.size.y) then
|
and (selection_y <= 0 or selection_y >= self.size.y) then
|
||||||
|
|
||||||
local lh = self:get_item_height()
|
local lh = self:get_item_height()
|
||||||
if not center and selection_y >= self.size.y - lh then
|
if selection_y >= self.size.y - lh then
|
||||||
selection_y = selection_y - self.size.y + lh
|
selection_y = selection_y - self.size.y + lh
|
||||||
end
|
end
|
||||||
if center then
|
|
||||||
selection_y = selection_y - (self.size.y - lh) / 2
|
|
||||||
end
|
|
||||||
local _, y = self:get_content_offset()
|
local _, y = self:get_content_offset()
|
||||||
self.scroll.to.y = selection_y - y
|
self.scroll.to.y = selection and (selection_y - y)
|
||||||
self.scroll.to.y = common.clamp(self.scroll.to.y, 0, self:get_scrollable_size() - self.size.y)
|
|
||||||
if instant then
|
|
||||||
self.scroll.y = self.scroll.to.y
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
---Sets the selection to the file with the specified path.
|
|
||||||
---
|
|
||||||
---@param path string #Absolute path of item to select
|
|
||||||
---@param expand boolean #Expand dirs leading to the item
|
|
||||||
---@param scroll_to boolean #Scroll to make the item visible
|
|
||||||
---@param instant boolean #Don't animate the scroll
|
|
||||||
---@return table? #The selected item
|
|
||||||
function TreeView:set_selection_to_path(path, expand, scroll_to, instant)
|
|
||||||
local to_select, to_select_y
|
|
||||||
local let_it_finish, done
|
|
||||||
::restart::
|
|
||||||
for item, x,y,w,h in self:each_item() do
|
|
||||||
if not done then
|
|
||||||
if item.type == "dir" then
|
|
||||||
local _, to = string.find(path, item.abs_filename..PATHSEP, 1, true)
|
|
||||||
if to and to == #item.abs_filename + #PATHSEP then
|
|
||||||
to_select, to_select_y = item, y
|
|
||||||
if expand and not item.expanded then
|
|
||||||
-- Use TreeView:toggle_expand to update the directory structure.
|
|
||||||
-- Directly using item.expanded doesn't update the cached tree.
|
|
||||||
self:toggle_expand(true, item)
|
|
||||||
-- Because we altered the size of the TreeView
|
|
||||||
-- and because TreeView:get_scrollable_size uses self.count_lines
|
|
||||||
-- which gets updated only when TreeView:each_item finishes,
|
|
||||||
-- we can't stop here or we risk that the scroll
|
|
||||||
-- gets clamped by View:clamp_scroll_position.
|
|
||||||
let_it_finish = true
|
|
||||||
-- We need to restart the process because if TreeView:toggle_expand
|
|
||||||
-- altered the cache, TreeView:each_item risks looping indefinitely.
|
|
||||||
goto restart
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if item.abs_filename == path then
|
|
||||||
to_select, to_select_y = item, y
|
|
||||||
done = true
|
|
||||||
if not let_it_finish then break end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if to_select then
|
|
||||||
self:set_selection(to_select, scroll_to and to_select_y, true, instant)
|
|
||||||
end
|
|
||||||
return to_select
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:get_text_bounding_box(item, x, y, w, h)
|
function TreeView:get_text_bounding_box(item, x, y, w, h)
|
||||||
|
@ -251,9 +193,10 @@ function TreeView:get_text_bounding_box(item, x, y, w, h)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:on_mouse_moved(px, py, ...)
|
function TreeView:on_mouse_moved(px, py, ...)
|
||||||
if not self.visible then return end
|
if not self.visible then return end
|
||||||
|
self.cursor_pos.x = px
|
||||||
|
self.cursor_pos.y = py
|
||||||
if TreeView.super.on_mouse_moved(self, px, py, ...) then
|
if TreeView.super.on_mouse_moved(self, px, py, ...) then
|
||||||
-- mouse movement handled by the View (scrollbar)
|
-- mouse movement handled by the View (scrollbar)
|
||||||
self.hovered_item = nil
|
self.hovered_item = nil
|
||||||
|
@ -280,12 +223,6 @@ function TreeView:on_mouse_moved(px, py, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function TreeView:on_mouse_left()
|
|
||||||
TreeView.super.on_mouse_left(self)
|
|
||||||
self.hovered_item = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:update()
|
function TreeView:update()
|
||||||
-- update width
|
-- update width
|
||||||
local dest = self.visible and self.target_size or 0
|
local dest = self.visible and self.target_size or 0
|
||||||
|
@ -296,7 +233,7 @@ function TreeView:update()
|
||||||
self:move_towards(self.size, "x", dest, nil, "treeview")
|
self:move_towards(self.size, "x", dest, nil, "treeview")
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.size.x == 0 or self.size.y == 0 or not self.visible then return end
|
if not self.visible then return end
|
||||||
|
|
||||||
local duration = system.get_time() - self.tooltip.begin
|
local duration = system.get_time() - self.tooltip.begin
|
||||||
if self.hovered_item and self.tooltip.x and duration > tooltip_delay then
|
if self.hovered_item and self.tooltip.x and duration > tooltip_delay then
|
||||||
|
@ -309,30 +246,10 @@ function TreeView:update()
|
||||||
self.item_text_spacing = style.icon_font:get_width("f") / 2
|
self.item_text_spacing = style.icon_font:get_width("f") / 2
|
||||||
|
|
||||||
-- this will make sure hovered_item is updated
|
-- this will make sure hovered_item is updated
|
||||||
local dy = math.abs(self.last_scroll_y - self.scroll.y)
|
-- we don't want events when the thing is scrolling fast
|
||||||
if dy > 0 then
|
local dy = math.abs(self.scroll.to.y - self.scroll.y)
|
||||||
self:on_mouse_moved(core.root_view.mouse.x, core.root_view.mouse.y, 0, 0)
|
if self.scroll.to.y ~= 0 and dy < self:get_item_height() then
|
||||||
self.last_scroll_y = self.scroll.y
|
self:on_mouse_moved(self.cursor_pos.x, self.cursor_pos.y, 0, 0)
|
||||||
end
|
|
||||||
|
|
||||||
local config = config.plugins.treeview
|
|
||||||
if config.highlight_focused_file then
|
|
||||||
-- Try to only highlight when we actually change tabs
|
|
||||||
local current_node = core.root_view:get_active_node()
|
|
||||||
local current_active_view = core.active_view
|
|
||||||
if current_node and not current_node.locked
|
|
||||||
and current_active_view ~= self and current_active_view ~= self.last_active_view then
|
|
||||||
self.selected_item = nil
|
|
||||||
self.last_active_view = current_active_view
|
|
||||||
if DocView:is_extended_by(current_active_view) then
|
|
||||||
local abs_filename = current_active_view.doc
|
|
||||||
and current_active_view.doc.abs_filename or ""
|
|
||||||
self:set_selection_to_path(abs_filename,
|
|
||||||
config.expand_dirs_to_focused_file,
|
|
||||||
config.scroll_to_focused_file,
|
|
||||||
not config.animate_scroll_to_focused_file)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
TreeView.super.update(self)
|
TreeView.super.update(self)
|
||||||
|
@ -505,8 +422,8 @@ function TreeView:get_previous(item)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function TreeView:toggle_expand(toggle, item)
|
function TreeView:toggle_expand(toggle)
|
||||||
item = item or self.selected_item
|
local item = self.selected_item
|
||||||
|
|
||||||
if not item then return end
|
if not item then return end
|
||||||
|
|
||||||
|
@ -524,11 +441,6 @@ function TreeView:toggle_expand(toggle, item)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function TreeView:open_doc(filename)
|
|
||||||
core.root_view:open_doc(core.open_doc(filename))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- init
|
-- init
|
||||||
local view = TreeView()
|
local view = TreeView()
|
||||||
local node = core.root_view:get_active_node()
|
local node = core.root_view:get_active_node()
|
||||||
|
@ -707,7 +619,8 @@ command.add(
|
||||||
if core.last_active_view and core.active_view == view then
|
if core.last_active_view and core.active_view == view then
|
||||||
core.set_active_view(core.last_active_view)
|
core.set_active_view(core.last_active_view)
|
||||||
end
|
end
|
||||||
view:open_doc(core.normalize_to_project_dir(item.abs_filename))
|
local doc_filename = core.normalize_to_project_dir(item.abs_filename)
|
||||||
|
core.root_view:open_doc(core.open_doc(doc_filename))
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -753,26 +666,6 @@ command.add(
|
||||||
view:toggle_expand(true)
|
view:toggle_expand(true)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["treeview-context:show"] = function()
|
|
||||||
if view.hovered_item then
|
|
||||||
menu:show(core.root_view.mouse.x, core.root_view.mouse.y)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local item = view.selected_item
|
|
||||||
if not item then return end
|
|
||||||
|
|
||||||
local x, y
|
|
||||||
for _i, _x, _y, _w, _h in view:each_item() do
|
|
||||||
if _i == item then
|
|
||||||
x = _x + _w / 2
|
|
||||||
y = _y + _h / 2
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
menu:show(x, y)
|
|
||||||
end
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -866,7 +759,7 @@ command.add(
|
||||||
local file = io.open(doc_filename, "a+")
|
local file = io.open(doc_filename, "a+")
|
||||||
file:write("")
|
file:write("")
|
||||||
file:close()
|
file:close()
|
||||||
view:open_doc(doc_filename)
|
core.root_view:open_doc(core.open_doc(doc_filename))
|
||||||
core.log("Created %s", doc_filename)
|
core.log("Created %s", doc_filename)
|
||||||
end,
|
end,
|
||||||
suggest = function(text)
|
suggest = function(text)
|
||||||
|
@ -896,12 +789,10 @@ command.add(
|
||||||
["treeview:open-in-system"] = function(item)
|
["treeview:open-in-system"] = function(item)
|
||||||
if PLATFORM == "Windows" then
|
if PLATFORM == "Windows" then
|
||||||
system.exec(string.format("start \"\" %q", item.abs_filename))
|
system.exec(string.format("start \"\" %q", item.abs_filename))
|
||||||
elseif string.find(PLATFORM, "Mac") or PLATFORM == "MorphOS" then
|
elseif string.find(PLATFORM, "Mac") then
|
||||||
system.exec(string.format("open %q", item.abs_filename))
|
system.exec(string.format("open %q", item.abs_filename))
|
||||||
elseif PLATFORM == "Linux" or string.find(PLATFORM, "BSD") then
|
elseif PLATFORM == "Linux" or string.find(PLATFORM, "BSD") then
|
||||||
system.exec(string.format("xdg-open %q", item.abs_filename))
|
system.exec(string.format("xdg-open %q", item.abs_filename))
|
||||||
elseif PLATFORM == "AmigaOS 4" then
|
|
||||||
system.exec(string.format("WBRUN %q SHOW=all VIEWBY=name", item.abs_filename))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
@ -935,25 +826,6 @@ command.add(function()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
command.add(
|
|
||||||
function()
|
|
||||||
return menu.show_context_menu == true and core.active_view:is(TreeView)
|
|
||||||
end, {
|
|
||||||
["treeview-context:focus-previous"] = function()
|
|
||||||
menu:focus_previous()
|
|
||||||
end,
|
|
||||||
["treeview-context:focus-next"] = function()
|
|
||||||
menu:focus_next()
|
|
||||||
end,
|
|
||||||
["treeview-context:hide"] = function()
|
|
||||||
menu:hide()
|
|
||||||
end,
|
|
||||||
["treeview-context:on-selected"] = function()
|
|
||||||
menu:call_selected_item()
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
keymap.add {
|
keymap.add {
|
||||||
["ctrl+\\"] = "treeview:toggle",
|
["ctrl+\\"] = "treeview:toggle",
|
||||||
["up"] = "treeview:previous",
|
["up"] = "treeview:previous",
|
||||||
|
@ -969,15 +841,6 @@ keymap.add {
|
||||||
["ctrl+lclick"] = "treeview:new-folder"
|
["ctrl+lclick"] = "treeview:new-folder"
|
||||||
}
|
}
|
||||||
|
|
||||||
keymap.add {
|
|
||||||
["menu"] = "treeview-context:show",
|
|
||||||
["return"] = "treeview-context:on-selected",
|
|
||||||
["up"] = "treeview-context:focus-previous",
|
|
||||||
["down"] = "treeview-context:focus-next",
|
|
||||||
["escape"] = "treeview-context:hide"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- The config specification used by gui generators
|
-- The config specification used by gui generators
|
||||||
config.plugins.treeview.config_spec = {
|
config.plugins.treeview.config_spec = {
|
||||||
name = "Treeview",
|
name = "Treeview",
|
||||||
|
|
|
@ -208,10 +208,10 @@ else
|
||||||
install_data('resources/icons/lite-xl.svg',
|
install_data('resources/icons/lite-xl.svg',
|
||||||
install_dir : 'share/icons/hicolor/scalable/apps'
|
install_dir : 'share/icons/hicolor/scalable/apps'
|
||||||
)
|
)
|
||||||
install_data('resources/linux/com.lite_xl.LiteXL.desktop',
|
install_data('resources/linux/org.lite_xl.lite_xl.desktop',
|
||||||
install_dir : 'share/applications'
|
install_dir : 'share/applications'
|
||||||
)
|
)
|
||||||
install_data('resources/linux/com.lite_xl.LiteXL.appdata.xml',
|
install_data('resources/linux/org.lite_xl.lite_xl.appdata.xml',
|
||||||
install_dir : 'share/metainfo'
|
install_dir : 'share/metainfo'
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -138,7 +138,7 @@ generate_appimage() {
|
||||||
mv AppRun LiteXL.AppDir/
|
mv AppRun LiteXL.AppDir/
|
||||||
# These could be symlinks but it seems they doesn't work with AppimageLauncher
|
# These could be symlinks but it seems they doesn't work with AppimageLauncher
|
||||||
cp resources/icons/lite-xl.svg LiteXL.AppDir/
|
cp resources/icons/lite-xl.svg LiteXL.AppDir/
|
||||||
cp resources/linux/com.lite_xl.LiteXL.desktop LiteXL.AppDir/
|
cp resources/linux/org.lite_xl.lite_xl.desktop LiteXL.AppDir/
|
||||||
|
|
||||||
if [[ $ADDONS == true ]]; then
|
if [[ $ADDONS == true ]]; then
|
||||||
addons_download "${BUILD_DIR}"
|
addons_download "${BUILD_DIR}"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#define MyAppName "Lite XL"
|
#define MyAppName "Lite XL"
|
||||||
#define MyAppVersion "@PROJECT_VERSION@"
|
#define MyAppVersion "@PROJECT_VERSION@"
|
||||||
#define MyAppPublisher "Lite XL Team"
|
#define MyAppPublisher "Lite XL Team"
|
||||||
#define MyAppURL "https://lite-xl.github.io"
|
#define MyAppURL "https://lite-xl.com"
|
||||||
#define MyAppExeName "lite-xl.exe"
|
#define MyAppExeName "lite-xl.exe"
|
||||||
#define BuildDir "@PROJECT_BUILD_DIR@"
|
#define BuildDir "@PROJECT_BUILD_DIR@"
|
||||||
#define SourceDir "@PROJECT_SOURCE_DIR@"
|
#define SourceDir "@PROJECT_SOURCE_DIR@"
|
||||||
|
@ -69,9 +69,7 @@ Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescrip
|
||||||
Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked
|
Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked
|
||||||
|
|
||||||
[Files]
|
[Files]
|
||||||
Source: "{#BuildDir}/src/lite-xl.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "{#SourceDir}/lite-xl/*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
|
||||||
Source: "{#BuildDir}/mingwLibs{#Arch}/*"; DestDir: "{app}"; Flags: ignoreversion ; Check: DirExists(ExpandConstant('{#BuildDir}/mingwLibs{#Arch}'))
|
|
||||||
Source: "{#SourceDir}/data/*"; DestDir: "{app}/data"; Flags: ignoreversion recursesubdirs
|
|
||||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||||
|
|
||||||
[Icons]
|
[Icons]
|
||||||
|
|
|
@ -29,3 +29,4 @@ void api_load_libs(lua_State *L) {
|
||||||
for (int i = 0; libs[i].name; i++)
|
for (int i = 0; libs[i].name; i++)
|
||||||
luaL_requiref(L, libs[i].name, libs[i].func, 1);
|
luaL_requiref(L, libs[i].name, libs[i].func, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -391,4 +391,3 @@ int luaopen_renderer(lua_State *L) {
|
||||||
lua_setfield(L, -2, "font");
|
lua_setfield(L, -2, "font");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -369,21 +369,6 @@ top:
|
||||||
lua_pushinteger(L, (lua_Integer)(e.tfinger.dy * h));
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.dy * h));
|
||||||
lua_pushinteger(L, e.tfinger.fingerId);
|
lua_pushinteger(L, e.tfinger.fingerId);
|
||||||
return 6;
|
return 6;
|
||||||
case SDL_APP_WILLENTERFOREGROUND:
|
|
||||||
case SDL_APP_DIDENTERFOREGROUND:
|
|
||||||
#ifdef LITE_USE_SDL_RENDERER
|
|
||||||
rencache_invalidate();
|
|
||||||
#else
|
|
||||||
SDL_UpdateWindowSurface(window_renderer->window);
|
|
||||||
#endif
|
|
||||||
lua_pushstring(L, e.type == SDL_APP_WILLENTERFOREGROUND ? "enteringforeground" : "enteredforeground");
|
|
||||||
return 1;
|
|
||||||
case SDL_APP_WILLENTERBACKGROUND:
|
|
||||||
lua_pushstring(L, "enteringbackground");
|
|
||||||
return 1;
|
|
||||||
case SDL_APP_DIDENTERBACKGROUND:
|
|
||||||
lua_pushstring(L, "enteredbackground");
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
goto top;
|
goto top;
|
||||||
|
@ -1087,7 +1072,6 @@ static int f_path_compare(lua_State *L) {
|
||||||
size_t offset = 0, i, j;
|
size_t offset = 0, i, j;
|
||||||
for (i = 0; i < len1 && i < len2; i++) {
|
for (i = 0; i < len1 && i < len2; i++) {
|
||||||
if (path1[i] != path2[i]) break;
|
if (path1[i] != path2[i]) break;
|
||||||
if (isdigit(path1[i])) break;
|
|
||||||
if (path1[i] == PATHSEP) {
|
if (path1[i] == PATHSEP) {
|
||||||
offset = i + 1;
|
offset = i + 1;
|
||||||
}
|
}
|
||||||
|
@ -1158,15 +1142,6 @@ static int f_path_compare(lua_State *L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int f_text_input(lua_State* L) {
|
|
||||||
if (lua_toboolean(L, 1))
|
|
||||||
SDL_StartTextInput();
|
|
||||||
else
|
|
||||||
SDL_StopTextInput();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const luaL_Reg lib[] = {
|
static const luaL_Reg lib[] = {
|
||||||
{ "poll_event", f_poll_event },
|
{ "poll_event", f_poll_event },
|
||||||
{ "wait_event", f_wait_event },
|
{ "wait_event", f_wait_event },
|
||||||
|
@ -1200,7 +1175,6 @@ static const luaL_Reg lib[] = {
|
||||||
{ "load_native_plugin", f_load_native_plugin },
|
{ "load_native_plugin", f_load_native_plugin },
|
||||||
{ "path_compare", f_path_compare },
|
{ "path_compare", f_path_compare },
|
||||||
{ "get_fs_type", f_get_fs_type },
|
{ "get_fs_type", f_get_fs_type },
|
||||||
{ "text_input", f_text_input },
|
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1212,4 +1186,3 @@ int luaopen_system(lua_State *L) {
|
||||||
luaL_newlib(L, lib);
|
luaL_newlib(L, lib);
|
||||||
return 1;
|
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
|
// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-140
|
||||||
#if defined(__x86_64__) || defined(_M_AMD64) || defined(__MINGW64__)
|
#if defined(__x86_64__) || defined(_M_AMD64) || defined(__MINGW64__)
|
||||||
#define ARCH_PROCESSOR "x86_64"
|
#define ARCH_PROCESSOR "x86_64"
|
||||||
#elif __arm__
|
|
||||||
#define ARCH_PROCESSOR "arm"
|
|
||||||
#elif defined(__amigaos4__) || defined(__morphos__)
|
#elif defined(__amigaos4__) || defined(__morphos__)
|
||||||
#define ARCH_PROCESSOR "ppc"
|
#define ARCH_PROCESSOR "ppc"
|
||||||
#elif defined(__i386__) || defined(_M_IX86) || defined(__MINGW32__)
|
#elif defined(__i386__) || defined(_M_IX86) || defined(__MINGW32__)
|
||||||
|
@ -157,7 +155,6 @@ int main(int argc, char **argv) {
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||||||
fprintf(stderr, "Error initializing sdl: %s", SDL_GetError());
|
fprintf(stderr, "Error initializing sdl: %s", SDL_GetError());
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -209,7 +206,7 @@ int main(int argc, char **argv) {
|
||||||
fprintf(stderr, "Error creating lite-xl window: %s", SDL_GetError());
|
fprintf(stderr, "Error creating lite-xl window: %s", SDL_GetError());
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
window_renderer = ren_init(window);
|
ren_init(window);
|
||||||
|
|
||||||
lua_State *L;
|
lua_State *L;
|
||||||
init_lua:
|
init_lua:
|
||||||
|
@ -269,23 +266,22 @@ init_lua:
|
||||||
" core.init()\n"
|
" core.init()\n"
|
||||||
" core.run()\n"
|
" core.run()\n"
|
||||||
"end, function(err)\n"
|
"end, function(err)\n"
|
||||||
" local error_path = 'error.txt'\n"
|
" local error_dir\n"
|
||||||
" io.stdout:write('Error: '..tostring(err)..'\\n')\n"
|
" io.stdout:write('Error: '..tostring(err)..'\\n')\n"
|
||||||
" io.stdout:write(debug.traceback(nil, 2)..'\\n')\n"
|
" io.stdout:write(debug.traceback(nil, 2)..'\\n')\n"
|
||||||
" if core and core.on_error then\n"
|
" if core and core.on_error then\n"
|
||||||
" error_path = USERDIR .. PATHSEP .. error_path\n"
|
" error_dir=USERDIR\n"
|
||||||
" pcall(core.on_error, err)\n"
|
" pcall(core.on_error, err)\n"
|
||||||
" else\n"
|
" else\n"
|
||||||
" local fp = io.open(error_path, 'wb')\n"
|
" error_dir=system.absolute_path('.')\n"
|
||||||
|
" local fp = io.open('error.txt', 'wb')\n"
|
||||||
" fp:write('Error: ' .. tostring(err) .. '\\n')\n"
|
" fp:write('Error: ' .. tostring(err) .. '\\n')\n"
|
||||||
" fp:write(debug.traceback(nil, 2)..'\\n')\n"
|
" fp:write(debug.traceback(nil, 4)..'\\n')\n"
|
||||||
" fp:close()\n"
|
" fp:close()\n"
|
||||||
" error_path = system.absolute_path(error_path)\n"
|
|
||||||
" end\n"
|
" end\n"
|
||||||
" system.show_fatal_error('Lite XL internal error',\n"
|
" system.show_fatal_error('Lite XL internal error',\n"
|
||||||
" 'An internal error occurred in a critical part of the application.\\n\\n'..\n"
|
" 'An internal error occurred in a critical part of the application.\\n\\n'..\n"
|
||||||
" 'Error: '..tostring(err)..'\\n\\n'..\n"
|
" 'Please verify the file \\\"error.txt\\\" in the directory '..error_dir)\n"
|
||||||
" 'Details can be found in \\\"'..error_path..'\\\"')\n"
|
|
||||||
" os.exit(1)\n"
|
" os.exit(1)\n"
|
||||||
"end)\n"
|
"end)\n"
|
||||||
"return core and core.restart_request\n";
|
"return core and core.restart_request\n";
|
||||||
|
|
|
@ -14,4 +14,3 @@ void rencache_begin_frame(RenWindow *window_renderer);
|
||||||
void rencache_end_frame(RenWindow *window_renderer);
|
void rencache_end_frame(RenWindow *window_renderer);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -515,7 +515,7 @@ void ren_init(SDL_Window *win) {
|
||||||
int error = FT_Init_FreeType( &library );
|
int error = FT_Init_FreeType( &library );
|
||||||
if ( error ) {
|
if ( error ) {
|
||||||
fprintf(stderr, "internal font error when starting the application\n");
|
fprintf(stderr, "internal font error when starting the application\n");
|
||||||
return NULL;
|
return;
|
||||||
}
|
}
|
||||||
window_renderer.window = win;
|
window_renderer.window = win;
|
||||||
renwin_init_surface(&window_renderer);
|
renwin_init_surface(&window_renderer);
|
||||||
|
@ -523,23 +523,6 @@ void ren_init(SDL_Window *win) {
|
||||||
renwin_clip_to_surface(&window_renderer);
|
renwin_clip_to_surface(&window_renderer);
|
||||||
draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32,
|
draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32,
|
||||||
0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
|
0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
|
||||||
|
|
||||||
return window_renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ren_free(RenWindow* window_renderer) {
|
|
||||||
assert(window_renderer);
|
|
||||||
renwin_free(window_renderer);
|
|
||||||
SDL_FreeSurface(draw_rect_surface);
|
|
||||||
free(window_renderer->command_buf);
|
|
||||||
window_renderer->command_buf = NULL;
|
|
||||||
window_renderer->command_buf_size = 0;
|
|
||||||
free(window_renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ren_resize_window(RenWindow *window_renderer) {
|
|
||||||
renwin_resize_surface(window_renderer);
|
|
||||||
renwin_update_scale(window_renderer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -149,4 +149,3 @@ void renwin_free(RenWindow *ren) {
|
||||||
SDL_FreeSurface(ren->rensurface.surface);
|
SDL_FreeSurface(ren->rensurface.surface);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
[wrap-file]
|
[wrap-file]
|
||||||
directory = lua-5.4.6
|
directory = lua-5.4.4
|
||||||
source_url = https://www.lua.org/ftp/lua-5.4.6.tar.gz
|
source_url = https://www.lua.org/ftp/lua-5.4.4.tar.gz
|
||||||
source_filename = lua-5.4.6.tar.gz
|
source_filename = lua-5.4.4.tar.gz
|
||||||
source_hash = 7d5ea1b9cb6aa0b59ca3dde1c6adcb57ef83a1ba8e5432c0ecd06bf439b3ad88
|
source_hash = 164c7849653b80ae67bec4b7473b884bf5cc8d2dca05653475ec2ed27b9ebf61
|
||||||
patch_filename = lua_5.4.6-3_patch.zip
|
patch_filename = lua_5.4.4-1_patch.zip
|
||||||
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.6-3/get_patch
|
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.4-1/get_patch
|
||||||
patch_hash = 9b72a95422fd47f79f969d9abdb589ee95712d5512a5246f94e7e4f63d2cb7b7
|
patch_hash = e61cd965c629d6543176f41a9f1cb9050edfd1566cf00ce768ff211086e40bdc
|
||||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/lua_5.4.6-3/lua-5.4.6.tar.gz
|
|
||||||
wrapdb_version = 5.4.6-3
|
|
||||||
|
|
||||||
[provide]
|
[provide]
|
||||||
lua-5.4 = lua_dep
|
lua-5.4 = lua_dep
|
||||||
lua = lua_dep
|
|
||||||
|
|
Loading…
Reference in New Issue