Compare commits
12 Commits
Author | SHA1 | Date |
---|---|---|
George Sokianos | ff3e5282f6 | |
George Sokianos | 89e2defb5b | |
George Sokianos | 1e90105944 | |
George Sokianos | f2e42ca2fa | |
George Sokianos | decd5deeae | |
George Sokianos | b5831ace20 | |
George Sokianos | 8fe3c75339 | |
George Sokianos | 9b599aaa78 | |
George Sokianos | ddb81648c2 | |
George Sokianos | b919e5b942 | |
George Sokianos | 05cf40c1c9 | |
George Sokianos | ad75b7521f |
|
@ -33,6 +33,3 @@
|
||||||
|
|
||||||
"Category: C Core":
|
"Category: C Core":
|
||||||
- src/**/*
|
- src/**/*
|
||||||
|
|
||||||
"Category: Libraries":
|
|
||||||
- lib/**/*
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
45
Makefile.os4
45
Makefile.os4
|
@ -5,20 +5,17 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
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
|
||||||
|
@ -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/
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,7 +1,23 @@
|
||||||
# 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.
|
||||||
|
@ -49,16 +65,20 @@ 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**
|
||||||
|
@ -103,6 +123,10 @@ 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
|
||||||
|
@ -117,14 +141,17 @@ 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
|
||||||
|
@ -136,34 +163,42 @@ 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
|
||||||
|
- 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
|
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.
|
||||||
|
|
||||||
|
|
118
changelog.md
118
changelog.md
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
end
|
core.root_view:open_doc(core.open_doc(filename))
|
||||||
local doc = core.open_doc(".lite_project.lua")
|
else
|
||||||
|
local doc = core.open_doc()
|
||||||
core.root_view:open_doc(doc)
|
core.root_view:open_doc(doc)
|
||||||
doc:save()
|
doc:save(filename)
|
||||||
|
end
|
||||||
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,
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
||||||
core.cursor_clipboard_whole_line[idx] = true
|
|
||||||
if delete then
|
|
||||||
if line1 < #doc().lines then
|
|
||||||
doc():remove(line1, 1, line1 + 1, 1)
|
|
||||||
else
|
else
|
||||||
doc():remove(line1 - 1, math.huge, line1, math.huge)
|
doc().cursor_clipboard[idx] = ""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
doc().cursor_clipboard["full"] = full_text
|
||||||
core.cursor_clipboard[idx] = text
|
|
||||||
end
|
|
||||||
core.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,29 +111,13 @@ 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]
|
|
||||||
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)
|
doc():text_input(value:gsub("\r", ""), idx)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["doc:newline"] = function()
|
["doc:newline"] = function()
|
||||||
|
@ -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,7 +388,7 @@ 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)
|
||||||
|
@ -521,18 +415,7 @@ local commands = {
|
||||||
|
|
||||||
["doc:split-cursor"] = function(x, y, clicks)
|
["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
|
|
||||||
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)
|
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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
||||||
})
|
|
|
@ -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,
|
|
||||||
})
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
||||||
if hcl ~= false then
|
|
||||||
for lidx, line1, col1, line2, col2 in self.doc:get_selections(false) do
|
for lidx, line1, col1, line2, col2 in self.doc:get_selections(false) do
|
||||||
if line1 == line then
|
if line1 == idx then
|
||||||
if hcl == "no_selection" then
|
|
||||||
if (line1 ~= line2) or (col1 ~= col2) then
|
|
||||||
draw_highlight = false
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
draw_highlight = true
|
draw_highlight = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
if draw_highlight and config.highlight_current_line and core.active_view == self then
|
||||||
if draw_highlight 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()
|
||||||
|
|
|
@ -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
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
system.set_clipboard(core.get_log(selected))
|
|
||||||
core.status_view:show_message("i", style.text, "copied entry #"..index.." to clipboard.")
|
|
||||||
else
|
|
||||||
self:expand_item(selected)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
function LogView:on_mouse_pressed(button, mx, my, clicks)
|
||||||
|
if LogView.super.on_mouse_pressed(self, button, mx, my, clicks) then return end
|
||||||
|
if self.hovered_item then
|
||||||
|
self:expand_item(self.hovered_item)
|
||||||
|
end
|
||||||
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
|
||||||
|
|
||||||
|
|
|
@ -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.visible = true
|
|
||||||
self.title = opts.title
|
self.title = opts.title
|
||||||
self.message = opts.message and opts.message .. "\n"
|
self.message = opts.message and opts.message .. "\n"
|
||||||
self.options = opts.options
|
self.options = opts.options
|
||||||
self.on_selected = opts.on_selected
|
self.on_selected = opts.on_selected
|
||||||
|
if self.message and self.options then
|
||||||
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.")
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
@ -48,8 +44,7 @@ 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
|
|
||||||
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)
|
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
|
|
||||||
local inserted = false
|
|
||||||
if item.onselect then
|
|
||||||
inserted = item.onselect(suggestions_idx, item)
|
|
||||||
end
|
|
||||||
if not inserted then
|
|
||||||
doc:insert(line, col, text)
|
doc:insert(line, col, text)
|
||||||
doc:remove(line, col, line, col - #partial)
|
doc:remove(line, col, line, col - #partial)
|
||||||
doc:set_selection(line, col + #text - #partial)
|
doc:set_selection(line, col + #text - #partial)
|
||||||
end
|
|
||||||
reset_suggestions()
|
reset_suggestions()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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", {
|
||||||
|
|
|
@ -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
|
||||||
return false
|
end
|
||||||
|
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
|
end
|
||||||
if score > 0 then
|
table.sort(bins, function(a, b) return a[2] > b[2] end)
|
||||||
break
|
return bins[1][1], bins[1][2]
|
||||||
end
|
|
||||||
end
|
|
||||||
return best_score > 0 and best_indent or nil, best_score
|
|
||||||
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
|
||||||
|
i = i + 2
|
||||||
end
|
end
|
||||||
if not found then
|
|
||||||
escaped = escaped .. token:sub(x, x)
|
|
||||||
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
|
|
||||||
for c=1, #comments do
|
|
||||||
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
|
i = i + 1
|
||||||
coroutine.yield(i, line)
|
coroutine.yield(i, line_start)
|
||||||
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
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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,7 +10,6 @@ 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" },
|
||||||
|
@ -17,45 +18,20 @@ syntax.add {
|
||||||
{ 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" },
|
|
||||||
-- 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" },
|
|
||||||
-- Somehow makes macros properly work
|
|
||||||
{ pattern = "#[%a_][%w_]*", type = "normal" },
|
|
||||||
-- Everything else to make the tokenizer work properly
|
|
||||||
{ pattern = "[%a_][%w_]*", type = "symbol" },
|
{ pattern = "[%a_][%w_]*", type = "symbol" },
|
||||||
|
{ pattern = "#include%s()<.->", type = {"keyword", "string"} },
|
||||||
|
{ pattern = "#[%a_][%w_]*", type = "keyword" },
|
||||||
},
|
},
|
||||||
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",
|
||||||
|
@ -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",
|
||||||
|
@ -154,3 +120,4 @@ syntax.add {
|
||||||
["#pragma"] = "keyword",
|
["#pragma"] = "keyword",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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" },
|
||||||
|
|
|
@ -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 = "HTML",
|
name = "HTML",
|
||||||
files = { "%.html?$" },
|
files = { "%.html?$" },
|
||||||
block_comment = { "<!--", "-->" },
|
|
||||||
patterns = {
|
patterns = {
|
||||||
{
|
{
|
||||||
pattern = {
|
pattern = {
|
||||||
|
|
|
@ -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" },
|
||||||
|
|
|
@ -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" },
|
||||||
|
|
|
@ -1,104 +1,25 @@
|
||||||
-- 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
|
|
||||||
-- 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" },
|
{ 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" },
|
||||||
|
@ -111,6 +32,7 @@ syntax.add {
|
||||||
{ 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 = { "```php", "```" }, type = "string", syntax = ".php" },
|
||||||
{ pattern = { "```nim", "```" }, type = "string", syntax = ".nim" },
|
{ pattern = { "```nim", "```" }, type = "string", syntax = ".nim" },
|
||||||
{ pattern = { "```typescript", "```" }, type = "string", syntax = ".ts" },
|
{ pattern = { "```typescript", "```" }, type = "string", syntax = ".ts" },
|
||||||
{ pattern = { "```rescript", "```" }, type = "string", syntax = ".res" },
|
{ pattern = { "```rescript", "```" }, type = "string", syntax = ".res" },
|
||||||
|
@ -119,105 +41,16 @@ syntax.add {
|
||||||
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
|
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
|
||||||
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
|
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
|
||||||
{ pattern = { "```", "```" }, type = "string" },
|
{ pattern = { "```", "```" }, type = "string" },
|
||||||
{ pattern = { "``", "``" }, type = "string" },
|
{ pattern = { "``", "``", "\\" }, type = "string" },
|
||||||
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" },
|
{ pattern = { "`", "`", "\\" }, type = "string" },
|
||||||
-- strike
|
{ pattern = { "~~", "~~", "\\" }, type = "keyword2" },
|
||||||
{ pattern = { "~~", "~~" }, type = "keyword2" },
|
{ pattern = "%-%-%-+", type = "comment" },
|
||||||
-- highlight
|
{ pattern = "%*%s+", type = "operator" },
|
||||||
{ pattern = { "==", "==" }, type = "literal" },
|
{ pattern = { "%*", "[%*\n]", "\\" }, type = "operator" },
|
||||||
-- lines
|
{ pattern = { "%_", "[%_\n]", "\\" }, type = "keyword2" },
|
||||||
{ pattern = "^%-%-%-+\n", type = "comment" },
|
{ pattern = "#.-\n", type = "keyword" },
|
||||||
{ pattern = "^%*%*%*+\n", type = "comment" },
|
{ pattern = "!?%[.-%]%(.-%)", type = "function" },
|
||||||
{ pattern = "^___+\n", type = "comment" },
|
{ pattern = "https?://%S+", type = "function" },
|
||||||
-- bullets
|
|
||||||
{ 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)
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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" },
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -262,7 +249,6 @@ command.add(nil, {
|
||||||
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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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})
|
||||||
|
|
||||||
|
|
||||||
|
command.add(function() return view.hovered_item ~= nil end, {
|
||||||
|
["treeview:rename"] = function()
|
||||||
|
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
|
||||||
|
core.error("Error while renaming \"%s\" to \"%s\": %s", old_abs_filename, abs_filename, err)
|
||||||
|
end
|
||||||
|
end, common.path_suggest)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["treeview:toggle-focus"] = function()
|
["treeview:new-file"] = function()
|
||||||
if not core.active_view:is(TreeView) then
|
if not is_project_folder(view.hovered_item.abs_filename) then
|
||||||
if core.active_view:is(CommandView) then
|
core.command_view:set_text(view.hovered_item.filename .. "/")
|
||||||
previous_view = core.last_active_view
|
|
||||||
else
|
|
||||||
previous_view = core.active_view
|
|
||||||
end
|
end
|
||||||
if not previous_view then
|
core.command_view:enter("Filename", function(filename)
|
||||||
previous_view = core.root_view:get_primary_node().active_view
|
local doc_filename = core.project_dir .. PATHSEP .. filename
|
||||||
end
|
local file = io.open(doc_filename, "a+")
|
||||||
core.set_active_view(view)
|
file:write("")
|
||||||
if not view.selected_item then
|
file:close()
|
||||||
for it, _, y in view:each_item() do
|
|
||||||
view:set_selection(it, y)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
core.set_active_view(
|
|
||||||
previous_view or core.root_view:get_primary_node().active_view
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
command.add(TreeView, {
|
|
||||||
["treeview:next"] = function()
|
|
||||||
local item = view.selected_item
|
|
||||||
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
|
|
||||||
|
|
||||||
view:set_selection(item, item_y)
|
|
||||||
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))
|
core.root_view:open_doc(core.open_doc(doc_filename))
|
||||||
end)
|
core.log("Created %s", doc_filename)
|
||||||
end
|
end, common.path_suggest)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["treeview:deselect"] = function()
|
["treeview:new-folder"] = function()
|
||||||
view.selected_item = nil
|
if not is_project_folder(view.hovered_item.abs_filename) then
|
||||||
|
core.command_view:set_text(view.hovered_item.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: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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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__
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
lite_includes += include_directories('.')
|
|
@ -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.
|
||||||
|
|
73
meson.build
73
meson.build
|
@ -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')
|
||||||
|
freetype_dep = dependency('freetype2')
|
||||||
pcre2_dep = dependency('libpcre2-8', fallback: ['pcre2', 'libpcre2_8'],
|
sdl_dep = dependency('sdl2', method: 'config-tool')
|
||||||
default_options: ['default_library=static', 'grep=false', 'test=false']
|
lite_deps = [lua_dep, sdl_dep, freetype_dep, pcre2_dep, libm, libdl, threads_dep]
|
||||||
)
|
|
||||||
|
|
||||||
freetype_dep = dependency('freetype2', fallback: ['freetype2', 'freetype_dep'],
|
|
||||||
default_options: ['default_library=static', 'zlib=disabled', 'bzip2=disabled', 'png=disabled', 'harfbuzz=disabled', 'brotli=disabled']
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -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')
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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@"
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,4 +72,4 @@ main() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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) {
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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) {
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
if (set == env_len && (!detach || setsid() != -1) && (!cwd || chdir(cwd) != -1))
|
|
||||||
execvp((const char*)cmd[0], (char* const*)cmd);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
158
src/api/system.c
158
src/api/system.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,10 +595,7 @@ 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);
|
||||||
|
@ -596,10 +608,10 @@ static int f_get_fs_type(lua_State *L) {
|
||||||
return 1;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ 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;
|
||||||
|
@ -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;
|
||||||
|
@ -194,12 +185,10 @@ 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:
|
||||||
|
@ -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;
|
||||||
|
@ -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, ¤t_color.r, ¤t_color.g, ¤t_color.b);
|
SDL_GetRGB(*d, surface->format, ¤t_color.r, ¤t_color.g, ¤t_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);
|
||||||
|
|
|
@ -128,3 +128,4 @@ void renwin_free(RenWindow *ren) {
|
||||||
SDL_FreeSurface(ren->surface);
|
SDL_FreeSurface(ren->surface);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
Loading…
Reference in New Issue