Compare commits

..

35 Commits

Author SHA1 Message Date
George Sokianos d5d87789d5 innosetup changes 2024-04-24 21:01:21 +01:00
Takase 6ba5328697 Release v2.1.4 (#1772)
* changelog: add changes for 2.1.4

* chore: update version and changelog
2024-04-24 21:01:05 +01:00
Takase b419dca6ad ci: fix missing permission for creating releases (#1770) 2024-04-24 21:01:05 +01:00
Guldoman 2d8d39c7c0 Skip patterns matching nothing in `tokenizer` (#1743)
These patterns cause infinite loops, so warn about them and skip them.
2024-04-24 21:01:05 +01:00
Andrei Vinca a2b340efbe Fix BufferSize in g_read for Windows (#1722) 2024-04-24 21:01:05 +01:00
PerilousBooklet eee6bc24d0 Added Arduino syntax highlighting support alongside C++. (#1767) 2024-04-24 21:01:05 +01:00
PerilousBooklet be84a85dc9 Fixed some typos in core.init (#1755)
* Fixed some typos.

* Update data/core/init.lua

Co-authored-by: Takase <20792268+takase1121@users.noreply.github.com>

* Update data/core/init.lua

Co-authored-by: Takase <20792268+takase1121@users.noreply.github.com>

---------

Co-authored-by: Takase <20792268+takase1121@users.noreply.github.com>
2024-04-24 21:01:05 +01:00
Guldoman e6d706f6b8 Limit `language_js` regex avoidance to numbers, and fix starting `/*` comments (#1744)
* Avoid starting regexes only after numbers in `language_js`

* Allow starting `/*` comments after numbers in `language_js`
2024-04-24 21:01:05 +01:00
Aziz Mazouz Jaber f76b7bd600 Add from symbol to support ESM (#1754) 2024-04-24 21:01:05 +01:00
Jan a6a4f2bd0b Add SerenityOS platform support (#1745)
* Add SerenityOS platform support

* remove cpp_std default option
2024-04-24 21:01:05 +01:00
Guldoman cd49490253 Rectify `LICENSE` dates and owners (#1748) 2024-04-24 21:01:05 +01:00
Takase 74c8d03aa0 scripts: not hardcode MSYSTEM (#1739) 2024-04-24 21:01:05 +01:00
Guldoman e69f3b8c13 Fix `language_js` regex/comment distinction (#1731) 2024-04-24 21:00:21 +01:00
Guldoman 27ae51762b Improve `CommandView` and `autocomplete` scroll behavior (#1732)
* Make command palette item scrolling more natural

Also add a config option for the maximum number of visible entries in the command palette.

* Make `autocomplete` item scrolling more natural
2024-04-24 21:00:21 +01:00
Guldoman ece51922a3 Improve `autocomplete` suggestions box behavior (#1734)
* Improve `autocomplete` suggestions box sizing

This avoids that the box gets too big because of non-visible items, and makes it reactive to window sizing.

* Draw ellipsis when `autocomplete` entries aren't fully visible
2024-04-24 21:00:21 +01:00
PerilousBooklet cf76b5857a Added .pyi extension to python. (#1728) 2024-04-24 21:00:21 +01:00
Velosofy 8fd5c78312 Add "Open with Lite XL" to windows' context menu (#1333)
Closes #423
2024-04-24 21:00:21 +01:00
George Sokianos 713bdfeb7d innosetup changes 2024-02-11 15:39:15 +00:00
Takase e87170b226 wasm cross file improvements (#1660)
* fix(wasm-cross): missing idbfs in newer emscripten

* refactor(wasm-cross): specify important option in cross file

* feat(wasm-cross): allow overriding emscripten path

* feat(wasm-cross): add file_packager as a binary
2024-02-11 15:38:51 +00:00
Takase 188297d6b4 readme: add minimum supported OSes (#1711)
* readme: add minimum supported OSes

* README: add linebreak between sentences
2024-02-11 15:38:51 +00:00
Takase 566da99680 process: fix unitialized variables (#1719)
* process: fix potential unitialized variables

* process: fix brace initializer error on Windows
2024-02-11 15:38:51 +00:00
Takase 14260166fc v2.1.3 (#1712)
* changelog: update changelog for v2.1.3

* update version in metadata

* changelog: fix formatting issue and release date

* appstream: update release date
2024-02-11 15:38:51 +00:00
Chloé Vulquin 2e26a0838c Add system.setenv (#1706)
* add system.setenv

* document system.setenv

* system.setenv: use wide versions of functions on windows

* do not include processenv.h

* system.setenv: report failure, including of utfconv

* system.setenv: free utfconv output
2024-02-11 15:38:51 +00:00
Luke aka SwissalpS 5ce34eb74c Fix comment typos in data/core/{init.lua,dirwatch.lua} (#1549)
* comment typo in data/core/init.lua

* init.lua reword comment

* Update dirwatch.lua

Some wording is still whacky, maybe next time

* Update dirwatch.lua

* clear up can/may be not nil confusion

* falsey vs falsy new wording to help out

* falsey -> falsy

* Update data/core/dirwatch.lua

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2024-02-11 15:38:51 +00:00
Takase 80c9128c43 process: style changes (#1709) 2024-02-11 15:38:51 +00:00
Takase ddddef076d language_js: support binary and octal representation (#1710) 2024-02-11 15:38:51 +00:00
vqn ad79e26425 autoreload docs only if their filename matches an actual file (#1698) 2024-02-11 15:38:51 +00:00
vqn dbeea0aff2 reorder nagview options on doc:save error to be more consistent with other nagview confirmations (#1696) 2024-02-11 15:38:51 +00:00
Fiji 3e91d07097 Improve number highlighting for python syntax highlighting (#1704)
* Improve number highlighting for python syntax highlighting

Adds support for octal and binary representation, as well uppercase characters (X, B and O)

* add underscore and negative hex/oct/bin value support

* Removed | from pattern
2024-02-11 15:38:51 +00:00
Chloé Vulquin 3897e59e6b Memory fixes (#1705)
* fix: free-before-init in renwin_init_surface when using sdl renderer

`ren->rensurface.surface` presupposes zero-initialized rensurface.
Rensurface was not actually zero-initialized.
It is now.

* fix: heap buffer overflow in process_env_free

`process_env_free` presupposed that it was null-terminated.
Pass length to free instead.

* use calloc instead of memset for zero-init

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2024-02-11 15:38:51 +00:00
vqn 2755117e2d Fix doc:create-cursor-previous/next-line with tabs (#1697)
* use DocView.translate to split cursor on previous/next line

* use dv.doc instead of doc()
2024-02-11 15:38:51 +00:00
Takase 97717b4511 v2.1.2 (#1695)
* chore: update versions

* chore(changelog): update changelog for v2.1.2
2024-02-11 15:38:51 +00:00
Velosofy 9378646680 Add "Open with Lite XL" to windows' context menu (#1333)
Closes #423
2024-02-11 15:38:51 +00:00
George Sokianos 721e0c8ee3 innosetup changes 2023-12-29 10:22:11 +00:00
Velosofy ffe1e6775d Add "Open with Lite XL" to windows' context menu (#1333)
Closes #423
2023-12-29 10:21:54 +00:00
168 changed files with 1666 additions and 9392 deletions

6
.gitignore vendored
View File

@ -19,12 +19,6 @@ compile_commands.json
error.txt
lite-xl*
LiteXL*
lite
.config/
*.lha
*.o
*.snalyzerinfo
!resources/windows/*.diff
!resources/windows/*.exe.manifest.in

View File

@ -1,89 +0,0 @@
#
# Project: Lite XL
#
# Created on: 26-12-2021
#
LiteXL_OBJ := \
src/main.o src/rencache.o src/renderer.o src/renwindow.o \
src/api/api.o src/api/dirmonitor.o \
src/api/regex.o src/api/renderer.o src/api/system.o \
src/api/utf8.o src/platform/morphos.o \
src/api/dirmonitor/mos.o src/platform/codesets.o
outfile := lite-xl
compiler := ppc-morphos-gcc-11
cxxcompiler := ppc-morphos-g++-11
INCPATH := -Isrc -Ilib/dmon -I/sdk/gg/usr/local/include/SDL2 \
-I/sdk/gg/usr/include/freetype -I/sdk/gg/usr/include/lua5.4
DFLAGS ?= -D__USE_INLINE__
CFLAGS ?= -Wall -Wwrite-strings -O2 -noixemul -g -std=gnu11 -fno-strict-aliasing
LFLAGS ?= -noixemul -lpcre2-8 -lSDL2 -llua54 -lagg -lfreetype -lm -lc -L/usr/local/lib
ifeq ($(DEBUG),1)
CFLAGS += -g -gstabs
LFLAGS += -gstabs
endif
.PHONY: LiteXL clean release
default: LiteXL
clean:
@echo "Cleaning compiler objects..."
@rm -f $(LiteXL_OBJ)
LiteXL: $(LiteXL_OBJ)
@echo "Linking LiteXL"
$(compiler) -o $(outfile) $(LiteXL_OBJ) $(LFLAGS)
.c.o:
@echo "Compiling $<"
$(compiler) -c $< -o $*.o $(CFLAGS) $(INCPATH) $(DFLAGS)
src/main.o: src/main.c src/api/api.h src/rencache.h \
src/renderer.h src/platform/morphos.h
src/rencache.o: src/rencache.c
src/renderer.o: src/renderer.c
src/renwindow.o: src/renwindow.c
src/api/api.o: src/api/api.c
src/api/regex.o: src/api/regex.c
src/api/renderer.o: src/api/renderer.c
src/api/system.o: src/api/system.c
src/platform/morphos.o: src/platform/morphos.c
src/api/dirmonitor.o: src/api/dirmonitor.c src/api/dirmonitor/mos.c
src/api/utf8.o: src/api/utf8.c
src/api/dirmonitor/mos.o: src/api/dirmonitor/mos.c
src/platform/codesets.o: src/platform/codesets.c
release: clean LiteXL
@echo "Creating release files..."
@mkdir -p release/LiteXL2
@cp -r resources/amiga/* release/LiteXL2/
@mv release/LiteXL2/LiteXL2.info release/
@rm release/LiteXL2/AutoInstall
@cp -r data release/LiteXL2/
@cp changelog.md release/LiteXL2/
@cp $(outfile) release/LiteXL2/
@strip release/LiteXL2/$(outfile)
@cp README.md release/LiteXL2/
@cp README_Amiga.md release/LiteXL2/
@cp LICENSE release/LiteXL2/
@cp -r licenses release/LiteXL2/
@echo "Creating release archive..."
@lha -aeqr3 a LiteXL2_MOS.lha release/
@echo "Clean release files..."
@delete release ALL QUIET FORCE

View File

@ -1,102 +0,0 @@
#
# Project: Lite XL
#
# Created on: 26-12-2021
#
LiteXL_OBJ := \
src/main.o src/rencache.o src/renderer.o src/renwindow.o \
src/api/api.o src/api/dirmonitor.o \
src/api/regex.o src/api/renderer.o src/api/system.o \
src/api/utf8.o src/platform/amigaos4.o \
src/api/dirmonitor/os4.o src/platform/codesets.o
outfile := lite-xl
compiler := gcc
cxxcompiler := g++
INCPATH := -Isrc -I/sdk/local/newlib/include/SDL2 \
-I/sdk/local/common/include/lua54 -I/sdk/local/common/include/freetype2
DFLAGS += -D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR
CFLAGS ?= -Werror -Wwrite-strings -O3 -std=gnu11 -fno-strict-aliasing
LFLAGS ?= -mcrt=newlib -lpcre2-8 -lSDL2 -llua54 -lfreetype -lpng -lz \
-lpthread -athread=native
ifeq ($(DEBUG),1)
CFLAGS += -g -gstabs
LFLAGS += -gstabs
ifeq ($(PROFYLER),1)
CFLAGS += -finstrument-functions -fno-inline -DPROFILING
LFLAGS += -lprofyle
endif
endif
.PHONY: LiteXL clean release
default: LiteXL
clean:
@echo "Cleaning compiler objects..."
@rm -f $(LiteXL_OBJ)
LiteXL: $(LiteXL_OBJ)
@echo "Linking LiteXL"
@$(compiler) -o $(outfile) $(LiteXL_OBJ) $(LFLAGS)
.c.o:
@echo "Compiling $<"
@$(compiler) -c $< -o $*.o $(CFLAGS) $(INCPATH) $(DFLAGS)
src/main.o: src/main.c src/api/api.h src/rencache.h \
src/renderer.h src/platform/amigaos4.h src/platform/codesets.h
src/rencache.o: src/rencache.c
src/renderer.o: src/renderer.c
src/renwindow.o: src/renwindow.c
src/api/api.o: src/api/api.c
src/api/regex.o: src/api/regex.c
src/api/renderer.o: src/api/renderer.c
src/api/system.o: src/api/system.c src/platform/amigaos4.h
src/platform/amigaos4.o: src/platform/amigaos4.c
src/platform/codesets.o: src/platform/codesets.c
src/api/dirmonitor.o: src/api/dirmonitor.c src/api/dirmonitor/os4.c
src/api/utf8.o: src/api/utf8.c src/platform/amigaos4.h
src/api/dirmonitor/os4.o: src/api/dirmonitor/os4.c
src/api/process.o: src/api/process.c
release: clean LiteXL
@echo "Creating release files..."
@mkdir -p release/LiteXL2
@cp -r resources/amiga/* release/LiteXL2/
@mv release/LiteXL2/LiteXL2.info release/
@mv release/LiteXL2/AutoInstall release/
@cp -r data release/LiteXL2/
@cp changelog.md release/LiteXL2/
@cp $(outfile) release/LiteXL2/
@strip release/LiteXL2/$(outfile)
@cp README.md release/LiteXL2/
@cp README_Amiga.md release/LiteXL2/
@cp LICENSE release/LiteXL2/
@cp -r licenses release/LiteXL2/
@echo "Creating release archive..."
@lha -aeqr3 a LiteXL2_OS4.lha release/
@echo "Clean release files..."
@delete release ALL QUIET FORCE

View File

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

View File

@ -1,368 +0,0 @@
# Lite XL v2 for AmigaOS 4.1 FE & MorphOS 3
Lite XL is a lightweight text editor written in Lua and SDL2.
The port is not perfect and it might have issues here and there. 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.
If it crashes on your system, try to delete to `.config` folder.
## Installation
You can extract the Lite XL archive wherever you want and run the *lite*
editor.
## Configuration folder
This editor creates a `.config` folder where the configuration is saved, as
well as plugins, themes etc.. By default this version uses the installation
folder, but if you want to override it, you can create an ENV variable
named `HOME` and set there your prefferable path.
You can check if there is one already set by executing the following command
in a shell
```
GetEnv HOME
```
If there is one set, then you will see the path at the output.
Otherwise, you can set your home path be executing the following command.
Change the path to the one of your preference.
```
SetEnv SAVE HOME "Sys:home/"
```
## Addons
### Colors
Colors are lua files that set the color scheme of the editor. There are
light and dark themes for you to choose.
To install and use them you have to copy the ones you would like from
`addons/colors/light` or `addons/colors/dark` into the folder
`.config/lite-xl/colors/`. Don't add light or dark folders. Just copy the
.lua files in there.
Then you have to start Lite XL and open your configuration by clicking
at the cog icon at the toolbar (bottom left sixth icon). Go at the line
that looks like below
```
-- core.reload_module("colors.summer")
```
and change the `summer` with the name of your color theme. Also, remove
the two dashes `--` at the start of the line and save the file. If you
did everything right, the color schema should change instantly.
The themes can also be found at
https://github.com/lite-xl/lite-xl-colors
### Plugins
LiteXL is able to use plugins to extend its features. Those can be found
at https://github.com/lite-xl/lite-xl-plugins and other websites. Not all
of them will work fine on AmigaOS 4 or MorphOS, because of missing
dependencies or filesystem issues.
To make it easier for you, I gathered some of the plugins that are working
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
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:
**autoinsert**
Automatically inserts closing brackets and quotes. Also allows selected
text to be wrapped with brackets or quotes.
**autosaveonfocuslost**
Automatically saves files that were changed when the main window loses
focus by switching to another application
**autowrap**
Automatically hardwraps lines when typing
**bigclock**
Shows the current time and date in a view with large text
**bracketmatch**
Underlines matching pair for bracket under the caret
**codesets**
This plugin uses the codesets.library on AmigaOS 4 and the
charsets.library on MorphOS to translate ISO encoded files to unicode
and vice-versa. When this is enabled new menu items are added to
load/save the code with a different encoding. Also there is a new
section at the status bar that show the file encoding.
This plugin is **EXPERIMENTAL** and heavily inspired from the encoding
plugin at https://github.com/jgmdev/lite-xl-encoding
**colorpreview**
Underlays color values (eg. `#ff00ff` or `rgb(255, 0, 255)`) with their
resultant color.
**custom_caret**
Customize the caret in the editor setting it to *underline*, *block* or
*line* at the init.lua file in your config folder.
For example add:
`config.plugins.custom_caret.shape = "block"`
**EditorConfig**
EditorConfig (https://editorconfig.org/) implementation for Lite XL
**ephemeral_tabs**
Preview tabs. Opening a doc will replace the contents of the preview tab.
Marks tabs as non-preview on any change or tab double clicking.
**ghmarkdown**
Opens a preview of the current markdown file in a browser window.
On AmigaOS 4 it uses *urlopen* and on MorphOS it uses *openurl* to load
the generated html in the browser. It requires a GitHub application token
because it uses its Rest API. Add it at the init.lua file in your config
folder like below:
`config.plugins.ghmarkdown.github_token = "<token here>"`
**indentguide**
Adds indent guides
**language_guide**
Syntax for the AmigaGuide scripting language
**language_hws**
Syntax for the Hollywood language
**language_make**
Syntax for the Make build system language
**language_sh**
Syntax for shell scripting language
**lfautoinsert**
Automatically inserts indentation and closing bracket/text after newline
**markers**
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**
Allows moving back and forward between document positions, reducing the
amount of scrolling
**nonicons**
File icons set for TreeView. Download TTF font to your config/fonts
folder from https://github.com/yamatsum/nonicons/tree/master/dist
**opacity**
Change the opaqueness/transparency of lite-xl using LAmiga+mousewheel
or a command.
**openfilelocation**
Opens the parent directory of the current file in the file manager
**rainbowparen**
Show nesting of parentheses with rainbow colours
**restoretabs**
Keep a list of recently closed tabs, and restore the tab in order on
cntrl+shift+t.
**select_colorscheme**
Select a color theme, like VScode, Sublime Text.
(plugin saves changes)
**selectionhighlight**
Highlights regions of code that match the current selection
**smallclock**
It adds a small clock at the bottom right corner.
**tetris**
Play Tetris inside Lite XL.
## Tips and tricks
### Transitions
If you want to disable the transitions and make the editor faster,
open your configuration file by clicking at the cog icon at the toolbar
(bottom left, 6th icon) and add the following line at the end of the file,
and then save it. You might need to restart your editor.
```
config.transitions = false
```
### Hide files from the file list
If you would like to hide files or whole folder from the left side bar list,
open your configuration by clicking at the cog icon at the toolbar
(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`
files. You might need to restart your editor.
```
config.ignore_files = {"^%.", "%.info$"}
```
You can add as many rules as you want in there, to hide files or
folders, as you like.
## I would like to thank
- IconDesigner for the proper glow icons that are included in the release
- Capehill for his tireless work on SDL port for AmigaOS 4.1 FE
- Michael Trebilcock for his port on liblua
- Bruno "BeWorld" Peloille for his great work on porting SDL to MorphOS
and for his valuable help
- Lite XL original team for being helpful and providing info
Without all the above Lite XL would not be possible
## Support
If you enjoy what I am doing and would like to keep me up during the night,
please consider to buy me a coffee at:
https://ko-fi.com/walkero
## Known issues
You can find the known issues at
https://git.walkero.gr/walkero/lite-xl/issues
# Changelog
## [2.1.4r1] - future
### Added
- Added the ability to open files and folders by drag 'n drop them on the
LiteXL icon when this is on the AmiDock
### Updated
- Updated the code to the upstream 2.1.4 release
### Fixed
- Fix opening files from the root of a device
## [2.1.3r1] - 2024-03-09
### Added
- Added AmiUpdate support
- Added the Tetris plugin
### Updated
- Updated the code to the upstream 2.1.3 release
- Compiled with SDL 2.30.0 that supports editing with ISO encodings, other
than English. Now the editor should be able to support any language
and in conjuction with the codesets plugin be able to make text
encodings conversions
- Now the codesets plugin supports MorphOS 3.18 and above
### Changed
- Changed the way the "Open in system" option executes the WBRun command
in AmigaOS 4, with a more secure way
- Did a lot of code cleanup in sync with the upstream, and parts of code
that were left over
- Compiled with pcre2 10.42 (MorphOS version only)
### Fixed
- I did a lot of changes on path manipulation and usage, fixing scanning
the root of a partition or an assign path
- Fixed an error with the codesets plugin, where an empty file could
not be opened
- Improved the folder suggestions when opening projects or changing paths.
Now even the root folders of a partition are presented
- Fixed ghmarkdown plugin, but now requires a GitHub token to be provided
## [2.1.2r1] - 2023-12-19
### Added
- Added the new experimental codesets plugin (AmigaOS4 version only).
MorphOS version is in WIP
### Changed
- Synced with the latest upstream v2.1.2 code
- Compiled with gcc 11.3.0
- Compiled with SDL 2.28.4
- Compiled with libfreetype 2.13.x
- Compiled with lua 5.4.6
- Compiled with linpng 1.6.40 (AmigaOS4 version only)
- Compiled with libz 1.2.13 (AmigaOS4 version only)
## [2.1.1r2] - 2022-05-14
### Changed
- Compiled with latest SDL v2.26.5-rc2
## [2.1.1r1] - 2022-01-29
### Changed
- Binary name changed to lite-xl
- Updated the colour themes and the plugins that are included in the release
- Compiled with latest SDL 2.26
- Compiled with gcc 11
- Synced the code with the upstream master branch at 8th January 2023
### Fixed
- Set the default locale on AmigaOS 4, so as to fix some issues with decimal
numbers
## [2.1.0r1] - 2022-10-10
### Added
- This version of LiteXL recognises changes that are done outside the editor
in files and folders, and updates the items when it gets focus again.
### Changed
- Synced the code with the latest upstream master branch, which means that
this version is based on the latest available source
- Now the plugins need to be version 3. The older versions will not work.
All the included plugins are updated to the latest available version.
- Compiled with SDL 2.24
- Compiled with Lua 5.4
### Fixed
- Fixed regex issues with some plugins
- Fixed "Open in System" on AmigaOS 4 and MorphOS. When you right click
at a file or a folder at the treeview at the left side, then depending
the type of the item opens on Workbench. A folder opens in a list view
and a file opens with its default tool
- Fixed markdown preview on MorphOS. Now, it calls openurl with the html
file (#20)
- Fixed locale issues on MorphOS (again), since the previous fix didn't
actually fixed the problem
## [2.0.3r3] - 2022-09-26
### Added
- Added plugin for AmigaGuide files
- Added plugin for Hollywood files
### Fixed
- Fixed non existing path crashes on OS4 and MorphOS
- Fixed editor refresh whenever init.lua is changed, no matter the working
folder
- Fixed an issue when the user added a directory in the project that
already existed
- Fixed locale issue on start for MorphOS. Now it should start just fine
no matter what locale the user has on his system.
- Fixed "Find" on MorphOS that was not working (shortcut CTRL+F)
- If the user selects to change the project folder and inserts Sys: or any
partition name, the included folders will be listed as suggestions
### Changed
- Removed linking with unix on OS4 build
- Makefiles updated
## [2.0.3r2] - 2022-06-18
### Added
- First public MorphOS version released
### Changed
- Merged source code for both AmigaOS 4 and MorphOS
- Moved the declaration of the $VER and $STACK for the AmigaOS 4 version,
so to happen only once (reported by capehill)
### Fixed
- Fixed the usage of NumPad (reported by root)
## [2.0.3r1] - 2022-03-30
### Changed
- 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 problems you might get
by using this software.

Binary file not shown.

View File

@ -185,7 +185,7 @@ command.add(nil, {
local dirname = common.dirname(core.project_dir)
local text
if dirname then
text = common.basepath(common.home_encode(dirname))
text = common.home_encode(dirname) .. PATHSEP
end
core.command_view:enter("Change Project Folder", {
text = text,

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
local core = require "core"
local common = require "core.common"
local config = require "core.config"
local style = require "core.style"
local Doc = require "core.doc"
local DocView = require "core.docview"
@ -20,8 +21,6 @@ local CommandView = DocView:extend()
CommandView.context = "application"
local max_suggestions = 10
local noop = function() end
---@class core.commandview.state
@ -85,6 +84,11 @@ function CommandView:get_line_screen_position(line, col)
end
function CommandView:supports_text_input()
return true
end
function CommandView:get_scrollable_size()
return 0
end
@ -125,7 +129,7 @@ function CommandView:move_suggestion_idx(dir)
end
local function get_suggestions_offset()
local max_visible = math.min(max_suggestions, #self.suggestions)
local max_visible = math.min(config.max_visible_commands, #self.suggestions)
if dir > 0 then
if self.suggestions_offset + max_visible < self.suggestion_idx + 1 then
return self.suggestion_idx - max_visible + 1
@ -317,7 +321,7 @@ function CommandView:update()
-- update suggestions box height
local lh = self:get_suggestion_line_height()
local dest = self.state.show_suggestions and math.min(#self.suggestions, max_suggestions) * lh or 0
local dest = self.state.show_suggestions and math.min(#self.suggestions, config.max_visible_commands) * lh or 0
self:move_towards("suggestions_height", dest, nil, "commandview")
-- update suggestion cursor offset
@ -368,7 +372,7 @@ local function draw_suggestions_box(self)
-- draw suggestion text
local first = math.max(self.suggestions_offset, 1)
local last = math.min(self.suggestions_offset + max_suggestions, #self.suggestions)
local last = math.min(self.suggestions_offset + config.max_visible_commands, #self.suggestions)
for i=first, last do
local item = self.suggestions[i]
local color = (i == self.suggestion_idx) and style.accent or style.text

View File

@ -226,13 +226,7 @@ function common.path_suggest(text, root)
if root and root:sub(-1) ~= PATHSEP then
root = root .. PATHSEP
end
local path, name
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
path, name = text:match("^(.-)([^:"..PATHSEP.."]*)$")
else
path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
end
local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
local clean_dotslash = false
-- ignore root if path is absolute
local is_absolute = common.is_absolute_path(text)
@ -285,12 +279,7 @@ end
---@param text string The input path.
---@return string[]
function common.dir_path_suggest(text)
local path, name
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
path, name = text:match("^(.-)([^:"..PATHSEP.."]*)$")
else
path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
end
local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
local files = system.list_dir(path == "" and "." or path) or {}
local res = {}
for _, file in ipairs(files) do
@ -309,13 +298,7 @@ end
---@param dir_list string[] A list of paths to filter.
---@return string[]
function common.dir_list_suggest(text, dir_list)
local path, name
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
path, name = text:match("^(.-)([^:"..PATHSEP.."]*)$")
else
path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
end
local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
local res = {}
for _, dir_path in ipairs(dir_list) do
if dir_path:lower():find(text:lower(), nil, true) == 1 then
@ -482,35 +465,11 @@ function common.basename(path)
end
---Returns the base path with the pathsep, if needed.
---@param path string
---@return string
function common.basepath(path)
-- Check for AmigaOS 4 and MorphOS if the last character is semicolon
-- In these systems the volume name doesn't have a / or \ after the name
-- but it is like VOLUME:
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") and (string.sub(path, -1) == ":") then
return path
end
return path .. PATHSEP
end
---Returns the directory name of a path.
---If the path doesn't have a directory, this function may return nil.
---@param path string
---@return string|nil
function common.dirname(path)
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
local drive, relpath = path:match('^([%w%s]*:)(.+)')
if drive and relpath then
local dir = relpath:match("(.+)["..PATHSEP.."][^"..PATHSEP.."]+$")
if dir then
return drive .. dir
end
end
return path
end
return path:match("(.+)["..PATHSEP.."][^"..PATHSEP.."]+$")
end
@ -548,9 +507,6 @@ end
---@param text string
---@return string
function common.home_expand(text)
if text == nil then
return HOME
end
return HOME and text:gsub("^~", HOME) or text
end
@ -560,13 +516,6 @@ local function split_on_slash(s, sep_pattern)
if s:match("^["..PATHSEP.."]") then
t[#t + 1] = ""
end
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
local drive = s:match("^([%w%s]*:)")
if drive then
t[#t + 1] = ""
s = s:gsub("^" .. drive, "")
end
end
for fragment in string.gmatch(s, "([^"..PATHSEP.."]+)") do
t[#t + 1] = fragment
end
@ -589,12 +538,6 @@ function common.normalize_volume(filename)
return drive:upper() .. rem
end
end
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
local drive, rem = filename:match('^([%w%s]*:)(.-)' .. PATHSEP .. '?$')
if drive then
return drive .. rem
end
end
return filename
end
@ -620,11 +563,6 @@ function common.normalize_path(filename)
volume, filename = drive, rem
end
end
elseif (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
local drive, relpath = filename:match('^([%w%s]*:)(.+)')
if relpath then
volume, filename = drive, relpath
end
else
local relpath = filename:match('^/(.+)')
if relpath then
@ -655,7 +593,7 @@ end
---@param path string
---@return boolean
function common.is_absolute_path(path)
return path:sub(1, 1) == PATHSEP or path:match("^(%a):\\") or path:match('^([%w%s]*):')
return path:sub(1, 1) == PATHSEP or path:match("^(%a):\\")
end
@ -664,7 +602,7 @@ end
---@param path string The parent path.
---@return boolean
function common.path_belongs_to(filename, path)
return string.find(filename, common.basepath(path), 1, true) == 1
return string.find(filename, path .. PATHSEP, 1, true) == 1
end
@ -674,9 +612,6 @@ end
---@return boolean
function common.relative_path(ref_dir, dir)
local drive_pattern = "^(%a):\\"
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
drive_pattern = "^([%w%s]*:)"
end
local drive, ref_drive = dir:match(drive_pattern), ref_dir:match(drive_pattern)
if drive and ref_drive and drive ~= ref_drive then
-- Windows, different drives, system.absolute_path fails for C:\..\D:\
@ -719,7 +654,7 @@ function common.mkdirp(path)
path = updir
end
for _, dirname in ipairs(subdirs) do
path = path and common.basepath(path) .. dirname or dirname
path = path and path .. PATHSEP .. dirname or dirname
if not system.mkdir(path) then
return false, "cannot create directory", path
end

View File

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

View File

@ -28,7 +28,7 @@ function dirwatch:scan(directory, bool)
end
-- Should be called on every directory in a subdirectory.
-- In windows, this is a no-op for anything underneath a top-level directory,
-- On 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.
-- Can also be called on individual files, though this should be used sparingly,
@ -134,11 +134,11 @@ local function compile_ignore_files()
-- 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
-- we ignore malformed patterns 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.
-- A '/' 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
})
@ -175,12 +175,12 @@ 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.
-- in project scan and return it 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(common.basepath(root) .. 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.
local info = system.get_file_info(root .. PATHSEP .. file)
-- In some cases info.type is nil even if info is valid.
-- This happens when it is neither a file nor a directory,
-- for example /dev/* entries on linux.
if info and info.type then
info.filename = file
return fileinfo_pass_filter(info, ignore_compiled) and info
@ -192,7 +192,7 @@ end
-- "path" will be a path starting without '/' and without trailing '/'
-- or the empty string.
-- It identifies a sub-path within "root".
-- The current path location will therefore always be: root .. path.
-- 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 '/'.
@ -201,8 +201,7 @@ function dirwatch.get_directory_files(dir, root, path, entries_count, recurse_pr
local t0 = system.get_time()
local ignore_compiled = compile_ignore_files()
local all = system.list_dir(common.basepath(root) .. path)
local all = system.list_dir(root .. PATHSEP .. path)
if not all then return nil end
local entries = { }
for _, file in ipairs(all) do

View File

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

View File

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

View File

@ -254,6 +254,11 @@ function DocView:scroll_to_line(line, ignore_if_visible, instant)
end
function DocView:supports_text_input()
return true
end
function DocView:scroll_to_make_visible(line, col)
local _, oy = self:get_content_offset()
local _, ly = self:get_line_screen_position(line, col)
@ -317,7 +322,7 @@ function DocView:mouse_selection(doc, snap_type, line1, col1, line2, col2)
line1, col1 = translate.start_of_word(doc, line1, col1)
line2, col2 = translate.end_of_word(doc, line2, col2)
elseif snap_type == "lines" then
col1, col2 = 1, math.huge
col1, col2, line2 = 1, 1, line2 + 1
end
if swap then
return line2, col2, line1, col1
@ -441,7 +446,7 @@ function DocView:draw_line_text(line, x, y)
local last_token = nil
local tokens = self.doc.highlighter:get_line(line).tokens
local tokens_count = #tokens
if tokens[tokens_count] ~= nil and string.sub(tokens[tokens_count], -1) == "\n" then
if string.sub(tokens[tokens_count], -1) == "\n" then
last_token = tokens_count - 1
end
for tidx, type, text in self.doc.highlighter:each_token(line) do
@ -455,9 +460,16 @@ function DocView:draw_line_text(line, x, y)
return self:get_line_height()
end
function DocView:draw_overwrite_caret(x, y, width)
local lh = self:get_line_height()
renderer.draw_rect(x, y + lh - style.caret_width, width, style.caret_width, style.caret)
end
function DocView:draw_caret(x, y)
local lh = self:get_line_height()
renderer.draw_rect(x, y, style.caret_width, lh, style.caret)
local lh = self:get_line_height()
renderer.draw_rect(x, y, style.caret_width, lh, style.caret)
end
function DocView:draw_line_body(line, x, y)
@ -554,7 +566,12 @@ function DocView:draw_overlay()
else
if config.disable_blink
or (core.blink_timer - core.blink_start) % T < T / 2 then
self:draw_caret(self:get_line_screen_position(line1, col1))
local x, y = self:get_line_screen_position(line1, col1)
if self.doc.overwrite then
self:draw_overwrite_caret(x, y, self:get_font():get_width(self.doc:get_char(line1, col1)))
else
self:draw_caret(x, y)
end
end
end
end

View File

@ -18,13 +18,13 @@ local Doc
local core = {}
local function load_session()
local ok, t = pcall(dofile, common.basepath(USERDIR) .. "session.lua")
local ok, t = pcall(dofile, USERDIR .. PATHSEP .. "session.lua")
return ok and t or {}
end
local function save_session()
local fp = io.open(common.basepath(USERDIR) .. "session.lua", "w")
local fp = io.open(USERDIR .. PATHSEP .. "session.lua", "w")
if fp then
fp:write("return {recents=", common.serialize(core.recent_projects),
", window=", common.serialize(table.pack(system.get_window_size())),
@ -188,7 +188,7 @@ local function refresh_directory(topdir, target)
-- If this file doesn't exist, we should be calling this on our parent directory, assume we'll do that.
-- Unwatch just in case.
if files == nil then
topdir.watch:unwatch(common.basepath(topdir.name) .. (target or ""))
topdir.watch:unwatch(topdir.name .. PATHSEP .. (target or ""))
return true
end
@ -212,7 +212,7 @@ local function refresh_directory(topdir, target)
-- If it's not there, remove the entry from the list as being out of order.
table.remove(topdir.files, old_idx)
if old_info.type == "dir" then
topdir.watch:unwatch(common.basepath(topdir.name) .. old_info.filename)
topdir.watch:unwatch(topdir.name .. PATHSEP .. old_info.filename)
end
directory_end_idx = directory_end_idx - 1
end
@ -223,7 +223,7 @@ local function refresh_directory(topdir, target)
end
end
for i, v in ipairs(new_directories) do
topdir.watch:watch(common.basepath(topdir.name) .. v.filename)
topdir.watch:watch(topdir.name .. PATHSEP .. v.filename)
if not topdir.files_limit or core.project_subdir_is_shown(topdir, v.filename) then
refresh_directory(topdir, v.filename)
end
@ -272,7 +272,7 @@ function core.add_project_directory(path)
refresh_directory(topdir)
else
for i,v in ipairs(t) do
if v.type == "dir" then topdir.watch:watch(common.basepath(path) .. v.filename) end
if v.type == "dir" then topdir.watch:watch(path .. PATHSEP .. v.filename) end
end
end
topdir.watch:watch(topdir.name)
@ -286,7 +286,7 @@ function core.add_project_directory(path)
local changed = topdir.watch:check(function(target)
if target == topdir.name then return refresh_directory(topdir) end
local dirpath = target:sub(#topdir.name + 2)
local abs_dirpath = common.basepath(topdir.name) .. dirpath
local abs_dirpath = topdir.name .. PATHSEP .. dirpath
if dirpath then
-- check if the directory is in the project files list, if not exit.
local dir_index, dir_match = file_search(topdir.files, {filename = dirpath, type = "dir"})
@ -373,9 +373,9 @@ function core.update_project_subdir(dir, filename, expanded)
assert(dir.files_limit, "function should be called only when directory is in files limit mode")
dir.shown_subdir[filename] = expanded
if expanded then
dir.watch:watch(common.basepath(dir.name) .. filename)
dir.watch:watch(dir.name .. PATHSEP .. filename)
else
dir.watch:unwatch(common.basepath(dir.name) .. filename)
dir.watch:unwatch(dir.name .. PATHSEP .. filename)
end
return refresh_directory(dir, filename)
end
@ -387,7 +387,7 @@ end
local function find_files_rec(root, path)
local all = system.list_dir(root .. path) or {}
for _, file in ipairs(all) do
local file = common.basepath(path) .. file
local file = path .. PATHSEP .. file
local info = system.get_file_info(root .. file)
if info then
info.filename = strip_leading_path(file)
@ -462,7 +462,7 @@ local function create_user_directory()
error("cannot create directory \"" .. USERDIR .. "\": " .. err)
end
for _, modname in ipairs {'plugins', 'colors', 'fonts'} do
local subdirname = common.basepath(USERDIR) .. modname
local subdirname = USERDIR .. PATHSEP .. modname
if not system.mkdir(subdirname) then
error("cannot create directory: \"" .. subdirname .. "\"")
end
@ -602,7 +602,7 @@ function core.load_user_directory()
if not stat_dir then
create_user_directory()
end
local init_filename = common.basepath(USERDIR) .. "init.lua"
local init_filename = USERDIR .. PATHSEP .. "init.lua"
local stat_file = system.get_file_info(init_filename)
if not stat_file then
write_user_init_file(init_filename)
@ -635,7 +635,7 @@ end
local function add_config_files_hooks()
-- auto-realod style when user's module is saved by overriding Doc:Save()
local doc_save = Doc.save
local user_filename = system.absolute_path(common.basepath(USERDIR) .. "init.lua")
local user_filename = system.absolute_path(USERDIR .. PATHSEP .. "init.lua")
function Doc:save(filename, abs_filename)
local module_filename = system.absolute_path(".lite_project.lua")
doc_save(self, filename, abs_filename)
@ -660,14 +660,17 @@ function core.project_absolute_path(filename)
return common.normalize_path(filename)
elseif not core.project_dir then
local cwd = system.absolute_path(".")
return common.basepath(cwd) .. common.normalize_path(filename)
return cwd .. PATHSEP .. common.normalize_path(filename)
else
return common.basepath(core.project_dir) .. filename
return core.project_dir .. PATHSEP .. filename
end
end
function core.init()
core.log_items = {}
core.log_quiet("Lite XL version %s - mod-version %s", VERSION, MOD_VERSION_STRING)
command = require "core.command"
keymap = require "core.keymap"
dirwatch = require "core.dirwatch"
@ -713,7 +716,7 @@ function core.init()
local file_abs = core.project_absolute_path(arg_filename)
if file_abs then
table.insert(files, file_abs)
project_dir = file_abs:match("^(.+)[:/\\].+$")
project_dir = file_abs:match("^(.+)[/\\].+$")
end
end
end
@ -721,7 +724,6 @@ function core.init()
core.frame_start = 0
core.clip_rect_stack = {{ 0,0,0,0 }}
core.log_items = {}
core.docs = {}
core.cursor_clipboard = {}
core.cursor_clipboard_whole_line = {}
@ -756,7 +758,7 @@ function core.init()
cur_node = cur_node:split("down", core.command_view, {y = true})
cur_node = cur_node:split("down", core.status_view, {y = true})
-- Load defaiult commands first so plugins can override them
-- Load default commands first so plugins can override them
command.add_defaults()
-- Load user module, plugins and project module
@ -784,11 +786,11 @@ function core.init()
end
end
-- Load core plugins after user ones to let the user override them
-- Load core and user plugins giving preference to user ones with same name.
local plugins_success, plugins_refuse_list = core.load_plugins()
do
local pdir, pname = project_dir_abs:match("(.*)[:/\\\\](.*)")
local pdir, pname = project_dir_abs:match("(.*)[/\\\\](.*)")
core.log("Opening project %q from directory %s", pname, pdir)
end
@ -823,15 +825,19 @@ function core.init()
local msg = {}
for _, entry in pairs(plugins_refuse_list) do
if #entry.plugins > 0 then
msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(entry.plugins, "\n"))
local msg_list = {}
for _, p in pairs(entry.plugins) do
table.insert(msg_list, string.format("%s[%s]", p.file, p.version_string))
end
msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(msg_list, "\n"))
end
end
core.nag_view:show(
"Refused Plugins",
string.format(
"Some plugins are not loaded due to version mismatch.\n\n%s.\n\n" ..
"Some plugins are not loaded due to version mismatch. Expected version %s.\n\n%s.\n\n" ..
"Please download a recent version from https://github.com/lite-xl/lite-xl-plugins.",
table.concat(msg, ".\n\n")),
MOD_VERSION_STRING, table.concat(msg, ".\n\n")),
opt, function(item)
if item.text == "Exit" then os.exit(1) end
end)
@ -878,7 +884,7 @@ function core.delete_temp_files(dir)
dir = type(dir) == "string" and common.normalize_path(dir) or USERDIR
for _, filename in ipairs(system.list_dir(dir) or {}) do
if filename:find(temp_file_prefix, 1, true) == 1 then
os.remove(common.basepath(dir) .. filename)
os.remove(dir .. PATHSEP .. filename)
end
end
end
@ -886,7 +892,7 @@ end
function core.temp_filename(ext, dir)
dir = type(dir) == "string" and common.normalize_path(dir) or USERDIR
temp_file_counter = temp_file_counter + 1
return common.basepath(dir) .. temp_file_prefix
return dir .. PATHSEP .. temp_file_prefix
.. string.format("%06x", temp_file_counter) .. (ext or "")
end
@ -920,6 +926,8 @@ function core.restart()
end
local mod_version_regex =
regex.compile([[--.*mod-version:(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:$|\s)]])
local function get_plugin_details(filename)
local info = system.get_file_info(filename)
if info ~= nil and info.type == "dir" then
@ -931,17 +939,32 @@ local function get_plugin_details(filename)
if not f then return false end
local priority = false
local version_match = false
local major, minor, patch
for line in f:lines() do
if not version_match then
local mod_version = line:match('%-%-.*%f[%a]mod%-version%s*:%s*(%d+)')
if mod_version then
version_match = (mod_version == MOD_VERSION)
local _major, _minor, _patch = mod_version_regex:match(line)
if _major then
_major = tonumber(_major) or 0
_minor = tonumber(_minor) or 0
_patch = tonumber(_patch) or 0
major, minor, patch = _major, _minor, _patch
version_match = major == MOD_VERSION_MAJOR
if version_match then
version_match = minor <= MOD_VERSION_MINOR
end
if version_match then
version_match = patch <= MOD_VERSION_PATCH
end
end
end
if not priority then
priority = line:match('%-%-.*%f[%a]priority%s*:%s*(%d+)')
if priority then priority = tonumber(priority) end
end
if version_match then
break
end
@ -949,6 +972,7 @@ local function get_plugin_details(filename)
f:close()
return true, {
version_match = version_match,
version = major and {major, minor, patch} or {},
priority = priority or 100
}
end
@ -962,7 +986,7 @@ function core.load_plugins()
}
local files, ordered = {}, {}
for _, root_dir in ipairs {DATADIR, USERDIR} do
local plugin_dir = common.basepath(root_dir) .. "plugins"
local plugin_dir = root_dir .. PATHSEP .. "plugins"
for _, filename in ipairs(system.list_dir(plugin_dir) or {}) do
if not files[filename] then
table.insert(
@ -977,13 +1001,15 @@ function core.load_plugins()
for _, plugin in ipairs(ordered) do
local dir = files[plugin.file]
local name = plugin.file:match("(.-)%.lua$") or plugin.file
local is_lua_file, details = get_plugin_details(common.basepath(dir) .. plugin.file)
local is_lua_file, details = get_plugin_details(dir .. PATHSEP .. plugin.file)
plugin.valid = is_lua_file
plugin.name = name
plugin.dir = dir
plugin.priority = details and details.priority or 100
plugin.version_match = details and details.version_match or false
plugin.version = details and details.version or {}
plugin.version_string = #plugin.version > 0 and table.concat(plugin.version, ".") or "unknown"
end
-- sort by priority or name for plugins that have same priority
@ -999,27 +1025,35 @@ function core.load_plugins()
if plugin.valid then
if not config.skip_plugins_version and not plugin.version_match then
core.log_quiet(
"Version mismatch for plugin %q from %s",
"Version mismatch for plugin %q[%s] from %s",
plugin.name,
plugin.version_string,
plugin.dir
)
local rlist = plugin.dir:find(USERDIR, 1, true) == 1
and 'userdir' or 'datadir'
local list = refused_list[rlist].plugins
table.insert(list, plugin.file)
table.insert(list, plugin)
elseif config.plugins[plugin.name] ~= false then
local start = system.get_time()
local ok = core.try(require, "plugins." .. plugin.name)
local ok, loaded_plugin = core.try(require, "plugins." .. plugin.name)
if ok then
local plugin_version = ""
if plugin.version_string ~= MOD_VERSION_STRING then
plugin_version = "["..plugin.version_string.."]"
end
core.log_quiet(
"Loaded plugin %q from %s in %.1fms",
"Loaded plugin %q%s from %s in %.1fms",
plugin.name,
plugin_version,
plugin.dir,
(system.get_time() - start) * 1000
)
end
if not ok then
no_errors = false
elseif config.plugins[plugin.name].onload then
core.try(config.plugins[plugin.name].onload, loaded_plugin)
end
end
end
@ -1073,6 +1107,7 @@ function core.set_active_view(view)
-- Reset the IME even if the focus didn't change
ime.stop()
if view ~= core.active_view then
system.text_input(view:supports_text_input())
if core.active_view and core.active_view.force_focus then
core.next_active_view = view
return
@ -1261,6 +1296,12 @@ function core.on_event(type, ...)
if not core.root_view:on_mouse_wheel(...) then
did_keymap = keymap.on_mouse_wheel(...)
end
elseif type == "touchpressed" then
core.root_view:on_touch_pressed(...)
elseif type == "touchreleased" then
core.root_view:on_touch_released(...)
elseif type == "touchmoved" then
core.root_view:on_touch_moved(...)
elseif type == "resized" then
core.window_mode = system.get_window_mode()
elseif type == "minimized" or type == "maximized" or type == "restored" then
@ -1310,6 +1351,11 @@ function core.step()
did_keymap = false
elseif type == "mousemoved" then
core.try(core.on_event, type, a, b, c, d)
elseif type == "enteringforeground" then
-- to break our frame refresh in two if we get entering/entered at the same time.
-- required to avoid flashing and refresh issues on mobile
core.redraw = true
break
else
local _, res = core.try(core.on_event, type, a, b, c, d)
did_keymap = res or did_keymap
@ -1366,11 +1412,10 @@ local run_threads = coroutine.wrap(function()
else
core.threads[k] = nil
end
elseif wait then
else
wait = wait or (1/30)
thread.wake = system.get_time() + wait
minimal_time_to_wake = math.min(minimal_time_to_wake, wait)
else
minimal_time_to_wake = 0
end
else
minimal_time_to_wake = math.min(minimal_time_to_wake, thread.wake - system.get_time())
@ -1451,7 +1496,7 @@ end
function core.on_error(err)
-- write error to file
local fp = io.open(common.basepath(USERDIR) .. "error.txt", "wb")
local fp = io.open(USERDIR .. PATHSEP .. "error.txt", "wb")
fp:write("Error: " .. tostring(err) .. "\n")
fp:write(debug.traceback("", 4) .. "\n")
fp:close()
@ -1464,4 +1509,15 @@ function core.on_error(err)
end
local alerted_deprecations = {}
---Show deprecation notice once per `kind`.
---
---@param kind string
function core.deprecation_log(kind)
if alerted_deprecations[kind] then return end
alerted_deprecations[kind] = true
core.warn("Used deprecated functionality [%s]. Check if your plugins are up to date.", kind)
end
return core

View File

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

View File

@ -24,11 +24,9 @@ keymap.map = {}
keymap.reverse_map = {}
local macos = PLATFORM == "Mac OS X"
local os4 = PLATFORM == "AmigaOS 4"
local mos = PLATFORM == "MORPHOS"
-- Thanks to mathewmariani, taken from his lite-macos github repository.
local modkeys_os = require("core.modkeys-" .. (macos and "macos" or os4 and "os4" or mos and "mos" or "generic"))
local modkeys_os = require("core.modkeys-" .. (macos and "macos" or "generic"))
---@type table<keymap.modkey, keymap.modkey>
local modkey_map = modkeys_os.map
@ -325,6 +323,8 @@ keymap.add_direct {
["wheel"] = "root:scroll",
["hwheel"] = "root:horizontal-scroll",
["shift+wheel"] = "root:horizontal-scroll",
["wheelup"] = "root:scroll-hovered-tabs-backward",
["wheeldown"] = "root:scroll-hovered-tabs-forward",
["ctrl+f"] = "find-replace:find",
["ctrl+r"] = "find-replace:replace",
@ -341,6 +341,7 @@ keymap.add_direct {
["ctrl+x"] = "doc:cut",
["ctrl+c"] = "doc:copy",
["ctrl+v"] = "doc:paste",
["insert"] = "doc:toggle-overwrite",
["ctrl+insert"] = "doc:copy",
["shift+insert"] = "doc:paste",
["escape"] = { "command:escape", "doc:select-none", "dialog:select-no" },
@ -390,7 +391,7 @@ keymap.add_direct {
["shift+1lclick"] = "doc:select-to-cursor",
["ctrl+1lclick"] = "doc:split-cursor",
["1lclick"] = "doc:set-cursor",
["2lclick"] = "doc:set-cursor-word",
["2lclick"] = { "doc:set-cursor-word", "emptyview:new-doc", "tabbar:new-doc" },
["3lclick"] = "doc:set-cursor-line",
["shift+left"] = "doc:select-to-previous-char",
["shift+right"] = "doc:select-to-next-char",

View File

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

View File

@ -1,15 +0,0 @@
local modkeys = {}
modkeys.map = {
["left amiga"] = "cmd",
["right amiga"] = "cmd",
["control"] = "ctrl",
["left shift"] = "shift",
["right shift"] = "shift",
["left alt"] = "alt",
["right alt"] = "altgr",
}
modkeys.keys = { "cmd", "ctrl", "alt", "altgr", "shift" }
return modkeys

View File

@ -1,15 +0,0 @@
local modkeys = {}
modkeys.map = {
["left amiga"] = "cmd",
["right amiga"] = "cmd",
["control"] = "ctrl",
["left shift"] = "shift",
["right shift"] = "shift",
["left alt"] = "alt",
["right alt"] = "altgr",
}
modkeys.keys = { "cmd", "ctrl", "alt", "altgr", "shift" }
return modkeys

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -128,6 +128,7 @@ syntax.add {
{ pattern = { "```go", "```" }, type = "string", syntax = ".go" },
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
{ pattern = { "```nix", "```" }, type = "string", syntax = ".nix" },
{ pattern = { "```", "```" }, type = "string" },
{ pattern = { "``", "``" }, type = "string" },
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" },

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ local LogView = require "core.logview"
local function workspace_files_for(project_dir)
local basename = common.basename(project_dir)
local workspace_dir = common.basepath(USERDIR) .. "ws"
local workspace_dir = USERDIR .. PATHSEP .. "ws"
local info_wsdir = system.get_file_info(workspace_dir)
if not info_wsdir then
local ok, err = system.mkdir(workspace_dir)
@ -22,7 +22,7 @@ local function workspace_files_for(project_dir)
if file:sub(1, n) == basename then
local id = tonumber(file:sub(n + 1):match("^-(%d+)$"))
if id then
coroutine.yield(common.basepath(workspace_dir) .. file, id)
coroutine.yield(workspace_dir .. PATHSEP .. file, id)
end
end
end
@ -52,7 +52,7 @@ local function get_workspace_filename(project_dir)
id = id + 1
end
local basename = common.basename(project_dir)
return common.basepath(USERDIR) .. "ws" .. PATHSEP .. basename .. "-" .. tostring(id)
return USERDIR .. PATHSEP .. "ws" .. PATHSEP .. basename .. "-" .. tostring(id)
end
@ -166,7 +166,7 @@ local function load_node(node, t)
active_view = view
end
if not view:is(DocView) then
view.scroll = v.scroll
view.scroll = v.scroll
end
end
end

View File

@ -324,5 +324,13 @@ function system.load_native_plugin(name, path) end
---@return boolean compare_result True if path1 < path2
function system.path_compare(path1, type1, path2, type2) end
---
---Sets an environment variable.
---The converse of os.getenv.
---
---@param key string
---@param val string
---@return boolean ok True if call succeeded
function system.setenv(key, val) end
return system

View File

@ -101,7 +101,7 @@ if not get_option('source-only')
endforeach
else
lua_dep = dependency('', fallback: ['lua', 'lua_dep'], required : true,
default_options: default_fallback_options + ['default_library=static', 'line_editing=false', 'interpreter=false']
default_options: default_fallback_options + ['default_library=static', 'line_editing=disabled', 'interpreter=false']
)
endif
@ -208,10 +208,10 @@ else
install_data('resources/icons/lite-xl.svg',
install_dir : 'share/icons/hicolor/scalable/apps'
)
install_data('resources/linux/org.lite_xl.lite_xl.desktop',
install_data('resources/linux/com.lite_xl.LiteXL.desktop',
install_dir : 'share/applications'
)
install_data('resources/linux/org.lite_xl.lite_xl.appdata.xml',
install_data('resources/linux/com.lite_xl.LiteXL.appdata.xml',
install_dir : 'share/metainfo'
)
endif

View File

@ -1,10 +0,0 @@
; Lite-XL AutoInstall
; $VER: Lite-XL AutoInstall 1.0 (15.02.2024)
; Get the path to the executable from the ENV variable
Set litexlPath `GetEnv AppDir/lite-xl`
copy LiteXL2/#? "$litexlPath" CLONE ALL
; Free the variable
UnSet litexlPath

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,29 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#02103d" }
style.background2 = { common.color "#02103d" }
style.background3 = { common.color "#02103d" }
style.text = { common.color "#0f6773" }
style.caret = { common.color "#6a8ca8" }
style.accent = { common.color "#6a8ca8" }
style.dim = { common.color "#303030" }
style.divider = { common.color "#151515" }
style.selection = { common.color "#242424" }
style.line_number = { common.color "#252525" }
style.line_number2 = { common.color "#444444" }
style.line_highlight = { common.color "#101010" }
style.scrollbar = { common.color "#252525" }
style.scrollbar2 = { common.color "#444444" }
style.syntax = {}
style.syntax["normal"] = { common.color "#a0a0a0" }
style.syntax["symbol"] = { common.color "#a0a0a0" }
style.syntax["comment"] = { common.color "#404040" }
style.syntax["keyword"] = { common.color "#dfdfdf" }
style.syntax["keyword2"] = { common.color "#dfdfdf" }
style.syntax["number"] = { common.color "#dfdfdf" }
style.syntax["literal"] = { common.color "#dfdfdf" }
style.syntax["string"] = { common.color "#132a52" }
style.syntax["operator"] = { common.color "#01A870" }
style.syntax["function"] = { common.color "#01A870" }

View File

@ -1,48 +0,0 @@
local style = require "core.style"
local common = require "core.common"
-- App --
style.background = { common.color "#222831" }
style.background2 = { common.color "#1e232b" }
style.background3 = { common.color "#1e232b" }
style.text = { common.color "#dfe2e7" }
style.caret = { common.color "#dfe2e7" }
style.accent = { common.color "#e2e4e9" }
style.dim = { common.color "#8893a5" }
style.divider = { common.color "#1e232b" }
style.selection = { common.color "#2c3440" }
style.line_number = { common.color "#8893a5" }
style.line_number2 = { common.color "#8893a5" }
style.line_highlight = { common.color "#242a34" }
style.scrollbar = { common.color "#2c3440" }
style.scrollbar2 = { common.color "#f5ad44" }
style.scrollbar_track = { common.color "#00000000" }
style.nagbar = { common.color "#db504a" }
style.nagbar_text = { common.color "#dfe2e7" }
style.nagbar_dim = { common.color "rgba(0, 0, 0, 0.45)" }
style.drag_overlay = { common.color "#dfe2e733" }
style.drag_overlay_tab = { common.color "#f5ad44" }
style.good = { common.color "#47e2b1" }
style.warn = { common.color "#f5ad44" }
style.error = { common.color "#db504a" }
style.modified = { common.color "#448bf5" }
-- Syntax --
style.syntax = {}
style.syntax["normal"] = { common.color "#dfe2e7" }
style.syntax["symbol"] = { common.color "#dfe2e7" }
style.syntax["comment"] = { common.color "#8893a5" }
style.syntax["keyword"] = { common.color "#448bf5" }
style.syntax["keyword2"] = { common.color "#f5ad44" }
style.syntax["number"] = { common.color "#f5ad44" }
style.syntax["literal"] = { common.color "#45e1df" }
style.syntax["string"] = { common.color "#f5ad44" }
style.syntax["operator"] = { common.color "#dfe2e7" }
style.syntax["function"] = { common.color "#f786aa" }
-- Lint+ --
style.lint = {}
style.lint["info"] = { common.color "#448bf5" }
style.lint["hint"] = { common.color "#47e2b1" }
style.lint["warning"] = { common.color "#f5ad44" }
style.lint["error"] = { common.color "#db504a" }

View File

@ -1,29 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#03071e" }
style.background2 = { common.color "#03071e" }
style.background3 = { common.color "#03071e" }
style.text = { common.color "#ffa848" }
style.caret = { common.color "#ffa848" }
style.accent = { common.color "#ffb86c" }
style.dim = { common.color "#4f526b" }
style.divider = { common.color "#22242e" }
style.selection = { common.color "#4c5163" }
style.line_number = { common.color "#44475a" }
style.line_number2 = { common.color "#717796" }
style.line_highlight = { common.color "#2d303d" }
style.scrollbar = { common.color "#44475a" }
style.scrollbar2 = { common.color "#4c5163" }
style.syntax = {}
style.syntax["normal"] = { common.color "#f5faff" }
style.syntax["symbol"] = { common.color "#f5faff" }
style.syntax["comment"] = { common.color "#081355" }
style.syntax["keyword"] = { common.color "#fc0fc0" }
style.syntax["keyword2"] = { common.color "#05e6fa" }
style.syntax["number"] = { common.color "#7612c5" }
style.syntax["literal"] = { common.color "#7612c5" }
style.syntax["string"] = { common.color "#fdd017" }
style.syntax["operator"] = { common.color "#fc0fc0" }
style.syntax["function"] = { common.color "#05e6fa" }

View File

@ -1,28 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#073642" }
style.background2 = { common.color "#073642" }
style.background3 = { common.color "#073642" }
style.text = { common.color "#00d1d1" }
style.caret = { common.color "#f053f3" }
style.accent = { common.color "#f053f3" }
style.dim = { common.color "#586e75" }
style.divider = { common.color "#6c71c4" }
style.selection = { common.color "#415256" }
style.line_number = { common.color "#586e75" }
style.line_number2 = { common.color "#f053f3" }
style.line_highlight = { common.color "#415256" }
style.scrollbar = { common.color "#6c71c4" }
style.scrollbar2 = { common.color "#6c71c4" }
style.syntax["normal"] = { common.color "#00d1d1" }
style.syntax["symbol"] = { common.color "#00ff7f" }
style.syntax["comment"] = { common.color "#6c71c4" }
style.syntax["keyword"] = { common.color "#6c71c4" }
style.syntax["keyword2"] = { common.color "#6c71c4" }
style.syntax["number"] = { common.color "#00ff7f" }
style.syntax["literal"] = { common.color "#1586d2" }
style.syntax["string"] = { common.color "#f7f97d" }
style.syntax["operator"] = { common.color "#00ff7f" }
style.syntax["function"] = { common.color "#55ffff" }

View File

@ -1,28 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#282a36" }
style.background2 = { common.color "#21222b" }
style.background3 = { common.color "#21222b" }
style.text = { common.color "#7b81a6" }
style.caret = { common.color "#f8f8f0" }
style.accent = { common.color "#8be9fd" }
style.dim = { common.color "#4f5873" }
style.divider = { common.color "#1f2029" }
style.selection = { common.color "#44475a" }
style.line_number = { common.color "#53576e" }
style.line_number2 = { common.color "#f8f8f0" }
style.line_highlight = { common.color "#313442" }
style.scrollbar = { common.color "#44475a" }
style.scrollbar2 = { common.color "#ff79c6" }
style.syntax["normal"] = { common.color "#f8f8f2" }
style.syntax["symbol"] = { common.color "#f8f8f2" }
style.syntax["comment"] = { common.color "#6272a4" }
style.syntax["keyword"] = { common.color "#ff79c6" }
style.syntax["keyword2"] = { common.color "#ff79c6" }
style.syntax["number"] = { common.color "#bd93f9" }
style.syntax["literal"] = { common.color "#f1fa8c" }
style.syntax["string"] = { common.color "#f1fa8c" }
style.syntax["operator"] = { common.color "#ff79c6" }
style.syntax["function"] = { common.color "#50fa7b" }

View File

@ -1,38 +0,0 @@
local style = require "core.style"
local common = require "core.common"
math.randomseed(os.time())
local color = {
math.random(90, 255),
math.random(90, 255),
math.random(90, 255),
255
}
style.background = { common.color "#151515" }
style.background2 = { common.color "#151515" }
style.background3 = { common.color "#151515" }
style.text = { common.color "#707070" }
style.caret = { common.color "#dfdfdf" }
style.accent = { common.color "#d0d0d0" }
style.dim = { common.color "#303030" }
style.divider = { common.color "#151515" }
style.selection = { common.color "#303030" }
style.line_number = { common.color "#252525" }
style.line_number2 = { common.color "#444444" }
style.line_highlight = { common.color "#101010" }
style.scrollbar = { common.color "#252525" }
style.scrollbar2 = { common.color "#444444" }
style.syntax = {}
style.syntax["normal"] = { common.color "#a0a0a0" }
style.syntax["symbol"] = { common.color "#a0a0a0" }
style.syntax["comment"] = { common.color "#404040" }
style.syntax["keyword"] = { common.color "#dfdfdf" }
style.syntax["keyword2"] = { common.color "#dfdfdf" }
style.syntax["number"] = { common.color "#dfdfdf" }
style.syntax["literal"] = { common.color "#dfdfdf" }
style.syntax["string"] = { common.color "#dfdfdf" }
style.syntax["operator"] = color
style.syntax["function"] = color

View File

@ -1,29 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#151515" }
style.background2 = { common.color "#151515" }
style.background3 = { common.color "#151515" }
style.text = { common.color "#707070" }
style.caret = { common.color "#dfdfdf" }
style.accent = { common.color "#d0d0d0" }
style.dim = { common.color "#303030" }
style.divider = { common.color "#151515" }
style.selection = { common.color "#242424" }
style.line_number = { common.color "#252525" }
style.line_number2 = { common.color "#444444" }
style.line_highlight = { common.color "#101010" }
style.scrollbar = { common.color "#252525" }
style.scrollbar2 = { common.color "#444444" }
style.syntax = {}
style.syntax["normal"] = { common.color "#a0a0a0" }
style.syntax["symbol"] = { common.color "#a0a0a0" }
style.syntax["comment"] = { common.color "#404040" }
style.syntax["keyword"] = { common.color "#dfdfdf" }
style.syntax["keyword2"] = { common.color "#dfdfdf" }
style.syntax["number"] = { common.color "#dfdfdf" }
style.syntax["literal"] = { common.color "#dfdfdf" }
style.syntax["string"] = { common.color "#dfdfdf" }
style.syntax["operator"] = { common.color "#01A870" }
style.syntax["function"] = { common.color "#01A870" }

View File

@ -1,38 +0,0 @@
local style = require "core.style"
local common = require "core.common"
-- GitHub color palette
-- Ported by Andrey Proskurin (proskur1n)
local bg = { common.color "#22272e" }
local bg2 = { common.color "#2d333b" }
local fg = { common.color "#adbac7" }
local fgdim = { common.color "#768390" }
local red = { common.color "#f47067" }
local blue = { common.color "#6cb6ff" }
local purple = { common.color "#dcbdfb" }
style.background = bg
style.background2 = bg
style.background3 = bg
style.text = fg
style.caret = red
style.accent = blue
style.dim = fgdim
style.divider = { common.color "#444c56" }
style.selection = { common.color "#2e4c77" }
style.line_number = fgdim
style.line_number2 = fg
style.line_highlight = bg2
style.scrol = fgdim
style.scrollbar2 = fg
style.syntax["normal"] = fg
style.syntax["symbol"] = fg
style.syntax["comment"] = fgdim
style.syntax["keyword"] = red
style.syntax["keyword2"] = red
style.syntax["number"] = blue
style.syntax["literal"] = blue
style.syntax["string"] = { common.color "#96d0ff" }
style.syntax["operator"] = fg
style.syntax["function"] = blue

View File

@ -1,41 +0,0 @@
local style = require "core.style"
local common = require "core.common"
-- GitHub color palette
-- Ported by Andrey Proskurin (proskur1n)
local bg = { common.color "#0d1117" }
local bg2 = { common.color "#161925" }
local fg = { common.color "#adbac7" }
local fgdim = { common.color "#768390" }
local red = { common.color "#f47067" }
local blue = { common.color "#6cb6ff" }
local purple = { common.color "#dcbdfb" }
style.background = bg
style.background2 = bg
style.background3 = bg2
style.text = fg
style.caret = red
style.accent = blue
style.dim = fgdim
style.divider = { common.color "#444c56" }
style.selection = { common.color "#2e4c77" }
style.line_number = fgdim
style.line_number2 = fg
style.line_highlight = {common.color "#1e202e"}
style.scrollbar = fgdim
style.scrollbar2 = fg
style.syntax["normal"] = fg
style.syntax["symbol"] = fg
style.syntax["comment"] = fgdim
style.syntax["keyword"] = red
style.syntax["keyword2"] = red
style.syntax["number"] = blue
style.syntax["literal"] = blue
style.syntax["string"] = { common.color "#96d0ff" }
style.syntax["operator"] = fg
style.syntax["function"] = blue
style.guide = { common.color "#404040" } -- indentguide

View File

@ -1,28 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#282828" }
style.background2 = { common.color "#1d2021" }
style.background3 = { common.color "#1d2021" }
style.text = { common.color "#928374" }
style.caret = { common.color "#fbf1c7" }
style.accent = { common.color "#ebdbb2" }
style.dim = { common.color "#928374" }
style.divider = { common.color "#1d2021" }
style.selection = { common.color "#3c3836" }
style.line_number = { common.color "#928374" }
style.line_number2 = { common.color "#ebdbb2" }
style.line_highlight = { common.color "#32302f" }
style.scrollbar = { common.color "#928374" }
style.scrollbar2 = { common.color "#fbf1c7" }
style.syntax["normal"] = { common.color "#ebdbb2" }
style.syntax["symbol"] = { common.color "#ebdbb2" }
style.syntax["comment"] = { common.color "#928374" }
style.syntax["keyword"] = { common.color "#fb4934" }
style.syntax["keyword2"] = { common.color "#83a598" }
style.syntax["number"] = { common.color "#d3869b" }
style.syntax["literal"] = { common.color "#d3869b" }
style.syntax["string"] = { common.color "#b8bb26" }
style.syntax["operator"] = { common.color "#ebdbb2" }
style.syntax["function"] = { common.color "#8ec07c" }

View File

@ -1,37 +0,0 @@
-- Colors from: https://github.com/nanotech/jellybeans.vim
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#151515" }
style.background2 = { common.color "#212121" }
style.background3 = { common.color "#212121" }
style.text = { common.color "#e8e8d3" }
style.caret = { common.color "#e8e8d3" }
style.accent = { common.color "#597bc5" } -- Text in autocomplete and command, col(>80) in satusbar
style.dim = { common.color "#888888" } -- Text of nonactive tabs, prefix in log
style.divider = { common.color "#151515" }
style.selection = { common.color "#404040" }
style.line_number = { common.color "#3b3b3b" }
style.line_number2 = { common.color "#888888" } -- Number on line with caret
style.line_highlight = { common.color "#191919"}
style.scrollbar = { common.color "#2e2e2e" }
style.scrollbar2 = { common.color "#3b3b3b" } -- Hovered
style.syntax["normal"] = { common.color "#6b8b9b" }
style.syntax["symbol"] = { common.color "#e8e8d3" }
style.syntax["comment"] = { common.color "#888888" }
style.syntax["keyword"] = { common.color "#8197bf" } -- local function end, if case
style.syntax["keyword2"] = { common.color "#FFB964" } -- self, int float
style.syntax["number"] = { common.color "#cf6a4c" }
style.syntax["literal"] = { common.color "#8FBFDC" }
style.syntax["string"] = { common.color "#99ad6a" }
style.syntax["operator"] = { common.color "#8FBFDC"} -- = + - / < >
style.syntax["function"] = { common.color "#FAD07A" }
-- PLUGINS
style.linter_warning = { common.color "#d8ad4c" } -- linter
style.bracketmatch_color = { common.color "#8197bf" } -- bracketmatch
style.guide = { common.color "#3b3b3b" }
style.guide_highlight = { common.color "#5b5b5b" } -- indentguide
style.guide_width = 1 -- indentguide

View File

@ -1,32 +0,0 @@
-- Liqube Dark Code for Lite <liqube.com>
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#13171e" }
style.background2 = { common.color "#21252b" }
style.background3 = { common.color "#21252b" }
style.text = { common.color "#abb2bf" }
style.caret = { common.color "#abb2bf" }
style.accent = { common.color "#ffffff" }
style.dim = { common.color "#545e70" }
style.divider = { common.color "#242223" }
style.selection = { common.color "#3e4451" }
style.line_number = { common.color "#323641" }
style.line_number2 = { common.color "#596275" }
style.line_highlight = { common.color "#1c1f25" }
style.scrollbar = { common.color "#3d3f43" }
style.scrollbar2 = { common.color "#595b5f" }
style.guide = { common.color "#1c1f25" } -- indentguide
style.syntax["normal"] = { common.color "#abb2bf" }
style.syntax["symbol"] = { common.color "#71a9d7" }
style.syntax["comment"] = { common.color "#5c6370" }
style.syntax["keyword"] = { common.color "#98c875" }
style.syntax["keyword2"] = { common.color "#ffffff" }
style.syntax["number"] = { common.color "#ffffff" }
style.syntax["literal"] = { common.color "#ea5964" }
style.syntax["string"] = { common.color "#ea5964" }
style.syntax["operator"] = { common.color "#657085" }
style.syntax["function"] = { common.color "#ffffff" }
style.syntax["preprocessor"] = { common.color "#98c875" } -- thinking ahead

View File

@ -1,28 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#303841" }
style.background2 = { common.color "#1d2227" }
style.background3 = { common.color "#1d2227" }
style.text = { common.color "#9ea191" }
style.caret = { common.color "#61efce" }
style.accent = { common.color "#ffd152" }
style.dim = { common.color "#4c5863" }
style.divider = { common.color "#242223" }
style.selection = { common.color "#4c5863" }
style.line_number = { common.color "#bfc5d0" }
style.line_number2 = { common.color "#848b95" }
style.line_highlight = { common.color "#303841" }
style.scrollbar = { common.color "#696f75" }
style.scrollbar2 = { common.color "#444b53" }
style.syntax["normal"] = { common.color "#d7dde9" }
style.syntax["symbol"] = { common.color "#d8dee9" }
style.syntax["comment"] = { common.color "#a6acb9" }
style.syntax["keyword"] = { common.color "#e55e66" }
style.syntax["keyword2"] = { common.color "#ef6179" }
style.syntax["number"] = { common.color "#ffd152" }
style.syntax["literal"] = { common.color "#e75550" }
style.syntax["string"] = { common.color "#939d5d" }
style.syntax["operator"] = { common.color "#c2674f" }
style.syntax["function"] = { common.color "#6699ca" }

View File

@ -1,29 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#080808" }
style.background2 = { common.color "#080808" }
style.background3 = { common.color "#101010" }
style.text = { common.color "#707070" }
style.caret = { common.color "#ffffff" }
style.accent = { common.color "#d0d0d0" }
style.dim = { common.color "#303030" }
style.divider = { common.color "#080808" }
style.selection = { common.color "#242424" }
style.line_number = { common.color "#202020" }
style.line_number2 = { common.color "#707070" }
style.line_highlight = { common.color "#101010" }
style.scrollbar = { common.color "#252525" }
style.scrollbar2 = { common.color "#303030" }
style.syntax = {}
style.syntax["normal"] = { common.color "#a0a0a0" }
style.syntax["symbol"] = { common.color "#a0a0a0" }
style.syntax["comment"] = { common.color "#404040" }
style.syntax["keyword"] = { common.color "#f0f0f0" }
style.syntax["keyword2"] = { common.color "#f0f0f0" }
style.syntax["number"] = { common.color "#f0f0f0" }
style.syntax["literal"] = { common.color "#f0f0f0" }
style.syntax["string"] = { common.color "#f0f0f0" }
style.syntax["operator"] = { common.color "#f0f0f0" }
style.syntax["function"] = { common.color "#a0a0a0" }

View File

@ -1,28 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#272822" }
style.background2 = { common.color "#22231C" }
style.background3 = { common.color "#22231C" }
style.text = { common.color "#9ea191" }
style.caret = { common.color "#F8F8F0" }
style.accent = { common.color "#F8F8F2" }
style.dim = { common.color "#5e6052" }
style.divider = { common.color "#1b1c17" }
style.selection = { common.color "#49483E" }
style.line_number = { common.color "#75715E" }
style.line_number2 = { common.color "#d2d0c6" }
style.line_highlight = { common.color "#36372f" }
style.scrollbar = { common.color "#49483E" }
style.scrollbar2 = { common.color "#636254" }
style.syntax["normal"] = { common.color "#F8F8F2" }
style.syntax["symbol"] = { common.color "#F8F8F2" }
style.syntax["comment"] = { common.color "#75715E" }
style.syntax["keyword"] = { common.color "#F92672" }
style.syntax["keyword2"] = { common.color "#66DAEF" }
style.syntax["number"] = { common.color "#AE81FF" }
style.syntax["literal"] = { common.color "#AE81FF" }
style.syntax["string"] = { common.color "#E6DB74" }
style.syntax["operator"] = { common.color "#F8F8F2" }
style.syntax["function"] = { common.color "#A6E22E" }

View File

@ -1,28 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#282923" }
style.background2 = { common.color "#181915" }
style.background3 = { common.color "#181915" }
style.text = { common.color "#9ea191" }
style.caret = { common.color "#f8f8f2" }
style.accent = { common.color "#f8f8f2" }
style.dim = { common.color "#5e6052" }
style.divider = { common.color "#1b1c17" }
style.selection = { common.color "#3a3a32" }
style.line_number = { common.color "#90918b" }
style.line_number2 = { common.color "#d2d0c6" }
style.line_highlight = { common.color "#282923" }
style.scrollbar = { common.color "#63635f" }
style.scrollbar2 = { common.color "#3d3d38" }
style.syntax["normal"] = { common.color "#f8f8f2" }
style.syntax["symbol"] = { common.color "#f8f8f2" }
style.syntax["comment"] = { common.color "#75715E" }
style.syntax["keyword"] = { common.color "#f92472" }
style.syntax["keyword2"] = { common.color "#f92472" }
style.syntax["number"] = { common.color "#ac80ff" }
style.syntax["literal"] = { common.color "#e7db74" }
style.syntax["string"] = { common.color "#e7db74" }
style.syntax["operator"] = { common.color "#f92472" }
style.syntax["function"] = { common.color "#5cd5ef" }

View File

@ -1,39 +0,0 @@
local style = require "core.style"
local common = require "core.common"
local config = require "core.config"
style.background = { common.color "#2E3440" }
style.background2 = { common.color "#2E3440" }
style.background3 = { common.color "#3B4252" }
style.text = { common.color "#D8DEE9" }
style.caret = { common.color "#D8DEE9" }
style.accent = { common.color "#88C0D0" }
style.dim = { common.color "#d8dee966" }
style.divider = { common.color "#3B4252" }
style.selection = { common.color "#434C5ECC" }
style.line_number = { common.color "#4C566A" }
style.line_number2 = { common.color "#D8DEE9" }
style.line_highlight = { common.color "#3B4252" }
style.scrollbar = { common.color "#434c5eaa" }
style.scrollbar2 = { common.color "#434c5e" }
style.good = { common.color "#72b886cc" }
style.warn = { common.color "#d08770" }
style.error = { common.color "#bf616a" }
style.modified = { common.color "#ebcb8b" }
style.syntax["normal"] = { common.color "#ECEFF4" }
style.syntax["symbol"] = { common.color "#D8DEE9" }
style.syntax["comment"] = { common.color "#616E88" }
style.syntax["keyword"] = { common.color "#81A1C1" }
style.syntax["keyword2"] = { common.color "#81A1C1" }
style.syntax["number"] = { common.color "#B48EAD" }
style.syntax["literal"] = { common.color "#81A1C1" }
style.syntax["string"] = { common.color "#A3BE8C" }
style.syntax["operator"] = { common.color "#81A1C1" }
style.syntax["function"] = { common.color "#88C0D0" }
config.highlight_current_line = "no_selection"
style.guide = { common.color "#434c5eb3" }
style.bracketmatch_color = { common.color "#8fbcbb" }

View File

@ -1,29 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#282c34" }
style.background2 = { common.color "#21252B" }
style.background3 = { common.color "#21252B" }
style.text = { common.color "#abb2bf" }
style.caret = { common.color "#528bff" }
style.accent = { common.color "#ffffff" }
style.dim = { common.color "#4f5873" }
style.divider = { common.color "#181A1F" }
style.selection = { common.color "#383D49" }
style.line_number = { common.color "#53576e" }
style.line_number2 = { common.color "#666B76" }
style.line_highlight = { common.color "#2C333E" }
style.scrollbar = { common.color "#4f5873" }
style.scrollbar2 = { common.color "#3060C1" }
style.syntax["normal"] = { common.color "#abb2bf" }
style.syntax["symbol"] = { common.color "#abb2bf" }
style.syntax["comment"] = { common.color "#5f697a" }
style.syntax["keyword"] = { common.color "#cd74e8" }
style.syntax["keyword2"] = { common.color "#eb6772" }
style.syntax["number"] = { common.color "#db9d63" }
style.syntax["literal"] = { common.color "#e6c07b" }
style.syntax["string"] = { common.color "#9acc76" }
style.syntax["operator"] = { common.color "#56B6C2" }
style.syntax["function"] = { common.color "#5cb3fa" }

View File

@ -1,31 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#242424" }
style.background2 = { common.color "#252528" }
style.background3 = { common.color "#44475A" }
style.text = { common.color "#fffff0" }
style.caret = { common.color "#69FF94" }
style.accent = { common.color "#ff0fff" }
style.dim = { common.color "#0fffff" }
style.divider = { common.color "#7b7f8b" }
style.selection = { common.color "#48484f" }
style.selectionhighlight = { common.color "#dddeee" }
style.line_number = { common.color "#525259" }
style.line_number2 = { common.color "#f6f6e0" }
style.line_highlight = { common.color "#343438" }
style.scrollbar = { common.color "#414146" }
style.scrollbar2 = { common.color "#4b4bff" }
style.syntax["normal"] = { common.color "#e1e1e6" }
style.syntax["symbol"] = { common.color "#97e1f1" }
style.syntax["comment"] = { common.color "#676b6f" }
style.syntax["keyword"] = { common.color "#E58AC9" }
style.syntax["keyword2"] = { common.color "#F77483" }
style.syntax["number"] = { common.color "#FFA94D" }
style.syntax["literal"] = { common.color "#ee6666" }
style.syntax["string"] = { common.color "#f7c95c" }
style.syntax["operator"] = { common.color "#93DDFA" }
style.syntax["function"] = { common.color "#bf9eee" }

View File

@ -1,29 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#222226" }
style.background2 = { common.color "#252528" }
style.background3 = { common.color "#1e1e21" }
style.text = { common.color "#dddddd" }
style.caret = { common.color "#aeafad" }
style.accent = { common.color "#0097fb" }
style.dim = { common.color "#9393a5" }
style.divider = { common.color "#1E1E1E" }
style.selection = { common.color "#264f78" }
style.line_number = { common.color "#858585" }
style.line_number2 = { common.color "#c6c6c6" }
style.line_highlight = { common.color "#2b2b2f"}
style.scrollbar = { common.color "#313136" }
style.scrollbar2 = { common.color "#bfbfbf" }
style.syntax["normal"] = { common.color "#dddddd" }
style.syntax["symbol"] = { common.color "#e06c75" }
style.syntax["comment"] = { common.color "#c5c5c5" }
style.syntax["keyword"] = { common.color "#61afef" }
style.syntax["keyword2"] = { common.color "#56B6C2" }
style.syntax["number"] = { common.color "#d19a66" }
style.syntax["literal"] = { common.color "#61AFEF" }
style.syntax["string"] = { common.color "#98C379" }
style.syntax["operator"] = { common.color "#dddddd" }
style.syntax["function"] = { common.color "#c678dd" }

View File

@ -1,36 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#252336" }
style.background2 = { common.color "#171521" }
style.background3 = { common.color "#171521" }
style.text = { common.color "#8f94bf" }
style.caret = { common.color "#f17e6e" }
style.accent = { common.color "#ff79c6" }
style.dim = { common.color "#4f526b" }
style.divider = { common.color "#171521" }
style.selection = { common.color "#4a445a" }
style.line_number = { common.color "#4a445a" }
style.line_number2 = { common.color "#ff79c6" }
style.line_highlight = { common.color "rgba(0, 0, 0, 0.30)" }
style.scrollbar = { common.color "#4f526b" }
style.scrollbar2 = { common.color "#717382" }
style.nagbar = { common.color "#ff79c6" }
style.nagbar_text = { common.color "#FFFFFF" }
style.nagbar_dim = { common.color "rgba(0, 0, 0, 0.30)" }
style.drag_overlay = { common.color "rgba(0, 0, 0, 0.30)" }
style.drag_overlay_tab = { common.color "#f17e6e" }
style.syntax["normal"] = { common.color "#FFFFFF" }
style.syntax["symbol"] = { common.color "#ff79c6" }
style.syntax["comment"] = { common.color "#9484bd" }
style.syntax["keyword"] = { common.color "#f5de4a" }
style.syntax["keyword2"] = { common.color "#f73f51" }
style.syntax["number"] = { common.color "#bd93f9" }
style.syntax["literal"] = { common.color "#5afad2" }
style.syntax["string"] = { common.color "#ff8b39" }
style.syntax["operator"] = { common.color "#f5de4a" }
style.syntax["function"] = { common.color "#8be9fd" }
style.guide = { common.color "#4a445a" }
style.bracketmatch_color = { common.color "#f17e6e" }

View File

@ -1,37 +0,0 @@
-- Colors from: https://github.com/enkia/tokyo-night-vscode-theme
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#1a1b26" }
style.background2 = { common.color "#16161e" }
style.background3 = { common.color "#24283b" }
style.text = { common.color "#a9b1d6" }
style.caret = { common.color "#a9b1d6" }
style.accent = { common.color "#7aa2f7" } -- Text in autocomplete and command, col(>80) in satusbar
style.dim = { common.color "#565f89" } -- Text of nonactive tabs, prefix in log
style.divider = { common.color "#101014" }
style.selection = { common.color "#282B3C" }
style.line_number = { common.color "#363B54" }
style.line_number2 = { common.color "#737AA2" } -- Number on line with caret
style.line_highlight = { common.color "#1E202E"}
style.scrollbar = { common.color "#24283b" }
style.scrollbar2 = { common.color "#414868" } -- Hovered
style.syntax["normal"] = { common.color "#9ABDF5" }
style.syntax["symbol"] = { common.color "#c0caf5" }
style.syntax["comment"] = { common.color "#414868" }
style.syntax["keyword"] = { common.color "#bb9af7" } -- local function end, if case
style.syntax["keyword2"] = { common.color "#bb9af7" } -- self, int float
style.syntax["number"] = { common.color "#ff9e64" }
style.syntax["literal"] = { common.color "#c0caf5" }
style.syntax["string"] = { common.color "#9ece6a" }
style.syntax["operator"] = { common.color "#2ac3de"} -- = + - / < >
style.syntax["function"] = { common.color "#7aa2f7" }
-- PLUGINS
style.linter_warning = { common.color "#e0af68" } -- linter
style.bracketmatch_color = { common.color "#565f89" } -- bracketmatch
style.guide = { common.color "#1E202E" }
style.guide_highlight = { common.color "#363B54" } -- indentguide
style.guide_width = 1 -- indentguide

View File

@ -1,37 +0,0 @@
-- Most of the colors are taken from:
-- https://github.com/microsoft/vscode/tree/master/extensions/theme-defaults/themes
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#1E1E1E" }
style.background2 = { common.color "#252526" }
style.background3 = { common.color "#252526" }
style.text = { common.color "#D4D4D4" }
style.caret = { common.color "#FFFFFF" }
style.accent = { common.color "#76BCFF" } -- Text in autocomplete and command, col(>80) in satusbar
style.dim = { common.color "#7A7A7A" } -- Text of nonactive tabs, prefix in log
style.divider = { common.color "#1E1E1E" }
style.selection = { common.color "#264F78" }
style.line_number = { common.color "#707070" }
style.line_number2 = { common.color "#A0A0A0" } -- Number on line with caret
style.line_highlight = { common.color "#333A40"}
style.scrollbar = { common.color "#404040" }
style.scrollbar2 = { common.color "#707070" } -- Hovered
style.syntax["normal"] = { common.color "#D4D4D4" }
style.syntax["symbol"] = { common.color "#D4D4D4" }
style.syntax["comment"] = { common.color "#6A9955" }
style.syntax["keyword"] = { common.color "#569CD6" } -- local function end, if case
style.syntax["keyword2"] = { common.color "#C586C0" } -- self, int float
style.syntax["number"] = { common.color "#B5CEA8" }
style.syntax["literal"] = { common.color "#569CD6" }
style.syntax["string"] = { common.color "#CE9178" }
style.syntax["operator"] = { common.color "#8590A5"} -- = + - / < >
style.syntax["function"] = { common.color "#DCDCAA" }
-- PLUGINS
style.linter_warning = { common.color "#B89500" } -- linter
style.bracketmatch_color = { common.color "#76BCFF" } -- bracketmatch
style.guide = { common.color "#404040" } -- indentguide
style.guide_width = 1 -- indentguide

View File

@ -1,28 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#282a36" }
style.background2 = { common.color "#22242e" }
style.background3 = { common.color "#22242e" }
style.text = { common.color "#aab3e6" }
style.caret = { common.color "#f5faff" }
style.accent = { common.color "#ffb86c" }
style.dim = { common.color "#4f526b" }
style.divider = { common.color "#22242e" }
style.selection = { common.color "#4c5163" }
style.line_number = { common.color "#44475a" }
style.line_number2 = { common.color "#717796" }
style.line_highlight = { common.color "#2d303d" }
style.scrollbar = { common.color "#44475a" }
style.scrollbar2 = { common.color "#4c5163" }
style.syntax["normal"] = { common.color "#f5faff" }
style.syntax["symbol"] = { common.color "#f5faff" }
style.syntax["comment"] = { common.color "#6272a4" }
style.syntax["keyword"] = { common.color "#ff79c6" }
style.syntax["keyword2"] = { common.color "#8be9fd" }
style.syntax["number"] = { common.color "#bd93f9" }
style.syntax["literal"] = { common.color "#bd93f9" }
style.syntax["string"] = { common.color "#f1fa8c" }
style.syntax["operator"] = { common.color "#ff79c6" }
style.syntax["function"] = { common.color "#8be9fd" }

View File

@ -1,28 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#404040" }
style.background2 = { common.color "#3d3d3d" }
style.background3 = { common.color "#2b2b2b" }
style.text = { common.color "#dcdccc" }
style.caret = { common.color "#f8f8f0" }
style.accent = { common.color "#dcdccc" }
style.dim = { common.color "#8f8f8f" }
style.divider = { common.color "#383838" }
style.selection = { common.color "#2f2f2f" }
style.line_number = { common.color "#545454" }
style.line_number2 = { common.color "#545454" }
style.line_highlight = { common.color "#383838" }
style.scrollbar = { common.color "#4c4c4c" }
style.scrollbar2 = { common.color "#5e5e5e" }
style.syntax["normal"] = { common.color "#dcdccc" }
style.syntax["symbol"] = { common.color "#dcdccc" }
style.syntax["comment"] = { common.color "#7f9f7f" }
style.syntax["keyword"] = { common.color "#f0dfaf" }
style.syntax["keyword2"] = { common.color "#dfdfbf" }
style.syntax["number"] = { common.color "#8cd0d3" }
style.syntax["literal"] = { common.color "#dfaf8f" }
style.syntax["string"] = { common.color "#cc9393" }
style.syntax["operator"] = { common.color "#f0efd0" }
style.syntax["function"] = { common.color "#efef8f" }

View File

@ -1,31 +0,0 @@
local style = require "core.style"
local common = require "core.common"
-- GitHubs style varies from language to language so its hard to get perfect
-- Originally written by thebirk, 2019
style.background = { common.color "#fbfbfb" }
style.background2 = { common.color "#f2f2f2" }
style.background3 = { common.color "#f2f2f2" }
style.text = { common.color "#404040" }
style.caret = { common.color "#181818" }
style.accent = { common.color "#0366d6" }
style.dim = { common.color "#b0b0b0" }
style.divider = { common.color "#e8e8e8" }
style.selection = { common.color "#b7dce8" }
style.line_number = { common.color "#d0d0d0" }
style.line_number2 = { common.color "#808080" }
style.line_highlight = { common.color "#f2f2f2" }
style.scrollbar = { common.color "#e0e0e0" }
style.scrollbar2 = { common.color "#c0c0c0" }
style.syntax["normal"] = { common.color "#24292e" }
style.syntax["symbol"] = { common.color "#24292e" }
style.syntax["comment"] = { common.color "#6a737d" }
style.syntax["keyword"] = { common.color "#d73a49" }
style.syntax["keyword2"] = { common.color "#d73a49" }
style.syntax["number"] = { common.color "#005cc5" }
style.syntax["literal"] = { common.color "#005cc5" }
style.syntax["string"] = { common.color "#032f62" }
style.syntax["operator"] = { common.color "#d73a49" }
style.syntax["function"] = { common.color "#005cc5" }

View File

@ -1,28 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#fbf1c7" }
style.background2 = { common.color "#f9f5d7" }
style.background3 = { common.color "#f9f5d7" }
style.text = { common.color "#928374" }
style.caret = { common.color "#282828" }
style.accent = { common.color "#3c3836" }
style.dim = { common.color "#928374" }
style.divider = { common.color "#f9f5d7" }
style.selection = { common.color "#ebdbb2" }
style.line_number = { common.color "#928374" }
style.line_number2 = { common.color "#3c3836" }
style.line_highlight = { common.color "#f2e5bc" }
style.scrollbar = { common.color "#928374" }
style.scrollbar2 = { common.color "#282828" }
style.syntax["normal"] = { common.color "#3c3836" }
style.syntax["symbol"] = { common.color "#3c3836" }
style.syntax["comment"] = { common.color "#928374" }
style.syntax["keyword"] = { common.color "#9d0006" }
style.syntax["keyword2"] = { common.color "#076678" }
style.syntax["number"] = { common.color "#8f3f71" }
style.syntax["literal"] = { common.color "#8f3f71" }
style.syntax["string"] = { common.color "#79740e" }
style.syntax["operator"] = { common.color "#3c3836" }
style.syntax["function"] = { common.color "#427b58" }

View File

@ -1,28 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#f7f9f9" }
style.background2 = { common.color "#f7f9f9" }
style.background3 = { common.color "#f7f9f9" }
style.text = { common.color "#404040" }
style.caret = { common.color "#ff5971" }
style.accent = { common.color "#ff5971" }
style.dim = { common.color "#b0b0b0" }
style.divider = { common.color "#e8e8e8" }
style.selection = { common.color "#fde6eb" }
style.line_number = { common.color "#d0d0d0" }
style.line_number2 = { common.color "#808080" }
style.line_highlight = { common.color "#f2f2f2" }
style.scrollbar = { common.color "#e0e0e0" }
style.scrollbar2 = { common.color "#c0c0c0" }
style.syntax["normal"] = { common.color "#181818" }
style.syntax["symbol"] = { common.color "#181818" }
style.syntax["comment"] = { common.color "#43cdbd" }
style.syntax["keyword"] = { common.color "#5f7dcd" }
style.syntax["keyword2"] = { common.color "#9c53c6" }
style.syntax["number"] = { common.color "#3daee9" }
style.syntax["literal"] = { common.color "#3daee9" }
style.syntax["string"] = { common.color "#3daee9" }
style.syntax["operator"] = { common.color "#5f7dcd" }
style.syntax["function"] = { common.color "#9c53c6" }

View File

@ -1,28 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#fdf6e3" }
style.background2 = { common.color "#eee8d5" }
style.background3 = { common.color "#eee8d5" }
style.text = { common.color "#657b83" }
style.caret = { common.color "#657b83" }
style.accent = { common.color "#002b36" }
style.dim = { common.color "#93a1a1" }
style.divider = { common.color "#e0dbc8" }
style.selection = { common.color "#073642" }
style.line_number = { common.color "#93a1a1" }
style.line_number2 = { common.color "#002b36" }
style.line_highlight = { common.color "#eee8d5" }
style.scrollbar = { common.color "#e0dbc8" }
style.scrollbar2 = { common.color "#bfbbaa" }
style.syntax["normal"] = { common.color "#657b83" }
style.syntax["symbol"] = { common.color "#657b83" }
style.syntax["comment"] = { common.color "#93a1a1" }
style.syntax["keyword"] = { common.color "#859900" }
style.syntax["keyword2"] = { common.color "#268bd2" }
style.syntax["number"] = { common.color "#d33682" }
style.syntax["literal"] = { common.color "#2aa198" }
style.syntax["string"] = { common.color "#2aa198" }
style.syntax["operator"] = { common.color "#859900" }
style.syntax["function"] = { common.color "#268bd2" }

View File

@ -1,28 +0,0 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#fdf6e3" }
style.background2 = { common.color "#2e2c29" }
style.background3 = { common.color "#3e3c37" }
style.text = { common.color "#b2ada1" }
style.caret = { common.color "#b2ada1" }
style.accent = { common.color "#6c71c4" }
style.dim = { common.color "#b2ada1" }
style.divider = { common.color "#201f1d" }
style.selection = { common.color "#eee8d5" }
style.line_number = { common.color "#93a1a1" }
style.line_number2 = { common.color "#002b36" }
style.line_highlight = { common.color "#fcefcd" }
style.scrollbar = { common.color "#e0dbc8" }
style.scrollbar2 = { common.color "#9d9988" }
style.syntax["normal"] = { common.color "#3e3c37" }
style.syntax["symbol"] = { common.color "#4c4f82" }
style.syntax["comment"] = { common.color "#93a1a1" }
style.syntax["keyword"] = { common.color "#d33682" }
style.syntax["keyword2"] = { common.color "#6c71c4" }
style.syntax["number"] = { common.color "#859900" }
style.syntax["literal"] = { common.color "#b58900" }
style.syntax["string"] = { common.color "#cb4b16" }
style.syntax["operator"] = { common.color "#859900" }
style.syntax["function"] = { common.color "#268bd2" }

Binary file not shown.

View File

@ -1,129 +0,0 @@
-- mod-version:3
local core = require "core"
local translate = require "core.doc.translate"
local config = require "core.config"
local common = require "core.common"
local DocView = require "core.docview"
local command = require "core.command"
local keymap = require "core.keymap"
config.plugins.autoinsert = common.merge({ map = {
["["] = "]",
["{"] = "}",
["("] = ")",
['"'] = '"',
["'"] = "'",
["`"] = "`",
} }, config.plugins.autoinsert)
-- Workaround for bug in Lite XL 2.1
-- Remove this when b029f5993edb7dee5ccd2ba55faac1ec22e24609 is in a release
local function get_selection(doc, sort)
local line1, col1, line2, col2 = doc:get_selection_idx(doc.last_selection)
if line1 then
return doc:get_selection_idx(doc.last_selection, sort)
else
return doc:get_selection_idx(1, sort)
end
end
local function is_closer(chr)
for _, v in pairs(config.plugins.autoinsert.map) do
if v == chr then
return true
end
end
end
local function count_char(text, chr)
local count = 0
for _ in text:gmatch(chr) do
count = count + 1
end
return count
end
local on_text_input = DocView.on_text_input
function DocView:on_text_input(text)
local mapping = config.plugins.autoinsert.map[text]
-- prevents plugin from operating on `CommandView`
if getmetatable(self) ~= DocView then
return on_text_input(self, text)
end
-- wrap selection if we have a selection
if mapping and self.doc:has_selection() then
local l1, c1, l2, c2, swap = get_selection(self.doc, true)
self.doc:insert(l2, c2, mapping)
self.doc:insert(l1, c1, text)
self.doc:set_selection(l1, c1, l2, c2 + 2, swap)
return
end
-- skip inserting closing text
local chr = self.doc:get_char(self.doc:get_selection())
if text == chr and is_closer(chr) then
self.doc:move_to(1)
return
end
-- don't insert closing quote if we have a non-even number on this line
local line = self.doc:get_selection()
if text == mapping and count_char(self.doc.lines[line], text) % 2 == 1 then
return on_text_input(self, text)
end
-- auto insert closing bracket
if mapping and (chr:find("%s") or is_closer(chr) and chr ~= '"') then
on_text_input(self, text)
on_text_input(self, mapping)
self.doc:move_to(-1)
return
end
on_text_input(self, text)
end
local function predicate()
return core.active_view:is(DocView)
and not core.active_view.doc:has_selection(), core.active_view.doc
end
command.add(predicate, {
["autoinsert:backspace"] = function(doc)
local l, c = doc:get_selection()
if c > 1 then
local chr = doc:get_char(l, c)
local mapped = config.plugins.autoinsert.map[doc:get_char(l, c - 1)]
if mapped and mapped == chr then
doc:delete_to(1)
end
end
command.perform "doc:backspace"
end,
["autoinsert:delete-to-previous-word-start"] = function(doc)
local le, ce = translate.previous_word_start(doc, doc:get_selection())
while true do
local l, c = doc:get_selection()
if l == le and c == ce then
break
end
command.perform "autoinsert:backspace"
end
end,
})
keymap.add {
["backspace"] = "autoinsert:backspace",
["ctrl+backspace"] = "autoinsert:delete-to-previous-word-start",
["ctrl+shift+backspace"] = "autoinsert:delete-to-previous-word-start",
}

View File

@ -1,71 +0,0 @@
-- mod-version:3
local core = require "core"
local config = require "core.config"
local command = require "core.command"
local common = require "core.common"
local DocView = require "core.docview"
config.plugins.autowrap = common.merge({
enabled = false,
files = { "%.md$", "%.txt$" },
-- The config specification used by the settings gui
config_spec = {
name = "Auto Wrap",
{
label = "Enable",
description = "Activates text auto wrapping by default.",
path = "enabled",
type = "toggle",
default = false
},
{
label = "Files",
description = "List of Lua patterns matching files to auto wrap.",
path = "files",
type = "list_strings",
default = { "%.md$", "%.txt$" },
}
}
}, config.plugins.autowrap)
local on_text_input = DocView.on_text_input
DocView.on_text_input = function(self, ...)
on_text_input(self, ...)
if not config.plugins.autowrap.enabled then return end
-- early-exit if the filename does not match a file type pattern
local filename = self.doc.filename or ""
local matched = false
for _, ptn in ipairs(config.plugins.autowrap.files) do
if filename:match(ptn) then
matched = true
break
end
end
if not matched then return end
-- do automatic reflow on line if we're typing at the end of the line and have
-- reached the line limit
local line, col = self.doc:get_selection()
local text = self.doc:get_text(line, 1, line, math.huge)
if #text >= config.line_limit and col > #text then
command.perform("doc:select-lines")
command.perform("reflow:reflow")
command.perform("doc:move-to-next-char")
command.perform("doc:move-to-end-of-line")
end
end
command.add(nil, {
["auto-wrap:toggle"] = function()
config.plugins.autowrap.enabled = not config.plugins.autowrap.enabled
if config.plugins.autowrap.enabled then
core.log("Auto wrap: on")
else
core.log("Auto wrap: off")
end
end
})

View File

@ -1,108 +0,0 @@
-- mod-version:3
local core = require "core"
local style = require "core.style"
local command = require "core.command"
local common = require "core.common"
local config = require "core.config"
local View = require "core.view"
config.plugins.bigclock = common.merge({
time_format = "%H:%M:%S",
date_format = "%A, %d %B %Y",
scale = 1,
-- The config specification used by the settings gui
config_spec = {
name = "Big Clock",
{
label = "Time Format",
description = "Time specification defined with Lua date/time place holders.",
path = "time_format",
type = "string",
default = "%H:%M:%S"
},
{
label = "Date Format",
description = "Date specification defined with Lua date/time place holders.",
path = "date_format",
type = "string",
default = "%A, %d %B %Y",
},
{
label = "Scale",
description = "Size of the clock relative to screen.",
path = "scale",
type = "number",
default = 1,
min = 0.5,
max = 3.0,
step = 0.1
}
}
}, config.plugins.bigclock)
local ClockView = View:extend()
function ClockView:new()
ClockView.super.new(self)
self.time_text = ""
self.date_text = ""
self.last_scale = 0
end
function ClockView:get_name()
return "Big Clock"
end
function ClockView:update_fonts()
if self.last_scale ~= config.plugins.bigclock.scale then
self.last_scale = config.plugins.bigclock.scale
else
return
end
local size = math.floor(self.size.x * 0.15 / 15) * 15 * config.plugins.bigclock.scale
if self.font_size ~= size then
self.time_font = renderer.font.copy(style["font"], size)
self.date_font = renderer.font.copy(style["font"], size * 0.3)
self.font_size = size
collectgarbage()
end
end
function ClockView:update()
local time_text = os.date(config.plugins.bigclock.time_format)
local date_text = os.date(config.plugins.bigclock.date_format)
if self.time_text ~= time_text or self.date_text ~= date_text then
core.redraw = true
self.time_text = time_text
self.date_text = date_text
end
ClockView.super.update(self)
end
function ClockView:draw()
self:update_fonts()
self:draw_background(style.background)
local x, y = self.position.x, self.position.y
local w, h = self.size.x, self.size.y
local _, y = common.draw_text(self.time_font, style.text, self.time_text, "center", x, y, w, h)
local th = self.date_font:get_height()
common.draw_text(self.date_font, style.dim, self.date_text, "center", x, y, w, th)
end
command.add(nil, {
["big-clock:open"] = function()
local node = core.root_view:get_active_node()
node:add_view(ClockView())
end,
})
return ClockView

View File

@ -1,265 +0,0 @@
--- mod-version:3
local core = require "core"
local style = require "core.style"
local command = require "core.command"
local keymap = require "core.keymap"
local DocView = require "core.docview"
local config = require "core.config"
local common = require "core.common"
-- Colors can be configured as follows:
-- underline color = `style.bracketmatch_color`
-- bracket color = `style.bracketmatch_char_color`
-- background color = `style.bracketmatch_block_color`
-- frame color = `style.bracketmatch_frame_color`
config.plugins.bracketmatch = common.merge({
-- highlight the current bracket too
highlight_both = true,
-- can be "underline", "block", "frame", "none"
style = "underline",
-- color the bracket
color_char = false,
-- the size of the lines used in "underline" and "frame"
line_size = math.ceil(1 * SCALE),
-- The config specification used by the settings gui
config_spec = {
name = "Bracket Match",
{
label = "Highlight Both",
description = "Highlight the current bracket too.",
path = "highlight_both",
type = "toggle",
default = true
},
{
label = "Style",
description = "The visual indicator for pair brackets.",
path = "style",
type = "selection",
default = "underline",
values = {
{"Underline", "underline"},
{"Block", "block"},
{"Frame", "frame"},
{"None", "none"}
}
},
{
label = "Colorize Bracket",
description = "Change the color of the matching brackets.",
path = "color_char",
type = "toggle",
default = false
},
{
label = "Line Size",
description = "Height of the underline on matching brackets.",
path = "line_size",
type = "number",
default = 1,
min = 1,
step = 1,
get_value = function(value)
return math.floor(value / SCALE)
end,
set_value = function(value)
return math.ceil(value * SCALE)
end
}
}
}, config.plugins.bracketmatch)
local bracket_maps = {
-- [ ] ( ) { }
{ [91] = 93, [40] = 41, [123] = 125, direction = 1 },
-- ] [ ) ( } {
{ [93] = 91, [41] = 40, [125] = 123, direction = -1 },
}
local function get_token_at(doc, line, col)
local column = 0
for _,type,text in doc.highlighter:each_token(line) do
column = column + #text
if column >= col then return type, text end
end
end
local function get_matching_bracket(doc, line, col, line_limit, open_byte, close_byte, direction)
local end_line = line + line_limit * direction
local depth = 0
while line ~= end_line do
local byte = doc.lines[line]:byte(col)
if byte == open_byte and get_token_at(doc, line, col) ~= "comment" then
depth = depth + 1
elseif byte == close_byte and get_token_at(doc, line, col) ~= "comment" then
depth = depth - 1
if depth == 0 then return line, col end
end
local prev_line, prev_col = line, col
line, col = doc:position_offset(line, col, direction)
if line == prev_line and col == prev_col then
break
end
end
end
local state = {}
local select_adj = 0
local function update_state(line_limit)
line_limit = line_limit or math.huge
-- reset if we don't have a document (eg. DocView isn't focused)
local doc = core.active_view.doc
if not doc then
state = {}
return
end
-- early exit if nothing has changed since the last call
local line, col = doc:get_selection()
local change_id = doc:get_change_id()
if state.doc == doc and state.line == line and state.col == col
and state.change_id == change_id and state.limit == line_limit then
return
end
-- find matching bracket if we're on a bracket
local line2, col2
for _, map in ipairs(bracket_maps) do
for i = 0, -1, -1 do
local line, col = doc:position_offset(line, col, i)
local open = doc.lines[line]:byte(col)
local close = map[open]
if close and get_token_at(doc, line, col) ~= "comment" then
-- i == 0 if the cursor is on the left side of a bracket (or -1 when on right)
select_adj = i + 1 -- if i == 0 then select_adj = 1 else select_adj = 0 end
line2, col2 = get_matching_bracket(doc, line, col, line_limit, open, close, map.direction)
goto found
end
end
end
::found::
-- update
state = {
change_id = change_id,
doc = doc,
line = line,
col = col,
line2 = line2,
col2 = col2,
limit = line_limit,
}
end
local update = DocView.update
function DocView:update(...)
update(self, ...)
update_state(100)
end
local function redraw_char(dv, x, y, line, col, bg_color, char_color)
local x1 = x + dv:get_col_x_offset(line, col)
local x2 = x + dv:get_col_x_offset(line, col + 1)
local lh = dv:get_line_height()
local token = get_token_at(dv.doc, line, col)
if not char_color then
char_color = style.syntax[token]
end
local font = style.syntax_fonts[token] or dv:get_font()
local char = string.sub(dv.doc.lines[line], col, col)
if not bg_color then
-- redraw background
core.push_clip_rect(x1, y, x2 - x1, lh)
local dlt = DocView.draw_line_text
DocView.draw_line_text = function() end
dv:draw_line_body(line, x, y)
DocView.draw_line_text = dlt
core.pop_clip_rect()
else
renderer.draw_rect(x1, y, x2 - x1, lh, bg_color)
end
renderer.draw_text(font, char, x1, y + dv:get_line_text_y_offset(), char_color)
end
local function draw_decoration(dv, x, y, line, col)
local conf = config.plugins.bracketmatch
local color = style.bracketmatch_color or style.syntax["function"]
local char_color = style.bracketmatch_char_color
or (conf.style == "block" and style.background or style.syntax["keyword"])
local block_color = style.bracketmatch_block_color or style.line_number2
local frame_color = style.bracketmatch_frame_color or style.line_number2
local h = conf.line_size
if conf.color_char or conf.style == "block" then
redraw_char(dv, x, y, line, col,
conf.style == "block" and block_color, conf.color_char and char_color)
end
if conf.style == "underline" then
local x1 = x + dv:get_col_x_offset(line, col)
local x2 = x + dv:get_col_x_offset(line, col + 1)
local lh = dv:get_line_height()
renderer.draw_rect(x1, y + lh - h, x2 - x1, h, color)
elseif conf.style == "frame" then
local x1 = x + dv:get_col_x_offset(line, col)
local x2 = x + dv:get_col_x_offset(line, col + 1)
local lh = dv:get_line_height()
renderer.draw_rect(x1, y + lh - h, x2 - x1, h, frame_color)
renderer.draw_rect(x1, y, x2 - x1, h, frame_color)
renderer.draw_rect(x1, y, h, lh, frame_color)
renderer.draw_rect(x2, y, h, lh, frame_color)
end
end
local draw_line_text = DocView.draw_line_text
function DocView:draw_line_text(line, x, y)
local lh = draw_line_text(self, line, x, y)
if self.doc == state.doc and state.line2 then
if line == state.line2 then
draw_decoration(self, x, y, line, state.col2)
end
if line == state.line and config.plugins.bracketmatch.highlight_both then
draw_decoration(self, x, y, line, state.col + select_adj - 1)
end
end
return lh
end
command.add("core.docview", {
["bracket-match:move-to-matching"] = function(dv)
update_state()
if state.line2 then
dv.doc:set_selection(state.line2, state.col2)
end
end,
["bracket-match:select-to-matching"] = function(dv)
update_state()
if state.line2 then
dv.doc:set_selection(state.line, state.col, state.line2, state.col2 + select_adj)
end
end,
})
keymap.add {
["ctrl+m"] = "bracket-match:move-to-matching",
["ctrl+shift+m"] = "bracket-match:select-to-matching",
}

View File

@ -1,370 +0,0 @@
--mod-version:3 --priority:5
--[[
This code is responsible for the encoding change
using codesets library. It requires LiteXL 2.1.1r3
and above to work.
Heavily inspired from the encoding plugin
https://github.com/jgmdev/lite-xl-encoding
Configuration:
useSystemEncoding
By default the system encoding is used to open
a file. If you want to disable that you may add
the following line in you config file
config.plugins.codesets.useSystemEncoding = false
]]
local core = require "core"
local common = require "core.common"
local command = require "core.command"
local config = require "core.config"
local style = require "core.style"
local Doc = require "core.doc"
local DocView = require "core.docview"
local CommandView = require "core.commandview"
local StatusView = require "core.statusview"
---@type encoding
local encoding = require "codesetsextra"
config.plugins.codesets = common.merge({
useSystemEncoding = true
}, config.plugins.codesets)
-- Reference to plugin config
local conf = config.plugins.codesets
local encodings = {}
---@class encodings.encoding
---@field charset string
---@field name string
---List of encoding regions.
---@type table<integer,string>
encodings.groups = {
"West European",
"East European",
"East Asian",
"SE & SW Asian",
"Middle Eastern",
"Unicode"
}
---Supported iconv encodings grouped by region.
---@type table<integer,encodings.encoding[]>
encodings.list = {
-- West European
{
{ charset = "ISO-8859-14", name = "Celtic" },
{ charset = "ISO-8859-7", name = "Greek" },
{ charset = "WINDOWS-1253", name = "Greek" },
{ charset = "ISO-8859-10", name = "Nordic" },
{ charset = "ISO-8859-3", name = "South European" },
{ charset = "IBM850", name = "Western" },
{ charset = "ISO-8859-1", name = "Western" },
{ charset = "ISO-8859-15", name = "Western" },
{ charset = "WINDOWS-1252", name = "Western" }
},
-- East European
{
{ charset = "ISO-8859-4", name = "Baltic" },
{ charset = "ISO-8859-13", name = "Baltic" },
{ charset = "WINDOWS-1257", name = "Baltic" },
{ charset = "IBM852", name = "Central European" },
{ charset = "ISO-8859-2", name = "Central European" },
{ charset = "WINDOWS-1250", name = "Central European" },
{ charset = "IBM855", name = "Cyrillic" },
{ charset = "ISO-8859-5", name = "Cyrillic" },
{ charset = "ISO-IR-111", name = "Cyrillic" },
{ charset = "KOI8-R", name = "Cyrillic" },
{ charset = "WINDOWS-1251", name = "Cyrillic" },
{ charset = "CP866", name = "Cyrillic/Russian" },
{ charset = "KOI8-U", name = "Cyrillic/Ukrainian" },
{ charset = "ISO-8859-16", name = "Romanian" }
},
-- East Asian
{
{ charset = "GB18030", name = "Chinese Simplified" },
{ charset = "GB2312", name = "Chinese Simplified" },
{ charset = "GBK", name = "Chinese Simplified" },
{ charset = "HZ", name = "Chinese Simplified" },
{ charset = "BIG5", name = "Chinese Traditional" },
{ charset = "BIG5-HKSCS", name = "Chinese Traditional" },
{ charset = "EUC-TW", name = "Chinese Traditional" },
{ charset = "EUC-JP", name = "Japanese" },
{ charset = "ISO-2022-JP", name = "Japanese" },
{ charset = "SHIFT_JIS", name = "Japanese" },
{ charset = "CP932", name = "Japanese" },
{ charset = "EUC-KR", name = "Korean" },
{ charset = "ISO-2022-KR", name = "Korean" },
{ charset = "JOHAB", name = "Korean" },
{ charset = "UHC", name = "Korean" }
},
-- SE & SW Asian
{
{ charset = "ARMSCII-8", name = "Armenian" },
{ charset = "GEORGIAN-ACADEMY", name = "Georgian" },
{ charset = "TIS-620", name = "Thai" },
{ charset = "IBM857", name = "Turkish" },
{ charset = "WINDOWS-1254", name = "Turkish" },
{ charset = "ISO-8859-9", name = "Turkish" },
{ charset = "TCVN", name = "Vietnamese" },
{ charset = "VISCII", name = "Vietnamese" },
{ charset = "WINDOWS-1258", name = "Vietnamese" }
},
-- Middle Eastern
{
{ charset = "IBM864", name = "Arabic" },
{ charset = "ISO-8859-6", name = "Arabic" },
{ charset = "WINDOWS-1256", name = "Arabic" },
{ charset = "IBM862", name = "Hebrew" },
{ charset = "ISO-8859-8-I", name = "Hebrew" },
{ charset = "WINDOWS-1255", name = "Hebrew" },
{ charset = "ISO-8859-8", name = "Hebrew Visual" }
},
-- Unicode
{
{ charset = "UTF-7", name = "Unicode" },
{ charset = "UTF-8", name = "Unicode" },
{ charset = "UTF-16LE", name = "Unicode" },
{ charset = "UTF-16BE", name = "Unicode" },
{ charset = "UCS-2LE", name = "Unicode" },
{ charset = "UCS-2BE", name = "Unicode" },
{ charset = "UTF-32LE", name = "Unicode" },
{ charset = "UTF-32BE", name = "Unicode" }
}
};
---Get the list of encodings associated to a region.
---@param label string
---@return encodings.encoding[] | nil
function encodings.get_group(label)
for idx, name in ipairs(encodings.groups) do
if name == label then
return encodings.list[idx]
end
end
end
---Get the list of encodings associated to a region.
---@return encodings.encoding[] | nil
function encodings.get_all()
local all = {}
for idx, _ in ipairs(encodings.groups) do
for _, item in ipairs(encodings.list[idx]) do
table.insert(all, item)
end
end
return all
end
---Open a commandview to select a charset and executes the given callback,
---@param title_label string Title displayed on the commandview
---@param callback fun(charset: string)
function encodings.select_encoding(title_label, callback)
core.command_view:enter(title_label, {
submit = function(_, item)
callback(item.charset)
end,
suggest = function(text)
local charsets = encodings.get_all()
local list_labels = {}
local list_charset = {}
for _, element in ipairs(charsets) do
local label = element.name .. " (" .. element.charset .. ")"
table.insert(list_labels, label)
list_charset[label] = element.charset
end
local res = common.fuzzy_match(list_labels, text)
for i, name in ipairs(res) do
res[i] = {
text = name,
charset = list_charset[name]
}
end
return res
end
})
end
--------------------------------------------------------------------------------
-- Overwrite Doc methods to properly add encoding detection and conversion.
--------------------------------------------------------------------------------
function Doc:new(filename, abs_filename, new_file)
self.new_file = new_file
self.encoding = nil
self.convert = false
self:reset()
if filename then
self:set_filename(filename, abs_filename)
if not new_file then
self:load(filename)
end
end
end
function Doc:load(filename)
if not self.encoding then
local errmsg
if conf.useSystemEncoding then
self.encoding, errmsg = encoding.systemCodeset();
else
self.encoding, errmsg = encoding.detect(filename);
end
if not self.encoding then core.error("%s", errmsg) error(errmsg) end
end
self.convert = false
if self.encoding ~= "UTF-8" and self.encoding ~= "ASCII"
and self.encoding ~= "US-ASCII" and self.encoding ~= "ISO-8859-1"
then
self.convert = true
end
local fp = assert( io.open(filename, "rb") )
self:reset()
self.lines = {}
local i = 1
if self.convert then
local content = fp:read("*a");
content = assert(encoding.convert("UTF-8", self.encoding, content, {
strict = false,
handle_from_bom = true
}))
for line in content:gmatch("([^\n]*)\n?") do
if line:byte(-1) == 13 then
line = line:sub(1, -2)
self.crlf = true
end
table.insert(self.lines, line .. "\n")
self.highlighter.lines[i] = false
i = i + 1
end
content = nil
else
for line in fp:lines() do
if (i == 1) then line = encoding.strip_bom(line, "UTF-8") end
if line:byte(-1) == 13 then
line = line:sub(1, -2)
self.crlf = true
end
table.insert(self.lines, line .. "\n")
self.highlighter.lines[i] = false
i = i + 1
end
end
if #self.lines == 0 then
table.insert(self.lines, "\n")
end
fp:close()
self:reset_syntax()
end
function Doc:save(filename, abs_filename)
if not filename then
assert(self.filename, "no filename set to default to")
filename = self.filename
abs_filename = self.abs_filename
else
assert(self.filename or abs_filename, "calling save on unnamed doc without absolute path")
end
local fp
local output = ""
if not self.convert then
fp = assert( io.open(filename, "wb") )
for _, line in ipairs(self.lines) do
if self.crlf then line = line:gsub("\n", "\r\n") end
fp:write(line)
end
else
output = table.concat(self.lines);
if self.crlf then output = output:gsub("\n", "\r\n") end
end
local conversion_error = false
if self.convert then
local errmsg
output, errmsg = encoding.convert(self.encoding, "UTF-8", output, {
strict = true,
handle_to_bom = true
})
if output then
fp = assert( io.open(filename, "wb") )
fp:write(encoding.get_charset_bom(self.encoding) .. output)
fp:close()
else
conversion_error = true
core.error("%s", errmsg)
end
else
fp:close()
end
self:set_filename(filename, abs_filename)
if not conversion_error then
self.new_file = false
else
self.new_file = true
end
self:clean()
end
--------------------------------------------------------------------------------
-- Register command to change current document encoding.
--------------------------------------------------------------------------------
command.add("core.docview", {
["doc:change-encoding"] = function(dv)
encodings.select_encoding("Select Output Encoding", function(charset)
dv.doc.encoding = charset
if charset ~= "UTF-8" and charset ~= "ASCII"
and charset ~= "US-ASCII" and charset ~= "ISO-8859-1"
then
dv.doc.convert = true
else
dv.doc.convert = false
end
dv.doc:save()
end)
end,
["doc:reload-with-encoding"] = function(dv)
encodings.select_encoding("Reload With Encoding", function(charset)
dv.doc.encoding = charset
if charset ~= "UTF-8" and charset ~= "ASCII"
and charset ~= "US-ASCII" and charset ~= "ISO-8859-1"
then
dv.doc.convert = true
else
dv.doc.convert = false
end
dv.doc:reload()
end)
end
})
--------------------------------------------------------------------------------
-- Register a statusbar item to view change current doc encoding.
--------------------------------------------------------------------------------
core.status_view:add_item({
predicate = function()
return core.active_view:is(DocView)
and not core.active_view:is(CommandView)
end,
name = "doc:encoding",
alignment = StatusView.Item.RIGHT,
get_item = function()
local dv = core.active_view
return {
style.text, dv.doc.encoding or "none"
}
end,
command = function(button)
if button == "left" then
command.perform "doc:change-encoding"
elseif button == "right" then
command.perform "doc:reload-with-encoding"
end
end,
tooltip = "encoding"
})
return encodings;

View File

@ -1,100 +0,0 @@
-- mod-version:3
local config = require "core.config"
local common = require "core.common"
local DocView = require "core.docview"
config.plugins.colorpreview = common.merge({
enabled = true,
-- The config specification used by the settings gui
config_spec = {
name = "Color Preview",
{
label = "Enable",
description = "Enable or disable the color preview feature.",
path = "enabled",
type = "toggle",
default = true
}
}
}, config.plugins.colorpreview)
local white = { common.color "#ffffff" }
local black = { common.color "#000000" }
local tmp = {}
-- Workaround for bug in Lite XL 2.1
-- Remove this when b029f5993edb7dee5ccd2ba55faac1ec22e24609 is in a release
local function get_selection(doc, sort)
local line1, col1, line2, col2 = doc:get_selection_idx(doc.last_selection)
if line1 then
return doc:get_selection_idx(doc.last_selection, sort)
else
return doc:get_selection_idx(1, sort)
end
end
local function draw_color_previews(self, line, x, y, ptn, base, nibbles)
local text = self.doc.lines[line]
local s, e = 0, 0
while true do
s, e = text:find(ptn, e + 1)
if not s then break end
local str = text:sub(s, e)
local r, g, b, a = str:match(ptn)
r, g, b = tonumber(r, base), tonumber(g, base), tonumber(b, base)
a = tonumber(a or "", base)
if a ~= nil then
if base ~= 16 then
a = a * 0xff
end
else
a = 0xff
end
-- #123 becomes #112233
if nibbles then
r = r * 16
g = g * 16
b = b * 16
end
local x1 = x + self:get_col_x_offset(line, s)
local x2 = x + self:get_col_x_offset(line, e + 1)
local oy = self:get_line_text_y_offset()
local text_color = math.max(r, g, b) < 128 and white or black
tmp[1], tmp[2], tmp[3], tmp[4] = r, g, b, a
local l1, _, l2, _ = get_selection(self.doc, true)
if not (self.doc:has_selection() and line >= l1 and line <= l2) then
renderer.draw_rect(x1, y, x2 - x1, self:get_line_height(), tmp)
renderer.draw_text(self:get_font(), str, x1, y + oy, text_color)
end
end
end
local draw_line_text = DocView.draw_line_text
function DocView:draw_line_text(line, x, y)
local lh = draw_line_text(self, line, x, y)
if config.plugins.colorpreview.enabled then
draw_color_previews(self, line, x, y,
"#(%x%x)(%x%x)(%x%x)(%x?%x?)%f[%W]",
16
)
-- support #fff css format
draw_color_previews(self, line, x, y, "#(%x)(%x)(%x)%f[%W]", 16, true)
draw_color_previews(self, line, x, y,
"rgba?%((%d+)%D+(%d+)%D+(%d+)[%s,]-([%.%d]-)%s-%)",
nil
)
end
return lh
end

View File

@ -1,156 +0,0 @@
-- mod-version:3
--[[
Author: techie-guy
Plugin to customize the caret in the editor
Thanks to @Guldoman for the initial example on Discord
Features
Change the Color and Opacity of the caret
Change the Shape of the caret, available shapes are Line, Block, Underline
Customizing the Caret: (this can be changed from the .config/lite-xl/init.lua
file or from the settings menu plugin)
config.plugins.custom_caret.shape - Change the shape of the caret [string]
style.caret - Change the rgba color of the caret [table]
Example Config(in the .config/lite-xl/init.lua)
style.caret = {0, 255, 255, 150}
config.plugins.custom_caret.shape = "block"
]]
local core = require "core"
local style = require "core.style"
local common = require "core.common"
local config = require "core.config"
local DocView = require "core.docview"
config.plugins.custom_caret = common.merge({
shape = "line",
custom_color = true,
color_r = style.caret[1],
color_g = style.caret[2],
color_b = style.caret[3],
opacity = style.caret[4]
}, config.plugins.custom_caret)
-- Reference to plugin config
local conf = config.plugins.custom_caret
-- Get real default caret color after everything is loaded up
core.add_thread(function()
if
conf.color_r == 147 and conf.color_g == 221
and
conf.color_b == 250 and conf.opacity == 255
and
(
style.caret[1] ~= conf.color_r or style.caret[2] ~= conf.color_g
or
style.caret[3] ~= conf.color_b or style.caret[4] ~= conf.opacity
)
then
conf.color_r = style.caret[1]
conf.color_g = style.caret[2]
conf.color_b = style.caret[3]
conf.opacity = style.caret[4]
end
local settings_loaded, settings = pcall(require, "plugins.settings")
if settings_loaded then
conf.config_spec = {
name = "Custom Caret",
{
label = "Shape",
description = "The Shape of the cursor.",
path = "shape",
type = "selection",
default = "line",
values = {
{"Line", "line"},
{"Block", "block"},
{"Underline", "underline"}
}
},
{
label = "Custom Color",
description = "Use a custom color for the caret as specified below.",
path = "custom_color",
type = "toggle",
default = true
},
{
label = "Red Component of Color",
description = "The color consists of 3 components RGB, "
.. "This modifies the 'R' component of the caret's color",
path = "color_r",
type = "number",
min = 0,
max = 255,
default = style.caret[1],
step = 1,
},
{
label = "Green Component of Color",
description = "The color consists of 3 components RGB, "
.. "This modifies the 'G' component of the caret's color",
path = "color_g",
type = "number",
min = 0,
max = 255,
default = style.caret[2],
step = 1,
},
{
label = "Blue Component of Color",
description = "The color consists of 3 components RGB, "
.. "This modifies the 'B' component of the caret's color",
path = "color_b",
type = "number",
min = 0,
max = 255,
default = style.caret[3],
step = 1,
},
{
label = "Opacity of the Cursor",
description = "The Opacity of the caret",
path = "opacity",
type = "number",
min = 0,
max = 255,
default = style.caret[4],
step = 1,
},
}
---@cast settings plugins.settings
settings.ui:enable_plugin("custom_caret")
end
end)
function DocView:draw_caret(x, y)
local caret_width = style.caret_width
local caret_height = self:get_line_height()
local current_caret_shape = conf.shape
local caret_color = conf.custom_color and {
conf.color_r,
conf.color_g,
conf.color_b,
conf.opacity
} or style.caret
if (current_caret_shape == "block") then
caret_width = math.ceil(self:get_font():get_width("a"))
elseif (current_caret_shape == "underline") then
caret_width = math.ceil(self:get_font():get_width("a"))
caret_height = style.caret_width*2
y = y+self:get_line_height()
else
caret_width = style.caret_width
caret_height = self:get_line_height()
end
renderer.draw_rect(x, y, caret_width, caret_height, caret_color)
end

View File

@ -1,61 +0,0 @@
# EditorConfig
This plugin implements the [EditorConfig](https://editorconfig.org/) spec
purely on lua by leveraging lua patterns and the regex engine on lite-xl.
Installing additional dependencies is not required.
The EditorConfig spec was implemented as best understood,
if you find any bugs please report them on this repository
[issue tracker](https://github.com/lite-xl/lite-xl-plugins/issues).
## Implemented Features
Global options:
* root - prevents upward searching of .editorconfig files
Applied to documents indent info:
* indent_style
* indent_size
* tab_width
Applied on document save:
* end_of_line - if set to `cr` it is ignored
* trim_trailing_whitespace
* insert_final_newline boolean
## Not implemented
* charset - this feature would need the encoding
[PR](https://github.com/lite-xl/lite-xl/pull/1161) or
[plugin](https://github.com/jgmdev/lite-xl-encoding)
## Extras
* Supports multiple project directories
* Implements hot reloading, so modifying an .editorconfig file from within
the editor will re-apply all rules to currently opened files.
## Testing
This plugin includes a test suite to check how well the .editorconfig parser
is working.
The [editorconfig-core-test](https://github.com/editorconfig/editorconfig-core-test)
glob, parser and properties cmake tests where ported and we are getting a 100%
pass rate.
If you are interested in running the test suite, from the terminal execute
the following:
```sh
lite-xl test editorconfig
```
To inspect the generated sections and regex rules:
```sh
lite-xl test editorconfig --parsers
```

View File

@ -1,441 +0,0 @@
-- mod-version:3
--
-- EditorConfig plugin for Lite XL
-- @copyright Jefferson Gonzalez <jgmdev@gmail.com>
-- @license MIT
--
-- Note: this plugin needs to be loaded after detectindent plugin,
-- since the name editorconfig.lua is ordered after detectindent.lua
-- there shouldn't be any issues. Just a reminder for the future in
-- case of a plugin that could also handle document identation type
-- and size, and has a name with more weight than this plugin.
--
local core = require "core"
local common = require "core.common"
local config = require "core.config"
local trimwhitespace = require "plugins.trimwhitespace"
local Doc = require "core.doc"
local Parser = require "plugins.editorconfig.parser"
---@class config.plugins.editorconfig
---@field debug boolean
config.plugins.editorconfig = common.merge({
debug = false,
-- The config specification used by the settings gui
config_spec = {
name = "EditorConfig",
{
label = "Debug",
description = "Display debugging messages on the log.",
path = "debug",
type = "toggle",
default = false
}
}
}, config.plugins.editorconfig)
---Cache of .editorconfig options to reduce parsing for every opened file.
---@type table<string, plugins.editorconfig.parser>
local project_configs = {}
---Keep track of main project directory so when changed we can assign a new
---.editorconfig object if neccesary.
---@type string
local main_project = core.project_dir
---Functionality that will be exposed by the plugin.
---@class plugins.editorconfig
local editorconfig = {}
---Load global .editorconfig options for a project.
---@param project_dir string
---@return boolean loaded
function editorconfig.load(project_dir)
local editor_config = project_dir .. "/" .. ".editorconfig"
local file = io.open(editor_config)
if file then
file:close()
project_configs[project_dir] = Parser.new(editor_config)
return true
end
return false
end
---Helper to add or substract final new line, it also makes final new line
---visble which lite-xl does not.
---@param doc core.doc
---@param raw? boolean If true does not register change on undo stack
---@return boolean handled_new_line
local function handle_final_new_line(doc, raw)
local handled = false
---@diagnostic disable-next-line
if doc.insert_final_newline then
handled = true
if doc.lines[#doc.lines] ~= "\n" then
if not raw then
doc:insert(#doc.lines, math.huge, "\n")
else
table.insert(doc.lines, "\n")
end
end
---@diagnostic disable-next-line
elseif type(doc.insert_final_newline) == "boolean" then
handled = true
if trimwhitespace.trim_empty_end_lines then
trimwhitespace.trim_empty_end_lines(doc, raw)
-- TODO: remove this once 2.1.1 is released
else
for _=#doc.lines, 1, -1 do
local l = #doc.lines
if l > 1 and doc.lines[l] == "\n" then
local current_line = doc:get_selection()
if current_line == l then
doc:set_selection(l-1, math.huge, l-1, math.huge)
end
if not raw then
doc:remove(l-1, math.huge, l, math.huge)
else
table.remove(doc.lines, l)
end
end
end
end
end
return handled
end
---Split the given relative path by / or \ separators.
---@param path string The path to split
---@return table
local function split_path(path)
local result = {};
for match in (path.."/"):gmatch("(.-)".."[:\\/]") do
table.insert(result, match);
end
return result;
end
---Check if the given file path exists.
---@param file_path string
local function file_exists(file_path)
local file = io.open(file_path, "r")
if not file then return false end
file:close()
return true
end
---Merge a config options to target if they don't already exists on target.
---@param config_target? plugins.editorconfig.parser.section
---@param config_from? plugins.editorconfig.parser.section
local function merge_config(config_target, config_from)
if config_target and config_from then
for name, value in pairs(config_from) do
if type(config_target[name]) == "nil" then
config_target[name] = value
end
end
end
end
---Scan for .editorconfig files from current file path to upper project path
---if root attribute is not found first and returns matching config.
---@param file_path string
---@return plugins.editorconfig.parser.section?
local function recursive_get_config(file_path)
local project_dir = ""
local root_config
for path, editor_config in pairs(project_configs) do
if common.path_belongs_to(file_path, path) then
project_dir = path
root_config = editor_config:getConfig(
common.relative_path(path, file_path)
)
break
end
end
if project_dir == "" then
for _, project in ipairs(core.project_directories) do
if common.path_belongs_to(file_path, project.name) then
project_dir = project.name
break
end
end
end
local relative_file_path = common.relative_path(project_dir, file_path)
local dir = common.dirname(relative_file_path)
local editor_config = {}
local config_found = false
if not dir and root_config then
editor_config = root_config
config_found = true
elseif dir then
local path_list = split_path(dir)
local root_found = false
for p=#path_list, 1, -1 do
local path = project_dir .. "/" .. table.concat(path_list, "/", 1, p)
if file_exists(path .. "/" .. ".editorconfig") then
---@type plugins.editorconfig.parser
local parser = Parser.new(path .. "/" .. ".editorconfig")
local pconfig = parser:getConfig(common.relative_path(path, file_path))
if pconfig then
merge_config(editor_config, pconfig)
config_found = true
end
if parser.root then
root_found = true
break
end
end
end
if not root_found and root_config then
merge_config(editor_config, root_config)
config_found = true
end
end
-- clean unset options
if config_found then
local all_unset = true
for name, value in pairs(editor_config) do
if value == "unset" then
editor_config[name] = nil
else
all_unset = false
end
end
if all_unset then config_found = false end
end
return config_found and editor_config or nil
end
---Apply editorconfig rules to given doc if possible.
---@param doc core.doc
function editorconfig.apply(doc)
if not doc.abs_filename and not doc.filename then return end
local file_path = doc.abs_filename or (main_project .. "/" .. doc.filename)
local options = recursive_get_config(file_path)
if options then
if config.plugins.editorconfig.debug then
core.log_quiet(
"[EditorConfig]: %s applied %s",
file_path, common.serialize(options, {pretty = true})
)
end
local indent_type, indent_size = doc:get_indent_info()
if options.indent_style then
if options.indent_style == "tab" then
indent_type = "hard"
else
indent_type = "soft"
end
end
if options.indent_size and options.indent_size == "tab" then
if options.tab_width then
options.indent_size = options.tab_width
else
options.indent_size = config.indent_size or 2
end
end
if options.indent_size then
indent_size = options.indent_size
end
if doc.indent_info then
doc.indent_info.type = indent_type
doc.indent_info.size = indent_size
doc.indent_info.confirmed = true
else
doc.indent_info = {
type = indent_type,
size = indent_size,
confirmed = true
}
end
if options.end_of_line then
if options.end_of_line == "crlf" then
doc.crlf = true
elseif options.end_of_line == "lf" then
doc.crlf = false
end
end
if options.trim_trailing_whitespace then
doc.trim_trailing_whitespace = true
elseif options.trim_trailing_whitespace == false then
doc.trim_trailing_whitespace = false
else
doc.trim_trailing_whitespace = nil
end
if options.insert_final_newline then
doc.insert_final_newline = true
elseif options.insert_final_newline == false then
doc.insert_final_newline = false
else
doc.insert_final_newline = nil
end
if
(
type(doc.trim_trailing_whitespace) == "boolean"
or
type(doc.insert_final_newline) == "boolean"
)
-- TODO: remove this once 2.1.1 is released
and
trimwhitespace.disable
then
trimwhitespace.disable(doc)
end
handle_final_new_line(doc, true)
end
end
---Applies .editorconfig options to all open documents if possible.
function editorconfig.apply_all()
for _, doc in ipairs(core.docs) do
editorconfig.apply(doc)
end
end
--------------------------------------------------------------------------------
-- Load .editorconfig on all projects loaded at startup and apply it
--------------------------------------------------------------------------------
core.add_thread(function()
local loaded = false
-- scan all opened project directories
if core.project_directories then
for i=1, #core.project_directories do
local found = editorconfig.load(core.project_directories[i].name)
if found then loaded = true end
end
end
-- if an editorconfig was found then try to apply it to opened docs
if loaded then
editorconfig.apply_all()
end
end)
--------------------------------------------------------------------------------
-- Override various core project loading functions for .editorconfig scanning
--------------------------------------------------------------------------------
local core_open_folder_project = core.open_folder_project
function core.open_folder_project(directory)
core_open_folder_project(directory)
if project_configs[main_project] then project_configs[main_project] = nil end
main_project = core.project_dir
editorconfig.load(main_project)
end
local core_remove_project_directory = core.remove_project_directory
function core.remove_project_directory(path)
local out = core_remove_project_directory(path)
if project_configs[path] then project_configs[path] = nil end
return out
end
local core_add_project_directory = core.add_project_directory
function core.add_project_directory(directory)
local out = core_add_project_directory(directory)
editorconfig.load(directory)
return out
end
--------------------------------------------------------------------------------
-- Hook into the core.doc to apply editor config options
--------------------------------------------------------------------------------
local doc_new = Doc.new
function Doc:new(...)
doc_new(self, ...)
editorconfig.apply(self)
end
---Cloned trimwitespace plugin until it is exposed for other plugins.
---@param doc core.doc
local function trim_trailing_whitespace(doc)
if trimwhitespace.trim then
trimwhitespace.trim(doc)
return
end
-- TODO: remove this once 2.1.1 is released
local cline, ccol = doc:get_selection()
for i = 1, #doc.lines do
local old_text = doc:get_text(i, 1, i, math.huge)
local new_text = old_text:gsub("%s*$", "")
-- don't remove whitespace which would cause the caret to reposition
if cline == i and ccol > #new_text then
new_text = old_text:sub(1, ccol - 1)
end
if old_text ~= new_text then
doc:insert(i, 1, new_text)
doc:remove(i, #new_text + 1, i, math.huge)
end
end
end
local doc_save = Doc.save
function Doc:save(...)
local new_file = self.new_file
---@diagnostic disable-next-line
if self.trim_trailing_whitespace then
trim_trailing_whitespace(self)
end
local lc = #self.lines
local handle_new_line = handle_final_new_line(self)
-- remove the unnecesary visible \n\n or the disabled \n
if handle_new_line then
self.lines[lc] = self.lines[lc]:gsub("\n$", "")
end
doc_save(self, ...)
-- restore the visible \n\n or disabled \n
if handle_new_line then
self.lines[lc] = self.lines[lc] .. "\n"
end
if common.basename(self.abs_filename) == ".editorconfig" then
-- blindlessly reload related project .editorconfig options
for _, project in ipairs(core.project_directories) do
if common.path_belongs_to(self.abs_filename, project.name) then
editorconfig.load(project.name)
break
end
end
-- re-apply editorconfig options to all open files
editorconfig.apply_all()
elseif new_file then
-- apply editorconfig options for file that was previously unsaved
editorconfig.apply(self)
end
end
--------------------------------------------------------------------------------
-- Run the test suite if requested on CLI with: lite-xl test editorconfig
--------------------------------------------------------------------------------
for i, argument in ipairs(ARGS) do
if argument == "test" and ARGS[i+1] == "editorconfig" then
require "plugins.editorconfig.runtest"
os.exit()
end
end
return editorconfig

View File

@ -1,553 +0,0 @@
-- Lua parser implementation of the .editorconfig spec as best understood.
-- @copyright Jefferson Gonzalez <jgmdev@gmail.com>
-- @license MIT
local core = require "core"
local config = require "core.config"
local STANDALONE = false
for i, argument in ipairs(ARGS) do
if argument == "test" and ARGS[i+1] == "editorconfig" then
STANDALONE = true
end
end
---Logger that will output using lite-xl logging functions or print to
---terminal if the parser is running in standalone mode.
---@param type "log" | "error"
---@param format string
---@param ... any
local function log(type, format, ...)
if not STANDALONE then
core[type]("[EditorConfig]: " .. format, ...)
else
print("[" .. type:upper() .. "]: " .. string.format(format, ...))
end
end
---Represents an .editorconfig path rule/expression.
---@class plugins.editorconfig.parser.rule
---Path expression as found between square brackets.
---@field expression string | table<integer,string>
---The expression converted to a regex.
---@field regex string | table<integer,string>
---@field regex_compiled any? | table<integer,string>
---@field negation boolean Indicates that the expression is a negation.
---@field ranges table<integer,number> List of ranges found on the expression.
---Represents a section of the .editorconfig with all its config options.
---@class plugins.editorconfig.parser.section
---@field rule plugins.editorconfig.parser.rule
---@field equivalent_rules plugins.editorconfig.parser.rule[]
---@field indent_style "tab" | "space"
---@field indent_size integer
---@field tab_width integer
---@field end_of_line "lf" | "cr" | "crlf"
---@field charset "latin1" | "utf-8" | "utf-8-bom" | "utf-16be" | "utf-16le"
---@field trim_trailing_whitespace boolean
---@field insert_final_newline boolean
---EditorConfig parser class and filename config matching.
---@class plugins.editorconfig.parser
---@field config_path string
---@field sections plugins.editorconfig.parser.section[]
---@field root boolean
local Parser = {}
Parser.__index = Parser
---Constructor
---@param config_path string
---@return plugins.editorconfig.parser
function Parser.new(config_path)
local self = {}
setmetatable(self, Parser)
self.config_path = config_path
self.sections = {}
self.root = false
self:read()
return self
end
--- char to hex cache and automatic converter
---@type table<string,string>
local hex_value = {}
setmetatable(hex_value, {
__index = function(t, k)
local v = rawget(t, k)
if v == nil then
v = string.format("%x", string.byte(k))
rawset(t, k, v)
end
return v
end
})
---Simplifies managing rules with other inner rules like {...} which can
---contain escaped \\{ \\} and expressions that are easier handled after
---converting the escaped special characters to \xXX counterparts.
---@param value string
---@return string escaped_values
local function escapes_to_regex_hex(value)
local escaped_chars = {}
for char in value:ugmatch("\\(.)") do
table.insert(escaped_chars, char)
end
for _, char in ipairs(escaped_chars) do
value = value:ugsub("\\" .. char, "\\x" .. hex_value[char])
end
return value
end
---An .editorconfig path expression to regex conversion rule.
---@class rule
---@field rule string Lua pattern.
---Callback conversion function.
---@field conversion fun(match:string, section:plugins.editorconfig.parser.section):string
---List of conversion rules applied to brace expressions.
---@type rule[]
local RULES_BRACES = {
{ rule = "^%(", conversion = function() return "\\(" end },
{ rule = "^%)", conversion = function() return "\\)" end },
{ rule = "^%.", conversion = function() return "\\." end },
{ rule = "^\\%[", conversion = function() return "\\[" end },
{ rule = "^\\%]", conversion = function() return "\\]" end },
{ rule = "^\\!", conversion = function() return "!" end },
{ rule = "^\\;", conversion = function() return ";" end },
{ rule = "^\\#", conversion = function() return "#" end },
{ rule = "^\\,", conversion = function() return "," end },
{ rule = "^\\{", conversion = function() return "{" end },
{ rule = "^\\}", conversion = function() return "}" end },
{ rule = "^,", conversion = function() return "|" end },
{ rule = "^\\%*", conversion = function() return "\\*" end },
{ rule = "^%*", conversion = function() return "[^\\/]*" end },
{ rule = "^%*%*", conversion = function() return ".*" end },
{ rule = "^%?", conversion = function() return "." end },
{ rule = "^{}", conversion = function() return "{}" end },
{ rule = "^{[^,]+}", conversion = function(match) return match end },
{ rule = "^%b{}",
conversion = function(match)
local out = match:ugsub("%(", "\\(")
:ugsub("%)", "\\)")
:ugsub("%.", "\\.")
:ugsub("\\%[", "[\\[]")
:ugsub("\\%]", "[\\]]")
:ugsub("^\\!", "!")
:ugsub("^\\;", ";")
:ugsub("^\\#", "#")
-- negation chars list
:ugsub("%[!(%a+)%]", "[^%1]")
:ugsub("\\\\", "[\\]")
-- escaped braces
:ugsub("\\{", "[{]")
:ugsub("\\}", "[}]")
-- non escaped braces
:ugsub("{([^%]])", "(%1")
:ugsub("}([^%]])", ")%1")
:ugsub("^{", "(")
:ugsub("}$", ")")
-- escaped globs
:ugsub("\\%*", "[\\*]")
:ugsub("\\%?", "[\\?]")
-- non escaped globs
:ugsub("%*%*", "[*][*]") -- prevent this glob from expanding to next sub
:ugsub("%*([^%]])", "[^\\/]*%1")
:ugsub("%[%*%]%[%*%]", ".*")
:ugsub("%?([^%]])", ".%1")
-- escaped comma
:ugsub("\\,", "[,]")
-- non escaped comma
:ugsub(",([^%]])", "|%1")
return out
end
},
{ rule = "^%[[^/%]]*%]",
conversion = function(match)
local negation = match:umatch("^%[!")
local chars = match:umatch("^%[!?(.-)%]")
chars = chars:ugsub("^%-", "\\-"):ugsub("%-$", "\\-")
local out = ""
if negation then
out = "[^"..chars.."]"
else
out = "["..chars.."]"
end
return out
end
},
}
---List of conversion rules applied to .editorconfig path expressions.
---@type rule[]
local RULES = {
-- normalize escaped .editorconfig special chars or keep them escaped
{ rule = "^\\x[a-fA-F][a-fA-F]", conversion = function(match) return match end },
{ rule = "^\\%*", conversion = function() return "\\*" end },
{ rule = "^\\%?", conversion = function() return "\\?" end },
{ rule = "^\\{", conversion = function() return "{" end },
{ rule = "^\\}", conversion = function() return "}" end },
{ rule = "^\\%[", conversion = function() return "\\[" end },
{ rule = "^\\%]", conversion = function() return "\\]" end },
{ rule = "^\\!", conversion = function() return "!" end },
{ rule = "^\\;", conversion = function() return ";" end },
{ rule = "^\\#", conversion = function() return "#" end },
-- escape special chars
{ rule = "^%.", conversion = function() return "\\." end },
{ rule = "^%(", conversion = function() return "\\(" end },
{ rule = "^%)", conversion = function() return "\\)" end },
{ rule = "^%[[^/%]]*%]",
conversion = function(match)
local negation = match:umatch("^%[!")
local chars = match:umatch("^%[!?(.-)%]")
chars = chars:ugsub("^%-", "\\-"):ugsub("%-$", "\\-")
local out = ""
if negation then
out = "[^"..chars.."]"
else
out = "["..chars.."]"
end
return out
end
},
-- Is this negation rule valid?
{ rule = "^!%w+",
conversion = function(match)
local chars = match:umatch("%w+")
return "[^"..chars.."]"
end
},
-- escape square brackets
{ rule = "^%[", conversion = function() return "\\[" end },
{ rule = "^%]", conversion = function() return "\\]" end },
-- match any characters
{ rule = "^%*%*", conversion = function() return ".*" end },
-- match any characters excluding path separators, \ not needed but just in case
{ rule = "^%*", conversion = function() return "[^\\/]*" end },
-- match optional character, doesn't matters what or should only be a \w?
{ rule = "^%?", conversion = function() return "[^/]" end },
-- threat empty braces literally
{ rule = "^{}", conversion = function() return "{}" end },
-- match a number range
{ rule = "^{%-?%d+%.%.%-?%d+}",
conversion = function(match, section)
local min, max = match:umatch("(-?%d+)%.%.(-?%d+)")
min = tonumber(min)
max = tonumber(max)
if min and max then
if not section.rule.ranges then section.rule.ranges = {} end
table.insert(section.rule.ranges, {
math.min(min, max),
math.max(min, max)
})
end
local minus = ""
if min < 0 or max < 0 then minus = "\\-?" end
return "(?<!0)("..minus.."[1-9]\\d*)"
end
},
-- threat single option braces literally
{ rule = "^{[^,]+}", conversion = function(match) return match end },
-- match invalid range
{ rule = "^{[^%.]+%.%.[^%.]+}", conversion = function(match) return match end },
-- match any of the strings separated by commas inside the curly braces
{ rule = "^%b{}",
conversion = function(rule, section)
rule = rule:gsub("^{", ""):gsub("}$", "")
local pos, len, exp = 1, rule:ulen(), ""
while pos <= len do
local found = false
for _, r in ipairs(RULES_BRACES) do
local match = rule:umatch(r.rule, pos)
if match then
exp = exp .. r.conversion(match, section)
pos = pos + match:ulen()
found = true
break
end
end
if not found then
exp = exp .. rule:usub(pos, pos)
pos = pos + 1
end
end
return "(" .. exp .. ")"
end
}
}
---Adds the regex equivalent of a section path expression.
---@param section plugins.editorconfig.parser.section | string
---@return plugins.editorconfig.parser.section
function Parser:rule_to_regex(section)
if type(section) == "string" then
section = {rule = {expression = section}}
end
local rule = section.rule.expression
-- match everything rule which is different from regular *
-- that doesn't matches path separators
if rule == "*" then
section.rule.regex = ".+"
section.rule.regex_compiled = regex.compile(".+")
return section
end
rule = escapes_to_regex_hex(section.rule.expression)
local pos, len, exp = 1, rule:ulen(), ""
-- if expression starts with ! it is treated entirely as a negation
local negation = rule:umatch("^%s*!")
if negation then
pos = pos + negation:ulen() + 1
end
-- apply all conversion rules by looping the path expression/rule
while pos <= len do
local found = false
for _, r in ipairs(RULES) do
local match = rule:umatch(r.rule, pos)
if match then
exp = exp .. r.conversion(match, section)
pos = pos + match:ulen()
found = true
break
end
end
if not found then
exp = exp .. rule:usub(pos, pos)
pos = pos + 1
end
end
-- force match up to the end
exp = exp .. "$"
-- allow expressions that start with * to match anything on start
if exp:match("^%[^\\/%]%*") then
exp = exp:gsub("^%[^\\/%]%*", ".*")
-- fixes two failing tests
elseif exp:match("^%[") then
exp = "^" .. exp
-- match only on root dir
elseif exp:match("^/") then
exp = exp:gsub("^/", "^")
end
-- store changes to the section rule
section.rule.regex, section.rule.negation = exp, negation
section.rule.regex_compiled = regex.compile(section.rule.regex)
if not section.rule.regex_compiled then
log(
"error",
"could not compile '[%s]' to regex '%s'",
rule, section.rule.regex
)
end
return section
end
---Parses the associated .editorconfig file and stores each section.
function Parser:read()
local file = io.open(self.config_path, "r")
self.sections = {}
if not file then
log("log", "could not read %s", self.config_path)
return
end
---@type plugins.editorconfig.parser.section
local section = {}
for line in file:lines() do
---@cast line string
-- first we try to see if the line is a rule section
local rule = ""
rule = line:umatch("^%s*%[(.+)%]%s*$")
if rule then
if section.rule then
-- save previous section and crerate new one
table.insert(self.sections, section)
section = {}
end
section.rule = {
expression = rule
}
-- convert the expression to a regex directly on the section table
self:rule_to_regex(section)
local clone = rule
if clone:match("//+") or clone:match("/%*%*/") then
section.equivalent_rules = {}
end
while clone:match("//+") or clone:match("/%*%*/") do
---@type plugins.editorconfig.parser.section[]
if clone:match("//+") then
clone = clone:ugsub("//+", "/", 1)
table.insert(section.equivalent_rules, self:rule_to_regex(clone).rule)
end
if clone:match("/%*%*/") then
clone = clone:ugsub("/%*%*/", "/", 1)
table.insert(section.equivalent_rules, self:rule_to_regex(clone).rule)
end
end
end
if not rule then
local name, value = line:umatch("^%s*(%w%S+)%s*=%s*([^\n\r]+)")
if name and value then
name = name:ulower()
-- do not lowercase property values that start with test_
if not name:match("^test_") then
value = value:ulower()
end
if value == "true" then
value = true
elseif value == "false" then
value = false
elseif math.tointeger and math.tointeger(value) then
value = math.tointeger(value)
elseif tonumber(value) then
value = tonumber(value)
end
if section.rule then
section[name] = value
elseif name == "root" and type(value) == "boolean" then
self.root = value
end
end
end
end
if section.rule then
table.insert(self.sections, section)
end
end
---Helper function that converts a regex offset results into a list
---of strings, omitting the first result which is the complete match.
---@param offsets table<integer,integer>
---@param value string
---@return table<integer, string>
local function regex_result_to_table(offsets, value)
local result = {}
local offset_fix = 0
if not regex.find_offsets then
offset_fix = 1
end
for i=3, #offsets, 2 do
table.insert(result, value:sub(offsets[i], offsets[i+1]-offset_fix))
end
return result
end
---Get a matching config for the given filename or nil if nothing found.
---@param file_name string
---@param defaults? boolean Set indent size to defaults when needed,
---@return plugins.editorconfig.parser.section?
function Parser:getConfig(file_name, defaults)
if PLATFORM == "Windows" then
file_name = file_name:gsub("\\", "/")
end
local regex_match = regex.match
if regex.find_offsets then
regex_match = regex.find_offsets
end
local properties = {}
local found = false
for _, section in ipairs(self.sections) do
if section.rule.regex_compiled then
local negation = section.rule.negation
-- default rule
local matched = {regex_match(section.rule.regex_compiled, file_name)}
-- try equivalent rules if available
if not matched[1] and section.equivalent_rules then
for _, esection in ipairs(section.equivalent_rules) do
matched = {regex_match(esection.regex_compiled, file_name)}
if matched[1] then
break
end
end
end
if (matched[1] and not negation) or (not matched[1] and negation) then
local ranges_match = true
if section.rule.ranges then
local results = regex_result_to_table(matched, file_name)
if #results < #section.rule.ranges then
ranges_match = false
else
for i, range in ipairs(section.rule.ranges) do
local number = tonumber(results[i])
if not number then
ranges_match = false
break
end
if number < range[1] or number > range[2] then
ranges_match = false
break
end
end
end
end
if ranges_match then
found = true
for name, value in pairs(section) do
if name ~= "rule" and name ~= "equivalent_rules" then
properties[name] = value
end
end
end
end
end
end
if found and defaults then
if properties.indent_style and properties.indent_style == "space" then
if properties.indent_size and not properties.tab_width then
properties.tab_width = 4
end
elseif properties.indent_style and properties.indent_style == "tab" then
if not properties.tab_width and not properties.indent_size then
properties.indent_size = "tab"
elseif properties.tab_width then
properties.indent_size = properties.tab_width
end
end
end
return found and properties or nil
end
---Get a matching config for the given filename or nil if nothing found.
---@param file_name string
---@return string
function Parser:getConfigString(file_name)
local out = ""
local properties = self:getConfig(file_name, true)
if properties then
local config_sorted = {}
for name, value in pairs(properties) do
table.insert(config_sorted, {name = name, value = value})
end
table.sort(config_sorted, function(a, b)
return a.name < b.name
end)
for _, value in ipairs(config_sorted) do
out = out .. value.name .. "=" .. tostring(value.value) .. "\n"
end
end
return out
end
return Parser

View File

@ -1,63 +0,0 @@
local core = require "core"
local tests = require "plugins.editorconfig.tests"
-- disable print buffer for immediate output
io.stdout:setvbuf "no"
-- overwrite to print into stdout
function core.error(format, ...)
print(string.format(format, ...))
end
function core.log(format, ...)
print(string.format(format, ...))
end
function core.log_quiet(format, ...)
print(string.format(format, ...))
end
-- check if --parsers flag was given to only output the path expressions and
-- their conversion into regular expressions.
local PARSERS = false
for _, argument in ipairs(ARGS) do
if argument == "--parsers" then
PARSERS = true
end
end
if not PARSERS then
require "plugins.editorconfig.tests.glob"
require "plugins.editorconfig.tests.parser"
require "plugins.editorconfig.tests.properties"
tests.run()
else
-- Globs
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/glob/braces.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/glob/brackets.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/glob/question.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/glob/star.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/glob/star_star.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/glob/utf8char.in")
-- Parser
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/parser/basic.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/parser/bom.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/parser/comments.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/parser/comments_and_newlines.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/parser/comments_only.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/parser/crlf.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/parser/empty.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/parser/limits.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/parser/newlines_only.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/parser/whitespace.in")
-- Properties
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/properties/indent_size_default.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/properties/lowercase_names.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/properties/lowercase_values.in")
tests.add_parser(USERDIR .. "/plugins/editorconfig/tests/properties/tab_width_default.in")
tests.run_parsers()
end

View File

@ -1,71 +0,0 @@
; test { and }
root=true
; word choice
[*.{py,js,html}]
choice=true
; single choice
[{single}.b]
choice=single
; empty choice
[{}.c]
empty=all
; choice with empty word
[a{b,c,}.d]
empty=word
; choice with empty words
[a{,b,,c,}.e]
empty=words
; no closing brace
[{.f]
closing=false
; nested braces
[{word,{also},this}.g]
nested=true
; nested braces, adjacent at start
[{{a,b},c}.k]
nested_start=true
; nested braces, adjacent at end
[{a,{b,c}}.l]
nested_end=true
; closing inside beginning
[{},b}.h]
closing=inside
; opening inside beginning
[{{,b,c{d}.i]
unmatched=true
; escaped comma
[{a\,b,cd}.txt]
comma=yes
; escaped closing brace
[{e,\},f}.txt]
closing=yes
; escaped backslash
[{g,\\,i}.txt]
backslash=yes
; patterns nested in braces
[{some,a{*c,b}[ef]}.j]
patterns=nested
; numeric braces
[{3..120}]
number=true
; alphabetical
[{aardvark..antelope}]
words=a

View File

@ -1,51 +0,0 @@
; test [ and ]
root=true
; Character choice
[[ab].a]
choice=true
; Negative character choice
[[!ab].b]
choice=false
; Character range
[[d-g].c]
range=true
; Negative character range
[[!d-g].d]
range=false
; Range and choice
[[abd-g].e]
range_and_choice=true
; Choice with dash
[[-ab].f]
choice_with_dash=true
; Close bracket inside
[[\]ab].g]
close_inside=true
; Close bracket outside
[[ab]].g]
close_outside=true
; Negative close bracket inside
[[!\]ab].g]
close_inside=false
; Negative¬close bracket outside
[[!ab]].g]
close_outside=false
; Slash inside brackets
[ab[e/]cd.i]
slash_inside=true
; Slash after an half-open bracket
[ab[/c]
slash_half_open=true

View File

@ -1,241 +0,0 @@
local tests = require "plugins.editorconfig.tests"
-- Tests for *
-- matches a single characters
tests.add("star_single_ML", "glob/star.in", "ace.c", "key=value[ \t\n\r]+keyc=valuec[ \t\n\r]*")
-- matches zero characters
tests.add("star_zero_ML", "glob/star.in", "ae.c", "key=value[ \t\n\r]+keyc=valuec[ \t\n\r]*")
-- matches multiple characters
tests.add("star_multiple_ML", "glob/star.in", "abcde.c", "key=value[ \t\n\r]+keyc=valuec[ \t\n\r]*")
-- does not match path separator
tests.add("star_over_slash", "glob/star.in", "a/e.c", "^[ \t\n\r]*keyc=valuec[ \t\n\r]*$")
-- star after a slash
tests.add("star_after_slash_ML", "glob/star.in", "Bar/foo.txt", "keyb=valueb[ \t\n\r]+keyc=valuec[ \t\n\r]*")
-- star matches a dot file after slash
tests.add("star_matches_dot_file_after_slash_ML", "glob/star.in", "Bar/.editorconfig", "keyb=valueb[ \t\n\r]+keyc=valuec[ \t\n\r]*")
-- star matches a dot file
tests.add("star_matches_dot_file", "glob/star.in", ".editorconfig", "^keyc=valuec[ \t\n\r]*$")
-- Tests for ?
-- matches a single character
tests.add("question_single", "glob/question.in", "some.c", "^key=value[ \t\n\r]*$")
-- does not match zero characters
tests.add("question_zero", "glob/question.in", "som.c", "^[ \t\n\r]*$")
-- does not match multiple characters
tests.add("question_multiple", "glob/question.in", "something.c", "^[ \t\n\r]*$")
-- does not match slash
tests.add("question_slash", "glob/question.in", "som/.c", "^[ \t\n\r]*$")
-- Tests for [ and ]
-- close bracket inside
tests.add("brackets_close_inside", "glob/brackets.in", "].g", "^close_inside=true[ \t\n\r]*$")
-- close bracket outside
tests.add("brackets_close_outside", "glob/brackets.in", "b].g", "^close_outside=true[ \t\n\r]*$")
-- negative close bracket inside
tests.add("brackets_nclose_inside", "glob/brackets.in", "c.g", "^close_inside=false[ \t\n\r]*$")
-- negative close bracket outside
tests.add("brackets_nclose_outside", "glob/brackets.in", "c].g", "^close_outside=false[ \t\n\r]*$")
-- character choice
tests.add("brackets_choice", "glob/brackets.in", "a.a", "^choice=true[ \t\n\r]*$")
-- character choice 2
tests.add("brackets_choice2", "glob/brackets.in", "c.a", "^[ \t\n\r]*$")
-- negative character choice
tests.add("brackets_nchoice", "glob/brackets.in", "c.b", "^choice=false[ \t\n\r]*$")
-- negative character choice 2
tests.add("brackets_nchoice2", "glob/brackets.in", "a.b", "^[ \t\n\r]*$")
-- character range
tests.add("brackets_range", "glob/brackets.in", "f.c", "^range=true[ \t\n\r]*$")
-- character range 2
tests.add("brackets_range2", "glob/brackets.in", "h.c", "^[ \t\n\r]*$")
-- negative character range
tests.add("brackets_nrange", "glob/brackets.in", "h.d", "^range=false[ \t\n\r]*$")
-- negative character range 2
tests.add("brackets_nrange2", "glob/brackets.in", "f.d", "^[ \t\n\r]*$")
-- range and choice
tests.add("brackets_range_and_choice", "glob/brackets.in", "e.e",
"^range_and_choice=true[ \t\n\r]*$")
-- character choice with a dash
tests.add("brackets_choice_with_dash", "glob/brackets.in", "-.f",
"^choice_with_dash=true[ \t\n\r]*$")
-- slash inside brackets
tests.add("brackets_slash_inside1", "glob/brackets.in", "ab/cd.i",
"^[ \t\n\r]*$")
tests.add("brackets_slash_inside2", "glob/brackets.in", "abecd.i",
"^[ \t\n\r]*$")
tests.add("brackets_slash_inside3", "glob/brackets.in", "ab[e/]cd.i",
"^slash_inside=true[ \t\n\r]*$")
tests.add("brackets_slash_inside4", "glob/brackets.in", "ab[/c",
"^slash_half_open=true[ \t\n\r]*$")
-- Tests for { and }
-- word choice
tests.add("braces_word_choice1", "glob/braces.in", "test.py", "^choice=true[ \t\n\r]*$")
tests.add("braces_word_choice2", "glob/braces.in", "test.js", "^choice=true[ \t\n\r]*$")
tests.add("braces_word_choice3", "glob/braces.in", "test.html", "^choice=true[ \t\n\r]*$")
tests.add("braces_word_choice4", "glob/braces.in", "test.pyc", "^[ \t\n\r]*$")
-- single choice
tests.add("braces_single_choice", "glob/braces.in", "{single}.b", "^choice=single[ \t\n\r]*$")
tests.add("braces_single_choice_negative", "glob/braces.in", ".b", "^[ \t\n\r]*$")
-- empty choice
tests.add("braces_empty_choice", "glob/braces.in", "{}.c", "^empty=all[ \t\n\r]*$")
tests.add("braces_empty_choice_negative", "glob/braces.in", ".c", "^[ \t\n\r]*$")
-- choice with empty word
tests.add("braces_empty_word1", "glob/braces.in", "a.d", "^empty=word[ \t\n\r]*$")
tests.add("braces_empty_word2", "glob/braces.in", "ab.d", "^empty=word[ \t\n\r]*$")
tests.add("braces_empty_word3", "glob/braces.in", "ac.d", "^empty=word[ \t\n\r]*$")
tests.add("braces_empty_word4", "glob/braces.in", "a,.d", "^[ \t\n\r]*$")
-- choice with empty words
tests.add("braces_empty_words1", "glob/braces.in", "a.e", "^empty=words[ \t\n\r]*$")
tests.add("braces_empty_words2", "glob/braces.in", "ab.e", "^empty=words[ \t\n\r]*$")
tests.add("braces_empty_words3", "glob/braces.in", "ac.e", "^empty=words[ \t\n\r]*$")
tests.add("braces_empty_words4", "glob/braces.in", "a,.e", "^[ \t\n\r]*$")
-- no closing brace
tests.add("braces_no_closing", "glob/braces.in", "{.f", "^closing=false[ \t\n\r]*$")
tests.add("braces_no_closing_negative", "glob/braces.in", ".f", "^[ \t\n\r]*$")
-- nested braces
tests.add("braces_nested1", "glob/braces.in", "word,this}.g", "^[ \t\n\r]*$")
tests.add("braces_nested2", "glob/braces.in", "{also,this}.g", "^[ \t\n\r]*$")
tests.add("braces_nested3", "glob/braces.in", "word.g", "^nested=true[ \t\n\r]*$")
tests.add("braces_nested4", "glob/braces.in", "{also}.g", "^nested=true[ \t\n\r]*$")
tests.add("braces_nested5", "glob/braces.in", "this.g", "^nested=true[ \t\n\r]*$")
-- nested braces, adjacent at start
tests.add("braces_nested_start1", "glob/braces.in", "{{a,b},c}.k", "^[ \t\n\r]*$")
tests.add("braces_nested_start2", "glob/braces.in", "{a,b}.k", "^[ \t\n\r]*$")
tests.add("braces_nested_start3", "glob/braces.in", "a.k", "^nested_start=true[ \t\n\r]*$")
tests.add("braces_nested_start4", "glob/braces.in", "b.k", "^nested_start=true[ \t\n\r]*$")
tests.add("braces_nested_start5", "glob/braces.in", "c.k", "^nested_start=true[ \t\n\r]*$")
-- nested braces, adjacent at end
tests.add("braces_nested_end1", "glob/braces.in", "{a,{b,c}}.l", "^[ \t\n\r]*$")
tests.add("braces_nested_end2", "glob/braces.in", "{b,c}.l", "^[ \t\n\r]*$")
tests.add("braces_nested_end3", "glob/braces.in", "a.l", "^nested_end=true[ \t\n\r]*$")
tests.add("braces_nested_end4", "glob/braces.in", "b.l", "^nested_end=true[ \t\n\r]*$")
tests.add("braces_nested_end5", "glob/braces.in", "c.l", "^nested_end=true[ \t\n\r]*$")
-- closing inside beginning
tests.add("braces_closing_in_beginning", "glob/braces.in", "{},b}.h", "^closing=inside[ \t\n\r]*$")
-- missing closing braces
tests.add("braces_unmatched1", "glob/braces.in", "{{,b,c{d}.i", "^unmatched=true[ \t\n\r]*$")
tests.add("braces_unmatched2", "glob/braces.in", "{.i", "^[ \t\n\r]*$")
tests.add("braces_unmatched3", "glob/braces.in", "b.i", "^[ \t\n\r]*$")
tests.add("braces_unmatched4", "glob/braces.in", "c{d.i", "^[ \t\n\r]*$")
tests.add("braces_unmatched5", "glob/braces.in", ".i", "^[ \t\n\r]*$")
-- escaped comma
tests.add("braces_escaped_comma1", "glob/braces.in", "a,b.txt", "^comma=yes[ \t\n\r]*$")
tests.add("braces_escaped_comma2", "glob/braces.in", "a.txt", "^[ \t\n\r]*$")
tests.add("braces_escaped_comma3", "glob/braces.in", "cd.txt", "^comma=yes[ \t\n\r]*$")
-- escaped closing brace
tests.add("braces_escaped_brace1", "glob/braces.in", "e.txt", "^closing=yes[ \t\n\r]*$")
tests.add("braces_escaped_brace2", "glob/braces.in", "}.txt", "^closing=yes[ \t\n\r]*$")
tests.add("braces_escaped_brace3", "glob/braces.in", "f.txt", "^closing=yes[ \t\n\r]*$")
-- escaped backslash
tests.add("braces_escaped_backslash1", "glob/braces.in", "g.txt", "^backslash=yes[ \t\n\r]*$")
if PLATFORM ~= "Windows" then
tests.add("braces_escaped_backslash2", "glob/braces.in", "\\.txt", "^backslash=yes[ \t\n\r]*$")
end
tests.add("braces_escaped_backslash3", "glob/braces.in", "i.txt", "^backslash=yes[ \t\n\r]*$")
-- patterns nested in braces
tests.add("braces_patterns_nested1", "glob/braces.in", "some.j", "^patterns=nested[ \t\n\r]*$")
tests.add("braces_patterns_nested2", "glob/braces.in", "abe.j", "^patterns=nested[ \t\n\r]*$")
tests.add("braces_patterns_nested3", "glob/braces.in", "abf.j", "^patterns=nested[ \t\n\r]*$")
tests.add("braces_patterns_nested4", "glob/braces.in", "abg.j", "^[ \t\n\r]*$")
tests.add("braces_patterns_nested5", "glob/braces.in", "ace.j", "^patterns=nested[ \t\n\r]*$")
tests.add("braces_patterns_nested6", "glob/braces.in", "acf.j", "^patterns=nested[ \t\n\r]*$")
tests.add("braces_patterns_nested7", "glob/braces.in", "acg.j", "^[ \t\n\r]*$")
tests.add("braces_patterns_nested8", "glob/braces.in", "abce.j", "^patterns=nested[ \t\n\r]*$")
tests.add("braces_patterns_nested9", "glob/braces.in", "abcf.j", "^patterns=nested[ \t\n\r]*$")
tests.add("braces_patterns_nested10", "glob/braces.in", "abcg.j", "^[ \t\n\r]*$")
tests.add("braces_patterns_nested11", "glob/braces.in", "ae.j", "^[ \t\n\r]*$")
tests.add("braces_patterns_nested12", "glob/braces.in", ".j", "^[ \t\n\r]*$")
-- numeric brace range
tests.add("braces_numeric_range1", "glob/braces.in", "1", "^[ \t\n\r]*$")
tests.add("braces_numeric_range2", "glob/braces.in", "3", "^number=true[ \t\n\r]*$")
tests.add("braces_numeric_range3", "glob/braces.in", "15", "^number=true[ \t\n\r]*$")
tests.add("braces_numeric_range4", "glob/braces.in", "60", "^number=true[ \t\n\r]*$")
tests.add("braces_numeric_range5", "glob/braces.in", "5a", "^[ \t\n\r]*$")
tests.add("braces_numeric_range6", "glob/braces.in", "120", "^number=true[ \t\n\r]*$")
tests.add("braces_numeric_range7", "glob/braces.in", "121", "^[ \t\n\r]*$")
tests.add("braces_numeric_range8", "glob/braces.in", "060", "^[ \t\n\r]*$")
-- alphabetical brace range: letters should not be considered for ranges
tests.add("braces_alpha_range1", "glob/braces.in", "{aardvark..antelope}", "^words=a[ \t\n\r]*$")
tests.add("braces_alpha_range2", "glob/braces.in", "a", "^[ \t\n\r]*$")
tests.add("braces_alpha_range3", "glob/braces.in", "aardvark", "^[ \t\n\r]*$")
tests.add("braces_alpha_range4", "glob/braces.in", "agreement", "^[ \t\n\r]*$")
tests.add("braces_alpha_range5", "glob/braces.in", "antelope", "^[ \t\n\r]*$")
tests.add("braces_alpha_range6", "glob/braces.in", "antimatter", "^[ \t\n\r]*$")
-- Tests for **
-- test EditorConfig files with UTF-8 characters larger than 127
tests.add("utf_8_char", "glob/utf8char.in", "中文.txt", "^key=value[ \t\n\r]*$")
-- matches over path separator
tests.add("star_star_over_separator1", "glob/star_star.in", "a/z.c", "^key1=value1[ \t\n\r]*$")
tests.add("star_star_over_separator2", "glob/star_star.in", "amnz.c", "^key1=value1[ \t\n\r]*$")
tests.add("star_star_over_separator3", "glob/star_star.in", "am/nz.c", "^key1=value1[ \t\n\r]*$")
tests.add("star_star_over_separator4", "glob/star_star.in", "a/mnz.c", "^key1=value1[ \t\n\r]*$")
tests.add("star_star_over_separator5", "glob/star_star.in", "amn/z.c", "^key1=value1[ \t\n\r]*$")
tests.add("star_star_over_separator6", "glob/star_star.in", "a/mn/z.c", "^key1=value1[ \t\n\r]*$")
tests.add("star_star_over_separator7", "glob/star_star.in", "b/z.c", "^key2=value2[ \t\n\r]*$")
tests.add("star_star_over_separator8", "glob/star_star.in", "b/mnz.c", "^key2=value2[ \t\n\r]*$")
tests.add("star_star_over_separator9", "glob/star_star.in", "b/mn/z.c", "^key2=value2[ \t\n\r]*$")
tests.add("star_star_over_separator10", "glob/star_star.in", "bmnz.c", "^[ \t\n\r]*$")
tests.add("star_star_over_separator11", "glob/star_star.in", "bm/nz.c", "^[ \t\n\r]*$")
tests.add("star_star_over_separator12", "glob/star_star.in", "bmn/z.c", "^[ \t\n\r]*$")
tests.add("star_star_over_separator13", "glob/star_star.in", "c/z.c", "^key3=value3[ \t\n\r]*$")
tests.add("star_star_over_separator14", "glob/star_star.in", "cmn/z.c", "^key3=value3[ \t\n\r]*$")
tests.add("star_star_over_separator15", "glob/star_star.in", "c/mn/z.c", "^key3=value3[ \t\n\r]*$")
tests.add("star_star_over_separator16", "glob/star_star.in", "cmnz.c", "^[ \t\n\r]*$")
tests.add("star_star_over_separator17", "glob/star_star.in", "cm/nz.c", "^[ \t\n\r]*$")
tests.add("star_star_over_separator18", "glob/star_star.in", "c/mnz.c", "^[ \t\n\r]*$")
tests.add("star_star_over_separator19", "glob/star_star.in", "d/z.c", "^key4=value4[ \t\n\r]*$")
tests.add("star_star_over_separator20", "glob/star_star.in", "d/mn/z.c", "^key4=value4[ \t\n\r]*$")
tests.add("star_star_over_separator21", "glob/star_star.in", "dmnz.c", "^[ \t\n\r]*$")
tests.add("star_star_over_separator22", "glob/star_star.in", "dm/nz.c", "^[ \t\n\r]*$")
tests.add("star_star_over_separator23", "glob/star_star.in", "d/mnz.c", "^[ \t\n\r]*$")
tests.add("star_star_over_separator24", "glob/star_star.in", "dmn/z.c", "^[ \t\n\r]*$")

View File

@ -1,7 +0,0 @@
; test ?
root=true
[som?.c]
key=value

View File

@ -1,12 +0,0 @@
; test *
root=true
[a*e.c]
key=value
[Bar/*]
keyb=valueb
[*]
keyc=valuec

View File

@ -1,15 +0,0 @@
; test **
root=true
[a**z.c]
key1=value1
[b/**z.c]
key2=value2
[c**/z.c]
key3=value3
[d/**/z.c]
key4=value4

Some files were not shown because too many files have changed in this diff Show More