Compare commits

..

12 Commits

100 changed files with 3331 additions and 4742 deletions

3
.github/labeler.yml vendored
View File

@ -33,6 +33,3 @@
"Category: C Core": "Category: C Core":
- src/**/* - src/**/*
"Category: Libraries":
- lib/**/*

4
.gitignore vendored
View File

@ -2,7 +2,8 @@ build*/
.build*/ .build*/
lhelper/ lhelper/
submodules/ submodules/
subprojects/*/ subprojects/lua/
subprojects/reproc/
/appimage* /appimage*
.ccls-cache .ccls-cache
.lite-debug.log .lite-debug.log
@ -22,3 +23,4 @@ lite
*.lha *.lha
release_files release_files
*.o *.o

View File

@ -1,24 +1,21 @@
# #
# Project: Lite XL # Project: Lite XL
# #
# Created on: 26-12-2021 # Created on: 26-12-2021
# #
LiteXL_OBJ := \ LiteXL_OBJ := \
src/main.o src/rencache.o src/renderer.o src/renwindow.o \ src/dirmonitor.o src/main.o src/rencache.o src/renderer.o \
src/api/dirmonitor/os4.o \ src/renwindow.o src/api/api.o src/api/regex.o \
src/api/api.o src/api/dirmonitor.o src/api/regex.o \ src/api/renderer.o src/api/system.o src/platform/amigaos4.o
src/api/renderer.o src/api/system.o \
src/platform/amigaos4.o
# src/api/process.o
outfile := lite outfile := lite
compiler := gcc compiler := gcc
cxxcompiler := g++ cxxcompiler := g++
INCPATH := -Isrc -Ilib/dmon -I/sdk/local/newlib/include/SDL2 -I/sdk/local/common/include/freetype2 INCPATH := -Isrc -Ilib/dmon -I/sdk/local/newlib/include/SDL2 -I/sdk/local/common/include/freetype2
DFLAGS := -D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR -DDIRMONITOR_BACKEND=os4 -DDIRMONITOR_OS4 DFLAGS := -D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR
# -DLITE_USE_SDL_RENDERER # -DLITE_USE_SDL_RENDERER
# -Wextra -Wall # -Wextra -Wall
CFLAGS := -Werror -Wwrite-strings -O3 -g -std=gnu11 -fno-strict-aliasing CFLAGS := -Werror -Wwrite-strings -O3 -g -std=gnu11 -fno-strict-aliasing
@ -38,7 +35,7 @@ clean:
LiteXL: $(LiteXL_OBJ) LiteXL: $(LiteXL_OBJ)
@echo "Linking LiteXL" @echo "Linking LiteXL"
@$(cxxcompiler) -o $(outfile) $(LiteXL_OBJ) $(LFLAGS) @$(cxxcompiler) -o $(outfile) $(LiteXL_OBJ) $(LFLAGS)
@ -46,8 +43,10 @@ LiteXL: $(LiteXL_OBJ)
@echo "Compiling $<" @echo "Compiling $<"
@$(compiler) -c $< -o $*.o $(CFLAGS) $(INCPATH) $(DFLAGS) @$(compiler) -c $< -o $*.o $(CFLAGS) $(INCPATH) $(DFLAGS)
src/dirmonitor.o: src/dirmonitor.c src/platform/amigaos4.h
src/main.o: src/main.c src/api/api.h src/rencache.h \ src/main.o: src/main.c src/api/api.h src/rencache.h \
src/renderer.h src/platform/amigaos4.h src/renderer.h src/platform/amigaos4.h src/dirmonitor.h
src/rencache.o: src/rencache.c src/rencache.o: src/rencache.c
@ -55,12 +54,8 @@ src/renderer.o: src/renderer.c
src/renwindow.o: src/renwindow.c src/renwindow.o: src/renwindow.c
src/api/dirmonitor/os4.o: src/api/dirmonitor/os4.c
src/api/api.o: src/api/api.c src/api/api.o: src/api/api.c
src/api/dirmonitor.o: src/api/dirmonitor.c src/api/dirmonitor/os4.c
src/api/regex.o: src/api/regex.c src/api/regex.o: src/api/regex.c
src/api/renderer.o: src/api/renderer.c src/api/renderer.o: src/api/renderer.c
@ -70,16 +65,18 @@ src/api/system.o: src/api/system.c
src/platform/amigaos4.o: src/platform/amigaos4.c src/platform/amigaos4.o: src/platform/amigaos4.c
release:
mkdir -p release/LiteXL
cp release_files/* release/LiteXL/ -r release:
mv release/LiteXL/LiteXL.info release/ mkdir -p release/LiteXL2
cp data release/LiteXL/ -r cp release_files/* release/LiteXL2/ -r
cp changelog.md release/LiteXL/ mv release/LiteXL2/LiteXL2.info release/
cp lite release/LiteXL/ cp data release/LiteXL2/ -r
strip release/LiteXL/lite cp changelog.md release/LiteXL2/
cp README.md release/LiteXL/ cp lite release/LiteXL2/
cp README_OS4.md release/LiteXL/ strip release/LiteXL2/lite
cp LICENSE release/LiteXL/ cp README.md release/LiteXL2/
lha -aeqr3 a LiteXL.lha release/ cp README_OS4.md release/LiteXL2/
cp LICENSE release/LiteXL2/
lha -aeqr3 a LiteXL2_OS4.lha release/

View File

@ -154,7 +154,6 @@ See the [licenses] file for details on licenses used by the required dependencie
[Get color themes]: https://github.com/lite-xl/lite-xl-colors [Get color themes]: https://github.com/lite-xl/lite-xl-colors
[changelog]: https://github.com/lite-xl/lite-xl/blob/master/changelog.md [changelog]: https://github.com/lite-xl/lite-xl/blob/master/changelog.md
[Lite XL plugins repository]: https://github.com/lite-xl/lite-xl-plugins [Lite XL plugins repository]: https://github.com/lite-xl/lite-xl-plugins
[plugins repository]: https://github.com/rxi/lite-plugins
[colors repository]: https://github.com/lite-xl/lite-xl-colors [colors repository]: https://github.com/lite-xl/lite-xl-colors
[LICENSE]: LICENSE [LICENSE]: LICENSE
[licenses]: licenses/licenses.md [licenses]: licenses/licenses.md

View File

@ -1,16 +1,32 @@
# Lite XL v2.1 for AmigaOS 4.1 FE # Lite XL v2 for AmigaOS 4.1 FE
Lite XL is a lightweight text editor written in Lua. Lite XL is a lightweight text editor written in Lua.
The port is not perfect and might has issues here and there. For example
the filesystem notifications are not working yet. So when you make changes
at a project folder those will not be reflected in Lite XL automatically.
It might crash from time to time, if there is a path problem, but overall
it works pretty well. This is my daily editor for any kind of development.
## New features against Lite XL v1
- Faster file scrolling
- Faster switch between tabs
- Reposition tabs at the side or at the bottom of other tabs, making
multiple columns/rows of opened files
- Multiple cursor editing
- Better font manipulation and appearance
- Faster transitions
## Installation ## Installation
You can extract the Lite XL archive wherever you want and run the *lite* You can extract the Lite XL archive wherever you want and run the *lite*
editor. editor.
## Configuration folder ## Configuration folder
This editor creates a `.config` folder where the configuration is saved, as This editor creates a `.config` folder where the configuration is saved, as
well as plugins, themes etc.. By default this AmigaOS 4.1 FE version uses the well as plugins, themes etc.. By default this AmigaOS 4.1 FE version uses the
executable folder, but if you want to ovveride it, create an ENV variable executable folder, but if you want to ovveride it, create an ENV variable
named `HOME` and set there your path. named `HOME` and set there your path.
You can check if there is one already set by executing the following command You can check if there is one already set by executing the following command
in a shell in a shell
@ -19,7 +35,7 @@ GetEnv HOME
``` ```
If there is one set, then you will see the path at the output. If there is one set, then you will see the path at the output.
Otherwise, you can set your home path be executing the following command. Otherwise, you can set your home path be executing the following command.
Change the path to the one of your preference. Change the path to the one of your preference.
``` ```
SetEnv SAVE HOME "Sys:home/" SetEnv SAVE HOME "Sys:home/"
@ -28,7 +44,7 @@ SetEnv SAVE HOME "Sys:home/"
## Addons ## Addons
### Colors ### Colors
Colors are lua files that set the color scheme of the editor. There are Colors are lua files that set the color scheme of the editor. There are
light and dark themes for you to choose. light and dark themes for you to choose.
To install and use them you have to copy the ones you would like from To install and use them you have to copy the ones you would like from
`addons/colors/light` or `addons/colors/dark` into the folder `addons/colors/light` or `addons/colors/dark` into the folder
@ -40,29 +56,33 @@ at the cog icon at the toolbar (bottom left sixth icon). Go at the line
that looks like below that looks like below
``` ```
-- core.reload_module("colors.summer") -- core.reload_module("colors.summer")
``` ```
and change the `summer` with the name of your color theme. Also, remove and change the `summer` with the name of your color theme. Also, remove
the two dashes `--` at the start of the line and save the file. If you the two dashes `--` at the start of the line and save the file. If you
did everything right, the color schema should change instantly. did everything right, the color schema should change instantly.
The themes can also be found at The themes can also be found at
https://github.com/lite-xl/lite-xl-colors https://github.com/lite-xl/lite-xl-colors
### Plugins ### Plugins
The Lite XL that you are using on AmigaOS 4 is based on version 1.16.12 The Lite XL that you are using on AmigaOS 4 is based on version 2.0.4
and not the latest version that is available by the development team. and not the latest version that is available by the development team.
This means that the latest plugins are not working at all or need some This means that some of the latest plugins might not working at all
modifications to work. or need some modifications to work.
To make it easier for you, I gathered some of the plugins that are working To make it easier for you, I gathered some of the plugins that are working
well, and I included them at the `addons/plugins`. For you to install the well, and I included them under `addons/plugins`. For you to install the
ones you would like to use, you have to copy the `.lua` files into the ones you would like to use, you have to copy the `.lua` files into the
folder `.config/lite-xl/plugins/` and restart the editor. folder `.config/lite-xl/plugins/` and restart the editor.
Please, choose wisely, because adding all the plugins might make the editor
slower on your system. I would recommend you add only those that you really
need.
The included plugins are the following: The included plugins are the following:
**autoinsert** **autoinsert**
Automatically inserts closing brackets and quotes. Also allows selected Automatically inserts closing brackets and quotes. Also allows selected
text to be wrapped with brackets or quotes. text to be wrapped with brackets or quotes.
**autowrap** **autowrap**
@ -75,7 +95,7 @@ Shows the current time and date in a view with large text
Underlines matching pair for bracket under the caret Underlines matching pair for bracket under the caret
**colorpreview** **colorpreview**
Underlays color values (eg. `#ff00ff` or `rgb(255, 0, 255)`) with their Underlays color values (eg. `#ff00ff` or `rgb(255, 0, 255)`) with their
resultant color. resultant color.
**eofnewline-xl** **eofnewline-xl**
@ -86,7 +106,7 @@ Preview tabs. Opening a doc will replace the contents of the preview tab.
Marks tabs as non-preview on any change or tab double clicking. Marks tabs as non-preview on any change or tab double clicking.
**ghmarkdown** **ghmarkdown**
Opens a preview of the current markdown file in a browser window Opens a preview of the current markdown file in a browser window
**indentguide** **indentguide**
Adds indent guides Adds indent guides
@ -103,28 +123,35 @@ Automatically inserts indentation and closing bracket/text after newline
**markers** **markers**
Add markers to docs and jump between them quickly Add markers to docs and jump between them quickly
**minimap**
Shows a minimap on the right-hand side of the docview. Please note that
this plugin will make the editor slower on file loading and scrolling.
**navigate** **navigate**
Allows moving back and forward between document positions, reducing the Allows moving back and forward between document positions, reducing the
amount of scrolling amount of scrolling
**rainbowparen** **rainbowparen**
Show nesting of parentheses with rainbow colours Show nesting of parentheses with rainbow colours
**restoretabs** **restoretabs**
Keep a list of recently closed tabs, and restore the tab in order on Keep a list of recently closed tabs, and restore the tab in order on
cntrl+shift+t. cntrl+shift+t.
**selectionhighlight** **selectionhighlight**
Highlights regions of code that match the current selection Highlights regions of code that match the current selection
**smallclock**
It adds a small clock at the bottom right corner.
## Tips and tricks ## Tips and tricks
### Transitions ### Transitions
If you want to disable the transitions and make the scrolling a little faster, If you want to disable the transitions and make the editor faster,
open your configuration by clicking at the cog icon at the toolbar open your configuration file by clicking at the cog icon at the toolbar
(bottom left sixth icon) and add the followline at the end of the file and (bottom left, 6th icon) and add the following line at the end of the file,
save it. and then save it. You might need to restart your editor (CTRL+SHIFT+R)
``` ```
config.transitions = false config.transitions = false
@ -133,37 +160,45 @@ config.transitions = false
### Hide files from the file list ### Hide files from the file list
If you would like to hide files or whole folder from the left side bar list, If you would like to hide files or whole folder from the left side bar list,
open your configuration by clicking at the cog icon at the toolbar open your configuration by clicking at the cog icon at the toolbar
(bottom left sixth icon) and add the followline at the end of the file and (bottom left sixth icon) and add the followline at the end of the file and
save it. This hides all the files that start with a dot, and all the `.info` save it. This hides all the files that start with a dot, and all the `.info`
files. files. You might need to restart your editor (CTRL+SHIFT+R)
``` ```
config.ignore_files = {"^%.", "%.info$"} config.ignore_files = {"^%.", "%.info$"}
``` ```
You can add as many rules as you want in there, to hide fore files or You can add as many rules as you want in there, to hide files or
folders, as you like. folders, as you like.
## I would like to thank
## Know issues - IconDesigner for the proper glow icons that are included in the release
You can find the known issues at - Capehill for his tireless work on SDL port
- Michael Trebilcock for his port on liblua
- Lite XL original team for being helpful and providing info
Without all the above Lite XL would not be possible
## Support
If you enjoy what I am doing and would like to keep me up during the night,
please consider to buy me a coffee at:
https://ko-fi.com/walkero
## Known issues
You can find the known issues at
https://git.walkero.gr/walkero/lite-xl/issues https://git.walkero.gr/walkero/lite-xl/issues
# Changelog # Changelog
## [2.1r1] - 2022-03-27 ## [2.0.3r1] - 2022-03-30
### Changed
- Synced with master-v2.1 tag of lite-xl official github page
- Applied all the necessary changes to make it run under AmigaOS 4.1 FE
## [2.0.5r1] - 2022-03-27
### Changed
- Synced with v2.0.5 tag of lite-xl official github page
- Applied all the necessary changes to make it run under AmigaOS 4.1 FE
## [2.0.3.1] - 2022-01-18
### Changed ### Changed
- Applied all the necessary changes to make it run under AmigaOS 4.1 FE - Applied all the necessary changes to make it run under AmigaOS 4.1 FE
- Fixes and changes
# Disclaimer
YOU MAY USE IT AT YOUR OWN RISK!
I will not be held responsible for any data loss or problem you might get
by using this software.

View File

@ -1,123 +1,5 @@
This files document the changes done in Lite XL for each release. This files document the changes done in Lite XL for each release.
### 2.1
Upgraded Lua to 5.4, which should improve performance, and provide useful extra functionality.
It should also be more available out of the box with most modern linux/unix-based package
managers.
Removed `dmon`, and implemented independent backends for dirmonitoring. Also more cleanly
split out dirmonitoring into its own class in lua, from core.init. We should now support
FreeBSD; and any other system that uses `kqueue` as their dirmonitoring library. We also
have a dummy-backend, which reverts transparnetly to scanning if there is some issue with
applying OS-level watches (such as system limits).
Removed `libagg` and the font renderer; compacted all font rendering into a single renderer.c
file which uses `libfreetype` directly. Now allows for ad-hoc bolding, italics, and underlining
of fonts.
Removed `reproc` and replaced this with a simple POSIX/Windows implementation in `process.c`.
This allows for greater tweakability (i.e. we can now `break` for debugging purposes),
performance (startup time of subprocesses is noticeably shorter), and simplicity
(we no longer have to link reproc, or winsock, on windows).
Split out `Node` and `EmptyView` into their own lua files, for plugin extensibility reasons.
Revamped StatusView API, so that plugins can more easily coexist with each other.
Removed `cp_replace`, and replaced this with a core plugin, `drawwhitespace.lua`.
Made distinction between line and block comments, and added all appropriate functionality
to the commenting/uncommenting lines.
Added in line paste mode, if you copy without a selection.
May improvements to treeview, including keyboard navigation of treeview, and ability to
specify single vs. double-click behavior.
Added in soft line wrapping as core plugin, under `linewrapping.lua`, with an
F10 to activate.
Bumped plugin mod-version number, as the rendering interface for docviews has changed.
Added in meson wraps for freetype, pcre2, and SDL2 which target public, rather than
lite-xl maintained repos.
Added in the ability to set up font fallback groups in the font renderer, if a token
doesn't have a corresponding glyph.
Added in a native plugin interface that allows for C-level interfacing with a
statically-linked lite-xl. The implementation of this may change in future.
Improved fuzzy_matching to probably give you something closer to what you're
looking for.
Improved handling of alternate keyboard layouts.
Improved ability for plugins to be loaded at a given time, by making the convention
of defining a config for the plugin use `common.merge` to merge existing hashes
together, rather than overwriting.
Added in the ability to specify mouseclicks in the keymap, allowing for easy binds of
`ctrl+lclick`, and the like.
Changed interface for keyhandling; now, all components should return true if they've
handled the event.
Added in a default keymap for `core:restart`, `ctrl+shift+r`.
Many, many, many more changes that are too numerous to list.
### 2.0.5
Revamp the project's user module so that modifications are immediately applied.
Add a mechanism to ignore files or directory based on their project's path.
The new mechanism is backward compatible.*
Essentially there are two mechanisms:
- if a '/' or a '/$' appear at the end of the pattern it will match only directories
- if a '/' appears anywhere in the pattern except at the end the pattern will be
applied to the path
In the first case, when the pattern corresponds to a directory, a '/' will be
appended to the name of each directory before checking the pattern.
In the second case, when the pattern corresponds to a path, the complete path of
the file or directory will be used with an initial '/' added to the path.
Fix several problems with the directory monitoring library.
Now the application should no longer assert when some related system call fails
and we fallback to rescan when an error happens.
On linux no longer use the recursive monitoring which was a source of problem.
Directory monitoring is now aware of symlinks and treat them appropriately.
Fix problem when encountering special files type on linux.
Improve directory monitoring so that the related thread actually waits without using
any CPU time when there are no events.
Improve the suggestion when changing project folder or opening a new one.
Now the previously used directory are suggested but if the path is changed the
actual existing directories that match the pattern are suggested.
In addition always use the text entered in the command view even if a suggested entry
is highlighted.
The NagView warning window now no longer moves the document content.
### 2.0.4
Fix some bugs related to newly introduced directory monitoring using the dmon library.
Fix a problem with plain text search using Lua patterns by error.
Fix a problem with visualization of UTF-8 characters that caused garbage characters
visualization.
Other fixes and improvements contributed by @Guldoman.
### 2.0.3 ### 2.0.3
Replace periodic rescan of project folder with a notification based system using the Replace periodic rescan of project folder with a notification based system using the

View File

@ -1,36 +0,0 @@
local bit = {}
local LUA_NBITS = 32
local ALLONES = (~(((~0) << (LUA_NBITS - 1)) << 1))
local function trim(x)
return (x & ALLONES)
end
local function mask(n)
return (~((ALLONES << 1) << ((n) - 1)))
end
local function check_args(field, width)
assert(field >= 0, "field cannot be negative")
assert(width > 0, "width must be positive")
assert(field + width < LUA_NBITS and field + width >= 0,
"trying to access non-existent bits")
end
function bit.extract(n, field, width)
local w = width or 1
check_args(field, w)
local m = trim(n)
return m >> field & mask(w)
end
function bit.replace(n, v, field, width)
local w = width or 1
check_args(field, w)
local m = trim(n)
local x = v & mask(width);
return m & ~(mask(w) << field) | (x << field)
end
return bit

View File

@ -64,7 +64,7 @@ end
function command.add_defaults() function command.add_defaults()
local reg = { local reg = {
"core", "root", "command", "doc", "findreplace", "core", "root", "command", "doc", "findreplace",
"files", "drawwhitespace", "dialog", "log", "statusbar" "files", "drawwhitespace", "dialog"
} }
for _, name in ipairs(reg) do for _, name in ipairs(reg) do
require("core.commands." .. name) require("core.commands." .. name)

View File

@ -10,18 +10,8 @@ local restore_title_view = false
local function suggest_directory(text) local function suggest_directory(text)
text = common.home_expand(text) text = common.home_expand(text)
local basedir = common.dirname(core.project_dir) return common.home_encode_list((text == "" or text == common.home_expand(common.dirname(core.project_dir)))
return common.home_encode_list((basedir and text == basedir .. PATHSEP or text == "") and and core.recent_projects or common.dir_path_suggest(text))
core.recent_projects or common.dir_path_suggest(text))
end
local function check_directory_path(path)
local abs_path = system.absolute_path(path)
local info = abs_path and system.get_file_info(abs_path)
if not info or info.type ~= 'dir' then
return nil
end
return abs_path
end end
command.add(nil, { command.add(nil, {
@ -103,15 +93,6 @@ command.add(nil, {
core.root_view:open_doc(core.open_doc()) core.root_view:open_doc(core.open_doc())
end, end,
["core:new-named-doc"] = function()
core.command_view:enter(
"File name",
function(text)
core.root_view:open_doc(core.open_doc(text))
end
)
end,
["core:open-file"] = function() ["core:open-file"] = function()
local view = core.active_view local view = core.active_view
if view.doc and view.doc.abs_filename then if view.doc and view.doc.abs_filename then
@ -160,50 +141,46 @@ command.add(nil, {
end, end,
["core:open-project-module"] = function() ["core:open-project-module"] = function()
if not system.get_file_info(".lite_project.lua") then local filename = ".lite_project.lua"
core.try(core.write_init_project_module, ".lite_project.lua") if system.get_file_info(filename) then
core.root_view:open_doc(core.open_doc(filename))
else
local doc = core.open_doc()
core.root_view:open_doc(doc)
doc:save(filename)
end end
local doc = core.open_doc(".lite_project.lua")
core.root_view:open_doc(doc)
doc:save()
end, end,
["core:change-project-folder"] = function() ["core:change-project-folder"] = function()
local dirname = common.dirname(core.project_dir) local dirname = common.dirname(core.project_dir)
if dirname then if dirname then
core.command_view:set_text(common.home_encode(dirname) .. PATHSEP) core.command_view:set_text(common.home_encode(dirname))
end end
core.command_view:enter("Change Project Folder", function(text) core.command_view:enter("Change Project Folder", function(text, item)
local path = common.home_expand(text) text = system.absolute_path(common.home_expand(item and item.text or text))
local abs_path = check_directory_path(path) if text == core.project_dir then return end
if not abs_path then local path_stat = system.get_file_info(text)
core.error("Cannot open directory %q", path) if not path_stat or path_stat.type ~= 'dir' then
core.error("Cannot open folder %q", text)
return return
end end
if abs_path == core.project_dir then return end core.confirm_close_docs(core.docs, core.open_folder_project, text)
core.confirm_close_docs(core.docs, function(dirpath)
core.open_folder_project(dirpath)
end, abs_path)
end, suggest_directory) end, suggest_directory)
end, end,
["core:open-project-folder"] = function() ["core:open-project-folder"] = function()
local dirname = common.dirname(core.project_dir) local dirname = common.dirname(core.project_dir)
if dirname then if dirname then
core.command_view:set_text(common.home_encode(dirname) .. PATHSEP) core.command_view:set_text(common.home_encode(dirname))
end end
core.command_view:enter("Open Project", function(text) core.command_view:enter("Open Project", function(text, item)
local path = common.home_expand(text) text = common.home_expand(item and item.text or text)
local abs_path = check_directory_path(path) local path_stat = system.get_file_info(text)
if not abs_path then if not path_stat or path_stat.type ~= 'dir' then
core.error("Cannot open directory %q", path) core.error("Cannot open folder %q", text)
return return
end end
if abs_path == core.project_dir then system.exec(string.format("%q %q", EXEFILE, text))
core.error("Directory %q is currently opened", abs_path)
return
end
system.exec(string.format("%q %q", EXEFILE, abs_path))
end, suggest_directory) end, suggest_directory)
end, end,

View File

@ -47,32 +47,19 @@ end
local function cut_or_copy(delete) local function cut_or_copy(delete)
local full_text = "" local full_text = ""
local text = ""
core.cursor_clipboard = {}
core.cursor_clipboard_whole_line = {}
for idx, line1, col1, line2, col2 in doc():get_selections() do for idx, line1, col1, line2, col2 in doc():get_selections() do
if line1 ~= line2 or col1 ~= col2 then if line1 ~= line2 or col1 ~= col2 then
text = doc():get_text(line1, col1, line2, col2) local text = doc():get_text(line1, col1, line2, col2)
full_text = full_text == "" and text or (full_text .. " " .. text)
core.cursor_clipboard_whole_line[idx] = false
if delete then if delete then
doc():delete_to_cursor(idx, 0) doc():delete_to_cursor(idx, 0)
end end
else -- Cut/copy whole line full_text = full_text == "" and text or (full_text .. "\n" .. text)
text = doc().lines[line1] doc().cursor_clipboard[idx] = text
full_text = full_text == "" and text or (full_text .. text) else
core.cursor_clipboard_whole_line[idx] = true doc().cursor_clipboard[idx] = ""
if delete then
if line1 < #doc().lines then
doc():remove(line1, 1, line1 + 1, 1)
else
doc():remove(line1 - 1, math.huge, line1, math.huge)
end
end
end end
core.cursor_clipboard[idx] = text
end end
core.cursor_clipboard["full"] = full_text doc().cursor_clipboard["full"] = full_text
system.set_clipboard(full_text) system.set_clipboard(full_text)
end end
@ -97,90 +84,7 @@ local function set_cursor(x, y, snap_type)
core.blink_reset() core.blink_reset()
end end
local function line_comment(comment, line1, col1, line2, col2) local selection_commands = {
local start_comment = (type(comment) == 'table' and comment[1] or comment) .. " "
local end_comment = (type(comment) == 'table' and " " .. comment[2])
local uncomment = true
local start_offset = math.huge
for line = line1, line2 do
local text = doc().lines[line]
local s = text:find("%S")
if s then
local cs, ce = text:find(start_comment, s, true)
if cs ~= s then
uncomment = false
end
start_offset = math.min(start_offset, s)
end
end
local end_line = col2 == #doc().lines[line2]
for line = line1, line2 do
local text = doc().lines[line]
local s = text:find("%S")
if s and uncomment then
if end_comment and text:sub(#text - #end_comment, #text - 1) == end_comment then
doc():remove(line, #text - #end_comment, line, #text)
end
local cs, ce = text:find(start_comment, s, true)
if ce then
doc():remove(line, cs, line, ce + 1)
end
elseif s then
doc():insert(line, start_offset, start_comment)
if end_comment then
doc():insert(line, #doc().lines[line], " " .. comment[2])
end
end
end
col1 = col1 + (col1 > start_offset and #start_comment or 0) * (uncomment and -1 or 1)
col2 = col2 + (col2 > start_offset and #start_comment or 0) * (uncomment and -1 or 1)
if end_comment and end_line then
col2 = col2 + #end_comment * (uncomment and -1 or 1)
end
return line1, col1, line2, col2
end
local function block_comment(comment, line1, col1, line2, col2)
-- automatically skip spaces
local word_start = doc():get_text(line1, col1, line1, math.huge):find("%S")
local word_end = doc():get_text(line2, 1, line2, col2):find("%s*$")
col1 = col1 + (word_start and (word_start - 1) or 0)
col2 = word_end and word_end or col2
local block_start = doc():get_text(line1, col1, line1, col1 + #comment[1])
local block_end = doc():get_text(line2, col2 - #comment[2], line2, col2)
if block_start == comment[1] and block_end == comment[2] then
-- remove up to 1 whitespace after the comment
local start_len, stop_len = #comment[1], #comment[2]
if doc():get_text(line1, col1 + #comment[1], line1, col1 + #comment[1] + 1):find("%s$") then
start_len = start_len + 1
end
if doc():get_text(line2, col2 - #comment[2] - 1, line2, col2):find("^%s") then
stop_len = stop_len + 1
end
doc():remove(line1, col1, line1, col1 + start_len)
col2 = col2 - (line1 == line2 and start_len or 0)
doc():remove(line2, col2 - stop_len, line2, col2)
return line1, col1, line2, col2 - stop_len
else
doc():insert(line1, col1, comment[1] .. " ")
col2 = col2 + (line1 == line2 and (#comment[1] + 1) or 0)
doc():insert(line2, col2, " " .. comment[2])
return line1, col1, line2, col2 + #comment[2] + 1
end
end
local commands = {
["doc:select-none"] = function()
local line, col = doc():get_selection()
doc():set_selection(line, col)
end,
["doc:cut"] = function() ["doc:cut"] = function()
cut_or_copy(true) cut_or_copy(true)
end, end,
@ -189,6 +93,13 @@ local commands = {
cut_or_copy(false) cut_or_copy(false)
end, end,
["doc:select-none"] = function()
local line, col = doc():get_selection()
doc():set_selection(line, col)
end
}
local commands = {
["doc:undo"] = function() ["doc:undo"] = function()
doc():undo() doc():undo()
end, end,
@ -200,28 +111,12 @@ local commands = {
["doc:paste"] = function() ["doc:paste"] = function()
local clipboard = system.get_clipboard() local clipboard = system.get_clipboard()
-- If the clipboard has changed since our last look, use that instead -- If the clipboard has changed since our last look, use that instead
local external_paste = core.cursor_clipboard["full"] ~= clipboard if doc().cursor_clipboard["full"] ~= clipboard then
if external_paste then doc().cursor_clipboard = {}
core.cursor_clipboard = {}
core.cursor_clipboard_whole_line = {}
end end
local value, whole_line
for idx, line1, col1, line2, col2 in doc():get_selections() do for idx, line1, col1, line2, col2 in doc():get_selections() do
if #core.cursor_clipboard_whole_line == (#doc().selections/4) then local value = doc().cursor_clipboard[idx] or clipboard
value = core.cursor_clipboard[idx] doc():text_input(value:gsub("\r", ""), idx)
whole_line = core.cursor_clipboard_whole_line[idx] == true
else
value = clipboard
whole_line = not external_paste and clipboard:find("\n") ~= nil
end
if whole_line then
doc():insert(line1, 1, value:gsub("\r", ""))
if col1 == 1 then
doc():move_to_cursor(idx, #value)
end
else
doc():text_input(value:gsub("\r", ""), idx)
end
end end
end, end,
@ -276,11 +171,6 @@ local commands = {
["doc:select-all"] = function() ["doc:select-all"] = function()
doc():set_selection(1, 1, math.huge, math.huge) doc():set_selection(1, 1, math.huge, math.huge)
-- avoid triggering DocView:scroll_to_make_visible
dv().last_line1 = 1
dv().last_col1 = 1
dv().last_line2 = #doc().lines
dv().last_col2 = #doc().lines[#doc().lines]
end, end,
["doc:select-lines"] = function() ["doc:select-lines"] = function()
@ -373,30 +263,34 @@ local commands = {
end end
end, end,
["doc:toggle-block-comments"] = function()
local comment = doc().syntax.block_comment
if not comment then
if doc().syntax.comment then
command.perform "doc:toggle-line-comments"
end
return
end
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
-- if nothing is selected, toggle the whole line
if line1 == line2 and col1 == col2 then
col1 = 1
col2 = #doc().lines[line2]
end
doc():set_selections(idx, block_comment(comment, line1, col1, line2, col2))
end
end,
["doc:toggle-line-comments"] = function() ["doc:toggle-line-comments"] = function()
local comment = doc().syntax.comment or doc().syntax.block_comment local comment = doc().syntax.comment
if comment then if not comment then return end
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do local indentation = doc():get_indent_string()
doc():set_selections(idx, line_comment(comment, line1, col1, line2, col2)) local comment_text = comment .. " "
for idx, line1, _, line2 in doc_multiline_selections(true) do
local uncomment = true
local start_offset = math.huge
for line = line1, line2 do
local text = doc().lines[line]
local s = text:find("%S")
local cs, ce = text:find(comment_text, s, true)
if s and cs ~= s then
uncomment = false
start_offset = math.min(start_offset, s)
end
end
for line = line1, line2 do
local text = doc().lines[line]
local s = text:find("%S")
if uncomment then
local cs, ce = text:find(comment_text, s, true)
if ce then
doc():remove(line, cs, line, ce + 1)
end
elseif s then
doc():insert(line, start_offset, comment_text)
end
end end
end end
end, end,
@ -494,45 +388,34 @@ local commands = {
end end
for i,docview in ipairs(core.get_views_referencing_doc(doc())) do for i,docview in ipairs(core.get_views_referencing_doc(doc())) do
local node = core.root_view.root_node:get_node_for_view(docview) local node = core.root_view.root_node:get_node_for_view(docview)
node:close_view(core.root_view.root_node, docview) node:close_view(core.root_view, docview)
end end
os.remove(filename) os.remove(filename)
core.log("Removed \"%s\"", filename) core.log("Removed \"%s\"", filename)
end, end,
["doc:select-to-cursor"] = function(x, y, clicks) ["doc:select-to-cursor"] = function(x, y, clicks)
local line1, col1 = select(3, doc():get_selection()) local line1, col1 = select(3, doc():get_selection())
local line2, col2 = dv():resolve_screen_position(x, y) local line2, col2 = dv():resolve_screen_position(x, y)
dv().mouse_selecting = { line1, col1, nil } dv().mouse_selecting = { line1, col1, nil }
doc():set_selection(line2, col2, line1, col1) doc():set_selection(line2, col2, line1, col1)
end, end,
["doc:set-cursor"] = function(x, y) ["doc:set-cursor"] = function(x, y)
set_cursor(x, y, "set") set_cursor(x, y, "set")
end, end,
["doc:set-cursor-word"] = function(x, y) ["doc:set-cursor-word"] = function(x, y)
set_cursor(x, y, "word") set_cursor(x, y, "word")
end,
["doc:set-cursor-line"] = function(x, y, clicks)
set_cursor(x, y, "lines")
end, end,
["doc:set-cursor-line"] = function(x, y, clicks) ["doc:split-cursor"] = function(x, y, clicks)
set_cursor(x, y, "lines")
end,
["doc:split-cursor"] = function(x, y, clicks)
local line, col = dv():resolve_screen_position(x, y) local line, col = dv():resolve_screen_position(x, y)
local removal_target = nil doc():add_selection(line, col, line, col)
for idx, line1, col1 in doc():get_selections(true) do
if line1 == line and col1 == col and #doc().selections > 4 then
removal_target = idx
end
end
if removal_target then
doc():remove_selection(removal_target)
else
doc():add_selection(line, col, line, col)
end
dv().mouse_selecting = { line, col, "set" }
end, end,
["doc:create-cursor-previous-line"] = function() ["doc:create-cursor-previous-line"] = function()
@ -549,49 +432,50 @@ local commands = {
local translations = { local translations = {
["previous-char"] = translate, ["previous-char"] = translate.previous_char,
["next-char"] = translate, ["next-char"] = translate.next_char,
["previous-word-start"] = translate, ["previous-word-start"] = translate.previous_word_start,
["next-word-end"] = translate, ["next-word-end"] = translate.next_word_end,
["previous-block-start"] = translate, ["previous-block-start"] = translate.previous_block_start,
["next-block-end"] = translate, ["next-block-end"] = translate.next_block_end,
["start-of-doc"] = translate, ["start-of-doc"] = translate.start_of_doc,
["end-of-doc"] = translate, ["end-of-doc"] = translate.end_of_doc,
["start-of-line"] = translate, ["start-of-line"] = translate.start_of_line,
["end-of-line"] = translate, ["end-of-line"] = translate.end_of_line,
["start-of-word"] = translate, ["start-of-word"] = translate.start_of_word,
["start-of-indentation"] = translate, ["start-of-indentation"] = translate.start_of_indentation,
["end-of-word"] = translate, ["end-of-word"] = translate.end_of_word,
["previous-line"] = DocView.translate, ["previous-line"] = DocView.translate.previous_line,
["next-line"] = DocView.translate, ["next-line"] = DocView.translate.next_line,
["previous-page"] = DocView.translate, ["previous-page"] = DocView.translate.previous_page,
["next-page"] = DocView.translate, ["next-page"] = DocView.translate.next_page,
} }
for name, obj in pairs(translations) do for name, fn in pairs(translations) do
commands["doc:move-to-" .. name] = function() doc():move_to(obj[name:gsub("-", "_")], dv()) end commands["doc:move-to-" .. name] = function() doc():move_to(fn, dv()) end
commands["doc:select-to-" .. name] = function() doc():select_to(obj[name:gsub("-", "_")], dv()) end commands["doc:select-to-" .. name] = function() doc():select_to(fn, dv()) end
commands["doc:delete-to-" .. name] = function() doc():delete_to(obj[name:gsub("-", "_")], dv()) end commands["doc:delete-to-" .. name] = function() doc():delete_to(fn, dv()) end
end end
commands["doc:move-to-previous-char"] = function() commands["doc:move-to-previous-char"] = function()
for idx, line1, col1, line2, col2 in doc():get_selections(true) do for idx, line1, col1, line2, col2 in doc():get_selections(true) do
if line1 ~= line2 or col1 ~= col2 then if line1 ~= line2 or col1 ~= col2 then
doc():set_selections(idx, line1, col1) doc():set_selections(idx, line1, col1)
else
doc():move_to_cursor(idx, translate.previous_char)
end end
end end
doc():move_to(translate.previous_char)
end end
commands["doc:move-to-next-char"] = function() commands["doc:move-to-next-char"] = function()
for idx, line1, col1, line2, col2 in doc():get_selections(true) do for idx, line1, col1, line2, col2 in doc():get_selections(true) do
if line1 ~= line2 or col1 ~= col2 then if line1 ~= line2 or col1 ~= col2 then
doc():set_selections(idx, line2, col2) doc():set_selections(idx, line2, col2)
else
doc():move_to_cursor(idx, translate.next_char)
end end
end end
doc():move_to(translate.next_char)
end end
command.add("core.docview", commands) command.add("core.docview", commands)
command.add(function()
return core.active_view:is(DocView) and core.active_view.doc:has_any_selection()
end ,selection_commands)

View File

@ -55,7 +55,7 @@ end
local function find(label, search_fn) local function find(label, search_fn)
last_view, last_sel = core.active_view, last_view, last_sel = core.active_view,
{ core.active_view.doc:get_selection() } { core.active_view.doc:get_selection() }
local text = last_view.doc:get_text(table.unpack(last_sel)) local text = last_view.doc:get_text(unpack(last_sel))
found_expression = false found_expression = false
core.command_view:set_text(text, true) core.command_view:set_text(text, true)

View File

@ -1,16 +0,0 @@
local core = require "core"
local command = require "core.command"
command.add(nil, {
["log:open-as-doc"] = function()
local doc = core.open_doc("logs.txt")
core.root_view:open_doc(doc)
doc:insert(1, 1, core.get_log())
doc.new_file = false
doc:clean()
end,
["log:copy-to-clipboard"] = function()
system.set_clipboard(core.get_log())
end
})

View File

@ -30,7 +30,7 @@ local t = {
for i, v in ipairs(core.docs) do if v ~= active_doc then table.insert(docs, v) end end for i, v in ipairs(core.docs) do if v ~= active_doc then table.insert(docs, v) end end
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() ["root:switch-to-previous-tab"] = function()
local node = core.root_view:get_active_node() local node = core.root_view:get_active_node()
local idx = node:get_view_idx(core.active_view) local idx = node:get_view_idx(core.active_view)
@ -64,7 +64,7 @@ local t = {
table.insert(node.views, idx + 1, core.active_view) table.insert(node.views, idx + 1, core.active_view)
end end
end, end,
["root:shrink"] = function() ["root:shrink"] = function()
local node = core.root_view:get_active_node() local node = core.root_view:get_active_node()
local parent = node:get_parent_node(core.root_view.root_node) local parent = node:get_parent_node(core.root_view.root_node)

View File

@ -1,71 +0,0 @@
local core = require "core"
local command = require "core.command"
local common = require "core.common"
local style = require "core.style"
local StatusView = require "core.statusview"
local function status_view_item_names()
local items = core.status_view:get_items_list()
local names = {}
for _, item in ipairs(items) do
table.insert(names, item.name)
end
return names
end
local function status_view_items_data(names)
local data = {}
for _, name in ipairs(names) do
local item = core.status_view:get_item(name)
table.insert(data, {
text = command.prettify_name(item.name),
info = item.alignment == StatusView.Item.LEFT and "Left" or "Right",
name = item.name
})
end
return data
end
local function status_view_get_items(text)
local names = status_view_item_names()
local results = common.fuzzy_match(names, text)
results = status_view_items_data(results)
return results
end
command.add(nil, {
["status-bar:toggle"] = function()
core.status_view:toggle()
end,
["status-bar:show"] = function()
core.status_view:show()
end,
["status-bar:hide"] = function()
core.status_view:hide()
end,
["status-bar:disable-messages"] = function()
core.status_view:display_messages(false)
end,
["status-bar:enable-messages"] = function()
core.status_view:display_messages(true)
end,
["status-bar:hide-item"] = function()
core.command_view:enter("Status bar item to hide",
function(text, item)
core.status_view:hide_items(item.name)
end,
status_view_get_items
)
end,
["status-bar:show-item"] = function()
core.command_view:enter("Status bar item to show",
function(text, item)
core.status_view:show_items(item.name)
end,
status_view_get_items
)
end,
["status-bar:reset-items"] = function()
core.status_view:show_items()
end,
})

View File

@ -56,8 +56,8 @@ function CommandView:get_name()
end end
function CommandView:get_line_screen_position(line, col) function CommandView:get_line_screen_position()
local x = CommandView.super.get_line_screen_position(self, 1, col) local x = CommandView.super.get_line_screen_position(self, 1)
local _, y = self:get_content_offset() local _, y = self:get_content_offset()
local lh = self:get_line_height() local lh = self:get_line_height()
return x, y + (self.size.y - lh) / 2 return x, y + (self.size.y - lh) / 2
@ -243,7 +243,6 @@ function CommandView:draw_line_gutter(idx, x, y)
x = x + style.padding.x x = x + style.padding.x
renderer.draw_text(self:get_font(), self.label, x, y + yoffset, color) renderer.draw_text(self:get_font(), self.label, x, y + yoffset, color)
core.pop_clip_rect() core.pop_clip_rect()
return self:get_line_height()
end end

View File

@ -17,14 +17,6 @@ function common.clamp(n, lo, hi)
end end
function common.merge(a, b)
local t = {}
for k, v in pairs(a) do t[k] = v end
if b then for k, v in pairs(b) do t[k] = v end end
return t
end
function common.round(n) function common.round(n)
return n >= 0 and math.floor(n + 0.5) or math.ceil(n - 0.5) return n >= 0 and math.floor(n + 0.5) or math.ceil(n - 0.5)
end end
@ -50,7 +42,7 @@ end
function common.distance(x1, y1, x2, y2) function common.distance(x1, y1, x2, y2)
return math.sqrt(((x2-x1) ^ 2)+((y2-y1) ^ 2)) return math.sqrt(math.pow(x2-x1, 2)+math.pow(y2-y1, 2))
end end
@ -445,5 +437,4 @@ function common.rm(path, recursively)
return true return true
end end
return common return common

View File

@ -6,19 +6,19 @@ config.message_timeout = 5
config.mouse_wheel_scroll = 50 * SCALE config.mouse_wheel_scroll = 50 * SCALE
config.scroll_past_end = true config.scroll_past_end = true
config.file_size_limit = 10 config.file_size_limit = 10
config.ignore_files = { "^%." } config.ignore_files = "^%."
config.symbol_pattern = "[%a_][%w_]*" config.symbol_pattern = "[%a_][%w_]*"
config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-" config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
config.undo_merge_timeout = 0.3 config.undo_merge_timeout = 0.3
config.max_undos = 10000 config.max_undos = 10000
config.max_tabs = 8 config.max_tabs = 8
config.always_show_tabs = true config.always_show_tabs = true
-- Possible values: false, true, "no_selection"
config.highlight_current_line = true config.highlight_current_line = true
config.line_height = 1.2 config.line_height = 1.2
config.indent_size = 2 config.indent_size = 2
config.tab_type = "soft" config.tab_type = "soft"
config.line_limit = 80 config.line_limit = 80
config.max_symbols = 4000
config.max_project_files = 2000 config.max_project_files = 2000
config.transitions = true config.transitions = true
config.animation_rate = 1.0 config.animation_rate = 1.0
@ -29,19 +29,10 @@ config.borderless = false
config.tab_close_button = true config.tab_close_button = true
config.max_clicks = 3 config.max_clicks = 3
-- set as true to be able to test non supported plugins -- Disable plugin loading setting to false the config entry
config.skip_plugins_version = false -- of the same name.
config.plugins = {} config.plugins = {}
-- Allow you to set plugin configs even if we haven't seen the plugin before.
setmetatable(config.plugins, {
__index = function(t, k)
if rawget(t, k) == nil then rawset(t, k, {}) end
return rawget(t, k)
end
})
-- Disable these plugins by default.
config.plugins.trimwhitespace = false config.plugins.trimwhitespace = false
config.plugins.lineguide = false config.plugins.lineguide = false
config.plugins.drawwhitespace = false config.plugins.drawwhitespace = false

View File

@ -91,7 +91,6 @@ function ContextMenu:show(x, y)
self.position.x, self.position.y = x, y self.position.x, self.position.y = x, y
self.show_context_menu = true self.show_context_menu = true
core.request_cursor("arrow")
return true return true
end end
return false return false
@ -102,7 +101,6 @@ function ContextMenu:hide()
self.items = nil self.items = nil
self.selected = -1 self.selected = -1
self.height = 0 self.height = 0
core.request_cursor(core.active_view.cursor)
end end
function ContextMenu:each_item() function ContextMenu:each_item()
@ -128,6 +126,9 @@ function ContextMenu:on_mouse_moved(px, py)
break break
end end
end end
if self.selected >= 0 then
core.request_cursor("arrow")
end
return true return true
end end
@ -139,38 +140,8 @@ function ContextMenu:on_selected(item)
end end
end end
local function change_value(value, change) function ContextMenu:on_mouse_pressed(button, x, y, clicks)
return value + change local selected = (self.items or {})[self.selected]
end
function ContextMenu:focus_previous()
self.selected = (self.selected == -1 or self.selected == 1) and #self.items or change_value(self.selected, -1)
if self:get_item_selected() == DIVIDER then
self.selected = change_value(self.selected, -1)
end
end
function ContextMenu:focus_next()
self.selected = (self.selected == -1 or self.selected == #self.items) and 1 or change_value(self.selected, 1)
if self:get_item_selected() == DIVIDER then
self.selected = change_value(self.selected, 1)
end
end
function ContextMenu:get_item_selected()
return (self.items or {})[self.selected]
end
function ContextMenu:call_selected_item()
local selected = self:get_item_selected()
self:hide()
if selected then
self:on_selected(selected)
end
end
function ContextMenu:on_mouse_pressed(button, px, py, clicks)
local selected = self:get_item_selected()
local caught = false local caught = false
self:hide() self:hide()
@ -182,7 +153,7 @@ function ContextMenu:on_mouse_pressed(button, px, py, clicks)
end end
if button == "right" then if button == "right" then
caught = self:show(px, py) caught = self:show(x, y)
end end
return caught return caught
end end

View File

@ -1,224 +0,0 @@
local common = require "core.common"
local config = require "core.config"
local dirwatch = {}
function dirwatch:__index(idx)
local value = rawget(self, idx)
if value ~= nil then return value end
return dirwatch[idx]
end
function dirwatch.new()
local t = {
scanned = {},
watched = {},
reverse_watched = {},
-- monitor = dirmonitor.new(),
monitor = 0,
windows_watch_top = nil,
windows_watch_count = 0
}
setmetatable(t, dirwatch)
return t
end
function dirwatch:scan(directory, bool)
if bool == false then return self:unwatch(directory) end
self.scanned[directory] = system.get_file_info(directory).modified
end
-- Should be called on every directory in a subdirectory.
-- In windows, this is a no-op for anything underneath a top-level directory,
-- but code should be called anyway, so we can ensure that we have a proper
-- experience across all platforms. Should be an absolute path.
function dirwatch:watch(directory, bool)
if bool == false then return self:unwatch(directory) end
if not self.watched[directory] and not self.scanned[directory] then
if PLATFORM == "Windows" then
if not self.windows_watch_top or directory:find(self.windows_watch_top, 1, true) ~= 1 then
-- Get the highest level of directory that is common to this directory, and the original.
local target = directory
while self.windows_watch_top and self.windows_watch_top:find(target, 1, true) ~= 1 do
target = common.dirname(target)
end
if target ~= self.windows_watch_top then
local value = self.monitor:watch(target)
if value and value < 0 then
return self:scan(directory)
end
self.windows_watch_top = target
self.windows_watch_count = self.windows_watch_count + 1
end
end
self.watched[directory] = true
else
-- TODO: Fix value to get it from monitor
-- local value = self.monitor:watch(directory)
local value = -1
-- If for whatever reason, we can't watch this directory, revert back to scanning.
-- Don't bother trying to find out why, for now.
if value and value < 0 then
return self:scan(directory)
end
self.watched[directory] = value
self.reverse_watched[value] = directory
end
end
end
-- this should be an absolute path
function dirwatch:unwatch(directory)
if self.watched[directory] then
if PLATFORM ~= "Windows" then
self.monitor:unwatch(self.watched[directory])
self.reverse_watched[directory] = nil
else
self.windows_watch_count = self.windows_watch_count - 1
if self.windows_watch_count == 0 then
self.windows_watch_top = nil
self.monitor:unwatch(directory)
end
end
self.watched[directory] = nil
elseif self.scanned[directory] then
self.scanned[directory] = nil
end
end
-- designed to be run inside a coroutine.
function dirwatch:check(change_callback, scan_time, wait_time)
self.monitor:check(function(id)
if PLATFORM == "Windows" then
change_callback(common.dirname(self.windows_watch_top .. PATHSEP .. id))
elseif self.reverse_watched[id] then
change_callback(self.reverse_watched[id])
end
end)
local start_time = system.get_time()
for directory, old_modified in pairs(self.scanned) do
if old_modified then
local new_modified = system.get_file_info(directory).modified
if old_modified < new_modified then
change_callback(directory)
self.scanned[directory] = new_modified
end
end
if system.get_time() - start_time > scan_time then
coroutine.yield(wait_time)
start_time = system.get_time()
end
end
end
-- inspect config.ignore_files patterns and prepare ready to use entries.
local function compile_ignore_files()
local ipatterns = config.ignore_files
local compiled = {}
-- config.ignore_files could be a simple string...
if type(ipatterns) ~= "table" then ipatterns = {ipatterns} end
for i, pattern in ipairs(ipatterns) do
-- we ignore malformed pattern that raise an error
if pcall(string.match, "a", pattern) then
table.insert(compiled, {
use_path = pattern:match("/[^/$]"), -- contains a slash but not at the end
-- An '/' or '/$' at the end means we want to match a directory.
match_dir = pattern:match(".+/%$?$"), -- to be used as a boolen value
pattern = pattern -- get the actual pattern
})
end
end
return compiled
end
local function fileinfo_pass_filter(info, ignore_compiled)
if info.size >= config.file_size_limit * 1e6 then return false end
local basename = common.basename(info.filename)
-- replace '\' with '/' for Windows where PATHSEP = '\'
local fullname = "/" .. info.filename:gsub("\\", "/")
for _, compiled in ipairs(ignore_compiled) do
local test = compiled.use_path and fullname or basename
if compiled.match_dir then
if info.type == "dir" and string.match(test .. "/", compiled.pattern) then
return false
end
else
if string.match(test, compiled.pattern) then
return false
end
end
end
return true
end
local function compare_file(a, b)
return a.filename < b.filename
end
-- compute a file's info entry completed with "filename" to be used
-- in project scan or falsy if it shouldn't appear in the list.
local function get_project_file_info(root, file, ignore_compiled)
local info = system.get_file_info(root .. PATHSEP .. file)
-- info can be not nil but info.type may be nil if is neither a file neither
-- a directory, for example for /dev/* entries on linux.
if info and info.type then
info.filename = file
return fileinfo_pass_filter(info, ignore_compiled) and info
end
end
-- "root" will by an absolute path without trailing '/'
-- "path" will be a path starting without '/' and without trailing '/'
-- or the empty string.
-- It will identifies a sub-path within "root.
-- The current path location will therefore always be: root .. path.
-- When recursing "root" will always be the same, only "path" will change.
-- Returns a list of file "items". In each item the "filename" will be the
-- complete file path relative to "root" *without* the trailing '/', and without the starting '/'.
function dirwatch.get_directory_files(dir, root, path, t, entries_count, recurse_pred)
local t0 = system.get_time()
local t_elapsed = system.get_time() - t0
local dirs, files = {}, {}
local ignore_compiled = compile_ignore_files()
local all = system.list_dir(root .. PATHSEP .. path)
if not all then return nil end
for _, file in ipairs(all or {}) do
local info = get_project_file_info(root, (path ~= "" and (path .. PATHSEP) or "") .. file, ignore_compiled)
if info then
table.insert(info.type == "dir" and dirs or files, info)
entries_count = entries_count + 1
end
end
local recurse_complete = true
table.sort(dirs, compare_file)
for _, f in ipairs(dirs) do
table.insert(t, f)
if recurse_pred(dir, f.filename, entries_count, t_elapsed) then
local _, complete, n = dirwatch.get_directory_files(dir, root, f.filename, t, entries_count, recurse_pred)
recurse_complete = recurse_complete and complete
entries_count = n
else
recurse_complete = false
end
end
table.sort(files, compare_file)
for _, f in ipairs(files) do
table.insert(t, f)
end
return t, recurse_complete, entries_count
end
return dirwatch

View File

@ -33,6 +33,7 @@ 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 }
self.cursor_clipboard = {}
self.undo_stack = { idx = 1 } self.undo_stack = { idx = 1 }
self.redo_stack = { idx = 1 } self.redo_stack = { idx = 1 }
self.clean_change_id = 1 self.clean_change_id = 1
@ -54,7 +55,6 @@ 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()
end end
@ -85,8 +85,6 @@ function Doc:save(filename, abs_filename)
assert(self.filename, "no filename set to default to") assert(self.filename, "no filename set to default to")
filename = self.filename filename = self.filename
abs_filename = self.abs_filename abs_filename = self.abs_filename
else
assert(self.filename or abs_filename, "calling save on unnamed doc without absolute path")
end end
local fp = assert( io.open(filename, "wb") ) local fp = assert( io.open(filename, "wb") )
for _, line in ipairs(self.lines) do for _, line in ipairs(self.lines) do
@ -96,6 +94,7 @@ function Doc:save(filename, abs_filename)
fp:close() fp:close()
self:set_filename(filename, abs_filename) self:set_filename(filename, abs_filename)
self.new_file = false self.new_file = false
self:reset_syntax()
self:clean() self:clean()
end end
@ -198,14 +197,8 @@ function Doc:add_selection(line1, col1, line2, col2, swap)
self:set_selections(target, line1, col1, line2, col2, swap, 0) self:set_selections(target, line1, col1, line2, col2, swap, 0)
end end
function Doc:remove_selection(idx)
common.splice(self.selections, (idx - 1) * 4 + 1, 4)
end
function Doc:set_selection(line1, col1, line2, col2, swap) function Doc:set_selection(line1, col1, line2, col2, swap)
self.selections = {} self.selections, self.cursor_clipboard = {}, {}
self:set_selections(1, line1, col1, line2, col2, swap) self:set_selections(1, line1, col1, line2, col2, swap)
end end
@ -215,10 +208,12 @@ function Doc:merge_cursors(idx)
if self.selections[i] == self.selections[j] and if self.selections[i] == self.selections[j] and
self.selections[i+1] == self.selections[j+1] then self.selections[i+1] == self.selections[j+1] then
common.splice(self.selections, i, 4) common.splice(self.selections, i, 4)
common.splice(self.cursor_clipboard, i, 1)
break break
end end
end end
end end
if #self.selections <= 4 then self.cursor_clipboard = {} end
end end
local function selection_iterator(invariant, idx) local function selection_iterator(invariant, idx)
@ -361,7 +356,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
-- splice lines into line array -- splice lines into line array
common.splice(self.lines, line, 1, lines) common.splice(self.lines, line, 1, lines)
-- keep cursors where they should be -- keep cursors where they should be
for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do
if cline1 < line then break end if cline1 < line then break end
@ -393,7 +388,7 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
-- splice line into line array -- splice line into line array
common.splice(self.lines, line1, line2 - line1 + 1, { before .. after }) common.splice(self.lines, line1, line2 - line1 + 1, { before .. after })
-- move all cursors back if they share a line with the removed text -- move all cursors back if they share a line with the removed text
for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do
if cline1 < line2 then break end if cline1 < line2 then break end
@ -463,7 +458,7 @@ end
function Doc:replace(fn) function Doc:replace(fn)
local has_selection, n = false, 0 local has_selection, n = false, 0
for idx, line1, col1, line2, col2 in self:get_selections(true) do for idx, line1, col1, line2, col2 in self:get_selections(true) do
if line1 ~= line2 or col1 ~= col2 then if line1 ~= line2 or col1 ~= col2 then
n = n + self:replace_cursor(idx, line1, col1, line2, col2, fn) n = n + self:replace_cursor(idx, line1, col1, line2, col2, fn)
has_selection = true has_selection = true
end end

View File

@ -121,18 +121,14 @@ function DocView:get_gutter_width()
end end
function DocView:get_line_screen_position(line, col) function DocView:get_line_screen_position(idx)
local x, y = self:get_content_offset() local x, y = self:get_content_offset()
local lh = self:get_line_height() local lh = self:get_line_height()
local gw = self:get_gutter_width() local gw = self:get_gutter_width()
y = y + (line-1) * lh + style.padding.y return x + gw, y + (idx-1) * lh + style.padding.y
if col then
return x + gw + self:get_col_x_offset(line, col), y
else
return x + gw, y
end
end end
function DocView:get_line_text_y_offset() function DocView:get_line_text_y_offset()
local lh = self:get_line_height() local lh = self:get_line_height()
local th = self:get_font():get_height() local th = self:get_font():get_height()
@ -202,9 +198,8 @@ end
function DocView:scroll_to_line(line, ignore_if_visible, instant) function DocView:scroll_to_line(line, ignore_if_visible, instant)
local min, max = self:get_visible_line_range() local min, max = self:get_visible_line_range()
if not (ignore_if_visible and line > min and line < max) then if not (ignore_if_visible and line > min and line < max) then
local x, y = self:get_line_screen_position(line) local lh = self:get_line_height()
local ox, oy = self:get_content_offset() self.scroll.to.y = math.max(0, lh * (line - 1) - self.size.y / 2)
self.scroll.to.y = math.max(0, y - oy - self.size.y / 2)
if instant then if instant then
self.scroll.y = self.scroll.to.y self.scroll.y = self.scroll.to.y
end end
@ -213,10 +208,10 @@ end
function DocView:scroll_to_make_visible(line, col) function DocView:scroll_to_make_visible(line, col)
local ox, oy = self:get_content_offset() local min = self:get_line_height() * (line - 1)
local _, ly = self:get_line_screen_position(line, col) local max = self:get_line_height() * (line + 2) - self.size.y
local lh = self:get_line_height() self.scroll.to.y = math.min(self.scroll.to.y, min)
self.scroll.to.y = common.clamp(self.scroll.to.y, ly - oy - self.size.y + lh * 2, ly - oy - lh) self.scroll.to.y = math.max(self.scroll.to.y, max)
local gw = self:get_gutter_width() local gw = self:get_gutter_width()
local xoffset = self:get_col_x_offset(line, col) local xoffset = self:get_col_x_offset(line, col)
local xmargin = 3 * self:get_font():get_width(' ') local xmargin = 3 * self:get_font():get_width(' ')
@ -229,6 +224,7 @@ function DocView:scroll_to_make_visible(line, col)
end end
end end
function DocView:on_mouse_moved(x, y, ...) function DocView:on_mouse_moved(x, y, ...)
DocView.super.on_mouse_moved(self, x, y, ...) DocView.super.on_mouse_moved(self, x, y, ...)
@ -288,15 +284,13 @@ end
function DocView:update() function DocView:update()
-- scroll to make caret visible and reset blink timer if it moved -- scroll to make caret visible and reset blink timer if it moved
local line1, col1, line2, col2 = self.doc:get_selection() local line, col = self.doc:get_selection()
if (line1 ~= self.last_line1 or col1 ~= self.last_col1 or if (line ~= self.last_line or col ~= self.last_col) and self.size.x > 0 then
line2 ~= self.last_line2 or col2 ~= self.last_col2) and self.size.x > 0 then
if core.active_view == self then if core.active_view == self then
self:scroll_to_make_visible(line1, col1) self:scroll_to_make_visible(line, col)
end end
core.blink_reset() core.blink_reset()
self.last_line1, self.last_col1 = line1, col1 self.last_line, self.last_col = line, col
self.last_line2, self.last_col2 = line2, col2
end end
-- update blink timer -- update blink timer
@ -319,15 +313,14 @@ function DocView:draw_line_highlight(x, y)
end end
function DocView:draw_line_text(line, x, y) function DocView:draw_line_text(idx, x, y)
local default_font = self:get_font() local default_font = self:get_font()
local tx, ty = x, y + self:get_line_text_y_offset() local tx, ty = x, y + self:get_line_text_y_offset()
for _, type, text in self.doc.highlighter:each_token(line) do for _, type, text in self.doc.highlighter:each_token(idx) do
local color = style.syntax[type] local color = style.syntax[type]
local font = style.syntax_fonts[type] or default_font local font = style.syntax_fonts[type] or default_font
tx = renderer.draw_text(font, text, tx, ty, color) tx = renderer.draw_text(font, text, tx, ty, color)
end end
return self:get_line_height()
end end
function DocView:draw_caret(x, y) function DocView:draw_caret(x, y)
@ -335,37 +328,28 @@ function DocView:draw_caret(x, y)
renderer.draw_rect(x, y, style.caret_width, lh, style.caret) renderer.draw_rect(x, y, style.caret_width, lh, style.caret)
end end
function DocView:draw_line_body(line, x, y) function DocView:draw_line_body(idx, x, y)
-- draw highlight if any selection ends on this line -- draw highlight if any selection ends on this line
local draw_highlight = false local draw_highlight = false
local hcl = config.highlight_current_line for lidx, line1, col1, line2, col2 in self.doc:get_selections(false) do
if hcl ~= false then if line1 == idx then
for lidx, line1, col1, line2, col2 in self.doc:get_selections(false) do draw_highlight = true
if line1 == line then break
if hcl == "no_selection" then
if (line1 ~= line2) or (col1 ~= col2) then
draw_highlight = false
break
end
end
draw_highlight = true
break
end
end end
end end
if draw_highlight and core.active_view == self then if draw_highlight and config.highlight_current_line and core.active_view == self then
self:draw_line_highlight(x + self.scroll.x, y) self:draw_line_highlight(x + self.scroll.x, y)
end end
-- draw selection if it overlaps this line -- draw selection if it overlaps this line
local lh = self:get_line_height()
for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
if line >= line1 and line <= line2 then if idx >= line1 and idx <= line2 then
local text = self.doc.lines[line] local text = self.doc.lines[idx]
if line1 ~= line then col1 = 1 end if line1 ~= idx then col1 = 1 end
if line2 ~= line then col2 = #text + 1 end if line2 ~= idx then col2 = #text + 1 end
local x1 = x + self:get_col_x_offset(line, col1) local x1 = x + self:get_col_x_offset(idx, col1)
local x2 = x + self:get_col_x_offset(line, col2) local x2 = x + self:get_col_x_offset(idx, col2)
local lh = self:get_line_height()
if x1 ~= x2 then if x1 ~= x2 then
renderer.draw_rect(x1, y, x2 - x1, lh, style.selection) renderer.draw_rect(x1, y, x2 - x1, lh, style.selection)
end end
@ -373,22 +357,21 @@ function DocView:draw_line_body(line, x, y)
end end
-- draw line's text -- draw line's text
return self:draw_line_text(line, x, y) self:draw_line_text(idx, x, y)
end end
function DocView:draw_line_gutter(line, x, y, width) function DocView:draw_line_gutter(idx, x, y, width)
local color = style.line_number local color = style.line_number
for _, line1, _, line2 in self.doc:get_selections(true) do for _, line1, _, line2 in self.doc:get_selections(true) do
if line >= line1 and line <= line2 then if idx >= line1 and idx <= line2 then
color = style.line_number2 color = style.line_number2
break break
end end
end end
local yoffset = self:get_line_text_y_offset()
x = x + style.padding.x x = x + style.padding.x
local lh = self:get_line_height() common.draw_text(self:get_font(), color, idx, "right", x, y + yoffset, width, self:get_line_height())
common.draw_text(self:get_font(), color, line, "right", x, y, width, lh)
return lh
end end
@ -402,7 +385,8 @@ function DocView:draw_overlay()
and system.window_has_focus() then and system.window_has_focus() then
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
self:draw_caret(self:get_line_screen_position(line, col)) local x, y = self:get_line_screen_position(line)
self:draw_caret(x + self:get_col_x_offset(line, col), y)
end end
end end
end end
@ -420,7 +404,8 @@ function DocView:draw()
local x, y = self:get_line_screen_position(minline) local x, y = self:get_line_screen_position(minline)
local gw, gpad = self:get_gutter_width() local gw, gpad = self:get_gutter_width()
for i = minline, maxline do for i = minline, maxline do
y = y + (self:draw_line_gutter(i, self.position.x, y, gpad and gw - gpad or gw) or lh) self:draw_line_gutter(i, self.position.x, y, gpad and gw - gpad or gw)
y = y + lh
end end
local pos = self.position local pos = self.position
@ -429,7 +414,8 @@ function DocView:draw()
-- right side it is redundant with the Node's clip. -- right side it is redundant with the Node's clip.
core.push_clip_rect(pos.x + gw, pos.y, self.size.x - gw, self.size.y) core.push_clip_rect(pos.x + gw, pos.y, self.size.x - gw, self.size.y)
for i = minline, maxline do for i = minline, maxline do
y = y + (self:draw_line_body(i, x, y) or lh) self:draw_line_body(i, x, y)
y = y + lh
end end
self:draw_overlay() self:draw_overlay()
core.pop_clip_rect() core.pop_clip_rect()

View File

@ -8,18 +8,8 @@ local function draw_text(x, y, color)
local th = style.big_font:get_height() local th = style.big_font:get_height()
local dh = 2 * th + style.padding.y * 2 local dh = 2 * th + style.padding.y * 2
local x1, y1 = x, y + (dh - th) / 2 local x1, y1 = x, y + (dh - th) / 2
local xv = x1 x = renderer.draw_text(style.big_font, "Lite XL", x1, y1, color)
local title = "Lite XL" renderer.draw_text(style.font, "version " .. VERSION, x1, y1 + th, color)
local version = "version " .. VERSION
local title_width = style.big_font:get_width(title)
local version_width = style.font:get_width(version)
if version_width > title_width then
version = VERSION
version_width = style.font:get_width(version)
xv = x1 - (version_width - title_width)
end
x = renderer.draw_text(style.big_font, title, x1, y1, color)
renderer.draw_text(style.font, version, xv, y1 + th, color)
x = x + style.padding.x x = x + style.padding.x
renderer.draw_rect(x, y, math.ceil(1 * SCALE), dh, color) renderer.draw_rect(x, y, math.ceil(1 * SCALE), dh, color)
local lines = { local lines = {

File diff suppressed because it is too large Load Diff

View File

@ -216,7 +216,6 @@ keymap.add_direct {
["ctrl+l"] = "doc:select-lines", ["ctrl+l"] = "doc:select-lines",
["ctrl+shift+l"] = { "find-replace:select-add-all", "doc:select-word" }, ["ctrl+shift+l"] = { "find-replace:select-add-all", "doc:select-word" },
["ctrl+/"] = "doc:toggle-line-comments", ["ctrl+/"] = "doc:toggle-line-comments",
["ctrl+shift+/"] = "doc:toggle-block-comments",
["ctrl+up"] = "doc:move-lines-up", ["ctrl+up"] = "doc:move-lines-up",
["ctrl+down"] = "doc:move-lines-down", ["ctrl+down"] = "doc:move-lines-down",
["ctrl+shift+d"] = "doc:duplicate-lines", ["ctrl+shift+d"] = "doc:duplicate-lines",
@ -261,3 +260,4 @@ keymap.add_direct {
} }
return keymap return keymap

View File

@ -1,6 +1,5 @@
local core = require "core" local core = require "core"
local common = require "core.common" local common = require "core.common"
local keymap = require "core.keymap"
local style = require "core.style" local style = require "core.style"
local View = require "core.view" local View = require "core.view"
@ -37,15 +36,12 @@ local LogView = View:extend()
LogView.context = "session" LogView.context = "session"
function LogView:new() function LogView:new()
LogView.super.new(self) LogView.super.new(self)
self.last_item = core.log_items[#core.log_items] self.last_item = core.log_items[#core.log_items]
self.expanding = {} self.expanding = {}
self.scrollable = true self.scrollable = true
self.yoffset = 0 self.yoffset = 0
core.status_view:show_message("i", style.text, "ctrl+click to copy entry")
end end
@ -81,30 +77,25 @@ function LogView:each_item()
end end
function LogView:on_mouse_pressed(button, px, py, clicks) function LogView:on_mouse_moved(px, py, ...)
if LogView.super.on_mouse_pressed(self, button, px, py, clicks) then LogView.super.on_mouse_moved(self, px, py, ...)
return true local hovered = false
end for _, item, x, y, w, h in self:each_item() do
local index, selected
for i, item, x, y, w, h in self:each_item() do
if px >= x and py >= y and px < x + w and py < y + h then if px >= x and py >= y and px < x + w and py < y + h then
index = i hovered = true
selected = item self.hovered_item = item
break break
end end
end end
if not hovered then self.hovered_item = nil end
end
if selected then
if keymap.modkeys["ctrl"] then function LogView:on_mouse_pressed(button, mx, my, clicks)
system.set_clipboard(core.get_log(selected)) if LogView.super.on_mouse_pressed(self, button, mx, my, clicks) then return end
core.status_view:show_message("i", style.text, "copied entry #"..index.." to clipboard.") if self.hovered_item then
else self:expand_item(self.hovered_item)
self:expand_item(selected)
end
end end
return true
end end
@ -140,37 +131,21 @@ local function draw_text_multiline(font, text, x, y, color)
return resx, y return resx, y
end end
-- this is just to get a date string that's consistent
local datestr = os.date()
function LogView:draw() function LogView:draw()
self:draw_background(style.background) self:draw_background(style.background)
local th = style.font:get_height() local th = style.font:get_height()
local lh = th + style.padding.y -- for one line local lh = th + style.padding.y -- for one line
local iw = math.max( for _, item, x, y, w in self:each_item() do
style.icon_font:get_width(style.log.ERROR.icon),
style.icon_font:get_width(style.log.INFO.icon)
)
local tw = style.font:get_width(datestr)
for _, item, x, y, w, h in self:each_item() do
core.push_clip_rect(x, y, w, h)
x = x + style.padding.x x = x + style.padding.x
x = common.draw_text(
style.icon_font,
style.log[item.level].color,
style.log[item.level].icon,
"center",
x, y, iw, lh
)
x = x + style.padding.x
-- timestamps are always 15% of the width
local time = os.date(nil, item.time) local time = os.date(nil, item.time)
common.draw_text(style.font, style.dim, time, "left", x, y, tw, lh) x = common.draw_text(style.font, style.dim, time, "left", x, y, w, lh)
x = x + tw + style.padding.x x = x + style.padding.x
x = common.draw_text(style.code_font, style.dim, is_expanded(item) and "-" or "+", "left", x, y, w, lh)
x = x + style.padding.x
w = w - (x - self:get_content_offset()) w = w - (x - self:get_content_offset())
if is_expanded(item) then if is_expanded(item) then
@ -190,8 +165,6 @@ function LogView:draw()
end end
_, y = common.draw_text(style.font, style.text, line, "left", x, y, w, lh) _, y = common.draw_text(style.font, style.text, line, "left", x, y, w, lh)
end end
core.pop_clip_rect()
end end
end end

0
data/core/modkeys-os4.lua Normal file → Executable file
View File

View File

@ -16,12 +16,8 @@ local NagView = View:extend()
function NagView:new() function NagView:new()
NagView.super.new(self) NagView.super.new(self)
self.size.y = 0 self.size.y = 0
self.show_height = 0
self.force_focus = false self.force_focus = false
self.queue = {} self.queue = {}
self.scrollable = true
self.target_height = 0
self.on_mouse_pressed_root = nil
end end
function NagView:get_title() function NagView:get_title()
@ -50,20 +46,20 @@ function NagView:get_target_height()
return self.target_height + 2 * style.padding.y return self.target_height + 2 * style.padding.y
end end
function NagView:get_scrollable_size() function NagView:update()
local w, h = system.get_window_size() NagView.super.update(self)
if self.visible and self:get_target_height() > h then
self.size.y = h if core.active_view == self and self.title then
return self:get_target_height() self:move_towards(self.size, "y", self:get_target_height())
self:move_towards(self, "underline_progress", 1)
else else
self.size.y = 0 self:move_towards(self.size, "y", 0)
end end
return 0
end end
function NagView:dim_window_content() function NagView:draw_overlay()
local ox, oy = self:get_content_offset() local ox, oy = self:get_content_offset()
oy = oy + self.show_height oy = oy + self.size.y
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()
renderer.draw_rect(ox, oy, w, h, style.nagbar_dim) renderer.draw_rect(ox, oy, w, h, style.nagbar_dim)
@ -85,7 +81,7 @@ function NagView:each_option()
bh = self:get_buttons_height() bh = self:get_buttons_height()
ox,oy = self:get_content_offset() ox,oy = self:get_content_offset()
ox = ox + self.size.x ox = ox + self.size.x
oy = oy + self.show_height - bh - style.padding.y oy = oy + self.size.y - bh - style.padding.y
for i = #self.options, 1, -1 do for i = #self.options, 1, -1 do
opt = self.options[i] opt = self.options[i]
@ -98,8 +94,6 @@ function NagView:each_option()
end end
function NagView:on_mouse_moved(mx, my, ...) function NagView:on_mouse_moved(mx, my, ...)
if not self.visible then return end
core.set_active_view(self)
NagView.super.on_mouse_moved(self, mx, my, ...) NagView.super.on_mouse_moved(self, mx, my, ...)
for i, _, x,y,w,h in self:each_option() do for i, _, x,y,w,h in self:each_option() do
if mx >= x and my >= y and mx < x + w and my < y + h then if mx >= x and my >= y and mx < x + w and my < y + h then
@ -109,55 +103,18 @@ function NagView:on_mouse_moved(mx, my, ...)
end end
end end
local function register_mouse_pressed(self)
if self.on_mouse_pressed_root then return end
-- RootView is loaded locally to avoid NagView and RootView being
-- mutually recursive
local RootView = require "core.rootview"
self.on_mouse_pressed_root = RootView.on_mouse_pressed
local this = self
function RootView:on_mouse_pressed(button, x, y, clicks)
if
not this:on_mouse_pressed(button, x, y, clicks)
then
return this.on_mouse_pressed_root(self, button, x, y, clicks)
else
return true
end
end
self.new_on_mouse_pressed_root = RootView.on_mouse_pressed
end
local function unregister_mouse_pressed(self)
local RootView = require "core.rootview"
if
self.on_mouse_pressed_root
and
-- just in case prevent overwriting what something else may
-- have overwrote after us, but after testing with various
-- plugins this doesn't seems to happen, but just in case
self.new_on_mouse_pressed_root == RootView.on_mouse_pressed
then
RootView.on_mouse_pressed = self.on_mouse_pressed_root
self.on_mouse_pressed_root = nil
self.new_on_mouse_pressed_root = nil
end
end
function NagView:on_mouse_pressed(button, mx, my, clicks) function NagView:on_mouse_pressed(button, mx, my, clicks)
if not self.visible then return false end if NagView.super.on_mouse_pressed(self, button, mx, my, clicks) then return end
if NagView.super.on_mouse_pressed(self, button, mx, my, clicks) then return true end
for i, _, x,y,w,h in self:each_option() do for i, _, x,y,w,h in self:each_option() do
if mx >= x and my >= y and mx < x + w and my < y + h then if mx >= x and my >= y and mx < x + w and my < y + h then
self:change_hovered(i) self:change_hovered(i)
command.perform "dialog:select" command.perform "dialog:select"
break
end end
end end
return true
end end
function NagView:on_text_input(text) function NagView:on_text_input(text)
if not self.visible then return end
if text:lower() == "y" then if text:lower() == "y" then
command.perform "dialog:select-yes" command.perform "dialog:select-yes"
elseif text:lower() == "n" then elseif text:lower() == "n" then
@ -165,39 +122,20 @@ function NagView:on_text_input(text)
end end
end end
function NagView:update()
if not self.visible and self.show_height <= 0 then return end
NagView.super.update(self)
if self.visible and core.active_view == self and self.title then function NagView:draw()
self:move_towards(self, "show_height", self:get_target_height()) if self.size.y <= 0 or not self.title then return end
self:move_towards(self, "underline_progress", 1)
else
self:move_towards(self, "show_height", 0)
if self.show_height <= 0 then
self.title = nil
self.message = nil
self.options = nil
self.on_selected = nil
end
end
end
local function draw_nagview_message(self) self:draw_overlay()
self:dim_window_content() self:draw_background(style.nagbar)
-- draw message's background
local ox, oy = self:get_content_offset() local ox, oy = self:get_content_offset()
renderer.draw_rect(ox, oy, self.size.x, self.show_height, style.nagbar)
ox = ox + style.padding.x ox = ox + style.padding.x
core.push_clip_rect(ox, oy, self.size.x, self.show_height)
-- if there are other items, show it -- if there are other items, show it
if #self.queue > 0 then if #self.queue > 0 then
local str = string.format("[%d]", #self.queue) local str = string.format("[%d]", #self.queue)
ox = common.draw_text(style.font, style.nagbar_text, str, "left", ox, oy, self.size.x, self.show_height) ox = common.draw_text(style.font, style.nagbar_text, str, "left", ox, oy, self.size.x, self.size.y)
ox = ox + style.padding.x ox = ox + style.padding.x
end end
@ -230,17 +168,6 @@ local function draw_nagview_message(self)
common.draw_text(opt.font, style.nagbar_text, opt.text, "center", fx,fy,fw,fh) common.draw_text(opt.font, style.nagbar_text, opt.text, "center", fx,fy,fw,fh)
end end
self:draw_scrollbar()
core.pop_clip_rect()
end
function NagView:draw()
if (not self.visible and self.show_height <= 0) or not self.title then
return
end
core.root_view:defer_draw(draw_nagview_message, self)
end end
function NagView:get_message_height() function NagView:get_message_height()
@ -251,31 +178,23 @@ function NagView:get_message_height()
return h return h
end end
function NagView:next() function NagView:next()
local opts = table.remove(self.queue, 1) or {} local opts = table.remove(self.queue, 1) or {}
if opts.title and opts.message and opts.options then self.title = opts.title
self.visible = true self.message = opts.message and opts.message .. "\n"
self.title = opts.title self.options = opts.options
self.message = opts.message and opts.message .. "\n" self.on_selected = opts.on_selected
self.options = opts.options if self.message and self.options then
self.on_selected = opts.on_selected
local message_height = self:get_message_height() local message_height = self:get_message_height()
-- self.target_height is the nagview height needed to display the message and -- self.target_height is the nagview height needed to display the message and
-- the buttons, excluding the top and bottom padding space. -- the buttons, excluding the top and bottom padding space.
self.target_height = math.max(message_height, self:get_buttons_height()) self.target_height = math.max(message_height, self:get_buttons_height())
self:change_hovered(common.find_index(self.options, "default_yes")) self:change_hovered(common.find_index(self.options, "default_yes"))
self.force_focus = true
core.set_active_view(self)
-- We add a hook to manage all the mouse_pressed events.
register_mouse_pressed(self)
else
self.force_focus = false
core.set_active_view(core.next_active_view or core.last_active_view)
self.visible = false
unregister_mouse_pressed(self)
end end
self.force_focus = self.message ~= nil
core.set_active_view(self.message ~= nil and self or
core.next_active_view or core.last_active_view)
end end
function NagView:show(title, message, options, on_select) function NagView:show(title, message, options, on_select)
@ -285,7 +204,7 @@ function NagView:show(title, message, options, on_select)
opts.options = assert(options, "No options") opts.options = assert(options, "No options")
opts.on_selected = on_select or noop opts.on_selected = on_select or noop
table.insert(self.queue, opts) table.insert(self.queue, opts)
self:next() if #self.queue > 0 and not self.title then self:next() end
end end
return NagView return NagView

View File

@ -260,8 +260,8 @@ end
local function close_button_location(x, w) local function close_button_location(x, w)
local cw = style.icon_font:get_width("C") local cw = style.icon_font:get_width("C")
local pad = style.padding.x / 2 local pad = style.padding.y
return x + w - cw - pad, cw, pad return x + w - pad - cw, cw, pad
end end
@ -476,59 +476,51 @@ function Node:update()
end end
end end
function Node:draw_tab_title(view, font, is_active, is_hovered, x, y, w, h) function Node:draw_tab(text, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
local text = view and view:get_name() or ""
local dots_width = font:get_width("")
local align = "center"
if font:get_width(text) > w then
align = "left"
for i = 1, #text do
local reduced_text = text:sub(1, #text - i)
if font:get_width(reduced_text) + dots_width <= w then
text = reduced_text .. ""
break
end
end
end
local color = style.dim
if is_active then color = style.text end
if is_hovered then color = style.text end
common.draw_text(font, color, text, align, x, y, w, h)
end
function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalone)
-- Tabs deviders
local ds = style.divider_size local ds = style.divider_size
local dots_width = style.font:get_width("")
local color = style.dim local color = style.dim
local padding_y = style.padding.y local padding_y = style.padding.y
renderer.draw_rect(x + w, y + padding_y, ds, h - padding_y*2, style.dim) renderer.draw_rect(x + w, y + padding_y, ds, h - padding_y * 2, style.dim)
if standalone then if standalone then
renderer.draw_rect(x-1, y-1, w+2, h+2, style.background2) renderer.draw_rect(x-1, y-1, w+2, h+2, style.background2)
end end
-- Full border
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 + 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
return x + ds, y, w - ds*2, h local cx, cw, cspace = close_button_location(x, w)
end
function Node:draw_tab(view, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalone)
-- Close button
local cx, cw, cpad = close_button_location(x, w)
local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button) local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button)
if show_close_button then if show_close_button then
local close_style = is_close_hovered and style.text or style.dim local close_style = is_close_hovered and style.text or style.dim
common.draw_text(style.icon_font, close_style, "C", nil, cx, y, cw, h) common.draw_text(style.icon_font, close_style, "C", nil, cx, y, 0, h)
end end
-- Title if is_hovered then
x = x + cpad color = style.text
w = cx - x end
core.push_clip_rect(x, y, w, h) local padx = style.padding.x
self:draw_tab_title(view, style.font, is_active, is_hovered, x, y, w, h) -- Normally we should substract "cspace" from text_avail_width and from the
-- clipping width. It is the padding space we give to the left and right of the
-- close button. However, since we are using dots to terminate filenames, we
-- choose to ignore "cspace" accepting that the text can possibly "touch" the
-- close button.
local text_avail_width = cx - x - padx
core.push_clip_rect(x, y, cx - x, h)
x, w = x + padx, w - padx * 2
local align = "center"
if style.font:get_width(text) > text_avail_width then
align = "left"
for i = 1, #text do
local reduced_text = text:sub(1, #text - i)
if style.font:get_width(reduced_text) + dots_width <= text_avail_width then
text = reduced_text .. ""
break
end
end
end
common.draw_text(style.font, color, text, align, x, y, w, h)
core.pop_clip_rect() core.pop_clip_rect()
end end
@ -555,7 +547,7 @@ function Node:draw_tabs()
for i = self.tab_offset, self.tab_offset + tabs_number - 1 do for i = self.tab_offset, self.tab_offset + tabs_number - 1 do
local view = self.views[i] local view = self.views[i]
local x, y, w, h = self:get_tab_rect(i) local x, y, w, h = self:get_tab_rect(i)
self:draw_tab(view, view == self.active_view, self:draw_tab(view:get_name(), view == self.active_view,
i == self.hovered_tab, i == self.hovered_close, i == self.hovered_tab, i == self.hovered_close,
x, y, w, h) x, y, w, h)
end end
@ -696,7 +688,7 @@ function Node:get_split_type(mouse_x, mouse_y)
local local_mouse_x = mouse_x - x local local_mouse_x = mouse_x - x
local local_mouse_y = mouse_y - y local local_mouse_y = mouse_y - y
if local_mouse_y < 0 then if local_mouse_y < 0 then
return "tab" return "tab"
else else

View File

@ -30,9 +30,7 @@ end
function RootView:get_active_node() function RootView:get_active_node()
local node = self.root_node:get_node_for_view(core.active_view) return self.root_node:get_node_for_view(core.active_view)
if not node then node = self:get_primary_node() end
return node
end end
@ -48,7 +46,6 @@ end
function RootView:get_active_node_default() function RootView:get_active_node_default()
local node = self.root_node:get_node_for_view(core.active_view) local node = self.root_node:get_node_for_view(core.active_view)
if not node then node = self:get_primary_node() end
if node.locked then if node.locked then
local default_view = self:get_primary_node().views[1] local default_view = self:get_primary_node().views[1]
assert(default_view, "internal error: cannot find original document node.") assert(default_view, "internal error: cannot find original document node.")
@ -257,7 +254,7 @@ function RootView:on_mouse_moved(x, y, dx, dy)
self.root_node:on_mouse_moved(x, y, dx, dy) self.root_node:on_mouse_moved(x, y, dx, dy)
self.overlapping_node = self.root_node:get_child_overlapping_point(x, y) self.overlapping_node = self.root_node:get_child_overlapping_point(x, y)
local div = self.root_node:get_divider_overlapping_point(x, y) local div = self.root_node:get_divider_overlapping_point(x, y)
local tab_index = self.overlapping_node and self.overlapping_node:get_tab_overlapping_point(x, y) 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 if self.overlapping_node and self.overlapping_node:get_scroll_button_index(x, y) then
@ -272,12 +269,6 @@ function RootView:on_mouse_moved(x, y, dx, dy)
end end
function RootView:on_file_dropped(filename, x, y)
local node = self.root_node:get_child_overlapping_point(x, y)
return node and node.active_view:on_file_dropped(filename, x, y)
end
function RootView:on_mouse_wheel(...) function RootView:on_mouse_wheel(...)
local x, y = self.mouse.x, self.mouse.y local x, y = self.mouse.x, self.mouse.y
local node = self.root_node:get_child_overlapping_point(x, y) local node = self.root_node:get_child_overlapping_point(x, y)
@ -390,8 +381,8 @@ function RootView:draw_grabbed_tab()
local _,_, w, h = dn.node:get_tab_rect(dn.idx) local _,_, w, h = dn.node:get_tab_rect(dn.idx)
local x = self.mouse.x - w / 2 local x = self.mouse.x - w / 2
local y = self.mouse.y - h / 2 local y = self.mouse.y - h / 2
local view = dn.node.views[dn.idx] local text = dn.node.views[dn.idx] and dn.node.views[dn.idx]:get_name() or ""
self.root_node:draw_tab(view, true, true, false, x, y, w, h, true) self.root_node:draw_tab(text, true, true, false, x, y, w, h, true)
end end

View File

@ -1,6 +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.1beta" VERSION = "2.0.3r1"
MOD_VERSION = "3" MOD_VERSION = "2"
SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or SCALE SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or SCALE
PATHSEP = package.config:sub(1, 1) PATHSEP = package.config:sub(1, 1)
@ -12,9 +12,8 @@ else
local prefix = EXEDIR:match("^(.+)[/\\]bin$") local prefix = EXEDIR:match("^(.+)[/\\]bin$")
DATADIR = prefix and (prefix .. '/share/lite-xl') or (EXEDIR .. '/data') DATADIR = prefix and (prefix .. '/share/lite-xl') or (EXEDIR .. '/data')
end end
USERDIR = (system.get_file_info(EXEDIR .. '/user') and (EXEDIR .. '/user')) USERDIR = (os.getenv("XDG_CONFIG_HOME") and os.getenv("XDG_CONFIG_HOME") .. "/lite-xl")
or ((os.getenv("XDG_CONFIG_HOME") and os.getenv("XDG_CONFIG_HOME") .. "/lite-xl")) or (HOME and (HOME .. '/.config/lite-xl') or (EXEDIR .. '/user'))
or (HOME and (HOME .. '/.config/lite-xl'))
package.path = DATADIR .. '/?.lua;' .. package.path package.path = DATADIR .. '/?.lua;' .. package.path
package.path = DATADIR .. '/?/init.lua;' .. package.path package.path = DATADIR .. '/?/init.lua;' .. package.path
@ -33,4 +32,3 @@ end }
table.pack = table.pack or pack or function(...) return {...} end table.pack = table.pack or pack or function(...) return {...} end
table.unpack = table.unpack or unpack table.unpack = table.unpack or unpack
bit32 = bit32 or require "core.bit"

File diff suppressed because it is too large Load Diff

View File

@ -72,9 +72,4 @@ style.syntax["function"] = { common.color "#93DDFA" }
style.syntax_fonts = {} style.syntax_fonts = {}
-- style.syntax_fonts["comment"] = renderer.font.load(path_to_font, size_of_font, rendering_options) -- style.syntax_fonts["comment"] = renderer.font.load(path_to_font, size_of_font, rendering_options)
style.log = {
INFO = { icon = "i", color = style.text },
ERROR = { icon = "!", color = style.error }
}
return style return style

View File

@ -136,42 +136,20 @@ function tokenizer.tokenize(incoming_syntax, text, state)
end end
local function find_text(text, p, offset, at_start, close) local function find_text(text, p, offset, at_start, close)
local target, res = p.pattern or p.regex, { 1, offset - 1 } local target, res = p.pattern or p.regex, { 1, offset - 1 }, p.regex
local p_idx = close and 2 or 1 local code = type(target) == "table" and target[close and 2 or 1] or target
local code = type(target) == "table" and target[p_idx] or target
if p.whole_line == nil then p.whole_line = { } end
if p.whole_line[p_idx] == nil then
-- Match patterns that start with '^'
p.whole_line[p_idx] = code:match("^%^") and true or false
if p.whole_line[p_idx] then
-- Remove '^' from the beginning of the pattern
if type(target) == "table" then
target[p_idx] = code:sub(2)
else
p.pattern = p.pattern and code:sub(2)
p.regex = p.regex and code:sub(2)
end
end
end
if p.regex and type(p.regex) ~= "table" then if p.regex and type(p.regex) ~= "table" then
p._regex = p._regex or regex.compile(p.regex) p._regex = p._regex or regex.compile(p.regex)
code = p._regex code = p._regex
end end
repeat repeat
local next = res[2] + 1 local next = res[2] + 1
-- If the pattern contained '^', allow matching only the whole line
if p.whole_line[p_idx] and next > 1 then
return
end
-- go to the start of the next utf-8 character -- go to the start of the next utf-8 character
while text:byte(next) and common.is_utf8_cont(text, next) do while text:byte(next) and common.is_utf8_cont(text, next) do
next = next + 1 next = next + 1
end end
res = p.pattern and { text:find((at_start or p.whole_line[p_idx]) and "^" .. code or code, next) } res = p.pattern and { text:find(at_start and "^" .. code or code, next) }
or { regex.match(code, text, next, (at_start or p.whole_line[p_idx]) and regex.ANCHORED or 0) } or { regex.match(code, text, next, at_start and regex.ANCHORED or 0) }
if res[1] and close and target[3] then if res[1] and close and target[3] then
local count = 0 local count = 0
for i = res[1] - 1, 1, -1 do for i = res[1] - 1, 1, -1 do

View File

@ -25,8 +25,7 @@ function View:move_towards(t, k, dest, rate)
return self:move_towards(self, t, k, dest, rate) return self:move_towards(self, t, k, dest, rate)
end end
local val = t[k] local val = t[k]
local diff = math.abs(val - dest) if not config.transitions or math.abs(val - dest) < 0.5 then
if not config.transitions or diff < 0.5 then
t[k] = dest t[k] = dest
else else
rate = rate or 0.5 rate = rate or 0.5
@ -36,7 +35,7 @@ function View:move_towards(t, k, dest, rate)
end end
t[k] = common.lerp(val, dest, rate) t[k] = common.lerp(val, dest, rate)
end end
if diff > 1e-8 then if val ~= dest then
core.redraw = true core.redraw = true
end end
end end
@ -99,11 +98,6 @@ function View:on_mouse_moved(x, y, dx, dy)
end end
function View:on_file_dropped(filename, x, y)
return false
end
function View:on_text_input(text) function View:on_text_input(text)
-- no-op -- no-op
end end

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local common = require "core.common" local common = require "core.common"
local config = require "core.config" local config = require "core.config"
@ -10,18 +10,14 @@ 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"
config.plugins.autocomplete = common.merge({ config.plugins.autocomplete = {
-- 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,
-- The max amount of visible items -- The max amount of visible items
max_height = 6, max_height = 6,
-- The max amount of scrollable items -- The max amount of scrollable items
max_suggestions = 100, max_suggestions = 100,
-- Maximum amount of symbols to cache per document }
max_symbols = 4000,
-- Font size of the description box
desc_font_size = 12
}, config.plugins.autocomplete)
local autocomplete = {} local autocomplete = {}
@ -37,7 +33,7 @@ local triggered_manually = false
local mt = { __tostring = function(t) return t.text end } local mt = { __tostring = function(t) return t.text end }
function autocomplete.add(t, manually_triggered) function autocomplete.add(t, triggered_manually)
local items = {} local items = {}
for text, info in pairs(t.items) do for text, info in pairs(t.items) do
if type(info) == "table" then if type(info) == "table" then
@ -47,10 +43,9 @@ function autocomplete.add(t, manually_triggered)
{ {
text = text, text = text,
info = info.info, info = info.info,
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 cb = info.cb, -- A callback called once when item is selected
onselect = info.onselect, -- A callback called when item is selected data = info.data -- Optional data that can be used on cb
data = info.data -- Optional data that can be used on cb
}, },
mt mt
) )
@ -61,7 +56,7 @@ function autocomplete.add(t, manually_triggered)
end end
end end
if not manually_triggered then if not triggered_manually then
autocomplete.map[t.name] = { files = t.files or ".*", items = items } autocomplete.map[t.name] = { files = t.files or ".*", items = items }
else else
autocomplete.map_manually[t.name] = { files = t.files or ".*", items = items } autocomplete.map_manually[t.name] = { files = t.files or ".*", items = items }
@ -71,7 +66,7 @@ end
-- --
-- Thread that scans open document symbols and cache them -- Thread that scans open document symbols and cache them
-- --
local max_symbols = config.plugins.autocomplete.max_symbols local max_symbols = config.max_symbols
core.add_thread(function() core.add_thread(function()
local cache = setmetatable({}, { __mode = "k" }) local cache = setmetatable({}, { __mode = "k" })
@ -90,9 +85,7 @@ core.add_thread(function()
doc.disable_symbols = true doc.disable_symbols = true
core.status_view:show_message("!", style.accent, core.status_view:show_message("!", style.accent,
"Too many symbols in document "..doc.filename.. "Too many symbols in document "..doc.filename..
": stopping auto-complete for this document according to ".. ": stopping auto-complete for this document according to config.max_symbols.")
"config.plugins.autocomplete.max_symbols."
)
collectgarbage('collect') collectgarbage('collect')
return {} return {}
end end
@ -166,6 +159,16 @@ local function reset_suggestions()
end end
end end
local function in_table(value, table_array)
for i, element in pairs(table_array) do
if element == value then
return true
end
end
return false
end
local function update_suggestions() local function update_suggestions()
local doc = core.active_view.doc local doc = core.active_view.doc
local filename = doc and doc.filename or "" local filename = doc and doc.filename or ""
@ -196,7 +199,6 @@ local function update_suggestions()
j = j + 1 j = j + 1
end end
end end
suggestions_idx = 1
end end
local function get_partial_symbol() local function get_partial_symbol()
@ -247,11 +249,6 @@ local function get_suggestions_rect(av)
max_width = 150 max_width = 150
end end
-- if portion not visiable to right, reposition to DocView right margin
if (x - av.position.x) + max_width > av.size.x then
x = (av.size.x + av.position.x) - max_width - (style.padding.x * 2)
end
return return
x - style.padding.x, x - style.padding.x,
y - style.padding.y, y - style.padding.y,
@ -259,99 +256,20 @@ local function get_suggestions_rect(av)
max_items * (th + style.padding.y) + style.padding.y max_items * (th + style.padding.y) + style.padding.y
end end
local function wrap_line(line, max_chars)
if #line > max_chars then
local lines = {}
local line_len = #line
local new_line = ""
local prev_char = ""
local position = 0
local indent = line:match("^%s+")
for char in line:gmatch(".") do
position = position + 1
if #new_line < max_chars then
new_line = new_line .. char
prev_char = char
if position >= line_len then
table.insert(lines, new_line)
end
else
if
not prev_char:match("%s")
and
not string.sub(line, position+1, 1):match("%s")
and
position < line_len
then
new_line = new_line .. "-"
end
table.insert(lines, new_line)
if indent then
new_line = indent .. char
else
new_line = char
end
end
end
return lines
end
return line
end
local previous_scale = SCALE
local desc_font = style.code_font:copy(
config.plugins.autocomplete.desc_font_size * SCALE
)
local function draw_description_box(text, av, sx, sy, sw, sh) local function draw_description_box(text, av, sx, sy, sw, sh)
if previous_scale ~= SCALE then
desc_font = style.code_font:copy(
config.plugins.autocomplete.desc_font_size * SCALE
)
previous_scale = SCALE
end
local font = desc_font
local lh = font:get_height()
local y = sy + style.padding.y
local x = sx + sw + style.padding.x / 4
local width = 0 local width = 0
local char_width = font:get_width(" ")
local draw_left = false;
local max_chars = 0
if sx - av.position.x < av.size.x - (sx - av.position.x) - sw then
max_chars = (((av.size.x+av.position.x) - x) / char_width) - 5
else
draw_left = true;
max_chars = (
(sx - av.position.x - (style.padding.x / 4) - style.scrollbar_size)
/ char_width
) - 5
end
local lines = {} local lines = {}
for line in string.gmatch(text.."\n", "(.-)\n") do for line in string.gmatch(text.."\n", "(.-)\n") do
local wrapper_lines = wrap_line(line, max_chars) width = math.max(width, style.font:get_width(line))
if type(wrapper_lines) == "table" then table.insert(lines, line)
for _, wrapped_line in pairs(wrapper_lines) do
width = math.max(width, font:get_width(wrapped_line))
table.insert(lines, wrapped_line)
end
else
width = math.max(width, font:get_width(line))
table.insert(lines, line)
end
end end
if draw_left then local height = #lines * style.font:get_height()
x = sx - (style.padding.x / 4) - width - (style.padding.x * 2)
end
local height = #lines * font:get_height()
-- draw background rect -- draw background rect
renderer.draw_rect( renderer.draw_rect(
x, sx + sw + style.padding.x / 4,
sy, sy,
width + style.padding.x * 2, width + style.padding.x * 2,
height + style.padding.y * 2, height + style.padding.y * 2,
@ -359,10 +277,13 @@ local function draw_description_box(text, av, sx, sy, sw, sh)
) )
-- draw text -- draw text
local lh = style.font:get_height()
local y = sy + style.padding.y
local x = sx + sw + style.padding.x / 4
for _, line in pairs(lines) do for _, line in pairs(lines) do
common.draw_text( common.draw_text(
font, style.text, line, "left", style.font, style.text, line, "left", x + style.padding.x, y, width, lh
x + style.padding.x, y, width, lh
) )
y = y + lh y = y + lh
end end
@ -399,9 +320,10 @@ local function draw_suggestions_box(av)
end end
y = y + lh y = y + lh
if suggestions_idx == i then if suggestions_idx == i then
if s.onhover then if s.cb then
s.onhover(suggestions_idx, s) s.cb(suggestions_idx, s)
s.onhover = nil s.cb = nil
s.data = nil
end end
if s.desc and #s.desc > 0 then if s.desc and #s.desc > 0 then
draw_description_box(s.desc, av, rx, ry, rw, rh) draw_description_box(s.desc, av, rx, ry, rw, rh)
@ -565,17 +487,10 @@ command.add(predicate, {
["autocomplete:complete"] = function() ["autocomplete:complete"] = function()
local doc = core.active_view.doc local doc = core.active_view.doc
local line, col = doc:get_selection() local line, col = doc:get_selection()
local item = suggestions[suggestions_idx] local text = suggestions[suggestions_idx].text
local text = item.text doc:insert(line, col, text)
local inserted = false doc:remove(line, col, line, col - #partial)
if item.onselect then doc:set_selection(line, col + #text - #partial)
inserted = item.onselect(suggestions_idx, item)
end
if not inserted then
doc:insert(line, col, text)
doc:remove(line, col, line, col - #partial)
doc:set_selection(line, col + #text - #partial)
end
reset_suggestions() reset_suggestions()
end, end,

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local config = require "core.config" local config = require "core.config"
local Doc = require "core.doc" local Doc = require "core.doc"

View File

@ -1,9 +1,8 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local command = require "core.command" local command = require "core.command"
local keymap = require "core.keymap" local keymap = require "core.keymap"
local ContextMenu = require "core.contextmenu" local ContextMenu = require "core.contextmenu"
local DocView = require "core.docview"
local RootView = require "core.rootview" local RootView = require "core.rootview"
local menu = ContextMenu() local menu = ContextMenu()
@ -33,7 +32,7 @@ function RootView:draw(...)
menu:draw() menu:draw()
end end
command.add(function() return getmetatable(core.active_view) == DocView end, { command.add(nil, {
["context:show"] = function() ["context:show"] = function()
menu:show(core.active_view.position.x, core.active_view.position.y) menu:show(core.active_view.position.x, core.active_view.position.y)
end end
@ -43,24 +42,23 @@ keymap.add {
["menu"] = "context:show" ["menu"] = "context:show"
} }
command.add(function() return menu.show_context_menu == true end, { local function copy_log()
["context:focus-previous"] = function() local item = core.active_view.hovered_item
menu:focus_previous() if item then
end, system.set_clipboard(core.get_log(item))
["context:focus-next"] = function() end
menu:focus_next() end
end,
["context:hide"] = function() local function open_as_doc()
menu:hide() local doc = core.open_doc("logs.txt")
end, core.root_view:open_doc(doc)
["context:on-selected"] = function() doc:insert(1, 1, core.get_log())
menu:call_selected_item() end
end,
menu:register("core.logview", {
{ text = "Copy entry", command = copy_log },
{ text = "Open as file", command = open_as_doc }
}) })
keymap.add { ["return"] = "context:on-selected" }
keymap.add { ["up"] = "context:focus-previous" }
keymap.add { ["down"] = "context:focus-next" }
keymap.add { ["escape"] = "context:hide" }
if require("plugins.scale") then if require("plugins.scale") then
menu:register("core.docview", { menu:register("core.docview", {

View File

@ -1,256 +1,95 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
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 core_syntax = require "core.syntax"
local DocView = require "core.docview" local DocView = require "core.docview"
local Doc = require "core.doc" local Doc = require "core.doc"
local tokenizer = require "core.tokenizer"
local cache = setmetatable({}, { __mode = "k" }) local cache = setmetatable({}, { __mode = "k" })
local comments_cache = {}
local auto_detect_max_lines = 150
local function indent_occurrences_more_than_once(stat, idx) local function add_to_stat(stat, val)
if stat[idx-1] and stat[idx-1] == stat[idx] then for i = 1, #stat do
return true if val == stat[i][1] then
elseif stat[idx+1] and stat[idx+1] == stat[idx] then stat[i][2] = stat[i][2] + 1
return true return
end
end end
return false stat[#stat + 1] = {val, 1}
end end
local function optimal_indent_from_stat(stat) local function optimal_indent_from_stat(stat)
if #stat == 0 then return nil, 0 end if #stat == 0 then return nil, 0 end
table.sort(stat, function(a, b) return a > b end) local bins = {}
local best_indent = 0 for k = 1, #stat do
local best_score = 0 local indent = stat[k][1]
local count = #stat
for x=1, count do
local indent = stat[x]
local score = 0 local score = 0
for y=1, count do local mult_prev, lines_prev
if y ~= x and stat[y] % indent == 0 then for i = k, #stat do
score = score + 1 if stat[i][1] % indent == 0 then
elseif local mult = stat[i][1] / indent
indent > stat[y] if not mult_prev or (mult_prev + 1 == mult and lines_prev / stat[i][2] > 0.1) then
and -- we add the number of lines to the score only if the previous
indent_occurrences_more_than_once(stat, y) -- multiple of "indent" was populated with enough lines.
then score = score + stat[i][2]
score = 0 end
break mult_prev, lines_prev = mult, stat[i][2]
end end
end end
if score > best_score then bins[#bins + 1] = {indent, score}
best_indent = indent
best_score = score
end
if score > 0 then
break
end
end end
return best_score > 0 and best_indent or nil, best_score table.sort(bins, function(a, b) return a[2] > b[2] end)
return bins[1][1], bins[1][2]
end end
local function escape_comment_tokens(token) -- return nil if it is a comment or blank line or the initial part of the
local special_chars = "*-%[].()+?^$" -- line otherwise.
local escaped = "" -- we don't need to have the whole line to detect indentation.
for x=1, token:len() do local function get_first_line_part(tokens)
local found = false local i, n = 1, #tokens
for y=1, special_chars:len() do while i + 1 <= n do
if token:sub(x, x) == special_chars:sub(y, y) then local ttype, ttext = tokens[i], tokens[i + 1]
escaped = escaped .. "%" .. token:sub(x, x) if ttype ~= "comment" and ttext:gsub("%s+", "") ~= "" then
found = true return ttext
break
end
end
if not found then
escaped = escaped .. token:sub(x, x)
end end
i = i + 2
end end
return escaped
end end
local function get_comment_patterns(syntax)
if comments_cache[syntax] then
if #comments_cache[syntax] > 0 then
return comments_cache[syntax]
else
return nil
end
end
local comments = {}
for idx=1, #syntax.patterns do
local pattern = syntax.patterns[idx]
local startp = ""
if
type(pattern.type) == "string"
and
(pattern.type == "comment" or pattern.type == "string")
then
local not_is_string = pattern.type ~= "string"
if pattern.pattern then
startp = type(pattern.pattern) == "table"
and pattern.pattern[1] or pattern.pattern
if not_is_string and startp:sub(1, 1) ~= "^" then
startp = "^%s*" .. startp
elseif not_is_string then
startp = "^%s*" .. startp:sub(2, startp:len())
end
if type(pattern.pattern) == "table" then
table.insert(comments, {"p", startp, pattern.pattern[2]})
elseif not_is_string then
table.insert(comments, {"p", startp})
end
elseif pattern.regex then
startp = type(pattern.regex) == "table"
and pattern.regex[1] or pattern.regex
if not_is_string and startp:sub(1, 1) ~= "^" then
startp = "^\\s*" .. startp
elseif not_is_string then
startp = "^\\s*" .. startp:sub(2, startp:len())
end
if type(pattern.regex) == "table" then
table.insert(comments, {
"r", regex.compile(startp), regex.compile(pattern.regex[2])
})
elseif not_is_string then
table.insert(comments, {"r", regex.compile(startp)})
end
end
elseif pattern.syntax then
local subsyntax = type(pattern.syntax) == 'table' and pattern.syntax
or core_syntax.get("file"..pattern.syntax, "")
local sub_comments = get_comment_patterns(subsyntax)
if sub_comments then
for s=1, #sub_comments do
table.insert(comments, sub_comments[s])
end
end
end
end
if #comments == 0 then
local single_line_comment = syntax.comment
and escape_comment_tokens(syntax.comment) or nil
local block_comment = nil
if syntax.block_comment then
block_comment = {
escape_comment_tokens(syntax.block_comment[1]),
escape_comment_tokens(syntax.block_comment[2])
}
end
if single_line_comment then
table.insert(comments, {"p", "^%s*" .. single_line_comment})
end
if block_comment then
table.insert(comments, {"p", "^%s*" .. block_comment[1], block_comment[2]})
end
end
comments_cache[syntax] = comments
if #comments > 0 then
return comments
end
return nil
end
local function get_non_empty_lines(syntax, lines) local function get_non_empty_lines(syntax, lines)
return coroutine.wrap(function() return coroutine.wrap(function()
local comments = get_comment_patterns(syntax) local tokens, state
local i = 0 local i = 0
local end_regex = nil
local end_pattern = nil
local inside_comment = false
for _, line in ipairs(lines) do for _, line in ipairs(lines) do
if line:gsub("^%s+", "") ~= "" then tokens, state = tokenizer.tokenize(syntax, line, state)
local is_comment = false local line_start = get_first_line_part(tokens)
if comments then if line_start then
if not inside_comment then i = i + 1
for c=1, #comments do coroutine.yield(i, line_start)
local comment = comments[c]
if comment[1] == "p" then
if comment[3] then
local start, ending = line:find(comment[2])
if start then
if not line:find(comment[3], ending+1) then
is_comment = true
inside_comment = true
end_pattern = comment[3]
end
break
end
elseif line:find(comment[2]) then
is_comment = true
break
end
else
if comment[3] then
local start, ending = regex.match(
comment[2], line, 1, regex.ANCHORED
)
if start then
if not regex.match(
comment[3], line, ending+1, regex.ANCHORED
)
then
is_comment = true
inside_comment = true
end_regex = comment[3]
end
break
end
elseif regex.match(comment[2], line, 1, regex.ANCHORED) then
is_comment = true
break
end
end
end
elseif end_pattern and line:find(end_pattern) then
is_comment = true
inside_comment = false
end_pattern = nil
elseif end_regex and regex.match(end_regex, line) then
is_comment = true
inside_comment = false
end_regex = nil
end
end
if
not is_comment
and
not inside_comment
then
i = i + 1
coroutine.yield(i, line)
end
end end
end end
end) end)
end end
local auto_detect_max_lines = 100
local function detect_indent_stat(doc) local function detect_indent_stat(doc)
local stat = {} local stat = {}
local tab_count = 0 local tab_count = 0
local runs = 1
local max_lines = auto_detect_max_lines
for i, text in get_non_empty_lines(doc.syntax, doc.lines) do for i, text in get_non_empty_lines(doc.syntax, doc.lines) do
local spaces = text:match("^ +") local str = text:match("^ %s+%S")
if spaces then table.insert(stat, spaces:len()) end if str then add_to_stat(stat, #str - 1) end
local tabs = text:match("^\t+") local str = text:match("^\t+")
if tabs then tab_count = tab_count + 1 end if str then tab_count = tab_count + 1 end
-- if nothing found for first lines try at least 4 more times
if i == max_lines and runs < 5 and #stat == 0 and tab_count == 0 then
max_lines = max_lines + auto_detect_max_lines
runs = runs + 1
-- Stop parsing when files is very long. Not needed for euristic determination. -- Stop parsing when files is very long. Not needed for euristic determination.
elseif i > max_lines then break end if i > auto_detect_max_lines then break end
end end
table.sort(stat, function(a, b) return a[1] < b[1] end)
local indent, score = optimal_indent_from_stat(stat) local indent, score = optimal_indent_from_stat(stat)
if tab_count > score then if tab_count > score then
return "hard", config.indent_size, tab_count return "hard", config.indent_size, tab_count
@ -262,7 +101,7 @@ end
local function update_cache(doc) local function update_cache(doc)
local type, size, score = detect_indent_stat(doc) local type, size, score = detect_indent_stat(doc)
local score_threshold = 2 local score_threshold = 4
if score < score_threshold then if score < score_threshold then
-- use default values -- use default values
type = config.tab_type type = config.tab_type
@ -291,11 +130,9 @@ end
local function set_indent_type(doc, type) local function set_indent_type(doc, type)
local _, indent_size = doc:get_indent_info() local _, indent_size = doc:get_indent_info()
cache[doc] = { cache[doc] = {type = type,
type = type, size = indent_size,
size = indent_size, confirmed = true}
confirmed = true
}
doc.indent_info = cache[doc] doc.indent_info = cache[doc]
end end
@ -321,11 +158,9 @@ end
local function set_indent_size(doc, size) local function set_indent_size(doc, size)
local indent_type = doc:get_indent_info() local indent_type = doc:get_indent_info()
cache[doc] = { cache[doc] = {type = indent_type,
type = indent_type, size = size,
size = size, confirmed = true}
confirmed = true
}
doc.indent_info = cache[doc] doc.indent_info = cache[doc]
end end
@ -333,14 +168,14 @@ local function set_indent_size_command()
core.command_view:enter( core.command_view:enter(
"Specify indent size for current file", "Specify indent size for current file",
function(value) -- submit function(value) -- submit
value = math.floor(tonumber(value)) local value = math.floor(tonumber(value))
local doc = core.active_view.doc local doc = core.active_view.doc
set_indent_size(doc, value) set_indent_size(doc, value)
end, end,
nil, -- suggest nil, -- suggest
nil, -- cancel nil, -- cancel
function(value) -- validate function(value) -- validate
value = tonumber(value) local value = tonumber(value)
return value ~= nil and value >= 1 return value ~= nil and value >= 1
end end
) )
@ -352,24 +187,20 @@ command.add("core.docview", {
["indent:set-file-indent-size"] = set_indent_size_command ["indent:set-file-indent-size"] = set_indent_size_command
}) })
command.add(
function() command.add(function()
return core.active_view:is(DocView) return core.active_view:is(DocView)
and cache[core.active_view.doc] and cache[core.active_view.doc]
and cache[core.active_view.doc].type == "soft" and cache[core.active_view.doc].type == "soft"
end, { end, {
["indent:switch-file-to-tabs-indentation"] = function() ["indent:switch-file-to-tabs-indentation"] = function() set_indent_type(core.active_view.doc, "hard") end
set_indent_type(core.active_view.doc, "hard")
end
}) })
command.add(
function() command.add(function()
return core.active_view:is(DocView) return core.active_view:is(DocView)
and cache[core.active_view.doc] and cache[core.active_view.doc]
and cache[core.active_view.doc].type == "hard" and cache[core.active_view.doc].type == "hard"
end, { end, {
["indent:switch-file-to-spaces-indentation"] = function() ["indent:switch-file-to-spaces-indentation"] = function() set_indent_type(core.active_view.doc, "soft") end
set_indent_type(core.active_view.doc, "soft")
end
}) })

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local style = require "core.style" local style = require "core.style"
local DocView = require "core.docview" local DocView = require "core.docview"

View File

@ -1,11 +1,10 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "C", name = "C",
files = { "%.c$" }, files = { "%.c$", "%.h$", "%.inl$" },
comment = "//", comment = "//",
block_comment = { "/*", "*/" },
patterns = { patterns = {
{ pattern = "//.-\n", type = "comment" }, { pattern = "//.-\n", type = "comment" },
{ pattern = { "/%*", "%*/" }, type = "comment" }, { pattern = { "/%*", "%*/" }, type = "comment" },
@ -17,21 +16,10 @@ syntax.add {
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" }, { pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
{ pattern = "struct%s()[%a_][%w_]*", type = {"keyword", "keyword2"} }, { pattern = "struct%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{ pattern = "union%s()[%a_][%w_]*", type = {"keyword", "keyword2"} }, { pattern = "union%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{
pattern = "^%s*#define%s+()[%a_][%a%d_]*",
type = { "keyword", "symbol" }
},
-- Uppercase constants of at least 2 chars in len
{
pattern = "_?%u[%u_][%u%d_]*%f[%s%+%*%-%.%(%)%?%^%%=/<>~|&;:,!]",
type = "number"
},
-- Magic constants
{ pattern = "__[%u%l]+__", type = "number" },
{ pattern = "[%a_][%w_]*%f[(]", type = "function" }, { pattern = "[%a_][%w_]*%f[(]", type = "function" },
{ pattern = "[%a_][%w_]*", type = "symbol" },
{ pattern = "#include%s()<.->", type = {"keyword", "string"} }, { pattern = "#include%s()<.->", type = {"keyword", "string"} },
{ pattern = "#[%a_][%w_]*", type = "keyword" }, { pattern = "#[%a_][%w_]*", type = "keyword" },
{ pattern = "[%a_][%w_]*", type = "symbol" },
}, },
symbols = { symbols = {
["if"] = "keyword", ["if"] = "keyword",
@ -56,8 +44,6 @@ syntax.add {
["case"] = "keyword", ["case"] = "keyword",
["default"] = "keyword", ["default"] = "keyword",
["auto"] = "keyword", ["auto"] = "keyword",
["struct"] = "keyword",
["union"] = "keyword",
["void"] = "keyword2", ["void"] = "keyword2",
["int"] = "keyword2", ["int"] = "keyword2",
["short"] = "keyword2", ["short"] = "keyword2",
@ -74,7 +60,6 @@ syntax.add {
["#if"] = "keyword", ["#if"] = "keyword",
["#ifdef"] = "keyword", ["#ifdef"] = "keyword",
["#ifndef"] = "keyword", ["#ifndef"] = "keyword",
["#elif"] = "keyword",
["#else"] = "keyword", ["#else"] = "keyword",
["#elseif"] = "keyword", ["#elseif"] = "keyword",
["#endif"] = "keyword", ["#endif"] = "keyword",

View File

@ -1,4 +1,6 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
pcall(require, "plugins.language_c")
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
@ -8,54 +10,28 @@ syntax.add {
"%.c++$", "%.hh$", "%.H$", "%.hxx$", "%.hpp$", "%.h++$" "%.c++$", "%.hh$", "%.H$", "%.hxx$", "%.hpp$", "%.h++$"
}, },
comment = "//", comment = "//",
block_comment = { "/*", "*/" },
patterns = { patterns = {
{ pattern = "//.-\n", type = "comment" }, { pattern = "//.-\n", type = "comment" },
{ pattern = { "/%*", "%*/" }, type = "comment" }, { pattern = { "/%*", "%*/" }, type = "comment" },
{ pattern = { '"', '"', '\\' }, type = "string" }, { pattern = { '"', '"', '\\' }, type = "string" },
{ pattern = { "'", "'", '\\' }, type = "string" }, { pattern = { "'", "'", '\\' }, type = "string" },
{ pattern = "0x%x+", type = "number" }, { pattern = "0x%x+", type = "number" },
{ pattern = "%d+[%d%.eE]*f?", type = "number" }, { pattern = "%d+[%d%.eE]*f?", type = "number" },
{ pattern = "%.?%d+f?", type = "number" }, { pattern = "%.?%d+f?", type = "number" },
{ { pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
pattern = "^%s*#define%s+()[%a_][%a%d_]*",
type = { "keyword", "symbol" }
},
{
pattern = "#include%s+()<.->",
type = { "keyword", "string" }
},
{ pattern = "[%+%-=/%*%^%%<>!~|:&]", type = "operator" },
{ pattern = "struct%s()[%a_][%w_]*", type = {"keyword", "keyword2"} }, { pattern = "struct%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{ pattern = "class%s()[%a_][%w_]*", type = {"keyword", "keyword2"} }, { pattern = "class%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{ pattern = "union%s()[%a_][%w_]*", type = {"keyword", "keyword2"} }, { pattern = "union%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{ pattern = "namespace%s()[%a_][%w_]*", type = {"keyword", "keyword2"} }, { pattern = "namespace%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{ pattern = "[%a_][%w_]*%f[(]", type = "function" }, { pattern = "[%a_][%w_]*::", type = "symbol" },
-- Match scope operator element access { pattern = "::", type = "symbol" },
{ pattern = "[%a_][%w_]*[%s]*%f[:]", type = "literal" }, { pattern = "[%a_][%w_]*", type = "symbol" },
-- Uppercase constants of at least 2 chars in len { pattern = "#include%s()<.->", type = {"keyword", "string"} },
{ { pattern = "#[%a_][%w_]*", type = "keyword" },
pattern = "_?%u[%u_][%u%d_]*%f[%s%+%*%-%.%(%)%?%^%%=/<>~|&;:,!]",
type = "number"
},
-- Magic constants
{ pattern = "__[%u%l]+__", type = "number" },
-- Somehow makes macros properly work
{ pattern = "#[%a_][%w_]*", type = "normal" },
-- Everything else to make the tokenizer work properly
{ pattern = "[%a_][%w_]*", type = "symbol" },
}, },
symbols = { symbols = {
["alignof"] = "keyword", ["alignof"] = "keyword",
["alignas"] = "keyword", ["alignas"] = "keyword",
["and"] = "keyword",
["and_eq"] = "keyword",
["not"] = "keyword",
["not_eq"] = "keyword",
["or"] = "keyword",
["or_eq"] = "keyword",
["xor"] = "keyword",
["xor_eq"] = "keyword",
["private"] = "keyword", ["private"] = "keyword",
["protected"] = "keyword", ["protected"] = "keyword",
["public"] = "keyword", ["public"] = "keyword",
@ -63,12 +39,9 @@ syntax.add {
["nullptr"] = "keyword", ["nullptr"] = "keyword",
["operator"] = "keyword", ["operator"] = "keyword",
["asm"] = "keyword", ["asm"] = "keyword",
["bitand"] = "keyword",
["bitor"] = "keyword",
["catch"] = "keyword", ["catch"] = "keyword",
["throw"] = "keyword", ["throw"] = "keyword",
["try"] = "keyword", ["try"] = "keyword",
["class"] = "keyword",
["compl"] = "keyword", ["compl"] = "keyword",
["explicit"] = "keyword", ["explicit"] = "keyword",
["export"] = "keyword", ["export"] = "keyword",
@ -78,8 +51,8 @@ syntax.add {
["constinit"] = "keyword", ["constinit"] = "keyword",
["const_cast"] = "keyword", ["const_cast"] = "keyword",
["dynamic_cast"] = "keyword", ["dynamic_cast"] = "keyword",
["reinterpret_cast"] = "keyword", ["reinterpret_cast"] = "keyword",
["static_cast"] = "keyword", ["static_cast"] = "keyword",
["static_assert"] = "keyword", ["static_assert"] = "keyword",
["template"] = "keyword", ["template"] = "keyword",
["this"] = "keyword", ["this"] = "keyword",
@ -90,6 +63,7 @@ syntax.add {
["co_yield"] = "keyword", ["co_yield"] = "keyword",
["decltype"] = "keyword", ["decltype"] = "keyword",
["delete"] = "keyword", ["delete"] = "keyword",
["export"] = "keyword",
["friend"] = "keyword", ["friend"] = "keyword",
["typeid"] = "keyword", ["typeid"] = "keyword",
["typename"] = "keyword", ["typename"] = "keyword",
@ -97,7 +71,6 @@ syntax.add {
["override"] = "keyword", ["override"] = "keyword",
["virtual"] = "keyword", ["virtual"] = "keyword",
["using"] = "keyword", ["using"] = "keyword",
["namespace"] = "keyword",
["new"] = "keyword", ["new"] = "keyword",
["noexcept"] = "keyword", ["noexcept"] = "keyword",
["if"] = "keyword", ["if"] = "keyword",
@ -111,8 +84,6 @@ syntax.add {
["continue"] = "keyword", ["continue"] = "keyword",
["return"] = "keyword", ["return"] = "keyword",
["goto"] = "keyword", ["goto"] = "keyword",
["struct"] = "keyword",
["union"] = "keyword",
["typedef"] = "keyword", ["typedef"] = "keyword",
["enum"] = "keyword", ["enum"] = "keyword",
["extern"] = "keyword", ["extern"] = "keyword",
@ -124,6 +95,7 @@ syntax.add {
["case"] = "keyword", ["case"] = "keyword",
["default"] = "keyword", ["default"] = "keyword",
["auto"] = "keyword", ["auto"] = "keyword",
["const"] = "keyword",
["void"] = "keyword2", ["void"] = "keyword2",
["int"] = "keyword2", ["int"] = "keyword2",
["short"] = "keyword2", ["short"] = "keyword2",
@ -133,18 +105,12 @@ syntax.add {
["char"] = "keyword2", ["char"] = "keyword2",
["unsigned"] = "keyword2", ["unsigned"] = "keyword2",
["bool"] = "keyword2", ["bool"] = "keyword2",
["true"] = "literal", ["true"] = "keyword2",
["false"] = "literal", ["false"] = "keyword2",
["NULL"] = "literal",
["wchar_t"] = "keyword2",
["char8_t"] = "keyword2",
["char16_t"] = "keyword2",
["char32_t"] = "keyword2",
["#include"] = "keyword", ["#include"] = "keyword",
["#if"] = "keyword", ["#if"] = "keyword",
["#ifdef"] = "keyword", ["#ifdef"] = "keyword",
["#ifndef"] = "keyword", ["#ifndef"] = "keyword",
["#elif"] = "keyword",
["#else"] = "keyword", ["#else"] = "keyword",
["#elseif"] = "keyword", ["#elseif"] = "keyword",
["#endif"] = "keyword", ["#endif"] = "keyword",
@ -152,5 +118,6 @@ syntax.add {
["#warning"] = "keyword", ["#warning"] = "keyword",
["#error"] = "keyword", ["#error"] = "keyword",
["#pragma"] = "keyword", ["#pragma"] = "keyword",
}, },
} }

View File

@ -1,10 +1,9 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "CSS", name = "CSS",
files = { "%.css$" }, files = { "%.css$" },
block_comment = { "/*", "*/" },
patterns = { patterns = {
{ pattern = "\\.", type = "normal" }, { pattern = "\\.", type = "normal" },
{ pattern = "//.-\n", type = "comment" }, { pattern = "//.-\n", type = "comment" },

View File

@ -1,32 +1,31 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "HTML", name = "HTML",
files = { "%.html?$" }, files = { "%.html?$" },
block_comment = { "<!--", "-->" },
patterns = { patterns = {
{ {
pattern = { pattern = {
"<%s*[sS][cC][rR][iI][pP][tT]%s+[tT][yY][pP][eE]%s*=%s*" .. "<%s*[sS][cC][rR][iI][pP][tT]%s+[tT][yY][pP][eE]%s*=%s*" ..
"['\"]%a+/[jJ][aA][vV][aA][sS][cC][rR][iI][pP][tT]['\"]%s*>", "['\"]%a+/[jJ][aA][vV][aA][sS][cC][rR][iI][pP][tT]['\"]%s*>",
"<%s*/[sS][cC][rR][iI][pP][tT]>" "<%s*/[sS][cC][rR][iI][pP][tT]>"
}, },
syntax = ".js", syntax = ".js",
type = "function" type = "function"
}, },
{ {
pattern = { pattern = {
"<%s*[sS][cC][rR][iI][pP][tT]%s*>", "<%s*[sS][cC][rR][iI][pP][tT]%s*>",
"<%s*/%s*[sS][cC][rR][iI][pP][tT]>" "<%s*/%s*[sS][cC][rR][iI][pP][tT]>"
}, },
syntax = ".js", syntax = ".js",
type = "function" type = "function"
}, },
{ {
pattern = { pattern = {
"<%s*[sS][tT][yY][lL][eE][^>]*>", "<%s*[sS][tT][yY][lL][eE][^>]*>",
"<%s*/%s*[sS][tT][yY][lL][eE]%s*>" "<%s*/%s*[sS][tT][yY][lL][eE]%s*>"
}, },
syntax = ".css", syntax = ".css",
type = "function" type = "function"

View File

@ -1,11 +1,10 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "JavaScript", name = "JavaScript",
files = { "%.js$", "%.json$", "%.cson$" }, files = { "%.js$", "%.json$", "%.cson$" },
comment = "//", comment = "//",
block_comment = { "/*", "*/" },
patterns = { patterns = {
{ pattern = "//.-\n", type = "comment" }, { pattern = "//.-\n", type = "comment" },
{ pattern = { "/%*", "%*/" }, type = "comment" }, { pattern = { "/%*", "%*/" }, type = "comment" },

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
@ -6,7 +6,6 @@ syntax.add {
files = "%.lua$", files = "%.lua$",
headers = "^#!.*[ /]lua", headers = "^#!.*[ /]lua",
comment = "--", comment = "--",
block_comment = { "--[[", "]]" },
patterns = { patterns = {
{ pattern = { '"', '"', '\\' }, type = "string" }, { pattern = { '"', '"', '\\' }, type = "string" },
{ pattern = { "'", "'", '\\' }, type = "string" }, { pattern = { "'", "'", '\\' }, type = "string" },

View File

@ -1,223 +1,56 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local syntax = require "core.syntax" local syntax = require "core.syntax"
local style = require "core.style"
local core = require "core"
local initial_color = style.syntax["keyword2"]
-- Add 3 type of font styles for use on markdown files
for _, attr in pairs({"bold", "italic", "bold_italic"}) do
local attributes = {}
if attr ~= "bold_italic" then
attributes[attr] = true
else
attributes["bold"] = true
attributes["italic"] = true
end
-- no way to copy user custom font with additional attributes :(
style.syntax_fonts["markdown_"..attr] = renderer.font.load(
DATADIR .. "/fonts/JetBrainsMono-Regular.ttf",
style.code_font:get_size(),
attributes
)
-- also add a color for it
style.syntax["markdown_"..attr] = style.syntax["keyword2"]
end
local in_squares_match = "^%[%]"
local in_parenthesis_match = "^%(%)"
syntax.add { syntax.add {
name = "Markdown", name = "Markdown",
files = { "%.md$", "%.markdown$" }, files = { "%.md$", "%.markdown$" },
block_comment = { "<!--", "-->" },
patterns = { patterns = {
---- HTML rules imported and adapted from language_html { pattern = "\\.", type = "normal" },
---- to not conflict with markdown rules { pattern = { "<!%-%-", "%-%->" }, type = "comment" },
-- Inline JS and CSS
{
pattern = {
"<%s*[sS][cC][rR][iI][pP][tT]%s+[tT][yY][pP][eE]%s*=%s*" ..
"['\"]%a+/[jJ][aA][vV][aA][sS][cC][rR][iI][pP][tT]['\"]%s*>",
"<%s*/[sS][cC][rR][iI][pP][tT]>"
},
syntax = ".js",
type = "function"
},
{
pattern = {
"<%s*[sS][cC][rR][iI][pP][tT]%s*>",
"<%s*/%s*[sS][cC][rR][iI][pP][tT]>"
},
syntax = ".js",
type = "function"
},
{
pattern = {
"<%s*[sS][tT][yY][lL][eE][^>]*>",
"<%s*/%s*[sS][tT][yY][lL][eE]%s*>"
},
syntax = ".css",
type = "function"
},
-- Comments
{ pattern = { "<!%-%-", "%-%->" }, type = "comment" },
-- Tags
{ pattern = "%f[^<]![%a_][%w_]*", type = "keyword2" },
{ pattern = "%f[^<][%a_][%w_]*", type = "function" },
{ pattern = "%f[^<]/[%a_][%w_]*", type = "function" },
-- Attributes
{
pattern = "[a-z%-]+%s*()=%s*()\".-\"",
type = { "keyword", "operator", "string" }
},
{
pattern = "[a-z%-]+%s*()=%s*()'.-'",
type = { "keyword", "operator", "string" }
},
{
pattern = "[a-z%-]+%s*()=%s*()%-?%d[%d%.]*",
type = { "keyword", "operator", "number" }
},
-- Entities
{ pattern = "&#?[a-zA-Z0-9]+;", type = "keyword2" },
---- Markdown rules
-- code blocks
{ pattern = { "```c++", "```" }, type = "string", syntax = ".cpp" }, { pattern = { "```c++", "```" }, type = "string", syntax = ".cpp" },
{ pattern = { "```cpp", "```" }, type = "string", syntax = ".cpp" },
{ pattern = { "```python", "```" }, type = "string", syntax = ".py" }, { pattern = { "```python", "```" }, type = "string", syntax = ".py" },
{ pattern = { "```ruby", "```" }, type = "string", syntax = ".rb" }, { pattern = { "```ruby", "```" }, type = "string", syntax = ".rb" },
{ pattern = { "```perl", "```" }, type = "string", syntax = ".pl" }, { pattern = { "```perl", "```" }, type = "string", syntax = ".pl" },
{ pattern = { "```php", "```" }, type = "string", syntax = ".php" }, { pattern = { "```php", "```" }, type = "string", syntax = ".php" },
{ pattern = { "```javascript", "```" }, type = "string", syntax = ".js" }, { pattern = { "```javascript", "```" }, type = "string", syntax = ".js" },
{ pattern = { "```json", "```" }, type = "string", syntax = ".js" },
{ pattern = { "```html", "```" }, type = "string", syntax = ".html" }, { pattern = { "```html", "```" }, type = "string", syntax = ".html" },
{ pattern = { "```ini", "```" }, type = "string", syntax = ".ini" },
{ pattern = { "```xml", "```" }, type = "string", syntax = ".xml" }, { pattern = { "```xml", "```" }, type = "string", syntax = ".xml" },
{ pattern = { "```css", "```" }, type = "string", syntax = ".css" }, { pattern = { "```css", "```" }, type = "string", syntax = ".css" },
{ pattern = { "```lua", "```" }, type = "string", syntax = ".lua" }, { pattern = { "```lua", "```" }, type = "string", syntax = ".lua" },
{ pattern = { "```bash", "```" }, type = "string", syntax = ".sh" }, { pattern = { "```bash", "```" }, type = "string", syntax = ".sh" },
{ pattern = { "```sh", "```" }, type = "string", syntax = ".sh" },
{ pattern = { "```java", "```" }, type = "string", syntax = ".java" }, { pattern = { "```java", "```" }, type = "string", syntax = ".java" },
{ pattern = { "```c#", "```" }, type = "string", syntax = ".cs" }, { pattern = { "```c#", "```" }, type = "string", syntax = ".cs" },
{ pattern = { "```cmake", "```" }, type = "string", syntax = ".cmake" }, { pattern = { "```cmake", "```" }, type = "string", syntax = ".cmake" },
{ pattern = { "```d", "```" }, type = "string", syntax = ".d" }, { pattern = { "```d", "```" }, type = "string", syntax = ".d" },
{ pattern = { "```glsl", "```" }, type = "string", syntax = ".glsl" }, { pattern = { "```glsl", "```" }, type = "string", syntax = ".glsl" },
{ pattern = { "```c", "```" }, type = "string", syntax = ".c" }, { pattern = { "```c", "```" }, type = "string", syntax = ".c" },
{ pattern = { "```julia", "```" }, type = "string", syntax = ".jl" }, { pattern = { "```julia", "```" }, type = "string", syntax = ".jl" },
{ pattern = { "```rust", "```" }, type = "string", syntax = ".rs" }, { pattern = { "```rust", "```" }, type = "string", syntax = ".rs" },
{ pattern = { "```dart", "```" }, type = "string", syntax = ".dart" }, { pattern = { "```dart", "```" }, type = "string", syntax = ".dart" },
{ pattern = { "```v", "```" }, type = "string", syntax = ".v" }, { pattern = { "```v", "```" }, type = "string", syntax = ".v" },
{ pattern = { "```toml", "```" }, type = "string", syntax = ".toml" }, { pattern = { "```toml", "```" }, type = "string", syntax = ".toml" },
{ pattern = { "```yaml", "```" }, type = "string", syntax = ".yaml" }, { pattern = { "```yaml", "```" }, type = "string", syntax = ".yaml" },
{ pattern = { "```nim", "```" }, type = "string", syntax = ".nim" }, { pattern = { "```php", "```" }, type = "string", syntax = ".php" },
{ pattern = { "```typescript", "```" }, type = "string", syntax = ".ts" }, { pattern = { "```nim", "```" }, type = "string", syntax = ".nim" },
{ pattern = { "```rescript", "```" }, type = "string", syntax = ".res" }, { pattern = { "```typescript", "```" }, type = "string", syntax = ".ts" },
{ pattern = { "```moon", "```" }, type = "string", syntax = ".moon" }, { pattern = { "```rescript", "```" }, type = "string", syntax = ".res" },
{ pattern = { "```go", "```" }, type = "string", syntax = ".go" }, { pattern = { "```moon", "```" }, type = "string", syntax = ".moon" },
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" }, { pattern = { "```go", "```" }, type = "string", syntax = ".go" },
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" }, { pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
{ pattern = { "```", "```" }, type = "string" }, { pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
{ pattern = { "``", "``" }, type = "string" }, { pattern = { "```", "```" }, type = "string" },
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" }, { pattern = { "``", "``", "\\" }, type = "string" },
-- strike { pattern = { "`", "`", "\\" }, type = "string" },
{ pattern = { "~~", "~~" }, type = "keyword2" }, { pattern = { "~~", "~~", "\\" }, type = "keyword2" },
-- highlight { pattern = "%-%-%-+", type = "comment" },
{ pattern = { "==", "==" }, type = "literal" }, { pattern = "%*%s+", type = "operator" },
-- lines { pattern = { "%*", "[%*\n]", "\\" }, type = "operator" },
{ pattern = "^%-%-%-+\n", type = "comment" }, { pattern = { "%_", "[%_\n]", "\\" }, type = "keyword2" },
{ pattern = "^%*%*%*+\n", type = "comment" }, { pattern = "#.-\n", type = "keyword" },
{ pattern = "^___+\n", type = "comment" }, { pattern = "!?%[.-%]%(.-%)", type = "function" },
-- bullets { pattern = "https?://%S+", type = "function" },
{ pattern = "^%s*%*%s", type = "number" },
{ pattern = "^%s*%-%s", type = "number" },
{ pattern = "^%s*%+%s", type = "number" },
-- numbered bullet
{ pattern = "^%s*[0-9]+%.%s", type = "number" },
-- blockquote
{ pattern = "^%s*>+%s", type = "string" },
-- bold and italic
{ pattern = { "%*%*%*%S", "%*%*%*" }, type = "markdown_bold_italic" },
{ pattern = { "%*%*%S", "%*%*" }, type = "markdown_bold" },
-- handle edge case where asterisk can be at end of line and not close
{
pattern = { "%f[\\%*]%*[%S]", "%*%f[^%*]" },
type = "markdown_italic"
},
-- alternative bold italic formats
{ pattern = "^___[%s%p%w]+___%s" , type = "markdown_bold_italic" },
{ pattern = "^__[%s%p%w]+__%s" , type = "markdown_bold" },
{ pattern = "^_[%s%p%w]+_%s" , type = "markdown_italic" },
{ pattern = { "%s___", "___%f[%s]" }, type = "markdown_bold_italic" },
{ pattern = { "%s__", "__%f[%s]" }, type = "markdown_bold" },
{ pattern = { "%s_[%S]", "_%f[%s]" }, type = "markdown_italic" },
-- heading with custom id
{
pattern = "^#+%s[%w%s%p]+(){()#[%w%-]+()}",
type = { "keyword", "function", "string", "function" }
},
-- headings
{ pattern = "^#+%s.+\n", type = "keyword" },
-- superscript and subscript
{
pattern = "%^()%d+()%^",
type = { "function", "number", "function" }
},
{
pattern = "%~()%d+()%~",
type = { "function", "number", "function" }
},
-- definitions
{ pattern = "^:%s.+", type = "function" },
-- emoji
{ pattern = ":[a-zA-Z0-9_%-]+:", type = "literal" },
-- images and link
{
pattern = "!?%[!?%[()["..in_squares_match.."]+()%]%(()["..in_parenthesis_match.."]+()%)%]%(()["..in_parenthesis_match.."]+()%)",
type = { "function", "string", "function", "number", "function", "number", "function" }
},
{
pattern = "!?%[!?%[?()["..in_squares_match.."]+()%]?%]%(()["..in_parenthesis_match.."]+()%)",
type = { "function", "string", "function", "number", "function" }
},
-- reference links
{
pattern = "%[()["..in_squares_match.."]+()%] *()%[()["..in_squares_match.."]+()%]",
type = { "function", "string", "function", "function", "number", "function" }
},
{
pattern = "^%s*%[%^()["..in_squares_match.."]+()%]: ",
type = { "function", "number", "function" }
},
{
pattern = "^%s*%[%^?()["..in_squares_match.."]+()%]:%s+.+\n",
type = { "function", "number", "function" }
},
{
pattern = "!?%[%^?()["..in_squares_match.."]+()%]",
type = { "function", "number", "function" }
},
-- url's and email
{
pattern = "<[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+%.[a-zA-Z0-9-.]+>",
type = "function"
},
{ pattern = "<https?://%S+>", type = "function" },
{ pattern = "https?://%S+", type = "function" }
}, },
symbols = { }, symbols = { },
} }
-- Adjust the color on theme changes
core.add_thread(function()
while true do
if initial_color ~= style.syntax["keyword2"] then
for _, attr in pairs({"bold", "italic", "bold_italic"}) do
style.syntax["markdown_"..attr] = style.syntax["keyword2"]
end
initial_color = style.syntax["keyword2"]
end
coroutine.yield(1)
end
end)

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {

View File

@ -1,11 +1,10 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "XML", name = "XML",
files = { "%.xml$" }, files = { "%.xml$" },
headers = "<%?xml", headers = "<%?xml",
block_comment = { "<!--", "-->" },
patterns = { patterns = {
{ pattern = { "<!%-%-", "%-%->" }, type = "comment" }, { pattern = { "<!%-%-", "%-%->" }, type = "comment" },
{ pattern = { '%f[^>][^<]', '%f[<]' }, type = "normal" }, { pattern = { '%f[^>][^<]', '%f[<]' }, type = "normal" },

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local config = require "core.config" local config = require "core.config"
local style = require "core.style" local style = require "core.style"
local DocView = require "core.docview" local DocView = require "core.docview"

View File

@ -1,523 +0,0 @@
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local common = require "core.common"
local DocView = require "core.docview"
local Doc = require "core.doc"
local style = require "core.style"
local config = require "core.config"
local command = require "core.command"
local keymap = require "core.keymap"
local translate = require "core.doc.translate"
config.plugins.linewrapping = {
-- The type of wrapping to perform. Can be "letter" or "word".
mode = "letter",
-- If nil, uses the DocView's size, otherwise, uses this exact width.
width_override = nil,
-- Whether or not to draw a guide
guide = true,
-- Whether or not we should indent ourselves like the first line of a wrapped block.
indent = true,
-- Whether or not to enable wrapping by default when opening files.
enable_by_default = true
}
local LineWrapping = {}
-- Computes the breaks for a given line, width and mode. Returns a list of columns
-- at which the line should be broken.
function LineWrapping.compute_line_breaks(doc, default_font, line, width, mode)
local xoffset, last_i, i, last_space, last_width, begin_width = 0, 1, 1, 1, 0, 0
local splits = { 1 }
for idx, type, text in doc.highlighter:each_token(line) do
local font = style.syntax_fonts[type] or default_font
if idx == 1 and config.plugins.linewrapping.indent then
local _, indent_end = text:find("^%s+")
if indent_end then begin_width = font:get_width(text:sub(1, indent_end)) end
end
local w = font:get_width(text)
if xoffset + w > width then
for char in common.utf8_chars(text) do
w = font:get_width(char)
xoffset = xoffset + w
if xoffset > width then
if mode == "word" then
table.insert(splits, last_space + 1)
xoffset = xoffset - last_width + w + begin_width
else
table.insert(splits, i)
xoffset = w + begin_width
end
elseif char == ' ' then
last_space = i
last_width = xoffset
end
i = i + #char
end
else
xoffset = xoffset + w
i = i + #text
end
end
return splits, begin_width
end
-- breaks are held in a single table that contains n*2 elements, where n is the amount of line breaks.
-- each element represents line and column of the break. line_offset will check from the specified line
-- if the first line has not changed breaks, it will stop there.
function LineWrapping.reconstruct_breaks(docview, default_font, width, line_offset)
if width ~= math.huge then
local doc = docview.doc
-- two elements per wrapped line; first maps to original line number, second to column number.
docview.wrapped_lines = { }
-- one element per actual line; maps to the first index of in wrapped_lines for this line
docview.wrapped_line_to_idx = { }
-- one element per actual line; gives the indent width for the acutal line
docview.wrapped_line_offsets = { }
docview.wrapped_settings = { ["width"] = width, ["font"] = default_font }
for i = line_offset or 1, #doc.lines do
local breaks, offset = LineWrapping.compute_line_breaks(doc, default_font, i, width, config.plugins.linewrapping.mode)
table.insert(docview.wrapped_line_offsets, offset)
for k, col in ipairs(breaks) do
table.insert(docview.wrapped_lines, i)
table.insert(docview.wrapped_lines, col)
end
end
-- list of indices for wrapped_lines, that are based on original line number
-- holds the index to the first in the wrapped_lines list.
local last_wrap = nil
for i = 1, #docview.wrapped_lines, 2 do
if not last_wrap or last_wrap ~= docview.wrapped_lines[i] then
table.insert(docview.wrapped_line_to_idx, (i + 1) / 2)
last_wrap = docview.wrapped_lines[i]
end
end
else
docview.wrapped_lines = nil
docview.wrapped_line_to_idx = nil
docview.wrapped_line_offsets = nil
docview.wrapped_settings = nil
end
end
-- When we have an insertion or deletion, we have four sections of text.
-- 1. The unaffected section, located prior to the cursor. This is completely ignored.
-- 2. The beginning of the affected line prior to the insertion or deletion. Begins on column 1 of the selection.
-- 3. The removed/pasted lines.
-- 4. Every line after the modification, begins one line after the selection in the initial document.
function LineWrapping.update_breaks(docview, old_line1, old_line2, net_lines)
-- Step 1: Determine the index for the line for #2.
local old_idx1 = docview.wrapped_line_to_idx[old_line1] or 1
-- Step 2: Determine the index of the line for #4.
local old_idx2 = (docview.wrapped_line_to_idx[old_line2 + 1] or ((#docview.wrapped_lines / 2) + 1)) - 1
-- Step 3: Remove all old breaks for the old lines from the table, and all old widths from wrapped_line_offsets.
local offset = (old_idx1 - 1) * 2 + 1
for i = old_idx1, old_idx2 do
table.remove(docview.wrapped_lines, offset)
table.remove(docview.wrapped_lines, offset)
end
for i = old_line1, old_line2 do
table.remove(docview.wrapped_line_offsets, old_line1)
end
-- Step 4: Shift the line number of wrapped_lines past #4 by the amount of inserted/deleted lines.
if net_lines ~= 0 then
for i = offset, #docview.wrapped_lines, 2 do
docview.wrapped_lines[i] = docview.wrapped_lines[i] + net_lines
end
end
-- Step 5: Compute the breaks and offsets for the lines for #2 and #3. Insert them into the table.
local new_line1 = old_line1
local new_line2 = old_line2 + net_lines
for line = new_line1, new_line2 do
local breaks, begin_width = LineWrapping.compute_line_breaks(docview.doc, docview.wrapped_settings.font, line, docview.wrapped_settings.width, config.plugins.linewrapping.mode)
table.insert(docview.wrapped_line_offsets, line, begin_width)
for i,b in ipairs(breaks) do
table.insert(docview.wrapped_lines, offset, b)
table.insert(docview.wrapped_lines, offset, line)
offset = offset + 2
end
end
-- Step 6: Recompute the wrapped_line_to_idx cache from #2.
local line = old_line1
offset = (old_idx1 - 1) * 2 + 1
while offset < #docview.wrapped_lines do
if docview.wrapped_lines[offset + 1] == 1 then
docview.wrapped_line_to_idx[line] = ((offset - 1) / 2) + 1
line = line + 1
end
offset = offset + 2
end
while line <= #docview.wrapped_line_to_idx do
table.remove(docview.wrapped_line_to_idx)
end
end
-- Draws a guide if applicable to show where wrapping is occurring.
function LineWrapping.draw_guide(docview)
if config.plugins.linewrapping.guide and docview.wrapped_settings.width ~= math.huge then
local x, y = docview:get_content_offset()
local gw = docview:get_gutter_width()
renderer.draw_rect(x + gw + docview.wrapped_settings.width, y, 1, core.root_view.size.y, style.selection)
end
end
function LineWrapping.update_docview_breaks(docview)
local x,y,w,h = docview:get_scrollbar_rect()
local width = config.plugins.linewrapping.width_override or (docview.size.x - docview:get_gutter_width() - w)
if (not docview.wrapped_settings or docview.wrapped_settings.width == nil or width ~= docview.wrapped_settings.width) then
docview.scroll.to.x = 0
LineWrapping.reconstruct_breaks(docview, docview:get_font(), width)
end
end
local function get_idx_line_col(docview, idx)
local doc = docview.doc
if not docview.wrapped_settings then
if idx > #doc.lines then return #doc.lines, #doc.lines[#doc.lines] + 1 end
return idx, 1
end
if idx < 1 then return 1, 1 end
local offset = (idx - 1) * 2 + 1
if offset > #docview.wrapped_lines then return #doc.lines, #doc.lines[#doc.lines] + 1 end
return docview.wrapped_lines[offset], docview.wrapped_lines[offset + 1]
end
local function get_idx_line_length(docview, idx)
local doc = docview.doc
if not docview.wrapped_settings then
if idx > #doc.lines then return #doc.lines[#doc.lines] + 1 end
return #doc.lines[idx]
end
local offset = (idx - 1) * 2 + 1
local start = docview.wrapped_lines[offset + 1]
if docview.wrapped_lines[offset + 2] and docview.wrapped_lines[offset + 2] == docview.wrapped_lines[offset] then
return docview.wrapped_lines[offset + 3] - docview.wrapped_lines[offset + 1]
else
return #doc.lines[docview.wrapped_lines[offset]] - docview.wrapped_lines[offset + 1] + 1
end
end
local function get_total_wrapped_lines(docview)
if not docview.wrapped_settings then return docview.doc and #docview.doc.lines end
return #docview.wrapped_lines / 2
end
-- If line end, gives the end of an index line, rather than the first character of the next line.
local function get_line_idx_col_count(docview, line, col, line_end, ndoc)
local doc = docview.doc
if not docview.wrapped_settings then return common.clamp(line, 1, #doc.lines), col, 1, 1 end
if line > #doc.lines then return get_line_idx_col_count(docview, #doc.lines, #doc.lines[#doc.lines] + 1) end
line = math.max(line, 1)
local idx = docview.wrapped_line_to_idx[line] or 1
local ncol, scol = 1, 1
if col then
local i = idx + 1
while line == docview.wrapped_lines[(i - 1) * 2 + 1] and col >= docview.wrapped_lines[(i - 1) * 2 + 2] do
local nscol = docview.wrapped_lines[(i - 1) * 2 + 2]
if line_end and col == nscol then
break
end
scol = nscol
i = i + 1
idx = idx + 1
end
ncol = (col - scol) + 1
end
local count = (docview.wrapped_line_to_idx[line + 1] or (get_total_wrapped_lines(docview) + 1)) - (docview.wrapped_line_to_idx[line] or get_total_wrapped_lines(docview))
return idx, ncol, count, scol
end
local function get_line_col_from_index_and_x(docview, idx, x)
local doc = docview.doc
local line, col = get_idx_line_col(docview, idx)
if idx < 1 then return 1, 1 end
local xoffset, last_i, i = (col ~= 1 and docview.wrapped_line_offsets[line] or 0), col, 1
if x < xoffset then return line, col end
local default_font = docview:get_font()
for _, type, text in docview.doc.highlighter:each_token(line) do
local font, w = style.syntax_fonts[type] or default_font, 0
for char in common.utf8_chars(text) do
if i >= col then
if xoffset >= x then
return line, (xoffset - x > (w / 2) and last_i or i)
end
w = font:get_width(char)
xoffset = xoffset + w
end
last_i = i
i = i + #char
end
end
return line, #doc.lines[line]
end
local open_files = {}
local old_doc_insert = Doc.raw_insert
function Doc:raw_insert(line, col, text, undo_stack, time)
local old_lines = #self.lines
old_doc_insert(self, line, col, text, undo_stack, time)
if open_files[self] then
for i,docview in ipairs(open_files[self]) do
if docview.wrapped_settings then
local lines = #self.lines - old_lines
LineWrapping.update_breaks(docview, line, line, lines)
end
end
end
end
local old_doc_remove = Doc.raw_remove
function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
local old_lines = #self.lines
old_doc_remove(self, line1, col1, line2, col2, undo_stack, time)
if open_files[self] then
for i,docview in ipairs(open_files[self]) do
if docview.wrapped_settings then
local lines = #self.lines - old_lines
LineWrapping.update_breaks(docview, line1, line2, lines)
end
end
end
end
local old_doc_update = DocView.update
function DocView:update()
old_doc_update(self)
if self.wrapped_settings and self.size.x > 0 then
LineWrapping.update_docview_breaks(self)
end
end
function DocView:get_scrollable_size()
if not config.scroll_past_end then
return self:get_line_height() * get_total_wrapped_lines(self) + style.padding.y * 2
end
return self:get_line_height() * (get_total_wrapped_lines(self) - 1) + self.size.y
end
local old_new = DocView.new
function DocView:new(doc)
old_new(self, doc)
if not open_files[doc] then open_files[doc] = {} end
table.insert(open_files[doc], self)
if config.plugins.linewrapping.enable_by_default then
LineWrapping.update_docview_breaks(self)
end
end
local old_scroll_to_make_visible = DocView.scroll_to_make_visible
function DocView:scroll_to_make_visible(line, col)
old_scroll_to_make_visible(self, line, col)
if self.wrapped_settings then self.scroll.to.x = 0 end
end
local old_get_visible_line_range = DocView.get_visible_line_range
function DocView:get_visible_line_range()
if not self.wrapped_settings then return old_get_visible_line_range(self) end
local x, y, x2, y2 = self:get_content_bounds()
local lh = self:get_line_height()
local minline = get_idx_line_col(self, math.max(1, math.floor(y / lh)))
local maxline = get_idx_line_col(self, math.min(get_total_wrapped_lines(self), math.floor(y2 / lh) + 1))
return minline, maxline
end
local old_get_x_offset_col = DocView.get_x_offset_col
function DocView:get_x_offset_col(line, x)
if not self.wrapped_settings then return old_get_x_offset_col(self, line, x) end
local idx = get_line_idx_col_count(self, line)
return get_line_col_from_index_and_x(self, idx, x)
end
-- If line end is true, returns the end of the previous line, in a multi-line break.
local old_get_col_x_offset = DocView.get_col_x_offset
function DocView:get_col_x_offset(line, col, line_end)
if not self.wrapped_settings then return old_get_col_x_offset(self, line, col) end
local idx, ncol, count, scol = get_line_idx_col_count(self, line, col, line_end)
local xoffset, i = (scol ~= 1 and self.wrapped_line_offsets[line] or 0), 1
local default_font = self:get_font()
for _, type, text in self.doc.highlighter:each_token(line) do
if i + #text >= scol then
if i < scol then
text = text:sub(scol - i + 1)
i = scol
end
local font = style.syntax_fonts[type] or default_font
for char in common.utf8_chars(text) do
if i >= col then
return xoffset
end
xoffset = xoffset + font:get_width(char)
i = i + #char
end
else
i = i + #text
end
end
return xoffset
end
local old_get_line_screen_position = DocView.get_line_screen_position
function DocView:get_line_screen_position(line, col)
if not self.wrapped_settings then return old_get_line_screen_position(self, line, col) end
local idx, ncol, count = get_line_idx_col_count(self, line, col)
local x, y = self:get_content_offset()
local lh = self:get_line_height()
local gw = self:get_gutter_width()
return x + gw + (col and self:get_col_x_offset(line, col) or 0), y + (idx-1) * lh + style.padding.y
end
local old_resolve_screen_position = DocView.resolve_screen_position
function DocView:resolve_screen_position(x, y)
if not self.wrapped_settings then return old_resolve_screen_position(self, x, y) end
local ox, oy = self:get_line_screen_position(1)
local idx = common.clamp(math.floor((y - oy) / self:get_line_height()) + 1, 1, get_total_wrapped_lines(self))
return get_line_col_from_index_and_x(self, idx, x - ox)
end
local old_draw_line_text = DocView.draw_line_text
function DocView:draw_line_text(line, x, y)
if not self.wrapped_settings then return old_draw_line_text(self, line, x, y) end
local default_font = self:get_font()
local tx, ty, begin_width = x, y + self:get_line_text_y_offset(), self.wrapped_line_offsets[line]
local lh = self:get_line_height()
local idx, _, count = get_line_idx_col_count(self, line)
local total_offset = 1
for _, type, text in self.doc.highlighter:each_token(line) do
local color = style.syntax[type]
local font = style.syntax_fonts[type] or default_font
local token_offset = 1
-- Split tokens if we're at the end of the document.
while text ~= nil and token_offset <= #text do
local next_line, next_line_start_col = get_idx_line_col(self, idx + 1)
if next_line ~= line then
next_line_start_col = #self.doc.lines[line]
end
local max_length = next_line_start_col - total_offset
local rendered_text = text:sub(token_offset, token_offset + max_length - 1)
tx = renderer.draw_text(font, rendered_text, tx, ty, color)
total_offset = total_offset + #rendered_text
if total_offset ~= next_line_start_col or max_length == 0 then break end
token_offset = token_offset + #rendered_text
idx = idx + 1
tx, ty = x + begin_width, ty + lh
end
end
return lh * count
end
local old_draw_line_body = DocView.draw_line_body
function DocView:draw_line_body(line, x, y)
if not self.wrapped_settings then return old_draw_line_body(self, line, x, y) end
local lh = self:get_line_height()
local idx0 = get_line_idx_col_count(self, line)
for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
if line >= line1 and line <= line2 then
if line1 ~= line then col1 = 1 end
if line2 ~= line then col2 = #self.doc.lines[line] + 1 end
if col1 ~= col2 then
local idx1, ncol1 = get_line_idx_col_count(self, line, col1)
local idx2, ncol2 = get_line_idx_col_count(self, line, col2)
for i = idx1, idx2 do
local x1, x2 = x + (idx1 == i and self:get_col_x_offset(line1, col1) or 0)
if idx2 == i then
x2 = x + self:get_col_x_offset(line, col2)
else
x2 = x + self:get_col_x_offset(line, get_idx_line_length(self, i, line) + 1, true)
end
renderer.draw_rect(x1, y + (i - idx0) * lh, x2 - x1, lh, style.selection)
end
end
end
end
local draw_highlight = nil
for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
-- draw line highlight if caret is on this line
if draw_highlight ~= false and config.highlight_current_line
and line1 == line and core.active_view == self then
draw_highlight = (line1 == line2 and col1 == col2)
end
end
if draw_highlight then
local _, _, count = get_line_idx_col_count(self, line)
for i=1,count do
self:draw_line_highlight(x + self.scroll.x, y + lh * (i - 1))
end
end
-- draw line's text
return self:draw_line_text(line, x, y)
end
local old_draw = DocView.draw
function DocView:draw()
old_draw(self)
if self.wrapped_settings then
LineWrapping.draw_guide(self)
end
end
local old_draw_line_gutter = DocView.draw_line_gutter
function DocView:draw_line_gutter(line, x, y, width)
local lh = self:get_line_height()
local _, _, count = get_line_idx_col_count(self, line)
return (old_draw_line_gutter(self, line, x, y, width) or lh) * count
end
local old_translate_end_of_line = translate.end_of_line
function translate.end_of_line(doc, line, col)
if not core.active_view or core.active_view.doc ~= doc or not core.active_view.wrapped_settings then old_translate_end_of_line(doc, line, col) end
local idx, ncol = get_line_idx_col_count(core.active_view, line, col)
local nline, ncol2 = get_idx_line_col(core.active_view, idx + 1)
if nline ~= line then return line, math.huge end
return line, ncol2 - 1
end
local old_translate_start_of_line = translate.start_of_line
function translate.start_of_line(doc, line, col)
if not core.active_view or core.active_view.doc ~= doc or not core.active_view.wrapped_settings then old_translate_start_of_line(doc, line, col) end
local idx, ncol = get_line_idx_col_count(core.active_view, line, col)
local nline, ncol2 = get_idx_line_col(core.active_view, idx - 1)
if nline ~= line then return line, 1 end
return line, ncol2 + 1
end
local old_previous_line = DocView.translate.previous_line
function DocView.translate.previous_line(doc, line, col, dv)
if not dv.wrapped_settings then return old_previous_line(doc, line, col, dv) end
local idx, ncol = get_line_idx_col_count(dv, line, col)
return get_line_col_from_index_and_x(dv, idx - 1, dv:get_col_x_offset(line, col))
end
local old_next_line = DocView.translate.next_line
function DocView.translate.next_line(doc, line, col, dv)
if not dv.wrapped_settings then return old_next_line(doc, line, col, dv) end
local idx, ncol = get_line_idx_col_count(dv, line, col)
return get_line_col_from_index_and_x(dv, idx + 1, dv:get_col_x_offset(line, col))
end
command.add(nil, {
["line-wrapping:enable"] = function()
if core.active_view and core.active_view.doc then
LineWrapping.update_docview_breaks(core.active_view)
end
end,
["line-wrapping:disable"] = function()
if core.active_view and core.active_view.doc then
LineWrapping.reconstruct_breaks(core.active_view, core.active_view:get_font(), math.huge)
end
end,
["line-wrapping:toggle"] = function()
if core.active_view and core.active_view.doc and core.active_view.wrapped_settings then
command.perform("line-wrapping:disable")
else
command.perform("line-wrapping:enable")
end
end
})
keymap.add {
["f10"] = "line-wrapping:toggle",
}
return LineWrapping

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local command = require "core.command" local command = require "core.command"
local keymap = require "core.keymap" local keymap = require "core.keymap"

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local common = require "core.common" local common = require "core.common"
local keymap = require "core.keymap" local keymap = require "core.keymap"
@ -176,7 +176,7 @@ function ResultsView:draw()
local text local text
if self.searching then if self.searching then
if files_number then if files_number then
text = string.format("Searching %.f%% (%d of %d files, %d matches) for %q...", text = string.format("Searching %d%% (%d of %d files, %d matches) for %q...",
per * 100, self.last_file_idx, files_number, per * 100, self.last_file_idx, files_number,
#self.results, self.query) #self.results, self.query)
else else
@ -229,21 +229,8 @@ local function begin_search(text, fn)
end end
local function set_command_view_text()
local view = core.active_view
local doc = (view and view.doc) and view.doc or nil
if doc then
core.command_view:set_text(
doc:get_text(table.unpack({ doc:get_selection() })),
true
)
end
end
command.add(nil, { command.add(nil, {
["project-search:find"] = function() ["project-search:find"] = function()
set_command_view_text()
core.command_view:enter("Find Text In Project", function(text) core.command_view:enter("Find Text In Project", function(text)
text = text:lower() text = text:lower()
begin_search(text, function(line_text) begin_search(text, function(line_text)
@ -256,13 +243,12 @@ command.add(nil, {
core.command_view:enter("Find Regex In Project", function(text) core.command_view:enter("Find Regex In Project", function(text)
local re = regex.compile(text, "i") local re = regex.compile(text, "i")
begin_search(text, function(line_text) begin_search(text, function(line_text)
return regex.cmatch(re, line_text) return regex.cmatch(re, line_text)
end) end)
end) end)
end, end,
["project-search:fuzzy-find"] = function() ["project-search:fuzzy-find"] = function()
set_command_view_text()
core.command_view:enter("Fuzzy Find Text In Project", function(text) core.command_view:enter("Fuzzy Find Text In Project", function(text)
begin_search(text, function(line_text) begin_search(text, function(line_text)
return common.fuzzy_match(line_text, text) and 1 return common.fuzzy_match(line_text, text) and 1
@ -292,22 +278,22 @@ command.add(ResultsView, {
["project-search:refresh"] = function() ["project-search:refresh"] = function()
core.active_view:refresh() core.active_view:refresh()
end, end,
["project-search:move-to-previous-page"] = function() ["project-search:move-to-previous-page"] = function()
local view = core.active_view local view = core.active_view
view.scroll.to.y = view.scroll.to.y - view.size.y view.scroll.to.y = view.scroll.to.y - view.size.y
end, end,
["project-search:move-to-next-page"] = function() ["project-search:move-to-next-page"] = function()
local view = core.active_view local view = core.active_view
view.scroll.to.y = view.scroll.to.y + view.size.y view.scroll.to.y = view.scroll.to.y + view.size.y
end, end,
["project-search:move-to-start-of-doc"] = function() ["project-search:move-to-start-of-doc"] = function()
local view = core.active_view local view = core.active_view
view.scroll.to.y = 0 view.scroll.to.y = 0
end, end,
["project-search:move-to-end-of-doc"] = function() ["project-search:move-to-end-of-doc"] = function()
local view = core.active_view local view = core.active_view
view.scroll.to.y = view:get_scrollable_size() view.scroll.to.y = view:get_scrollable_size()

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local command = require "core.command" local command = require "core.command"
local keymap = require "core.keymap" local keymap = require "core.keymap"

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local config = require "core.config" local config = require "core.config"
local command = require "core.command" local command = require "core.command"

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local common = require "core.common" local common = require "core.common"
local command = require "core.command" local command = require "core.command"
@ -8,10 +8,10 @@ local style = require "core.style"
local RootView = require "core.rootview" local RootView = require "core.rootview"
local CommandView = require "core.commandview" local CommandView = require "core.commandview"
config.plugins.scale = common.merge({ config.plugins.scale = {
mode = "code", mode = "code",
use_mousewheel = true use_mousewheel = true
}, config.plugins.scale) }
local scale_steps = 0.05 local scale_steps = 0.05
@ -50,8 +50,12 @@ local function set_scale(scale)
style.code_font = renderer.font.copy(style.code_font, s * style.code_font:get_size()) style.code_font = renderer.font.copy(style.code_font, s * style.code_font:get_size())
end end
for name, font in pairs(style.syntax_fonts) do for _, font in pairs(style.syntax_fonts) do
style.syntax_fonts[name] = renderer.font.copy(font, s * font:get_size()) renderer.font.set_size(font, s * font:get_size())
end
for _, font in pairs(style.syntax_fonts) do
renderer.font.set_size(font, s * font:get_size())
end end
-- restore scroll positions -- restore scroll positions

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local command = require "core.command" local command = require "core.command"
local translate = require "core.doc.translate" local translate = require "core.doc.translate"

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local common = require "core.common" local common = require "core.common"
local command = require "core.command" local command = require "core.command"
@ -84,12 +84,11 @@ end
function ToolbarView:on_mouse_pressed(button, x, y, clicks) function ToolbarView:on_mouse_pressed(button, x, y, clicks)
local caught = ToolbarView.super.on_mouse_pressed(self, button, x, y, clicks) local caught = ToolbarView.super.on_mouse_pressed(self, button, x, y, clicks)
if caught then return caught end if caught then return end
core.set_active_view(core.last_active_view) core.set_active_view(core.last_active_view)
if self.hovered_item then if self.hovered_item then
command.perform(self.hovered_item.command) command.perform(self.hovered_item.command)
end end
return true
end end

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local common = require "core.common" local common = require "core.common"
local command = require "core.command" local command = require "core.command"
@ -8,15 +8,9 @@ local style = require "core.style"
local View = require "core.view" 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"
config.plugins.treeview = common.merge({
-- Amount of clicks to open a file
clicks_to_open = 2,
-- Default treeview width
size = 200 * SCALE
}, config.plugins.treeview)
local default_treeview_size = 200 * SCALE
local tooltip_offset = style.font:get_height() local tooltip_offset = style.font:get_height()
local tooltip_border = 1 local tooltip_border = 1
local tooltip_delay = 0.5 local tooltip_delay = 0.5
@ -45,26 +39,19 @@ function TreeView:new()
self.scrollable = true self.scrollable = true
self.visible = true self.visible = true
self.init_size = true self.init_size = true
self.target_size = config.plugins.treeview.size self.target_size = default_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.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
self:add_core_hooks() local on_dirmonitor_modify = core.on_dirmonitor_modify
end function core.on_dirmonitor_modify(dir, filepath)
if self.cache[dir.name] then
self.cache[dir.name][filepath] = nil
function TreeView:add_core_hooks() end
-- When a file or directory is deleted we delete the corresponding cache entry on_dirmonitor_modify(dir, filepath)
-- because if the entry is recreated we may use wrong information from cache.
local on_delete = core.on_dirmonitor_delete
core.on_dirmonitor_delete = function(dir, filepath)
local cache = self.cache[dir.name]
if cache then cache[filepath] = nil end
on_delete(dir, filepath)
end end
end end
@ -103,7 +90,7 @@ function TreeView:get_cached(dir, item, dirname)
end end
t.name = basename t.name = basename
t.type = item.type t.type = item.type
t.dir_name = dir.name -- points to top level "dir" item t.dir = dir -- points to top level "dir" item
dir_cache[cache_name] = t dir_cache[cache_name] = t
end end
return t return t
@ -183,21 +170,6 @@ function TreeView:each_item()
end end
function TreeView:set_selection(selection, selection_y)
self.selected_item = selection
if selection and selection_y
and (selection_y <= 0 or selection_y >= self.size.y) then
local lh = self:get_item_height()
if selection_y >= self.size.y - lh then
selection_y = selection_y - self.size.y + lh
end
local _, y = self:get_content_offset()
self.scroll.to.y = selection and (selection_y - y)
end
end
function TreeView:get_text_bounding_box(item, x, y, w, h) function TreeView:get_text_bounding_box(item, x, y, w, h)
local icon_width = style.icon_font:get_width("D") local icon_width = style.icon_font:get_width("D")
local xoffset = item.depth * style.padding.x + style.padding.x + icon_width local xoffset = item.depth * style.padding.x + style.padding.x + icon_width
@ -208,14 +180,8 @@ end
function TreeView:on_mouse_moved(px, py, ...) function TreeView:on_mouse_moved(px, py, ...)
if not self.visible then return end
TreeView.super.on_mouse_moved(self, px, py, ...) TreeView.super.on_mouse_moved(self, px, py, ...)
self.cursor_pos.x = px if self.dragging_scrollbar then return end
self.cursor_pos.y = py
if self.dragging_scrollbar then
self.hovered_item = nil
return
end
local item_changed, tooltip_changed local item_changed, tooltip_changed
for item, x,y,w,h in self:each_item() do for item, x,y,w,h in self:each_item() do
@ -251,28 +217,32 @@ end
function TreeView:on_mouse_pressed(button, x, y, clicks) function TreeView:on_mouse_pressed(button, x, y, clicks)
if not self.visible then return end
local caught = TreeView.super.on_mouse_pressed(self, button, x, y, clicks) local caught = TreeView.super.on_mouse_pressed(self, button, x, y, clicks)
if caught or button ~= "left" then if caught or button ~= "left" then
return true return true
end end
local hovered_item = self.hovered_item
if self.hovered_item then if not hovered_item then
self:set_selection(self.hovered_item) return false
elseif hovered_item.type == "dir" then
if keymap.modkeys["ctrl"] and button == "left" then if keymap.modkeys["ctrl"] and button == "left" then
create_directory_in(self.selected_item) create_directory_in(hovered_item)
elseif self.selected_item.type == "dir" else
or (self.selected_item.type == "file" hovered_item.expanded = not hovered_item.expanded
and clicks == config.plugins.treeview.clicks_to_open if hovered_item.dir.files_limit then
) core.update_project_subdir(hovered_item.dir, hovered_item.filename, hovered_item.expanded)
then core.project_subdir_set_show(hovered_item.dir, hovered_item.filename, hovered_item.expanded)
command.perform "treeview:open" end
end end
else else
return false core.try(function()
if core.last_active_view and core.active_view == self then
core.set_active_view(core.last_active_view)
end
local doc_filename = core.normalize_to_project_dir(hovered_item.abs_filename)
core.root_view:open_doc(core.open_doc(doc_filename))
end)
end end
return true return true
end end
@ -287,8 +257,6 @@ function TreeView:update()
self:move_towards(self.size, "x", dest) self:move_towards(self.size, "x", dest)
end 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
self:move_towards(self.tooltip, "alpha", tooltip_alpha, tooltip_alpha_rate) self:move_towards(self.tooltip, "alpha", tooltip_alpha, tooltip_alpha_rate)
@ -299,13 +267,6 @@ function TreeView:update()
self.item_icon_width = style.icon_font:get_width("D") self.item_icon_width = style.icon_font:get_width("D")
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
-- we don't want events when the thing is scrolling fast
local dy = math.abs(self.scroll.to.y - self.scroll.y)
if self.scroll.to.y ~= 0 and dy < self:get_item_height() then
self:on_mouse_moved(self.cursor_pos.x, self.cursor_pos.y, 0, 0)
end
TreeView.super.update(self) TreeView.super.update(self)
end end
@ -389,10 +350,6 @@ end
function TreeView:draw_item_background(item, active, hovered, x, y, w, h) function TreeView:draw_item_background(item, active, hovered, x, y, w, h)
if hovered then if hovered then
local hover_color = { table.unpack(style.line_highlight) }
hover_color[4] = 160
renderer.draw_rect(x, y, w, h, hover_color)
elseif active then
renderer.draw_rect(x, y, w, h, style.line_highlight) renderer.draw_rect(x, y, w, h, style.line_highlight)
end end
end end
@ -409,7 +366,6 @@ end
function TreeView:draw() function TreeView:draw()
if not self.visible then return end
self:draw_background(style.background2) self:draw_background(style.background2)
local _y, _h = self.position.y, self.size.y local _y, _h = self.position.y, self.size.y
@ -419,46 +375,18 @@ function TreeView:draw()
for item, x,y,w,h in self:each_item() do for item, x,y,w,h in self:each_item() do
if y + h >= _y and y < _y + _h then if y + h >= _y and y < _y + _h then
self:draw_item(item, self:draw_item(item,
item == self.selected_item, item.abs_filename == active_filename,
item == self.hovered_item, item == self.hovered_item,
x, y, w, h) x, y, w, h)
end end
end end
self:draw_scrollbar() self:draw_scrollbar()
if self.hovered_item and self.tooltip.x and self.tooltip.alpha > 0 then if self.hovered_item and self.tooltip.alpha > 0 then
core.root_view:defer_draw(self.draw_tooltip, self) core.root_view:defer_draw(self.draw_tooltip, self)
end end
end end
function TreeView:get_parent(item)
local parent_path = common.dirname(item.abs_filename)
if not parent_path then return end
for it, _, y in self:each_item() do
if it.abs_filename == parent_path then
return it, y
end
end
end
function TreeView:toggle_expand(toggle)
local item = self.selected_item
if not item then return end
if item.type == "dir" then
if type(toggle) == "boolean" then
item.expanded = toggle
else
item.expanded = not item.expanded
end
local hovered_dir = core.project_dir_by_name(item.dir_name)
if hovered_dir and hovered_dir.files_limit then
core.update_project_subdir(hovered_dir, item.filename, item.expanded)
end
end
end
-- init -- init
local view = TreeView() local view = TreeView()
@ -477,7 +405,7 @@ if config.plugins.toolbarview ~= false and toolbar_plugin then
toolbar_view = ToolbarView() toolbar_view = ToolbarView()
treeview_node:split("down", toolbar_view, {y = true}) treeview_node:split("down", toolbar_view, {y = true})
local min_toolbar_width = toolbar_view:get_min_width() local min_toolbar_width = toolbar_view:get_min_width()
view:set_target_size("x", math.max(config.plugins.treeview.size, min_toolbar_width)) view:set_target_size("x", math.max(default_treeview_size, min_toolbar_width))
command.add(nil, { command.add(nil, {
["toolbar:toggle"] = function() ["toolbar:toggle"] = function()
toolbar_view:toggle_visible() toolbar_view:toggle_visible()
@ -518,12 +446,6 @@ function RootView:draw(...)
menu:draw() menu:draw()
end end
local on_quit_project = core.on_quit_project
function core.on_quit_project()
view.cache = {}
on_quit_project()
end
local function is_project_folder(path) local function is_project_folder(path)
return core.project_dir == path return core.project_dir == path
end end
@ -554,131 +476,65 @@ menu:register(
} }
) )
local previous_view = nil
-- Register the TreeView commands and keymap -- Register the TreeView commands and keymap
command.add(nil, { command.add(nil, {
["treeview:toggle"] = function() ["treeview:toggle"] = function()
view.visible = not view.visible view.visible = not view.visible
end, end})
["treeview:toggle-focus"] = function()
if not core.active_view:is(TreeView) then command.add(function() return view.hovered_item ~= nil end, {
if core.active_view:is(CommandView) then ["treeview:rename"] = function()
previous_view = core.last_active_view local old_filename = view.hovered_item.filename
local old_abs_filename = view.hovered_item.abs_filename
core.command_view:set_text(old_filename)
core.command_view:enter("Rename", function(filename)
filename = core.normalize_to_project_dir(filename)
local abs_filename = core.project_absolute_path(filename)
local res, err = os.rename(old_abs_filename, abs_filename)
if res then -- successfully renamed
for _, doc in ipairs(core.docs) do
if doc.abs_filename and old_abs_filename == doc.abs_filename then
doc:set_filename(filename, abs_filename) -- make doc point to the new filename
doc:reset_syntax()
break -- only first needed
end
end
core.log("Renamed \"%s\" to \"%s\"", old_filename, filename)
else else
previous_view = core.active_view core.error("Error while renaming \"%s\" to \"%s\": %s", old_abs_filename, abs_filename, err)
end
if not previous_view then
previous_view = core.root_view:get_primary_node().active_view
end
core.set_active_view(view)
if not view.selected_item then
for it, _, y in view:each_item() do
view:set_selection(it, y)
break
end
end end
end, common.path_suggest)
end,
else ["treeview:new-file"] = function()
core.set_active_view( if not is_project_folder(view.hovered_item.abs_filename) then
previous_view or core.root_view:get_primary_node().active_view core.command_view:set_text(view.hovered_item.filename .. "/")
)
end end
end core.command_view:enter("Filename", function(filename)
}) local doc_filename = core.project_dir .. PATHSEP .. filename
local file = io.open(doc_filename, "a+")
file:write("")
file:close()
core.root_view:open_doc(core.open_doc(doc_filename))
core.log("Created %s", doc_filename)
end, common.path_suggest)
end,
command.add(TreeView, { ["treeview:new-folder"] = function()
["treeview:next"] = function() if not is_project_folder(view.hovered_item.abs_filename) then
local item = view.selected_item core.command_view:set_text(view.hovered_item.filename .. "/")
local item_y
local stop = false
for it, _, y in view:each_item() do
if item == it then
stop = true
elseif stop then
item = it
item_y = y
break
end
end end
core.command_view:enter("Folder Name", function(filename)
view:set_selection(item, item_y) local dir_path = core.project_dir .. PATHSEP .. filename
common.mkdirp(dir_path)
core.log("Created %s", dir_path)
end, common.path_suggest)
end, end,
["treeview:previous"] = function()
local last_item
local last_item_y
for it, _, y in view:each_item() do
if it == view.selected_item then
if not last_item then
last_item = it
last_item_y = y
end
break
end
last_item = it
last_item_y = y
end
view:set_selection(last_item, last_item_y)
end,
["treeview:open"] = function()
local item = view.selected_item
if not item then return end
if item.type == "dir" then
view:toggle_expand()
else
core.try(function()
if core.last_active_view and core.active_view == view then
core.set_active_view(core.last_active_view)
end
local doc_filename = core.normalize_to_project_dir(item.abs_filename)
core.root_view:open_doc(core.open_doc(doc_filename))
end)
end
end,
["treeview:deselect"] = function()
view.selected_item = nil
end,
["treeview:collapse"] = function()
if view.selected_item then
if view.selected_item.type == "dir" and view.selected_item.expanded then
view:toggle_expand(false)
else
local parent_item, y = view:get_parent(view.selected_item)
if parent_item then
view:set_selection(parent_item, y)
end
end
end
end,
["treeview:expand"] = function()
view:toggle_expand(true)
end,
})
local function treeitem() return view.hovered_item or view.selected_item end
command.add(
function()
return treeitem() ~= nil
and (
core.active_view == view or core.active_view == menu
or (view.toolbar and core.active_view == view.toolbar)
-- sometimes the context menu is shown on top of statusbar
or core.active_view == core.status_view
)
end, {
["treeview:delete"] = function() ["treeview:delete"] = function()
local filename = treeitem().abs_filename local filename = view.hovered_item.abs_filename
local relfilename = treeitem().filename local relfilename = view.hovered_item.filename
local file_info = system.get_file_info(filename) local file_info = system.get_file_info(filename)
local file_type = file_info.type == "dir" and "Directory" or "File" local file_type = file_info.type == "dir" and "Directory" or "File"
-- Ask before deleting -- Ask before deleting
@ -712,83 +568,22 @@ command.add(
end end
end end
) )
end
})
command.add(function() return treeitem() ~= nil end, {
["treeview:rename"] = function()
local old_filename = treeitem().filename
local old_abs_filename = treeitem().abs_filename
core.command_view:set_text(old_filename)
core.command_view:enter("Rename", function(filename)
filename = core.normalize_to_project_dir(filename)
local abs_filename = core.project_absolute_path(filename)
local res, err = os.rename(old_abs_filename, abs_filename)
if res then -- successfully renamed
for _, doc in ipairs(core.docs) do
if doc.abs_filename and old_abs_filename == doc.abs_filename then
doc:set_filename(filename, abs_filename) -- make doc point to the new filename
doc:reset_syntax()
break -- only first needed
end
end
core.log("Renamed \"%s\" to \"%s\"", old_filename, filename)
else
core.error("Error while renaming \"%s\" to \"%s\": %s", old_abs_filename, abs_filename, err)
end
end, common.path_suggest)
end,
["treeview:new-file"] = function()
if not is_project_folder(treeitem().abs_filename) then
core.command_view:set_text(treeitem().filename .. "/")
end
core.command_view:enter("Filename", function(filename)
local doc_filename = core.project_dir .. PATHSEP .. filename
local file = io.open(doc_filename, "a+")
file:write("")
file:close()
core.root_view:open_doc(core.open_doc(doc_filename))
core.log("Created %s", doc_filename)
end, common.path_suggest)
end,
["treeview:new-folder"] = function()
if not is_project_folder(treeitem().abs_filename) then
core.command_view:set_text(treeitem().filename .. "/")
end
core.command_view:enter("Folder Name", function(filename)
local dir_path = core.project_dir .. PATHSEP .. filename
common.mkdirp(dir_path)
core.log("Created %s", dir_path)
end, common.path_suggest)
end, end,
["treeview:open-in-system"] = function() ["treeview:open-in-system"] = function()
local hovered_item = treeitem() local hovered_item = view.hovered_item
if PLATFORM == "Windows" then if PLATFORM == "Windows" then
system.exec(string.format("start \"\" %q", hovered_item.abs_filename)) system.exec(string.format("start \"\" %q", hovered_item.abs_filename))
elseif string.find(PLATFORM, "Mac") then elseif string.find(PLATFORM, "Mac") then
system.exec(string.format("open %q", hovered_item.abs_filename)) system.exec(string.format("open %q", hovered_item.abs_filename))
elseif PLATFORM == "Linux" or string.find(PLATFORM, "BSD") then elseif PLATFORM == "Linux" then
system.exec(string.format("xdg-open %q", hovered_item.abs_filename)) system.exec(string.format("xdg-open %q", hovered_item.abs_filename))
end end
end, end,
}) })
keymap.add { keymap.add { ["ctrl+\\"] = "treeview:toggle" }
["ctrl+\\"] = "treeview:toggle",
["up"] = "treeview:previous",
["down"] = "treeview:next",
["left"] = "treeview:collapse",
["right"] = "treeview:expand",
["return"] = "treeview:open",
["escape"] = "treeview:deselect",
["delete"] = "treeview:delete",
["ctrl+return"] = "treeview:new-folder"
}
-- Return the treeview with toolbar and contextmenu to allow -- Return the treeview with toolbar and contextmenu to allow
-- user or plugin modifications -- user or plugin modifications

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local command = require "core.command" local command = require "core.command"
local Doc = require "core.doc" local Doc = require "core.doc"

View File

@ -1,4 +1,4 @@
-- mod-version:3 -- lite-xl 2.1 -- mod-version:2 -- lite-xl 2.0
local core = require "core" local core = require "core"
local common = require "core.common" local common = require "core.common"
local DocView = require "core.docview" local DocView = require "core.docview"

View File

@ -124,7 +124,7 @@ process.options = {}
---@return process | nil ---@return process | nil
---@return string errmsg ---@return string errmsg
---@return process.errortype | integer errcode ---@return process.errortype | integer errcode
function process.start(command_and_params, options) end function process:start(command_and_params, options) end
--- ---
---Translates an error code into a useful text message ---Translates an error code into a useful text message

View File

@ -192,12 +192,6 @@ function system.get_clipboard() end
---@param text string ---@param text string
function system.set_clipboard(text) end function system.set_clipboard(text) end
---
---Get the process id of lite-xl it self.
---
---@return integer
function system.get_process_id() end
--- ---
---Get amount of iterations since the application was launched ---Get amount of iterations since the application was launched
---also known as SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency() ---also known as SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency()

1595
lib/dmon/dmon.h Normal file

File diff suppressed because it is too large Load Diff

162
lib/dmon/dmon_extra.h Normal file
View File

@ -0,0 +1,162 @@
#ifndef __DMON_EXTRA_H__
#define __DMON_EXTRA_H__
//
// Copyright 2021 Sepehr Taghdisian (septag@github). All rights reserved.
// License: https://github.com/septag/dmon#license-bsd-2-clause
//
// Extra header functionality for dmon.h for the backend based on inotify
//
// Add/Remove directory functions:
// dmon_watch_add: Adds a sub-directory to already valid watch_id. sub-directories are assumed to be relative to watch root_dir
// dmon_watch_add: Removes a sub-directory from already valid watch_id. sub-directories are assumed to be relative to watch root_dir
// Reason: The inotify backend does not work well when watching in recursive mode a root directory of a large tree, it may take
// quite a while until all inotify watches are established, and events will not be received in this time. Also, since one
// inotify watch will be established per subdirectory, it is possible that the maximum amount of inotify watches per user
// will be reached. The default maximum is 8192.
// When using inotify backend users may turn off the DMON_WATCHFLAGS_RECURSIVE flag and add/remove selectively the
// sub-directories to be watched based on application-specific logic about which sub-directory actually needs to be watched.
// The function dmon_watch_add and dmon_watch_rm are used to this purpose.
//
#ifndef __DMON_H__
#error "Include 'dmon.h' before including this file"
#endif
#ifdef __cplusplus
extern "C" {
#endif
DMON_API_DECL bool dmon_watch_add(dmon_watch_id id, const char* subdir);
DMON_API_DECL bool dmon_watch_rm(dmon_watch_id id, const char* watchdir);
#ifdef __cplusplus
}
#endif
#ifdef DMON_IMPL
#if DMON_OS_LINUX
DMON_API_IMPL bool dmon_watch_add(dmon_watch_id id, const char* watchdir)
{
DMON_ASSERT(id.id > 0 && id.id <= DMON_MAX_WATCHES);
bool skip_lock = pthread_self() == _dmon.thread_handle;
if (!skip_lock)
pthread_mutex_lock(&_dmon.mutex);
dmon__watch_state* watch = &_dmon.watches[id.id - 1];
// check if the directory exists
// if watchdir contains absolute/root-included path, try to strip the rootdir from it
// else, we assume that watchdir is correct, so save it as it is
struct stat st;
dmon__watch_subdir subdir;
if (stat(watchdir, &st) == 0 && (st.st_mode & S_IFDIR)) {
dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watchdir);
if (strstr(subdir.rootdir, watch->rootdir) == subdir.rootdir) {
dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watchdir + strlen(watch->rootdir));
}
} else {
char fullpath[DMON_MAX_PATH];
dmon__strcpy(fullpath, sizeof(fullpath), watch->rootdir);
dmon__strcat(fullpath, sizeof(fullpath), watchdir);
if (stat(fullpath, &st) != 0 || (st.st_mode & S_IFDIR) == 0) {
_DMON_LOG_ERRORF("Watch directory '%s' is not valid", watchdir);
if (!skip_lock)
pthread_mutex_unlock(&_dmon.mutex);
return false;
}
dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watchdir);
}
int dirlen = (int)strlen(subdir.rootdir);
if (subdir.rootdir[dirlen - 1] != '/') {
subdir.rootdir[dirlen] = '/';
subdir.rootdir[dirlen + 1] = '\0';
}
// check that the directory is not already added
for (int i = 0, c = stb_sb_count(watch->subdirs); i < c; i++) {
if (strcmp(subdir.rootdir, watch->subdirs[i].rootdir) == 0) {
_DMON_LOG_ERRORF("Error watching directory '%s', because it is already added.", watchdir);
if (!skip_lock)
pthread_mutex_unlock(&_dmon.mutex);
return false;
}
}
const uint32_t inotify_mask = IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE | IN_MODIFY;
char fullpath[DMON_MAX_PATH];
dmon__strcpy(fullpath, sizeof(fullpath), watch->rootdir);
dmon__strcat(fullpath, sizeof(fullpath), subdir.rootdir);
int wd = inotify_add_watch(watch->fd, fullpath, inotify_mask);
if (wd == -1) {
_DMON_LOG_ERRORF("Error watching directory '%s'. (inotify_add_watch:err=%d)", watchdir, errno);
if (!skip_lock)
pthread_mutex_unlock(&_dmon.mutex);
return false;
}
stb_sb_push(watch->subdirs, subdir);
stb_sb_push(watch->wds, wd);
if (!skip_lock)
pthread_mutex_unlock(&_dmon.mutex);
return true;
}
DMON_API_IMPL bool dmon_watch_rm(dmon_watch_id id, const char* watchdir)
{
DMON_ASSERT(id.id > 0 && id.id <= DMON_MAX_WATCHES);
bool skip_lock = pthread_self() == _dmon.thread_handle;
if (!skip_lock)
pthread_mutex_lock(&_dmon.mutex);
dmon__watch_state* watch = &_dmon.watches[id.id - 1];
char subdir[DMON_MAX_PATH];
dmon__strcpy(subdir, sizeof(subdir), watchdir);
if (strstr(subdir, watch->rootdir) == subdir) {
dmon__strcpy(subdir, sizeof(subdir), watchdir + strlen(watch->rootdir));
}
int dirlen = (int)strlen(subdir);
if (subdir[dirlen - 1] != '/') {
subdir[dirlen] = '/';
subdir[dirlen + 1] = '\0';
}
int i, c = stb_sb_count(watch->subdirs);
for (i = 0; i < c; i++) {
if (strcmp(watch->subdirs[i].rootdir, subdir) == 0) {
break;
}
}
if (i >= c) {
_DMON_LOG_ERRORF("Watch directory '%s' is not valid", watchdir);
if (!skip_lock)
pthread_mutex_unlock(&_dmon.mutex);
return false;
}
inotify_rm_watch(watch->fd, watch->wds[i]);
/* Remove entry from subdirs and wds by swapping position with the last entry */
watch->subdirs[i] = stb_sb_last(watch->subdirs);
stb_sb_pop(watch->subdirs);
watch->wds[i] = stb_sb_last(watch->wds);
stb_sb_pop(watch->wds);
if (!skip_lock)
pthread_mutex_unlock(&_dmon.mutex);
return true;
}
#endif // DMON_OS_LINUX
#endif // DMON_IMPL
#endif // __DMON_EXTRA_H__

1
lib/dmon/meson.build Normal file
View File

@ -0,0 +1 @@
lite_includes += include_directories('.')

View File

@ -22,6 +22,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
## septag/dmon
Copyright 2019 Sepehr Taghdisian. All rights reserved.
https://github.com/septag/dmon
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## Fira Sans ## Fira Sans
Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.

View File

@ -1,41 +1,18 @@
project('lite-xl', project('lite-xl',
['c'], ['c'],
version : '2.0.5', version : '2.0.3',
license : 'MIT', license : 'MIT',
meson_version : '>= 0.47', meson_version : '>= 0.54',
default_options : [ default_options : ['c_std=gnu11']
'c_std=gnu11',
'wrap_mode=nofallback'
]
) )
#===============================================================================
# Project version including git commit if possible
#===============================================================================
version = meson.project_version()
if get_option('buildtype') != 'release'
git_command = find_program('git', required : false)
if git_command.found()
git_commit = run_command(
[git_command, 'rev-parse', 'HEAD'],
check : false
).stdout().strip()
if git_commit != ''
version += ' (git-' + git_commit.substring(0, 8) + ')'
endif
endif
endif
#=============================================================================== #===============================================================================
# Configuration # Configuration
#=============================================================================== #===============================================================================
conf_data = configuration_data() conf_data = configuration_data()
conf_data.set('PROJECT_BUILD_DIR', meson.current_build_dir()) conf_data.set('PROJECT_BUILD_DIR', meson.current_build_dir())
conf_data.set('PROJECT_SOURCE_DIR', meson.current_source_dir()) conf_data.set('PROJECT_SOURCE_DIR', meson.current_source_dir())
conf_data.set('PROJECT_VERSION', version) conf_data.set('PROJECT_VERSION', meson.project_version())
#=============================================================================== #===============================================================================
# Compiler Settings # Compiler Settings
@ -47,7 +24,7 @@ endif
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
lite_includes = [] lite_includes = []
lite_cargs = ['-DSDL_MAIN_HANDLED', '-DPCRE2_STATIC'] lite_cargs = []
# On macos we need to use the SDL renderer to support retina displays # On macos we need to use the SDL renderer to support retina displays
if get_option('renderer') or host_machine.system() == 'darwin' if get_option('renderer') or host_machine.system() == 'darwin'
lite_cargs += '-DLITE_USE_SDL_RENDERER' lite_cargs += '-DLITE_USE_SDL_RENDERER'
@ -69,32 +46,14 @@ endif
if not get_option('source-only') if not get_option('source-only')
libm = cc.find_library('m', required : false) libm = cc.find_library('m', required : false)
libdl = cc.find_library('dl', required : false) libdl = cc.find_library('dl', required : false)
lua_fallback = ['lua', 'lua_dep'] threads_dep = dependency('threads')
lua_quick_fallback = [] lua_dep = dependency('lua5.2', fallback: ['lua', 'lua_dep'],
if get_option('wrap_mode') == 'forcefallback' default_options: ['shared=false', 'use_readline=false', 'app=false']
lua_quick_fallback = lua_fallback
endif
lua_dep = dependency('lua5.4', fallback: lua_quick_fallback, required : false)
if not lua_dep.found()
lua_dep = dependency('lua', fallback: ['lua', 'lua_dep'],
default_options: ['default_library=static', 'line_editing=false', 'interpreter=false']
)
endif
pcre2_dep = dependency('libpcre2-8', fallback: ['pcre2', 'libpcre2_8'],
default_options: ['default_library=static', 'grep=false', 'test=false']
) )
pcre2_dep = dependency('libpcre2-8')
freetype_dep = dependency('freetype2', fallback: ['freetype2', 'freetype_dep'], freetype_dep = dependency('freetype2')
default_options: ['default_library=static', 'zlib=disabled', 'bzip2=disabled', 'png=disabled', 'harfbuzz=disabled', 'brotli=disabled'] sdl_dep = dependency('sdl2', method: 'config-tool')
) lite_deps = [lua_dep, sdl_dep, freetype_dep, pcre2_dep, libm, libdl, threads_dep]
sdl_dep = dependency('sdl2', fallback: ['sdl2', 'sdl2_dep'],
default_options: ['default_library=static']
)
lite_deps = [lua_dep, sdl_dep, freetype_dep, pcre2_dep, libm, libdl]
endif endif
#=============================================================================== #===============================================================================
# Install Configuration # Install Configuration
@ -135,19 +94,21 @@ endif
install_data('licenses/licenses.md', install_dir : lite_docdir) install_data('licenses/licenses.md', install_dir : lite_docdir)
install_subdir('data/core' , install_dir : lite_datadir, exclude_files : 'start.lua') install_subdir('data' / 'core' , install_dir : lite_datadir, exclude_files : 'start.lua')
foreach data_module : ['fonts', 'plugins', 'colors'] foreach data_module : ['fonts', 'plugins', 'colors']
install_subdir(join_paths('data', data_module), install_dir : lite_datadir) install_subdir('data' / data_module , install_dir : lite_datadir)
endforeach endforeach
configure_file( configure_file(
input : 'data/core/start.lua', input : 'data/core/start.lua',
output : 'start.lua', output : 'start.lua',
configuration : conf_data, configuration : conf_data,
install_dir : join_paths(lite_datadir, 'core'), install : true,
install_dir : lite_datadir / 'core',
) )
if not get_option('source-only') if not get_option('source-only')
subdir('lib/dmon')
subdir('src') subdir('src')
subdir('scripts') subdir('scripts')
endif endif

View File

@ -2,4 +2,3 @@ option('bundle', type : 'boolean', value : false, description: 'Build a macOS bu
option('source-only', type : 'boolean', value : false, description: 'Configure source files only, doesn\'t checks for dependencies') option('source-only', type : 'boolean', value : false, description: 'Configure source files only, doesn\'t checks for dependencies')
option('portable', type : 'boolean', value : false, description: 'Portable install') option('portable', type : 'boolean', value : false, description: 'Portable install')
option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer') option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer')
option('dirmonitor_backend', type : 'combo', value : '', choices : ['', 'inotify', 'kqueue', 'win32', 'dummy'], description: 'define what dirmonitor backend to use')

View File

@ -17,11 +17,11 @@
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<caption>The editor window</caption> <caption>The editor window</caption>
<image>https://lite-xl.com/assets/img/editor.png</image> <image>https://lite-xl.github.io/assets/img/screenshots/editor.png</image>
</screenshot> </screenshot>
</screenshots> </screenshots>
<url type="homepage">https://lite-xl.com</url> <url type="homepage">https://lite-xl.github.io</url>
<provides> <provides>
<binary>lite-xl</binary> <binary>lite-xl</binary>

View File

@ -8,8 +8,6 @@
<string>lite-xl</string> <string>lite-xl</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>icon.icns</string> <string>icon.icns</string>
<key>CFBundleIdentifier</key>
<string>com.lite-xl</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>Lite XL</string> <string>Lite XL</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>

View File

@ -0,0 +1,54 @@
`core.set_project_dir`:
Reset project directories and set its directory.
It chdir into the directory, empty the `core.project_directories` and add
the given directory.
`core.add_project_directory`:
Add a new top-level directory to the project.
Also called from modules and commands outside core.init.
local function `scan_project_folder`:
Scan all files for a given top-level project directory.
Can emit a warning about file limit.
Called only from within core.init module.
`core.scan_project_subdir`: (before was named `core.scan_project_folder`)
scan a single folder, without recursion. Used when too many files.
Local function `scan_project_folder`:
Populate the project folder top directory. Done only once when the directory
is added to the project.
`core.add_project_directory`:
Add a new top-level folder to the project.
`core.set_project_dir`:
Set the initial project directory.
`core.dir_rescan_add_job`:
Add a job to rescan after an elapsed time a project's subdirectory to fix for any
changes.
Local function `rescan_project_subdir`:
Rescan a project's subdirectory, compare to the current version and patch the list if
a difference is found.
`core.project_scan_thread`:
Should disappear now that we use dmon.
`core.project_scan_topdir`:
New function to scan a top level project folder.
`config.project_scan_rate`:
`core.project_scan_thread_id`:
`core.reschedule_project_scan`:
`core.project_files_limit`:
A eliminer.
`core.get_project_files`:
To be fixed. Use `find_project_files_co` for a single directory
In TreeView remove usage of self.last to detect new scan that changed the files list.

View File

@ -1,7 +1,7 @@
#define MyAppName "Lite XL" #define MyAppName "Lite XL"
#define MyAppVersion "@PROJECT_VERSION@" #define MyAppVersion "@PROJECT_VERSION@"
#define MyAppPublisher "Lite XL Team" #define MyAppPublisher "Lite XL Team"
#define MyAppURL "https://lite-xl.com" #define MyAppURL "https://lite-xl.github.io"
#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@"

View File

@ -63,10 +63,10 @@ main() {
elif [[ "$OSTYPE" == "msys" ]]; then elif [[ "$OSTYPE" == "msys" ]]; then
if [[ $lhelper == true ]]; then if [[ $lhelper == true ]]; then
pacman --noconfirm -S \ pacman --noconfirm -S \
${MINGW_PACKAGE_PREFIX}-{gcc,meson,ninja,ntldd,pkg-config,mesa} unzip ${MINGW_PACKAGE_PREFIX}-{gcc,meson,ninja,ntldd,pkg-config} unzip
else else
pacman --noconfirm -S \ pacman --noconfirm -S \
${MINGW_PACKAGE_PREFIX}-{gcc,meson,ninja,ntldd,pkg-config,mesa,freetype,pcre2,SDL2} unzip ${MINGW_PACKAGE_PREFIX}-{gcc,meson,ninja,ntldd,pkg-config,freetype,pcre2,SDL2} unzip
fi fi
fi fi
} }

View File

@ -72,4 +72,4 @@ main() {
fi fi
} }
main "$@" main

View File

@ -216,11 +216,11 @@ main() {
rm -rf "Lite XL.app"; mv "${dest_dir}" "Lite XL.app" rm -rf "Lite XL.app"; mv "${dest_dir}" "Lite XL.app"
dest_dir="Lite XL.app" dest_dir="Lite XL.app"
exe_file="$(pwd)/${dest_dir}/Contents/MacOS/lite-xl" exe_file="$(pwd)/${dest_dir}/Contents/MacOS/lite-xl"
data_dir="$(pwd)/${dest_dir}/Contents/Resources"
fi fi
fi fi
if [[ $bundle == false && $portable == false ]]; then if [[ $bundle == false && $portable == false ]]; then
echo "Creating a compressed archive..."
data_dir="$(pwd)/${dest_dir}/$prefix/share/lite-xl" data_dir="$(pwd)/${dest_dir}/$prefix/share/lite-xl"
exe_file="$(pwd)/${dest_dir}/$prefix/bin/lite-xl" exe_file="$(pwd)/${dest_dir}/$prefix/bin/lite-xl"
fi fi
@ -240,7 +240,6 @@ main() {
$stripcmd "${exe_file}" $stripcmd "${exe_file}"
echo "Creating a compressed archive ${package_name}"
if [[ $binary == true ]]; then if [[ $binary == true ]]; then
rm -f "${package_name}".tar.gz rm -f "${package_name}".tar.gz
rm -f "${package_name}".zip rm -f "${package_name}".zip

View File

@ -1,21 +1,19 @@
#include "api.h" #include "api.h"
int luaopen_system(lua_State *L); int luaopen_system(lua_State *L);
int luaopen_renderer(lua_State *L); int luaopen_renderer(lua_State *L);
int luaopen_regex(lua_State *L); int luaopen_regex(lua_State *L);
// int luaopen_process(lua_State *L); // int luaopen_process(lua_State *L);
int luaopen_dirmonitor(lua_State* L);
static const luaL_Reg libs[] = { static const luaL_Reg libs[] = {
{ "system", luaopen_system }, { "system", luaopen_system },
{ "renderer", luaopen_renderer }, { "renderer", luaopen_renderer },
{ "regex", luaopen_regex }, { "regex", luaopen_regex },
// { "process", luaopen_process }, // { "process", luaopen_process },
{ "dirmonitor", luaopen_dirmonitor },
{ NULL, NULL } { NULL, NULL }
}; };
void api_load_libs(lua_State *L) { void api_load_libs(lua_State *L) {
for (int i = 0; libs[i].name; i++) for (int i = 0; libs[i].name; i++)
luaL_requiref(L, libs[i].name, libs[i].func, 1); luaL_requiref(L, libs[i].name, libs[i].func, 1);

View File

@ -7,7 +7,6 @@
#define API_TYPE_FONT "Font" #define API_TYPE_FONT "Font"
#define API_TYPE_PROCESS "Process" #define API_TYPE_PROCESS "Process"
#define API_TYPE_DIRMONITOR "Dirmonitor"
#define API_CONSTANT_DEFINE(L, idx, key, n) (lua_pushnumber(L, n), lua_setfield(L, idx - 1, key)) #define API_CONSTANT_DEFINE(L, idx, key, n) (lua_pushnumber(L, n), lua_setfield(L, idx - 1, key))

View File

@ -1,99 +0,0 @@
#include "api.h"
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#elif __linux__
#include <sys/inotify.h>
#include <limits.h>
#elif __amigaos4__
// #define DIRMONITOR_BACKEND 'os4'
#else
#include <sys/event.h>
#endif
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <stdbool.h>
#ifndef DIRMONITOR_BACKEND
#error No dirmonitor backend defined
#endif
#define GLUE_HELPER(x, y) x##y
#define GLUE(x, y) GLUE_HELPER(x, y)
#define init_dirmonitor GLUE(init_dirmonitor_, DIRMONITOR_BACKEND)
#define deinit_dirmonitor GLUE(deinit_dirmonitor_, DIRMONITOR_BACKEND)
#define check_dirmonitor GLUE(check_dirmonitor_, DIRMONITOR_BACKEND)
#define add_dirmonitor GLUE(add_dirmonitor_, DIRMONITOR_BACKEND)
#define remove_dirmonitor GLUE(remove_dirmonitor_, DIRMONITOR_BACKEND)
struct dirmonitor {}; // dirmonitor struct is defined in each backend
// define functions so we know their signature
struct dirmonitor* init_dirmonitor();
void deinit_dirmonitor(struct dirmonitor*);
int check_dirmonitor(struct dirmonitor*, int (*)(int, const char*, void*), void*);
int add_dirmonitor(struct dirmonitor*, const char*);
void remove_dirmonitor(struct dirmonitor*, int);
static int f_check_dir_callback(int watch_id, const char* path, void* L) {
lua_pushvalue(L, -1);
#ifdef DIRMONITOR_WIN32
char buffer[PATH_MAX*4];
int count = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)path, watch_id, buffer, PATH_MAX*4 - 1, NULL, NULL);
lua_pushlstring(L, buffer, count);
#else
lua_pushnumber(L, watch_id);
#endif
lua_call(L, 1, 1);
int result = lua_toboolean(L, -1);
lua_pop(L, 1);
return !result;
}
static int f_dirmonitor_new(lua_State* L) {
struct dirmonitor** monitor = lua_newuserdata(L, sizeof(struct dirmonitor**));
*monitor = init_dirmonitor();
luaL_setmetatable(L, API_TYPE_DIRMONITOR);
return 1;
}
static int f_dirmonitor_gc(lua_State* L) {
deinit_dirmonitor(*((struct dirmonitor**)luaL_checkudata(L, 1, API_TYPE_DIRMONITOR)));
return 0;
}
static int f_dirmonitor_watch(lua_State *L) {
lua_pushnumber(L, add_dirmonitor(*(struct dirmonitor**)luaL_checkudata(L, 1, API_TYPE_DIRMONITOR), luaL_checkstring(L, 2)));
return 1;
}
static int f_dirmonitor_unwatch(lua_State *L) {
remove_dirmonitor(*(struct dirmonitor**)luaL_checkudata(L, 1, API_TYPE_DIRMONITOR), luaL_checknumber(L, 2));
return 0;
}
static int f_dirmonitor_check(lua_State* L) {
lua_pushnumber(L, check_dirmonitor(*(struct dirmonitor**)luaL_checkudata(L, 1, API_TYPE_DIRMONITOR), f_check_dir_callback, L));
return 1;
}
static const luaL_Reg dirmonitor_lib[] = {
{ "new", f_dirmonitor_new },
{ "__gc", f_dirmonitor_gc },
{ "watch", f_dirmonitor_watch },
{ "unwatch", f_dirmonitor_unwatch },
{ "check", f_dirmonitor_check },
{NULL, NULL}
};
int luaopen_dirmonitor(lua_State* L) {
luaL_newmetatable(L, API_TYPE_DIRMONITOR);
luaL_setfuncs(L, dirmonitor_lib, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
return 1;
}

View File

@ -1,22 +0,0 @@
#include <stdlib.h>
struct dirmonitor {
};
struct dirmonitor* init_dirmonitor_dummy() {
return NULL;
}
void deinit_dirmonitor_dummy(struct dirmonitor* monitor) {
}
int check_dirmonitor_dummy(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
return -1;
}
int add_dirmonitor_dummy(struct dirmonitor* monitor, const char* path) {
return -1;
}
void remove_dirmonitor_dummy(struct dirmonitor* monitor, int fd) {
}

View File

@ -1,58 +0,0 @@
#include <sys/inotify.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
struct dirmonitor {
int fd;
};
struct dirmonitor* init_dirmonitor_inotify() {
struct dirmonitor* monitor = calloc(sizeof(struct dirmonitor), 1);
monitor->fd = inotify_init();
fcntl(monitor->fd, F_SETFL, O_NONBLOCK);
return monitor;
}
void deinit_dirmonitor_inotify(struct dirmonitor* monitor) {
close(monitor->fd);
free(monitor);
}
int check_dirmonitor_inotify(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
char buf[PATH_MAX + sizeof(struct inotify_event)];
ssize_t offset = 0;
while (1) {
ssize_t len = read(monitor->fd, &buf[offset], sizeof(buf) - offset);
if (len == -1 && errno != EAGAIN) {
return errno;
}
if (len <= 0) {
return 0;
}
while (len > sizeof(struct inotify_event) && len >= ((struct inotify_event*)buf)->len + sizeof(struct inotify_event)) {
change_callback(((const struct inotify_event *)buf)->wd, NULL, data);
len -= sizeof(struct inotify_event) + ((struct inotify_event*)buf)->len;
memmove(buf, &buf[sizeof(struct inotify_event) + ((struct inotify_event*)buf)->len], len);
offset = len;
}
}
}
int add_dirmonitor_inotify(struct dirmonitor* monitor, const char* path) {
return inotify_add_watch(monitor->fd, path, IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO);
}
void remove_dirmonitor_inotify(struct dirmonitor* monitor, int fd) {
inotify_rm_watch(monitor->fd, fd);
}

View File

@ -1,53 +0,0 @@
#include <sys/event.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
struct dirmonitor {
int fd;
};
struct dirmonitor* init_dirmonitor_kqueue() {
struct dirmonitor* monitor = calloc(sizeof(struct dirmonitor), 1);
monitor->fd = kqueue();
return monitor;
}
void deinit_dirmonitor_kqueue(struct dirmonitor* monitor) {
close(monitor->fd);
free(monitor);
}
int check_dirmonitor_kqueue(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
struct kevent event;
while (1) {
struct timespec tm = {0};
int nev = kevent(monitor->fd, NULL, 0, &event, 1, &tm);
if (nev == -1) {
return errno;
}
if (nev <= 0) {
return 0;
}
change_callback(event.ident, NULL, data);
}
}
int add_dirmonitor_kqueue(struct dirmonitor* monitor, const char* path) {
int fd = open(path, O_RDONLY);
struct kevent change;
EV_SET(&change, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME, 0, (void*)path);
kevent(monitor->fd, &change, 1, NULL, 0, NULL);
return fd;
}
void remove_dirmonitor_kqueue(struct dirmonitor* monitor, int fd) {
close(fd);
}

View File

@ -1,23 +0,0 @@
#include <stdlib.h>
struct dirmonitor {
};
struct dirmonitor* init_dirmonitor_os4() {
return NULL;
}
void deinit_dirmonitor_os4(struct dirmonitor* monitor) {
}
int check_dirmonitor_os4(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
return -1;
}
int add_dirmonitor_os4(struct dirmonitor* monitor, const char* path) {
return -1;
}
void remove_dirmonitor_os4(struct dirmonitor* monitor, int fd) {
}

View File

@ -1,77 +0,0 @@
#include <stdbool.h>
#include <windows.h>
struct dirmonitor {
HANDLE handle;
char buffer[8192];
OVERLAPPED overlapped;
bool running;
};
struct dirmonitor* init_dirmonitor_win32() {
struct dirmonitor* monitor = calloc(sizeof(struct dirmonitor), 1);
return monitor;
}
static void close_monitor_handle(struct dirmonitor* monitor) {
if (monitor->handle) {
if (monitor->running) {
BOOL result = CancelIoEx(monitor->handle, &monitor->overlapped);
DWORD error = GetLastError();
if (result == TRUE || error != ERROR_NOT_FOUND) {
DWORD bytes_transferred;
GetOverlappedResult( monitor->handle, &monitor->overlapped, &bytes_transferred, TRUE );
}
monitor->running = false;
}
CloseHandle(monitor->handle);
}
monitor->handle = NULL;
}
void deinit_dirmonitor_win32(struct dirmonitor* monitor) {
close_monitor_handle(monitor);
free(monitor);
}
int check_dirmonitor_win32(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
if (!monitor->running) {
if (ReadDirectoryChangesW(monitor->handle, monitor->buffer, sizeof(monitor->buffer), TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &monitor->overlapped, NULL) == 0) {
return GetLastError();
}
monitor->running = true;
}
DWORD bytes_transferred;
if (!GetOverlappedResult(monitor->handle, &monitor->overlapped, &bytes_transferred, FALSE)) {
int error = GetLastError();
return error == ERROR_IO_PENDING || error == ERROR_IO_INCOMPLETE ? 0 : error;
}
monitor->running = false;
for (FILE_NOTIFY_INFORMATION* info = (FILE_NOTIFY_INFORMATION*)monitor->buffer; (char*)info < monitor->buffer + sizeof(monitor->buffer); info = (FILE_NOTIFY_INFORMATION*)((char*)info) + info->NextEntryOffset) {
change_callback(info->FileNameLength, (char*)info->FileName, data);
if (!info->NextEntryOffset)
break;
}
monitor->running = false;
return 0;
}
int add_dirmonitor_win32(struct dirmonitor* monitor, const char* path) {
close_monitor_handle(monitor);
monitor->handle = CreateFileA(path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
if (monitor->handle && monitor->handle != INVALID_HANDLE_VALUE) {
return 1;
}
monitor->handle = NULL;
return -1;
}
void remove_dirmonitor_win32(struct dirmonitor* monitor, int fd) {
close_monitor_handle(monitor);
}

View File

@ -2,7 +2,6 @@
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <SDL.h> #include <SDL.h>
@ -60,18 +59,17 @@ typedef enum {
#ifdef _WIN32 #ifdef _WIN32
static volatile long PipeSerialNumber; static volatile long PipeSerialNumber;
static void close_fd(HANDLE* handle) { if (*handle) CloseHandle(*handle); *handle = NULL; } static void close_fd(HANDLE handle) { CloseHandle(handle); }
#else #else
static void close_fd(int* fd) { if (*fd) close(*fd); *fd = 0; } static void close_fd(int fd) { close(fd); }
#endif #endif
static bool poll_process(process_t* proc, int timeout) { static bool poll_process(process_t* proc, int timeout) {
if (!proc->running) if (!proc->running)
return false; return false;
uint32_t ticks = SDL_GetTicks(); unsigned int ticks = SDL_GetTicks();
if (timeout == WAIT_DEADLINE) if (timeout == WAIT_DEADLINE)
timeout = proc->deadline; timeout = proc->deadline;
do { do {
#ifdef _WIN32 #ifdef _WIN32
DWORD exit_code = -1; DWORD exit_code = -1;
@ -92,8 +90,13 @@ static bool poll_process(process_t* proc, int timeout) {
if (timeout) if (timeout)
SDL_Delay(5); SDL_Delay(5);
} while (timeout == WAIT_INFINITE || SDL_GetTicks() - ticks < timeout); } while (timeout == WAIT_INFINITE || SDL_GetTicks() - ticks < timeout);
if (!proc->running) {
return proc->running; close_fd(proc->child_pipes[STDIN_FD ][1]);
close_fd(proc->child_pipes[STDOUT_FD][0]);
close_fd(proc->child_pipes[STDERR_FD][0]);
return false;
}
return true;
} }
static bool signal_process(process_t* proc, signal_e sig) { static bool signal_process(process_t* proc, signal_e sig) {
@ -106,9 +109,9 @@ static bool signal_process(process_t* proc, signal_e sig) {
} }
#else #else
switch (sig) { switch (sig) {
case SIGNAL_TERM: terminate = kill(-proc->pid, SIGTERM) == 1; break; case SIGNAL_TERM: terminate = kill(proc->pid, SIGTERM) == 1; break;
case SIGNAL_KILL: terminate = kill(-proc->pid, SIGKILL) == 1; break; case SIGNAL_KILL: terminate = kill(proc->pid, SIGKILL) == 1; break;
case SIGNAL_INTERRUPT: kill(-proc->pid, SIGINT); break; case SIGNAL_INTERRUPT: kill(proc->pid, SIGINT); break;
} }
#endif #endif
if (terminate) if (terminate)
@ -118,19 +121,19 @@ static bool signal_process(process_t* proc, signal_e sig) {
static int process_start(lua_State* L) { static int process_start(lua_State* L) {
size_t env_len = 0, key_len, val_len; size_t env_len = 0, key_len, val_len;
const char *cmd[256], *env_names[256] = { NULL }, *env_values[256] = { NULL }, *cwd = NULL; const char *cmd[256], *env[256] = { NULL }, *cwd = NULL;
bool detach = false; bool detach = false;
int deadline = 10, new_fds[3] = { STDIN_FD, STDOUT_FD, STDERR_FD }; int deadline = 10, new_fds[3] = { STDIN_FD, STDOUT_FD, STDERR_FD };
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
#if LUA_VERSION_NUM > 501 #if LUA_VERSION_NUM > 501
lua_len(L, 1); lua_len(L, 1);
#else #else
lua_pushinteger(L, (int)lua_objlen(L, 1)); lua_pushnumber(L, (int)lua_objlen(L, 1));
#endif #endif
size_t cmd_len = luaL_checknumber(L, -1); lua_pop(L, 1); size_t cmd_len = luaL_checknumber(L, -1); lua_pop(L, 1);
size_t arg_len = lua_gettop(L); size_t arg_len = lua_gettop(L);
for (size_t i = 1; i <= cmd_len; ++i) { for (size_t i = 1; i <= cmd_len; ++i) {
lua_pushinteger(L, i); lua_pushnumber(L, i);
lua_rawget(L, 1); lua_rawget(L, 1);
cmd[i-1] = luaL_checkstring(L, -1); cmd[i-1] = luaL_checkstring(L, -1);
} }
@ -142,12 +145,9 @@ static int process_start(lua_State* L) {
while (lua_next(L, -2) != 0) { while (lua_next(L, -2) != 0) {
const char* key = luaL_checklstring(L, -2, &key_len); const char* key = luaL_checklstring(L, -2, &key_len);
const char* val = luaL_checklstring(L, -1, &val_len); const char* val = luaL_checklstring(L, -1, &val_len);
env_names[env_len] = malloc(key_len+1); env[env_len] = malloc(key_len+val_len+2);
strcpy((char*)env_names[env_len], key); snprintf((char*)env[env_len++], key_len+val_len+2, "%s=%s", key, val);
env_values[env_len] = malloc(val_len+1);
strcpy((char*)env_values[env_len], val);
lua_pop(L, 1); lua_pop(L, 1);
++env_len;
} }
} else } else
lua_pop(L, 1); lua_pop(L, 1);
@ -162,6 +162,7 @@ static int process_start(lua_State* L) {
return luaL_error(L, "redirect to handles, FILE* and paths are not supported"); return luaL_error(L, "redirect to handles, FILE* and paths are not supported");
} }
} }
env[env_len] = NULL;
process_t* self = lua_newuserdata(L, sizeof(process_t)); process_t* self = lua_newuserdata(L, sizeof(process_t));
memset(self, 0, sizeof(process_t)); memset(self, 0, sizeof(process_t));
@ -216,28 +217,26 @@ static int process_start(lua_State* L) {
siStartInfo.hStdInput = self->child_pipes[STDIN_FD][0]; siStartInfo.hStdInput = self->child_pipes[STDIN_FD][0];
siStartInfo.hStdOutput = self->child_pipes[STDOUT_FD][1]; siStartInfo.hStdOutput = self->child_pipes[STDOUT_FD][1];
siStartInfo.hStdError = self->child_pipes[STDERR_FD][1]; siStartInfo.hStdError = self->child_pipes[STDERR_FD][1];
char commandLine[32767] = { 0 }, environmentBlock[32767], wideEnvironmentBlock[32767*2]; char commandLine[32767] = { 0 }, environmentBlock[32767];
strcpy(commandLine, cmd[0]);
int offset = 0; int offset = 0;
strcpy(commandLine, cmd[0]);
for (size_t i = 1; i < cmd_len; ++i) { for (size_t i = 1; i < cmd_len; ++i) {
size_t len = strlen(cmd[i]); size_t len = strlen(cmd[i]);
offset += len + 1; if (offset + len + 1 >= sizeof(commandLine))
if (offset >= sizeof(commandLine))
break; break;
strcat(commandLine, " "); strcat(commandLine, " ");
strcat(commandLine, cmd[i]); strcat(commandLine, cmd[i]);
} }
offset = 0;
for (size_t i = 0; i < env_len; ++i) { for (size_t i = 0; i < env_len; ++i) {
if (offset + strlen(env_values[i]) + strlen(env_names[i]) + 1 >= sizeof(environmentBlock)) size_t len = strlen(env[i]);
if (offset + len >= sizeof(environmentBlock))
break; break;
offset += snprintf(&environmentBlock[offset], sizeof(environmentBlock) - offset, "%s=%s", env_names[i], env_values[i]); memcpy(&environmentBlock[offset], env[i], len);
offset += len;
environmentBlock[offset++] = 0; environmentBlock[offset++] = 0;
} }
environmentBlock[offset++] = 0; environmentBlock[offset++] = 0;
if (env_len > 0) if (!CreateProcess(NULL, commandLine, NULL, NULL, true, (detach ? DETACHED_PROCESS : CREATE_NO_WINDOW) | CREATE_UNICODE_ENVIRONMENT, env_len > 0 ? environmentBlock : NULL, cwd, &siStartInfo, &self->process_information))
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, environmentBlock, offset, (LPWSTR)wideEnvironmentBlock, sizeof(wideEnvironmentBlock));
if (!CreateProcess(NULL, commandLine, NULL, NULL, true, (detach ? DETACHED_PROCESS : CREATE_NO_WINDOW) | CREATE_UNICODE_ENVIRONMENT, env_len > 0 ? wideEnvironmentBlock : NULL, cwd, &siStartInfo, &self->process_information))
return luaL_error(L, "Error creating a process: %d.", GetLastError()); return luaL_error(L, "Error creating a process: %d.", GetLastError());
self->pid = (long)self->process_information.dwProcessId; self->pid = (long)self->process_information.dwProcessId;
if (detach) if (detach)
@ -256,7 +255,6 @@ static int process_start(lua_State* L) {
} }
return luaL_error(L, "Error running fork: %s.", strerror(errno)); return luaL_error(L, "Error running fork: %s.", strerror(errno));
} else if (!self->pid) { } else if (!self->pid) {
setpgrp();
for (int stream = 0; stream < 3; ++stream) { for (int stream = 0; stream < 3; ++stream) {
if (new_fds[stream] == REDIRECT_DISCARD) { // Close the stream if we don't want it. if (new_fds[stream] == REDIRECT_DISCARD) { // Close the stream if we don't want it.
close(self->child_pipes[stream][stream == STDIN_FD ? 0 : 1]); close(self->child_pipes[stream][stream == STDIN_FD ? 0 : 1]);
@ -265,21 +263,17 @@ static int process_start(lua_State* L) {
dup2(self->child_pipes[new_fds[stream]][new_fds[stream] == STDIN_FD ? 0 : 1], stream); dup2(self->child_pipes[new_fds[stream]][new_fds[stream] == STDIN_FD ? 0 : 1], stream);
close(self->child_pipes[stream][stream == STDIN_FD ? 1 : 0]); close(self->child_pipes[stream][stream == STDIN_FD ? 1 : 0]);
} }
int set; if ((!detach || setsid() != -1) && (!cwd || chdir(cwd) != -1))
for (set = 0; set < env_len && setenv(env_names[set], env_values[set], 1) == 0; ++set); execvp((const char*)cmd[0], (char* const*)cmd);
if (set == env_len && (!detach || setsid() != -1) && (!cwd || chdir(cwd) != -1))
execvp((const char*)cmd[0], (char* const*)cmd);
const char* msg = strerror(errno); const char* msg = strerror(errno);
int result = write(STDERR_FD, msg, strlen(msg)+1); int result = write(STDERR_FD, msg, strlen(msg)+1);
_exit(result == strlen(msg)+1 ? -1 : -2); exit(result == strlen(msg)+1 ? -1 : -2);
} }
#endif #endif
for (size_t i = 0; i < env_len; ++i) { for (size_t i = 0; i < env_len; ++i)
free((char*)env_names[i]); free((char*)env[i]);
free((char*)env_values[i]);
}
for (int stream = 0; stream < 3; ++stream) for (int stream = 0; stream < 3; ++stream)
close_fd(&self->child_pipes[stream][stream == STDIN_FD ? 0 : 1]); close_fd(self->child_pipes[stream][stream == STDIN_FD ? 0 : 1]);
self->running = true; self->running = true;
return 1; return 1;
} }
@ -312,7 +306,7 @@ static int g_read(lua_State* L, int stream, unsigned long read_size) {
#else #else
luaL_Buffer b; luaL_Buffer b;
luaL_buffinit(L, &b); luaL_buffinit(L, &b);
uint8_t* buffer = (uint8_t*)luaL_prepbuffsize(&b, READ_BUF_SIZE); uint8_t* buffer = (uint8_t*)luaL_prepbuffer(&b);
length = read(self->child_pipes[stream][0], buffer, read_size > READ_BUF_SIZE ? READ_BUF_SIZE : read_size); length = read(self->child_pipes[stream][0], buffer, read_size > READ_BUF_SIZE ? READ_BUF_SIZE : read_size);
if (length == 0 && !poll_process(self, WAIT_NONE)) if (length == 0 && !poll_process(self, WAIT_NONE))
return 0; return 0;
@ -336,9 +330,8 @@ static int f_write(lua_State* L) {
#if _WIN32 #if _WIN32
DWORD dwWritten; DWORD dwWritten;
if (!WriteFile(self->child_pipes[STDIN_FD][1], data, data_size, &dwWritten, NULL)) { if (!WriteFile(self->child_pipes[STDIN_FD][1], data, data_size, &dwWritten, NULL)) {
int lastError = GetLastError();
signal_process(self, SIGNAL_TERM); signal_process(self, SIGNAL_TERM);
return luaL_error(L, "error writing to process: %d", lastError); return luaL_error(L, "error writing to process: %d", GetLastError());
} }
length = dwWritten; length = dwWritten;
#else #else
@ -346,19 +339,18 @@ static int f_write(lua_State* L) {
if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
length = 0; length = 0;
else if (length < 0) { else if (length < 0) {
const char* lastError = strerror(errno);
signal_process(self, SIGNAL_TERM); signal_process(self, SIGNAL_TERM);
return luaL_error(L, "error writing to process: %s", lastError); return luaL_error(L, "error writing to process: %s", strerror(errno));
} }
#endif #endif
lua_pushinteger(L, length); lua_pushnumber(L, length);
return 1; return 1;
} }
static int f_close_stream(lua_State* L) { static int f_close_stream(lua_State* L) {
process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS);
int stream = luaL_checknumber(L, 2); int stream = luaL_checknumber(L, 2);
close_fd(&self->child_pipes[stream][stream == STDIN_FD ? 1 : 0]); close_fd(self->child_pipes[stream][stream == STDIN_FD ? 1 : 0]);
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
return 1; return 1;
} }
@ -383,7 +375,7 @@ static int f_tostring(lua_State* L) {
static int f_pid(lua_State* L) { static int f_pid(lua_State* L) {
process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS);
lua_pushinteger(L, self->pid); lua_pushnumber(L, self->pid);
return 1; return 1;
} }
@ -391,7 +383,7 @@ static int f_returncode(lua_State *L) {
process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS); process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS);
if (self->running) if (self->running)
return 0; return 0;
lua_pushinteger(L, self->returncode); lua_pushnumber(L, self->returncode);
return 1; return 1;
} }
@ -412,7 +404,7 @@ static int f_wait(lua_State* L) {
int timeout = luaL_optnumber(L, 2, 0); int timeout = luaL_optnumber(L, 2, 0);
if (poll_process(self, timeout)) if (poll_process(self, timeout))
return 0; return 0;
lua_pushinteger(L, self->returncode); lua_pushnumber(L, self->returncode);
return 1; return 1;
} }
@ -425,19 +417,11 @@ static int self_signal(lua_State* L, signal_e sig) {
static int f_terminate(lua_State* L) { return self_signal(L, SIGNAL_TERM); } static int f_terminate(lua_State* L) { return self_signal(L, SIGNAL_TERM); }
static int f_kill(lua_State* L) { return self_signal(L, SIGNAL_KILL); } static int f_kill(lua_State* L) { return self_signal(L, SIGNAL_KILL); }
static int f_interrupt(lua_State* L) { return self_signal(L, SIGNAL_INTERRUPT); } static int f_interrupt(lua_State* L) { return self_signal(L, SIGNAL_INTERRUPT); }
static int f_gc(lua_State* L) { static int f_gc(lua_State* L) { return self_signal(L, SIGNAL_TERM); }
process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS);
signal_process(self, SIGNAL_TERM);
close_fd(&self->child_pipes[STDIN_FD ][1]);
close_fd(&self->child_pipes[STDOUT_FD][0]);
close_fd(&self->child_pipes[STDERR_FD][0]);
poll_process(self, 10);
return 0;
}
static int f_running(lua_State* L) { static int f_running(lua_State* L) {
process_t* self = (process_t*)luaL_checkudata(L, 1, API_TYPE_PROCESS); process_t* self = (process_t*)luaL_checkudata(L, 1, API_TYPE_PROCESS);
lua_pushboolean(L, poll_process(self, WAIT_NONE)); lua_pushboolean(L, self->running);
return 1; return 1;
} }

View File

@ -60,14 +60,12 @@ static int f_pcre_match(lua_State *L) {
const char* str = luaL_checklstring(L, 2, &len); const char* str = luaL_checklstring(L, 2, &len);
if (lua_gettop(L) > 2) if (lua_gettop(L) > 2)
offset = luaL_checknumber(L, 3); offset = luaL_checknumber(L, 3);
offset -= 1;
len -= offset;
if (lua_gettop(L) > 3) if (lua_gettop(L) > 3)
opts = luaL_checknumber(L, 4); opts = luaL_checknumber(L, 4);
lua_rawgeti(L, 1, 1); lua_rawgeti(L, 1, 1);
pcre2_code* re = (pcre2_code*)lua_touserdata(L, -1); pcre2_code* re = (pcre2_code*)lua_touserdata(L, -1);
pcre2_match_data* md = pcre2_match_data_create_from_pattern(re, NULL); pcre2_match_data* md = pcre2_match_data_create_from_pattern(re, NULL);
int rc = pcre2_match(re, (PCRE2_SPTR)&str[offset], len, 0, opts, md, NULL); int rc = pcre2_match(re, (PCRE2_SPTR)str, len, offset - 1, opts, md, NULL);
if (rc < 0) { if (rc < 0) {
pcre2_match_data_free(md); pcre2_match_data_free(md);
if (rc != PCRE2_ERROR_NOMATCH) { if (rc != PCRE2_ERROR_NOMATCH) {
@ -88,7 +86,7 @@ static int f_pcre_match(lua_State *L) {
return 0; return 0;
} }
for (int i = 0; i < rc*2; i++) for (int i = 0; i < rc*2; i++)
lua_pushnumber(L, ovector[i]+offset+1); lua_pushnumber(L, ovector[i]+1);
pcre2_match_data_free(md); pcre2_match_data_free(md);
return rc*2; return rc*2;
} }
@ -106,17 +104,17 @@ int luaopen_regex(lua_State *L) {
lua_setfield(L, -2, "__name"); lua_setfield(L, -2, "__name");
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "regex"); lua_setfield(L, LUA_REGISTRYINDEX, "regex");
lua_pushinteger(L, PCRE2_ANCHORED); lua_pushnumber(L, PCRE2_ANCHORED);
lua_setfield(L, -2, "ANCHORED"); lua_setfield(L, -2, "ANCHORED");
lua_pushinteger(L, PCRE2_ANCHORED) ; lua_pushnumber(L, PCRE2_ANCHORED) ;
lua_setfield(L, -2, "ENDANCHORED"); lua_setfield(L, -2, "ENDANCHORED");
lua_pushinteger(L, PCRE2_NOTBOL); lua_pushnumber(L, PCRE2_NOTBOL);
lua_setfield(L, -2, "NOTBOL"); lua_setfield(L, -2, "NOTBOL");
lua_pushinteger(L, PCRE2_NOTEOL); lua_pushnumber(L, PCRE2_NOTEOL);
lua_setfield(L, -2, "NOTEOL"); lua_setfield(L, -2, "NOTEOL");
lua_pushinteger(L, PCRE2_NOTEMPTY); lua_pushnumber(L, PCRE2_NOTEMPTY);
lua_setfield(L, -2, "NOTEMPTY"); lua_setfield(L, -2, "NOTEMPTY");
lua_pushinteger(L, PCRE2_NOTEMPTY_ATSTART); lua_pushnumber(L, PCRE2_NOTEMPTY_ATSTART);
lua_setfield(L, -2, "NOTEMPTY_ATSTART"); lua_setfield(L, -2, "NOTEMPTY_ATSTART");
return 1; return 1;
} }

View File

@ -1,6 +1,6 @@
#include "api.h" #include "api.h"
#include "../renderer.h" #include "renderer.h"
#include "../rencache.h" #include "rencache.h"
static int f_font_load(lua_State *L) { static int f_font_load(lua_State *L) {
const char *filename = luaL_checkstring(L, 1); const char *filename = luaL_checkstring(L, 1);
@ -92,7 +92,7 @@ static int f_font_copy(lua_State *L) {
return 1; return 1;
} }
static int f_font_group(lua_State* L) { static int f_font_group(lua_State* L) {
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
luaL_setmetatable(L, API_TYPE_FONT); luaL_setmetatable(L, API_TYPE_FONT);
return 1; return 1;
@ -106,10 +106,8 @@ static int f_font_set_tab_size(lua_State *L) {
} }
static int f_font_gc(lua_State *L) { static int f_font_gc(lua_State *L) {
if (lua_istable(L, 1)) return 0; // do not run if its FontGroup
RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT);
ren_font_free(*self); ren_font_free(*self);
return 0; return 0;
} }
@ -251,3 +249,4 @@ int luaopen_renderer(lua_State *L) {
lua_setfield(L, -2, "font"); lua_setfield(L, -2, "font");
return 1; return 1;
} }

View File

@ -1,5 +1,4 @@
#include <SDL.h> #include <SDL.h>
#include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <ctype.h> #include <ctype.h>
#include <dirent.h> #include <dirent.h>
@ -7,6 +6,7 @@
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "api.h" #include "api.h"
// #include "dirmonitor.h"
#include "rencache.h" #include "rencache.h"
#ifdef _WIN32 #ifdef _WIN32
#include <direct.h> #include <direct.h>
@ -101,6 +101,8 @@ static const char *numpad[] = { "end", "down", "pagedown", "left", "", "right",
static const char *get_key_name(const SDL_Event *e, char *buf) { static const char *get_key_name(const SDL_Event *e, char *buf) {
SDL_Scancode scancode = e->key.keysym.scancode; SDL_Scancode scancode = e->key.keysym.scancode;
#if !defined(__amigaos4__) && !defined(__MORPHOS__)
/* Is the scancode from the keypad and the number-lock off? /* Is the scancode from the keypad and the number-lock off?
** We assume that SDL_SCANCODE_KP_1 up to SDL_SCANCODE_KP_9 and SDL_SCANCODE_KP_0 ** We assume that SDL_SCANCODE_KP_1 up to SDL_SCANCODE_KP_9 and SDL_SCANCODE_KP_0
** and SDL_SCANCODE_KP_PERIOD are declared in SDL2 in that order. */ ** and SDL_SCANCODE_KP_PERIOD are declared in SDL2 in that order. */
@ -108,23 +110,23 @@ static const char *get_key_name(const SDL_Event *e, char *buf) {
!(e->key.keysym.mod & KMOD_NUM)) { !(e->key.keysym.mod & KMOD_NUM)) {
return numpad[scancode - SDL_SCANCODE_KP_1]; return numpad[scancode - SDL_SCANCODE_KP_1];
} else { } else {
#endif
/* We need to correctly handle non-standard layouts such as dvorak. /* We need to correctly handle non-standard layouts such as dvorak.
Therefore, if a Latin letter(code<128) is pressed in the current layout, Therefore, if a Latin letter(code<128) is pressed in the current layout,
then we transmit it as it is. But we also need to support shortcuts in then we transmit it as it is. But we also need to support shortcuts in
other languages, so for non-Latin characters(code>128) we pass the other languages, so for non-Latin characters we pass the scancode that
scancode based name that matches the letter in the QWERTY layout. matches the letter in the QWERTY layout. */
if (e->key.keysym.sym < 128)
In SDL, the codes of all special buttons such as control, shift, arrows
and others, are masked with SDLK_SCANCODE_MASK, which moves them outside
the unicode range (>0x10FFFF). Users can remap these buttons, so we need
to return the correct name, not scancode based. */
if ((e->key.keysym.sym < 128) || (e->key.keysym.sym & SDLK_SCANCODE_MASK))
strcpy(buf, SDL_GetKeyName(e->key.keysym.sym)); strcpy(buf, SDL_GetKeyName(e->key.keysym.sym));
else else
strcpy(buf, SDL_GetScancodeName(scancode)); strcpy(buf, SDL_GetScancodeName(scancode));
str_tolower(buf); str_tolower(buf);
return buf; return buf;
#if !defined(__amigaos4__) && !defined(__MORPHOS__)
} }
#endif
} }
static int f_poll_event(lua_State *L) { static int f_poll_event(lua_State *L) {
@ -147,8 +149,8 @@ top:
ren_resize_window(); ren_resize_window();
lua_pushstring(L, "resized"); lua_pushstring(L, "resized");
/* The size below will be in points. */ /* The size below will be in points. */
lua_pushinteger(L, e.window.data1); lua_pushnumber(L, e.window.data1);
lua_pushinteger(L, e.window.data2); lua_pushnumber(L, e.window.data2);
return 3; return 3;
} else if (e.window.event == SDL_WINDOWEVENT_EXPOSED) { } else if (e.window.event == SDL_WINDOWEVENT_EXPOSED) {
rencache_invalidate(); rencache_invalidate();
@ -181,8 +183,8 @@ top:
SDL_GetWindowPosition(window, &wx, &wy); SDL_GetWindowPosition(window, &wx, &wy);
lua_pushstring(L, "filedropped"); lua_pushstring(L, "filedropped");
lua_pushstring(L, e.drop.file); lua_pushstring(L, e.drop.file);
lua_pushinteger(L, mx - wx); lua_pushnumber(L, mx - wx);
lua_pushinteger(L, my - wy); lua_pushnumber(L, my - wy);
SDL_free(e.drop.file); SDL_free(e.drop.file);
return 4; return 4;
@ -222,17 +224,17 @@ top:
if (e.button.button == 1) { SDL_CaptureMouse(1); } if (e.button.button == 1) { SDL_CaptureMouse(1); }
lua_pushstring(L, "mousepressed"); lua_pushstring(L, "mousepressed");
lua_pushstring(L, button_name(e.button.button)); lua_pushstring(L, button_name(e.button.button));
lua_pushinteger(L, e.button.x); lua_pushnumber(L, e.button.x);
lua_pushinteger(L, e.button.y); lua_pushnumber(L, e.button.y);
lua_pushinteger(L, e.button.clicks); lua_pushnumber(L, e.button.clicks);
return 5; return 5;
case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONUP:
if (e.button.button == 1) { SDL_CaptureMouse(0); } if (e.button.button == 1) { SDL_CaptureMouse(0); }
lua_pushstring(L, "mousereleased"); lua_pushstring(L, "mousereleased");
lua_pushstring(L, button_name(e.button.button)); lua_pushstring(L, button_name(e.button.button));
lua_pushinteger(L, e.button.x); lua_pushnumber(L, e.button.x);
lua_pushinteger(L, e.button.y); lua_pushnumber(L, e.button.y);
return 4; return 4;
case SDL_MOUSEMOTION: case SDL_MOUSEMOTION:
@ -245,17 +247,37 @@ top:
e.motion.yrel += event_plus.motion.yrel; e.motion.yrel += event_plus.motion.yrel;
} }
lua_pushstring(L, "mousemoved"); lua_pushstring(L, "mousemoved");
lua_pushinteger(L, e.motion.x); lua_pushnumber(L, e.motion.x);
lua_pushinteger(L, e.motion.y); lua_pushnumber(L, e.motion.y);
lua_pushinteger(L, e.motion.xrel); lua_pushnumber(L, e.motion.xrel);
lua_pushinteger(L, e.motion.yrel); lua_pushnumber(L, e.motion.yrel);
return 5; return 5;
case SDL_MOUSEWHEEL: case SDL_MOUSEWHEEL:
lua_pushstring(L, "mousewheel"); lua_pushstring(L, "mousewheel");
lua_pushinteger(L, e.wheel.y); lua_pushnumber(L, e.wheel.y);
return 2; return 2;
case SDL_USEREVENT:
lua_pushstring(L, "dirchange");
lua_pushnumber(L, e.user.code >> 16);
// switch (e.user.code & 0xffff) {
// case DMON_ACTION_DELETE:
// lua_pushstring(L, "delete");
// break;
// case DMON_ACTION_CREATE:
// lua_pushstring(L, "create");
// break;
// case DMON_ACTION_MODIFY:
// lua_pushstring(L, "modify");
// break;
// default:
// return luaL_error(L, "unknown dmon event action: %d", e.user.code & 0xffff);
// }
lua_pushstring(L, e.user.data1);
free(e.user.data1);
return 4;
default: default:
goto top; goto top;
} }
@ -322,10 +344,10 @@ static int f_set_window_mode(lua_State *L) {
int n = luaL_checkoption(L, 1, "normal", window_opts); int n = luaL_checkoption(L, 1, "normal", window_opts);
SDL_SetWindowFullscreen(window, SDL_SetWindowFullscreen(window,
n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
if (n == WIN_NORMAL) if (n == WIN_NORMAL)
{ {
ren_resize_window(); ren_resize_window();
SDL_RestoreWindow(window); SDL_RestoreWindow(window);
} }
if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window); } if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window); }
if (n == WIN_MINIMIZED) { SDL_MinimizeWindow(window); } if (n == WIN_MINIMIZED) { SDL_MinimizeWindow(window); }
@ -357,10 +379,10 @@ static int f_get_window_size(lua_State *L) {
int x, y, w, h; int x, y, w, h;
SDL_GetWindowSize(window, &w, &h); SDL_GetWindowSize(window, &w, &h);
SDL_GetWindowPosition(window, &x, &y); SDL_GetWindowPosition(window, &x, &y);
lua_pushinteger(L, w); lua_pushnumber(L, w);
lua_pushinteger(L, h); lua_pushnumber(L, h);
lua_pushinteger(L, x); lua_pushnumber(L, x);
lua_pushinteger(L, y); lua_pushnumber(L, y);
return 4; return 4;
} }
@ -507,6 +529,7 @@ static int f_list_dir(lua_State *L) {
#include <windows.h> #include <windows.h>
#define realpath(x, y) _fullpath(y, x, MAX_PATH) #define realpath(x, y) _fullpath(y, x, MAX_PATH)
#endif #endif
#ifdef __amigaos4__ #ifdef __amigaos4__
#define realpath(x, y) _fullpath(x) #define realpath(x, y) _fullpath(x)
#endif #endif
@ -533,10 +556,10 @@ static int f_get_file_info(lua_State *L) {
} }
lua_newtable(L); lua_newtable(L);
lua_pushinteger(L, s.st_mtime); lua_pushnumber(L, s.st_mtime);
lua_setfield(L, -2, "modified"); lua_setfield(L, -2, "modified");
lua_pushinteger(L, s.st_size); lua_pushnumber(L, s.st_size);
lua_setfield(L, -2, "size"); lua_setfield(L, -2, "size");
if (S_ISREG(s.st_mode)) { if (S_ISREG(s.st_mode)) {
@ -548,14 +571,6 @@ static int f_get_file_info(lua_State *L) {
} }
lua_setfield(L, -2, "type"); lua_setfield(L, -2, "type");
#if __linux__
if (S_ISDIR(s.st_mode)) {
if (lstat(path, &s) == 0) {
lua_pushboolean(L, S_ISLNK(s.st_mode));
lua_setfield(L, -2, "symlink");
}
}
#endif
return 1; return 1;
} }
@ -580,26 +595,23 @@ static struct f_type_names fs_names[] = {
{ 0x0, NULL }, { 0x0, NULL },
}; };
#endif
static int f_get_fs_type(lua_State *L) { static int f_get_fs_type(lua_State *L) {
#if __linux__ const char *path = luaL_checkstring(L, 1);
const char *path = luaL_checkstring(L, 1); struct statfs buf;
struct statfs buf; int status = statfs(path, &buf);
int status = statfs(path, &buf); if (status != 0) {
if (status != 0) { return luaL_error(L, "error calling statfs on %s", path);
return luaL_error(L, "error calling statfs on %s", path); }
for (int i = 0; fs_names[i].magic; i++) {
if (fs_names[i].magic == buf.f_type) {
lua_pushstring(L, fs_names[i].name);
return 1;
} }
for (int i = 0; fs_names[i].magic; i++) { }
if (fs_names[i].magic == buf.f_type) {
lua_pushstring(L, fs_names[i].name);
return 1;
}
}
#endif
lua_pushstring(L, "unknown"); lua_pushstring(L, "unknown");
return 1; return 1;
} }
#endif
static int f_mkdir(lua_State *L) { static int f_mkdir(lua_State *L) {
@ -637,16 +649,6 @@ static int f_set_clipboard(lua_State *L) {
} }
static int f_get_process_id(lua_State *L) {
#ifdef _WIN32
lua_pushinteger(L, GetCurrentProcessId());
#else
lua_pushinteger(L, getpid());
#endif
return 1;
}
static int f_get_time(lua_State *L) { static int f_get_time(lua_State *L) {
double n = SDL_GetPerformanceCounter() / (double) SDL_GetPerformanceFrequency(); double n = SDL_GetPerformanceCounter() / (double) SDL_GetPerformanceFrequency();
lua_pushnumber(L, n); lua_pushnumber(L, n);
@ -703,7 +705,7 @@ static int f_fuzzy_match(lua_State *L) {
strTarget += increment; strTarget += increment;
} }
if (ptnTarget >= ptn && *ptnTarget) { return 0; } if (ptnTarget >= ptn && *ptnTarget) { return 0; }
lua_pushinteger(L, score - (int)strLen * 10); lua_pushnumber(L, score - (int)strLen * 10);
return 1; return 1;
} }
@ -728,13 +730,13 @@ static void* api_require(const char* symbol) {
P(error), P(gc), P(getallocf), P(getfield), P(error), P(gc), P(getallocf), P(getfield),
P(gethook), P(gethookcount), P(gethookmask), P(getinfo), P(getlocal), P(gethook), P(gethookcount), P(gethookmask), P(getinfo), P(getlocal),
P(getmetatable), P(getstack), P(gettable), P(gettop), P(getupvalue), P(getmetatable), P(getstack), P(gettable), P(gettop), P(getupvalue),
P(isnumber), P(isstring), P(isuserdata), P(insert), P(isnumber), P(isstring), P(isuserdata),
P(load), P(newstate), P(newthread), P(next), P(load), P(newstate), P(newthread), P(newuserdata), P(next),
P(pushboolean), P(pushcclosure), P(pushfstring), P(pushinteger), P(pushboolean), P(pushcclosure), P(pushfstring), P(pushinteger),
P(pushlightuserdata), P(pushlstring), P(pushnil), P(pushnumber), P(pushlightuserdata), P(pushlstring), P(pushnil), P(pushnumber),
P(pushstring), P(pushthread), P(pushvalue), P(pushstring), P(pushthread), P(pushvalue),
P(pushvfstring), P(rawequal), P(rawget), P(rawgeti), P(pushvfstring), P(rawequal), P(rawget), P(rawgeti),
P(rawset), P(rawseti), P(resume), P(rawset), P(rawseti), P(remove), P(replace), P(resume),
P(setallocf), P(setfield), P(sethook), P(setlocal), P(setallocf), P(setfield), P(sethook), P(setlocal),
P(setmetatable), P(settable), P(settop), P(setupvalue), P(setmetatable), P(settable), P(settop), P(setupvalue),
P(status), P(tocfunction), P(tointegerx), P(tolstring), P(toboolean), P(status), P(tocfunction), P(tointegerx), P(tolstring), P(toboolean),
@ -747,12 +749,12 @@ static void* api_require(const char* symbol) {
U(newstate), U(setfuncs), U(buffinit), U(addlstring), U(addstring), U(newstate), U(setfuncs), U(buffinit), U(addlstring), U(addstring),
U(addvalue), U(pushresult), U(addvalue), U(pushresult),
#if LUA_VERSION_NUM >= 502 #if LUA_VERSION_NUM >= 502
P(absindex), P(arith), P(callk), P(compare), P(getglobal), P(absindex), P(arith), P(callk), P(compare), P(getctx), P(getglobal), P(getuservalue),
P(len), P(pcallk), P(rawgetp), P(rawlen), P(rawsetp), P(setglobal), P(len), P(pcallk), P(pushunsigned), P(rawgetp), P(rawlen), P(rawsetp), P(setglobal),
P(iscfunction), P(yieldk), P(iscfunction), P(setuservalue), P(tounsignedx), P(yieldk),
U(checkversion_), U(tolstring), U(len), U(getsubtable), U(prepbuffsize), U(checkversion_), U(tolstring), U(checkunsigned), U(len), U(getsubtable), U(prepbuffsize),
U(pushresultsize), U(buffinitsize), U(checklstring), U(checkoption), U(gsub), U(loadbufferx), U(pushresultsize), U(buffinitsize), U(checklstring), U(checkoption), U(gsub), U(loadbufferx),
U(loadfilex), U(optinteger), U(optlstring), U(requiref), U(traceback) U(loadfilex), U(optinteger), U(optlstring), U(optunsigned), U(requiref), U(traceback)
#else #else
P(objlen) P(objlen)
#endif #endif
@ -801,6 +803,35 @@ static int f_load_native_plugin(lua_State *L) {
return result; return result;
} }
static int f_watch_dir(lua_State *L) {
const char *path = luaL_checkstring(L, 1);
const int recursive = lua_toboolean(L, 2);
// uint32_t dmon_flags = (recursive ? DMON_WATCHFLAGS_RECURSIVE : 0);
// dmon_watch_id watch_id = dmon_watch(path, dirmonitor_watch_callback, dmon_flags, NULL);
// if (watch_id.id == 0) { luaL_error(L, "directory monitoring watch failed"); }
// lua_pushnumber(L, watch_id.id);
lua_pushnumber(L, 0);
return 1;
}
#if __linux__
static int f_watch_dir_add(lua_State *L) {
// dmon_watch_id watch_id;
// watch_id.id = luaL_checkinteger(L, 1);
// const char *subdir = luaL_checkstring(L, 2);
// lua_pushboolean(L, dmon_watch_add(watch_id, subdir));
return 1;
}
static int f_watch_dir_rm(lua_State *L) {
// dmon_watch_id watch_id;
// watch_id.id = luaL_checkinteger(L, 1);
// const char *subdir = luaL_checkstring(L, 2);
// lua_pushboolean(L, dmon_watch_rm(watch_id, subdir));
return 1;
}
#endif
#ifdef _WIN32 #ifdef _WIN32
#define PATHSEP '\\' #define PATHSEP '\\'
#else #else
@ -880,15 +911,19 @@ static const luaL_Reg lib[] = {
{ "get_file_info", f_get_file_info }, { "get_file_info", f_get_file_info },
{ "get_clipboard", f_get_clipboard }, { "get_clipboard", f_get_clipboard },
{ "set_clipboard", f_set_clipboard }, { "set_clipboard", f_set_clipboard },
{ "get_process_id", f_get_process_id },
{ "get_time", f_get_time }, { "get_time", f_get_time },
{ "sleep", f_sleep }, { "sleep", f_sleep },
{ "exec", f_exec }, { "exec", f_exec },
{ "fuzzy_match", f_fuzzy_match }, { "fuzzy_match", f_fuzzy_match },
{ "set_window_opacity", f_set_window_opacity }, { "set_window_opacity", f_set_window_opacity },
{ "load_native_plugin", f_load_native_plugin }, { "load_native_plugin", f_load_native_plugin },
{ "watch_dir", f_watch_dir },
{ "path_compare", f_path_compare }, { "path_compare", f_path_compare },
#if __linux__
{ "watch_dir_add", f_watch_dir_add },
{ "watch_dir_rm", f_watch_dir_rm },
{ "get_fs_type", f_get_fs_type }, { "get_fs_type", f_get_fs_type },
#endif
{ NULL, NULL } { NULL, NULL }
}; };
@ -897,3 +932,4 @@ int luaopen_system(lua_State *L) {
luaL_newlib(L, lib); luaL_newlib(L, lib);
return 1; return 1;
} }

61
src/dirmonitor.c Normal file
View File

@ -0,0 +1,61 @@
#include <stdio.h>
#include <string.h>
#include <SDL.h>
#ifndef __amigaos4__
#define DMON_IMPL
#include "dmon.h"
#include "dmon_extra.h"
#endif
#include "dirmonitor.h"
static void send_sdl_event(dmon_watch_id watch_id, dmon_action action, const char *filepath) {
SDL_Event ev;
const int size = strlen(filepath) + 1;
/* The string allocated below should be deallocated as soon as the event is
treated in the SDL main loop. */
char *new_filepath = malloc(size);
if (!new_filepath) return;
memcpy(new_filepath, filepath, size);
#ifdef _WIN32
for (int i = 0; i < size; i++) {
if (new_filepath[i] == '/') {
new_filepath[i] = '\\';
}
}
#endif
SDL_zero(ev);
ev.type = SDL_USEREVENT;
ev.user.code = ((watch_id.id & 0xffff) << 16) | (action & 0xffff);
ev.user.data1 = new_filepath;
SDL_PushEvent(&ev);
}
void dirmonitor_init() {
//dmon_init();
/* In theory we should register our user event but since we
have just one type of user event this is not really needed. */
/* sdl_dmon_event_type = SDL_RegisterEvents(1); */
}
void dirmonitor_deinit() {
//dmon_deinit();
}
void dirmonitor_watch_callback(dmon_watch_id watch_id, dmon_action action, const char *rootdir,
const char *filepath, const char *oldfilepath, void *user)
{
(void) rootdir;
(void) user;
switch (action) {
case DMON_ACTION_MOVE:
//send_sdl_event(watch_id, DMON_ACTION_DELETE, oldfilepath);
//send_sdl_event(watch_id, DMON_ACTION_CREATE, filepath);
break;
//default:
//send_sdl_event(watch_id, action, filepath);
}
}

15
src/dirmonitor.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef DIRMONITOR_H
#define DIRMONITOR_H
#include <stdint.h>
#include "dmon.h"
#include "dmon_extra.h"
void dirmonitor_init();
void dirmonitor_deinit();
void dirmonitor_watch_callback(dmon_watch_id watch_id, dmon_action action, const char *rootdir,
const char *filepath, const char *oldfilepath, void *user);
#endif

View File

@ -7,7 +7,7 @@
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#elif __linux__ || __FreeBSD__ #elif __linux__
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
#elif __APPLE__ #elif __APPLE__
@ -16,6 +16,8 @@
#include "platform/amigaos4.h" #include "platform/amigaos4.h"
#endif #endif
#include "dirmonitor.h"
SDL_Window *window; SDL_Window *window;
@ -110,10 +112,13 @@ int main(int argc, char **argv) {
SDL_DisplayMode dm; SDL_DisplayMode dm;
SDL_GetCurrentDisplayMode(0, &dm); SDL_GetCurrentDisplayMode(0, &dm);
// dirmonitor_init();
window = SDL_CreateWindow( window = SDL_CreateWindow(
"", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8, "", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8,
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN); SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
SDL_SetWindowDisplayMode(window, &dm); SDL_SetWindowDisplayMode(window, &dm);
init_window_icon(); init_window_icon();
ren_init(window); ren_init(window);
@ -196,6 +201,8 @@ init_lua:
lua_close(L); lua_close(L);
ren_free_window_resources(); ren_free_window_resources();
// dirmonitor_deinit();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -4,47 +4,13 @@ lite_sources = [
'api/regex.c', 'api/regex.c',
'api/system.c', 'api/system.c',
'api/process.c', 'api/process.c',
'dirmonitor.c',
'renderer.c', 'renderer.c',
'renwindow.c', 'renwindow.c',
'rencache.c', 'rencache.c',
'main.c', 'main.c',
] ]
# dirmonitor backend
if get_option('dirmonitor_backend') == ''
if cc.has_function('inotify_init', prefix : '#include<sys/inotify.h>')
dirmonitor_backend = 'inotify'
elif cc.has_function('kqueue', prefix : '#include<sys/event.h>')
dirmonitor_backend = 'kqueue'
elif dependency('libkqueue', required : false).found()
dirmonitor_backend = 'kqueue'
elif host_machine.system() == 'windows'
dirmonitor_backend = 'win32'
else
dirmonitor_backend = 'dummy'
warning('no suitable backend found, defaulting to dummy backend')
endif
else
dirmonitor_backend = get_option('dirmonitor_backend')
endif
message('dirmonitor_backend: @0@'.format(dirmonitor_backend))
if dirmonitor_backend == 'kqueue'
libkqueue_dep = dependency('libkqueue', required : false)
if libkqueue_dep.found()
lite_deps += libkqueue_dep
endif
endif
lite_sources += [
'api/dirmonitor.c',
'api/dirmonitor/' + dirmonitor_backend + '.c',
]
lite_cargs += '-DDIRMONITOR_BACKEND=' + dirmonitor_backend
lite_cargs += '-DDIRMONITOR_' + dirmonitor_backend.to_upper()
lite_rc = [] lite_rc = []
if host_machine.system() == 'windows' if host_machine.system() == 'windows'
windows = import('windows') windows = import('windows')

View File

@ -4,7 +4,7 @@
#include <proto/dos.h> #include <proto/dos.h>
#include <proto/exec.h> #include <proto/exec.h>
#define VSTRING "Lite XL OS4 2.1beta (27.03.2022)" #define VSTRING "Lite XL OS4 2.0.3r1 (30.04.2022)"
#define VERSTAG "\0$VER: " VSTRING #define VERSTAG "\0$VER: " VSTRING
static CONST_STRPTR stack USED = "$STACK:102400"; static CONST_STRPTR stack USED = "$STACK:102400";
@ -14,3 +14,4 @@ char *_fullpath(const char *);
#endif #endif

View File

@ -95,7 +95,7 @@ static Command* push_command(int type, int size) {
return NULL; return NULL;
} }
command_buf_idx = n; command_buf_idx = n;
memset(cmd, 0, size); memset(cmd, 0, COMMAND_BARE_SIZE);
cmd->type = type; cmd->type = type;
cmd->size = size; cmd->size = size;
return cmd; return cmd;

View File

@ -9,9 +9,10 @@ void rencache_show_debug(bool enable);
void rencache_set_clip_rect(RenRect rect); void rencache_set_clip_rect(RenRect rect);
void rencache_draw_rect(RenRect rect, RenColor color); void rencache_draw_rect(RenRect rect, RenColor color);
float rencache_draw_text(lua_State *L, RenFont **font, float rencache_draw_text(lua_State *L, RenFont **font,
const char *text, float x, int y, RenColor color); const char *text, float x, int y, RenColor color);
void rencache_invalidate(void); void rencache_invalidate(void);
void rencache_begin_frame(lua_State *L); void rencache_begin_frame(lua_State *L);
void rencache_end_frame(lua_State *L); void rencache_end_frame(lua_State *L);
#endif #endif

View File

@ -36,14 +36,14 @@ typedef struct {
typedef struct { typedef struct {
SDL_Surface* surface; SDL_Surface* surface;
GlyphMetric metrics[MAX_GLYPHSET]; GlyphMetric metrics[MAX_GLYPHSET];
} GlyphSet; } GlyphSet;
typedef struct RenFont { typedef struct RenFont {
FT_Face face; FT_Face face;
GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][MAX_LOADABLE_GLYPHSETS]; GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][MAX_LOADABLE_GLYPHSETS];
float size, space_advance, tab_advance; float size, space_advance, tab_advance;
short max_height, baseline, height; short max_height;
ERenFontAntialiasing antialiasing; ERenFontAntialiasing antialiasing;
ERenFontHinting hinting; ERenFontHinting hinting;
unsigned char style; unsigned char style;
@ -67,7 +67,7 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
} }
static int font_set_load_options(RenFont* font) { static int font_set_load_options(RenFont* font) {
int load_target = font->antialiasing == FONT_ANTIALIASING_NONE ? FT_LOAD_TARGET_MONO int load_target = font->antialiasing == FONT_ANTIALIASING_NONE ? FT_LOAD_TARGET_MONO
: (font->hinting == FONT_HINTING_SLIGHT ? FT_LOAD_TARGET_LIGHT : FT_LOAD_TARGET_NORMAL); : (font->hinting == FONT_HINTING_SLIGHT ? FT_LOAD_TARGET_LIGHT : FT_LOAD_TARGET_NORMAL);
int hinting = font->hinting == FONT_HINTING_NONE ? FT_LOAD_NO_HINTING : FT_LOAD_FORCE_AUTOHINT; int hinting = font->hinting == FONT_HINTING_NONE ? FT_LOAD_NO_HINTING : FT_LOAD_FORCE_AUTOHINT;
return load_target | hinting; return load_target | hinting;
@ -114,10 +114,8 @@ static void font_load_glyphset(RenFont* font, int idx) {
font->sets[j][idx] = set; font->sets[j][idx] = set;
for (int i = 0; i < MAX_GLYPHSET; ++i) { for (int i = 0; i < MAX_GLYPHSET; ++i) {
int glyph_index = FT_Get_Char_Index(font->face, i + idx * MAX_GLYPHSET); int glyph_index = FT_Get_Char_Index(font->face, i + idx * MAX_GLYPHSET);
if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option | FT_LOAD_BITMAP_METRICS_ONLY) if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option | FT_LOAD_BITMAP_METRICS_ONLY) || font_set_style(&font->face->glyph->outline, j * (64 / SUBPIXEL_BITMAPS_CACHED), font->style) || FT_Render_Glyph(font->face->glyph, render_option))
|| font_set_style(&font->face->glyph->outline, j * (64 / SUBPIXEL_BITMAPS_CACHED), font->style) || FT_Render_Glyph(font->face->glyph, render_option)) {
continue; continue;
}
FT_GlyphSlot slot = font->face->glyph; FT_GlyphSlot slot = font->face->glyph;
int glyph_width = slot->bitmap.width / byte_width; int glyph_width = slot->bitmap.width / byte_width;
if (font->antialiasing == FONT_ANTIALIASING_NONE) if (font->antialiasing == FONT_ANTIALIASING_NONE)
@ -125,13 +123,6 @@ static void font_load_glyphset(RenFont* font, int idx) {
set->metrics[i] = (GlyphMetric){ pen_x, pen_x + glyph_width, 0, slot->bitmap.rows, true, slot->bitmap_left, slot->bitmap_top, (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f}; set->metrics[i] = (GlyphMetric){ pen_x, pen_x + glyph_width, 0, slot->bitmap.rows, true, slot->bitmap_left, slot->bitmap_top, (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f};
pen_x += glyph_width; pen_x += glyph_width;
font->max_height = slot->bitmap.rows > font->max_height ? slot->bitmap.rows : font->max_height; font->max_height = slot->bitmap.rows > font->max_height ? slot->bitmap.rows : font->max_height;
// In order to fix issues with monospacing; we need the unhinted xadvance; as FreeType doesn't correctly report the hinted advance for spaces on monospace fonts (like RobotoMono). See #843.
if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, (load_option | FT_LOAD_BITMAP_METRICS_ONLY | FT_LOAD_NO_HINTING) & ~FT_LOAD_FORCE_AUTOHINT)
|| font_set_style(&font->face->glyph->outline, j * (64 / SUBPIXEL_BITMAPS_CACHED), font->style) || FT_Render_Glyph(font->face->glyph, render_option)) {
continue;
}
slot = font->face->glyph;
set->metrics[i].xadvance = slot->advance.x / 64.0f;
} }
if (pen_x == 0) if (pen_x == 0)
continue; continue;
@ -144,7 +135,7 @@ static void font_load_glyphset(RenFont* font, int idx) {
FT_GlyphSlot slot = font->face->glyph; FT_GlyphSlot slot = font->face->glyph;
font_set_style(&slot->outline, (64 / bitmaps_cached) * j, font->style); font_set_style(&slot->outline, (64 / bitmaps_cached) * j, font->style);
if (FT_Render_Glyph(slot, render_option)) if (FT_Render_Glyph(slot, render_option))
continue; continue;
for (int line = 0; line < slot->bitmap.rows; ++line) { for (int line = 0; line < slot->bitmap.rows; ++line) {
int target_offset = set->surface->pitch * line + set->metrics[i].x0 * byte_width; int target_offset = set->surface->pitch * line + set->metrics[i].x0 * byte_width;
int source_offset = line * slot->bitmap.pitch; int source_offset = line * slot->bitmap.pitch;
@ -194,15 +185,13 @@ RenFont* ren_font_load(const char* path, float size, ERenFontAntialiasing antial
strcpy(font->path, path); strcpy(font->path, path);
font->face = face; font->face = face;
font->size = size; font->size = size;
font->height = (short)((face->height / (float)face->units_per_EM) * font->size);
font->baseline = (short)((face->bbox.yMax / (float)face->units_per_EM) * font->size);
font->antialiasing = antialiasing; font->antialiasing = antialiasing;
font->hinting = hinting; font->hinting = hinting;
font->style = style; font->style = style;
font->space_advance = font_get_glyphset(font, ' ', 0)->metrics[' '].xadvance; font->space_advance = (int)font_get_glyphset(font, ' ', 0)->metrics[' '].xadvance;
font->tab_advance = font->space_advance * 2; font->tab_advance = font->space_advance * 2;
return font; return font;
failure: failure:
FT_Done_Face(face); FT_Done_Face(face);
return NULL; return NULL;
} }
@ -227,7 +216,7 @@ void ren_font_free(RenFont* font) {
void ren_font_group_set_tab_size(RenFont **fonts, int n) { void ren_font_group_set_tab_size(RenFont **fonts, int n) {
for (int j = 0; j < FONT_FALLBACK_MAX && fonts[j]; ++j) { for (int j = 0; j < FONT_FALLBACK_MAX && fonts[j]; ++j) {
for (int i = 0; i < (fonts[j]->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? SUBPIXEL_BITMAPS_CACHED : 1); ++i) for (int i = 0; i < (fonts[j]->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? SUBPIXEL_BITMAPS_CACHED : 1); ++i)
font_get_glyphset(fonts[j], '\t', i)->metrics['\t'].xadvance = fonts[j]->space_advance * n; font_get_glyphset(fonts[j], '\t', i)->metrics['\t'].xadvance = fonts[j]->space_advance * n;
} }
} }
@ -240,7 +229,7 @@ float ren_font_group_get_size(RenFont **fonts) {
return fonts[0]->size; return fonts[0]->size;
} }
int ren_font_group_get_height(RenFont **fonts) { int ren_font_group_get_height(RenFont **fonts) {
return fonts[0]->height; return fonts[0]->size + 3;
} }
float ren_font_group_get_width(RenFont **fonts, const char *text) { float ren_font_group_get_width(RenFont **fonts, const char *text) {
@ -250,8 +239,8 @@ float ren_font_group_get_width(RenFont **fonts, const char *text) {
while (text < end) { while (text < end) {
unsigned int codepoint; unsigned int codepoint;
text = utf8_to_codepoint(text, &codepoint); text = utf8_to_codepoint(text, &codepoint);
RenFont* font = font_group_get_glyph(&set, &metric, fonts, codepoint, 0); font_group_get_glyph(&set, &metric, fonts, codepoint, 0);
width += (!font || metric->xadvance) ? metric->xadvance : fonts[0]->space_advance; width += metric->xadvance ? metric->xadvance : fonts[0]->space_advance;
} }
const int surface_scale = renwin_surface_scale(&window_renderer); const int surface_scale = renwin_surface_scale(&window_renderer);
return width / surface_scale; return width / surface_scale;
@ -268,11 +257,11 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor
const char* end = text + strlen(text); const char* end = text + strlen(text);
unsigned char* destination_pixels = surface->pixels; unsigned char* destination_pixels = surface->pixels;
int clip_end_x = clip.x + clip.width, clip_end_y = clip.y + clip.height; int clip_end_x = clip.x + clip.width, clip_end_y = clip.y + clip.height;
while (text < end) { while (text < end) {
unsigned int codepoint, r, g, b; unsigned int codepoint, r, g, b;
text = utf8_to_codepoint(text, &codepoint); text = utf8_to_codepoint(text, &codepoint);
GlyphSet* set = NULL; GlyphMetric* metric = NULL; GlyphSet* set = NULL; GlyphMetric* metric = NULL;
RenFont* font = font_group_get_glyph(&set, &metric, fonts, codepoint, (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED)); RenFont* font = font_group_get_glyph(&set, &metric, fonts, codepoint, (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED));
int start_x = floor(pen_x) + metric->bitmap_left; int start_x = floor(pen_x) + metric->bitmap_left;
int end_x = (metric->x1 - metric->x0) + start_x; int end_x = (metric->x1 - metric->x0) + start_x;
@ -282,14 +271,14 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor
if (set->surface && color.a > 0 && end_x >= clip.x && start_x < clip_end_x) { if (set->surface && color.a > 0 && end_x >= clip.x && start_x < clip_end_x) {
unsigned char* source_pixels = set->surface->pixels; unsigned char* source_pixels = set->surface->pixels;
for (int line = metric->y0; line < metric->y1; ++line) { for (int line = metric->y0; line < metric->y1; ++line) {
int target_y = line + y - metric->bitmap_top + font->baseline * surface_scale; int target_y = line + y - metric->y0 - metric->bitmap_top + font->size * surface_scale;
if (target_y < clip.y) if (target_y < clip.y)
continue; continue;
if (target_y >= clip_end_y) if (target_y >= clip_end_y)
break; break;
if (start_x + (glyph_end - glyph_start) >= clip_end_x) if (start_x + (glyph_end - glyph_start) >= clip_end_x)
glyph_end = glyph_start + (clip_end_x - start_x); glyph_end = glyph_start + (clip_end_x - start_x);
if (start_x < clip.x) { else if (start_x < clip.x) {
int offset = clip.x - start_x; int offset = clip.x - start_x;
start_x += offset; start_x += offset;
glyph_start += offset; glyph_start += offset;
@ -344,6 +333,7 @@ void ren_draw_rect(RenRect rect, RenColor color) {
SDL_Surface *surface = renwin_get_surface(&window_renderer); SDL_Surface *surface = renwin_get_surface(&window_renderer);
uint32_t *d = surface->pixels; uint32_t *d = surface->pixels;
#ifdef __amigaos4__ #ifdef __amigaos4__
d += x1 + y1 * surface->pitch/sizeof(uint32_t); d += x1 + y1 * surface->pitch/sizeof(uint32_t);
int dr = surface->pitch/sizeof(uint32_t) - (x2 - x1); int dr = surface->pitch/sizeof(uint32_t) - (x2 - x1);
@ -351,7 +341,6 @@ void ren_draw_rect(RenRect rect, RenColor color) {
d += x1 + y1 * surface->w; d += x1 + y1 * surface->w;
int dr = surface->w - (x2 - x1); int dr = surface->w - (x2 - x1);
#endif #endif
if (color.a == 0xff) { if (color.a == 0xff) {
uint32_t translated = SDL_MapRGB(surface->format, color.r, color.g, color.b); uint32_t translated = SDL_MapRGB(surface->format, color.r, color.g, color.b);
SDL_Rect rect = { x1, y1, x2 - x1, y2 - y1 }; SDL_Rect rect = { x1, y1, x2 - x1, y2 - y1 };
@ -360,8 +349,7 @@ void ren_draw_rect(RenRect rect, RenColor color) {
RenColor current_color; RenColor current_color;
RenColor blended_color; RenColor blended_color;
for (int j = y1; j < y2; j++) { for (int j = y1; j < y2; j++) {
for (int i = x1; i < x2; i++, d++) for (int i = x1; i < x2; i++, d++) {
{
SDL_GetRGB(*d, surface->format, &current_color.r, &current_color.g, &current_color.b); SDL_GetRGB(*d, surface->format, &current_color.r, &current_color.g, &current_color.b);
blended_color = blend_pixel(current_color, color); blended_color = blend_pixel(current_color, color);
*d = SDL_MapRGB(surface->format, blended_color.r, blended_color.g, blended_color.b); *d = SDL_MapRGB(surface->format, blended_color.r, blended_color.g, blended_color.b);

View File

@ -9,7 +9,7 @@ static int query_surface_scale(RenWindow *ren) {
SDL_GetWindowSize(ren->window, &w_points, &h_points); SDL_GetWindowSize(ren->window, &w_points, &h_points);
/* We consider that the ratio pixel/point will always be an integer and /* We consider that the ratio pixel/point will always be an integer and
it is the same along the x and the y axis. */ it is the same along the x and the y axis. */
#ifdef __amigaos4__ #ifdef __amigaos4__
// This is a workaround when the w_pixels != w_points and h_pixels != h_points // This is a workaround when the w_pixels != w_points and h_pixels != h_points
// because of redraw delays, especially when the "Resize with contents" is enabled // because of redraw delays, especially when the "Resize with contents" is enabled
@ -20,7 +20,7 @@ static int query_surface_scale(RenWindow *ren) {
h_pixels = h_points; h_pixels = h_points;
} }
#endif #endif
assert(w_pixels % w_points == 0 && h_pixels % h_points == 0 && w_pixels / w_points == h_pixels / h_points); assert(w_pixels % w_points == 0 && h_pixels % h_points == 0 && w_pixels / w_points == h_pixels / h_points);
return w_pixels / w_points; return w_pixels / w_points;
} }
@ -128,3 +128,4 @@ void renwin_free(RenWindow *ren) {
SDL_FreeSurface(ren->surface); SDL_FreeSurface(ren->surface);
#endif #endif
} }

View File

@ -1,9 +0,0 @@
[wrap-file]
directory = freetype-2.11.1
source_url = https://download.savannah.gnu.org/releases/freetype/freetype-2.11.1.tar.gz
source_filename = freetype-2.11.1.tar.gz
source_hash = f8db94d307e9c54961b39a1cc799a67d46681480696ed72ecf78d4473770f09b
[provide]
freetype2 = freetype_dep

View File

@ -1,12 +1,4 @@
[wrap-file] [wrap-git]
directory = lua-5.4.3 directory = lua
source_url = https://www.lua.org/ftp/lua-5.4.3.tar.gz url = https://github.com/franko/lua
source_filename = lua-5.4.3.tar.gz revision = v5.2.4-7
source_hash = f8612276169e3bfcbcfb8f226195bfc6e466fe13042f1076cbde92b7ec96bbfb
patch_filename = lua_5.4.3-2_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.3-2/get_patch
patch_hash = 3c23ec14a3f000d80fe2e2fdddba63a56e13c758d74195daa4ff0da7bfdb02da
[provide]
lua-5.4 = lua_dep

View File

@ -1,15 +0,0 @@
[wrap-file]
directory = pcre2-10.39
source_url = https://github.com/PhilipHazel/pcre2/releases/download/pcre2-10.39/pcre2-10.39.tar.bz2
source_filename = pcre2-10.39.tar.bz2
source_hash = 0f03caf57f81d9ff362ac28cd389c055ec2bf0678d277349a1a4bee00ad6d440
patch_filename = pcre2_10.39-2_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/pcre2_10.39-2/get_patch
patch_hash = c4cfffff83e7bb239c8c330339b08f4367b019f79bf810f10c415e35fb09cf14
[provide]
libpcre2-8 = -libpcre2_8
libpcre2-16 = -libpcre2_16
libpcre2-32 = -libpcre2_32
libpcre2-posix = -libpcre2_posix

View File

@ -1,12 +0,0 @@
[wrap-file]
directory = SDL2-2.0.18
source_url = https://www.libsdl.org/release/SDL2-2.0.18.tar.gz
source_filename = SDL2-2.0.18.tar.gz
source_hash = 94d40cd73dbfa10bb6eadfbc28f355992bb2d6ef6761ad9d4074eff95ee5711c
patch_filename = sdl2_2.0.18-2_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/sdl2_2.0.18-2/get_patch
patch_hash = cd77f33395d3d019bb89217b9da41fc640ed8c78cbbbebc5c662155a25e2820e
[provide]
sdl2 = sdl2_dep