Compare commits
162 Commits
Author | SHA1 | Date |
---|---|---|
George Sokianos | 2c0f92766f | |
George Sokianos | 3f6207b4af | |
George Sokianos | 79f42686a7 | |
George Sokianos | 9944a91f55 | |
George Sokianos | 19e95ed791 | |
George Sokianos | 759bccce52 | |
George Sokianos | a7971556d4 | |
George Sokianos | 6807a8e29a | |
George Sokianos | 1be1c7fb0b | |
George Sokianos | 7efe75fe7d | |
George Sokianos | 9c2eec9066 | |
George Sokianos | df8eaa64d1 | |
George Sokianos | ad4c221dd8 | |
George Sokianos | 66fb996e76 | |
takase1121 | c6a7ff98b0 | |
takase1121 | caf30574ed | |
takase1121 | 328ed55f83 | |
takase1121 | b4d750013d | |
Takase | b856dc371a | |
Takase | bbc524ad82 | |
vqn | a389adbaf5 | |
vqn | df12a5dde6 | |
Fiji | e353c5322a | |
Chloé Vulquin | 4ae92ae128 | |
vqn | 2ce8c58bea | |
takase1121 | b68efcd9e4 | |
takase1121 | 97a49661e0 | |
vqn | 9c21903af7 | |
Adam Harrison | 1d4e01a192 | |
ThaCuber | f1bdd840a1 | |
Guldoman | 34cebe8fe4 | |
Takase | 311651333a | |
Guldoman | 8e2928aeb8 | |
Guldoman | bc935906d1 | |
Guldoman | 9be5a46a22 | |
Guldoman | 885e6b3c50 | |
Guldoman | b1a647814f | |
Guldoman | 8eacca7ae1 | |
Guldoman | 19cef97bcd | |
Guldoman | c31a8ae0f6 | |
takase1121 | 9272f5ef2d | |
takase1121 | 88dcb25396 | |
takase1121 | 513432a784 | |
takase1121 | 43643a16c0 | |
Guldoman | 5c5c77219b | |
Guldoman | 6cd1f96234 | |
Adam | 3bf3266ca5 | |
Guldoman | 4adfd44d9f | |
Daniel Margarido | 35ef0a9484 | |
Guldoman | 01cab0611c | |
Guldoman | fb7ed49a44 | |
Takase | 34d163aa25 | |
Takase | 63d99d4431 | |
Takase | 5d53b13cf4 | |
Guldoman | 4005a46144 | |
Guldoman | 8c451928bf | |
Guldoman | a066190ee2 | |
Guldoman | 1ad3b70e9e | |
Takase | 82589526c0 | |
Guldoman | 316fbbe743 | |
Guldoman | 784b911d41 | |
Robert Hildebrandt | a9934c08d9 | |
Guldoman | 397b61e7c6 | |
Takase | 890e4882f3 | |
Takase | 09dd111c61 | |
Jan | d937693ddb | |
Guldoman | 3d93e16597 | |
Takase | eb27e543b4 | |
Guldoman | 86cfbe5f3b | |
Adam Harrison | c279ef0034 | |
Shreyas A S | 9120fb0046 | |
Delta-official | e62a672d7e | |
Takase | 07818934b6 | |
Takase | 5d0bcc99fa | |
Takase | b5b6682303 | |
Takase | ad0d280ecc | |
Takase | 553251834b | |
Takase | 7f84ed311b | |
Guldoman | f7400c924e | |
Guldoman | e9678cc140 | |
Luke aka SwissalpS | b5617a3eef | |
Luke aka SwissalpS | bd53bc3718 | |
Jan | 2af3082640 | |
Guldoman | 964b8fe29d | |
Guldoman | f1f81c8851 | |
Guldoman | 68e9c4670e | |
Guldoman | ff884d7d4a | |
Guldoman | 3febcf454c | |
Jefferson González | 6c17f6e2ee | |
takase1121 | 2a9b367e13 | |
takase1121 | 64e5fd8ead | |
takase1121 | 4c320a10c0 | |
Guldoman | bd36b3f615 | |
takase1121 | c0b1fe348f | |
takase1121 | 6111b071ec | |
Adam Harrison | 793af14dca | |
takase1121 | 2517d34113 | |
Takase | 1952848caa | |
Jan | 760271d416 | |
Takase | 2fe1f52a1f | |
Takase | 64a6d88618 | |
Guldoman | 97f3159415 | |
Takase | 8e57b71118 | |
Takase | a44a7eafe8 | |
Takase | 1fe90da664 | |
Jefferson González | 0d0f1b00d9 | |
vqn | f60228f610 | |
Guldoman | d497402c30 | |
Takase | fdd6ca3426 | |
Takase | 84aeea61c2 | |
Takase | a0c8f01312 | |
Takase | 4e3d6824ff | |
Takase | bb31a1adf2 | |
Takase | a24432941c | |
vqn | 12e0634f9c | |
Guldoman | 6d217204f6 | |
Adam | 6deca53303 | |
Guldoman | bf35417f82 | |
Takase | 84c7bb9de6 | |
Guldoman | 89864ee88c | |
Guldoman | a61531dbf0 | |
Guldoman | 1d0725f904 | |
Takase | 32860c111e | |
Guldoman | 8c47fad637 | |
Takase | f685293417 | |
Guldoman | 95611366bb | |
vqn | 067271bc02 | |
Adam | c8f033ec8b | |
Guldoman | b0e524dd15 | |
Jefferson González | 24491bc3fd | |
Jefferson González | 70ed171612 | |
Jefferson González | 6925c06599 | |
Takase | b95fdfcf5f | |
takase1121 | 218ba3ebac | |
Adam | 017711b369 | |
jgmdev | b8eb6865a6 | |
Jefferson González | 03d11c869d | |
Jan | a951c3cd39 | |
Adam Harrison | 5907118683 | |
Jefferson González | 95f18a1148 | |
Guldoman | bddb5e274d | |
Jan | d54a5d0672 | |
Adam Harrison | 4c18cf6744 | |
Guldoman | a9d8f12cb7 | |
xwii | 38fa9f976c | |
adityaraj | ae218bc005 | |
Jan | c8afe3d1bf | |
Takase | 2d0ddc302f | |
jgmdev | 291e7eab6f | |
sammyette | 1984573214 | |
Jan | f5a224999a | |
Jefferson González | 4b6134a839 | |
jgmdev | 60f0e3f3da | |
Guldoman | f00f41b468 | |
Julien Voisin | 1ab320bb9b | |
Guldoman | 138cea45d5 | |
Julien Voisin | d86413cc30 | |
Julien Voisin | d06c9f401c | |
Julien Voisin | d755fa6fba | |
Takase | 69ce580970 | |
Guldoman | bd4e64cc7e | |
Dave | 0fa0a59c8b |
|
@ -9,13 +9,15 @@ on:
|
|||
inputs:
|
||||
version:
|
||||
description: Release Version
|
||||
default: v2.1.1
|
||||
default: v2.1.4
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
version: ${{ steps.tag.outputs.version }}
|
||||
|
|
4
LICENSE
4
LICENSE
|
@ -1,4 +1,6 @@
|
|||
Copyright (c) 2020-present Lite XL Team
|
||||
Copyright (c) 2020 rxi
|
||||
Copyright (c) 2020-2022 Francesco Abbate
|
||||
Copyright (c) 2022-present Lite XL Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
|
32
Makefile.mos
32
Makefile.mos
|
@ -9,18 +9,22 @@ LiteXL_OBJ := \
|
|||
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/api/dirmonitor/mos.o src/platform/codesets.o
|
||||
|
||||
outfile := lite-xl
|
||||
compiler := gcc
|
||||
cxxcompiler := g++
|
||||
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 -lSDL2 -llua54 -lagg -lfreetype -lm -lc -L/usr/local/lib
|
||||
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
|
||||
|
||||
|
@ -32,11 +36,11 @@ clean:
|
|||
|
||||
LiteXL: $(LiteXL_OBJ)
|
||||
@echo "Linking LiteXL"
|
||||
@$(cxxcompiler) -o $(outfile) $(LiteXL_OBJ) $(LFLAGS)
|
||||
$(compiler) -o $(outfile) $(LiteXL_OBJ) $(LFLAGS)
|
||||
|
||||
.c.o:
|
||||
@echo "Compiling $<"
|
||||
@$(compiler) -c $< -o $*.o $(CFLAGS) $(INCPATH) $(DFLAGS)
|
||||
$(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
|
||||
|
@ -63,18 +67,22 @@ 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 resources/amiga/* release/LiteXL2/ -r
|
||||
@cp -r resources/amiga/* release/LiteXL2/
|
||||
@mv release/LiteXL2/LiteXL2.info release/
|
||||
@cp data release/LiteXL2/ -r
|
||||
@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..."
|
||||
|
|
13
Makefile.os4
13
Makefile.os4
|
@ -12,18 +12,16 @@ LiteXL_OBJ := \
|
|||
src/api/dirmonitor/os4.o src/platform/codesets.o
|
||||
|
||||
outfile := lite-xl
|
||||
compiler := gcc-11
|
||||
cxxcompiler := g++-11
|
||||
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 -lSDL2 -llua54 -lfreetype -lpng -lz -lpthread -athread=native
|
||||
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
|
||||
|
@ -89,6 +87,7 @@ release: clean LiteXL
|
|||
@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/
|
||||
|
|
52
README.md
52
README.md
|
@ -1,7 +1,7 @@
|
|||
# Lite XL
|
||||
|
||||
[![CI]](https://github.com/lite-xl/lite-xl/actions/workflows/build.yml)
|
||||
[![Discord Badge Image]](https://discord.gg/UQKnzBhY5H)
|
||||
[![Discord Badge Image]](https://discord.gg/RWzqC3nx7K)
|
||||
|
||||
![screenshot-dark]
|
||||
|
||||
|
@ -124,7 +124,6 @@ cd lite-xl
|
|||
```
|
||||
|
||||
To run lite-xl without installing:
|
||||
|
||||
```sh
|
||||
./lite-xl
|
||||
```
|
||||
|
@ -137,68 +136,31 @@ mkdir -p $HOME/.local/bin && cp lite-xl $HOME/.local/bin/
|
|||
mkdir -p $HOME/.local/share/lite-xl && cp -r data/* $HOME/.local/share/lite-xl/
|
||||
```
|
||||
|
||||
#### Add Lite XL to PATH
|
||||
|
||||
To run Lite XL from the command line, you must add it to PATH.
|
||||
|
||||
If `$HOME/.local/bin` is not in PATH:
|
||||
|
||||
```sh
|
||||
echo -e 'export PATH=$PATH:$HOME/.local/bin' >> $HOME/.bashrc
|
||||
```
|
||||
|
||||
Alternatively on recent versions of GNOME and KDE Plasma,
|
||||
you can add `$HOME/.local/bin` to PATH via `~/.config/environment.d/envvars.conf`:
|
||||
|
||||
```ini
|
||||
PATH=$HOME/.local/bin:$PATH
|
||||
```
|
||||
|
||||
> **Note**
|
||||
> Some systems might not load `.bashrc` when logging in.
|
||||
> This can cause problems with launching applications from the desktop / menu.
|
||||
|
||||
#### Add Lite XL to application launchers
|
||||
|
||||
To get the icon to show up in app launcher, you need to create a desktop
|
||||
entry and put it into `/usr/share/applications` or `~/.local/share/applications`.
|
||||
|
||||
Here is an example for a desktop entry in `~/.local/share/applications/com.lite_xl.LiteXL.desktop`,
|
||||
assuming Lite XL is in PATH:
|
||||
|
||||
```ini
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Lite XL
|
||||
Comment=A lightweight text editor written in Lua
|
||||
Exec=lite-xl %F
|
||||
Icon=lite-xl
|
||||
Terminal=false
|
||||
StartupWMClass=lite-xl
|
||||
Categories=Development;IDE;
|
||||
MimeType=text/plain;inode/directory;
|
||||
```
|
||||
|
||||
To get the icon to show up in app launcher immediately, run:
|
||||
To get the icon to show up in app launcher:
|
||||
|
||||
```sh
|
||||
xdg-desktop-menu forceupdate
|
||||
```
|
||||
|
||||
Alternatively, you may log out and log in again.
|
||||
You may need to logout and login again to see icon in app launcher.
|
||||
|
||||
#### Uninstall
|
||||
|
||||
To uninstall Lite XL, run:
|
||||
To uninstall just run:
|
||||
|
||||
```sh
|
||||
rm -f $HOME/.local/bin/lite-xl
|
||||
rm -rf $HOME/.local/share/icons/hicolor/scalable/apps/lite-xl.svg \
|
||||
$HOME/.local/share/applications/com.lite_xl.LiteXL.desktop \
|
||||
$HOME/.local/share/metainfo/com.lite_xl.LiteXL.appdata.xml \
|
||||
$HOME/.local/share/applications/org.lite_xl.lite_xl.desktop \
|
||||
$HOME/.local/share/metainfo/org.lite_xl.lite_xl.appdata.xml \
|
||||
$HOME/.local/share/lite-xl
|
||||
```
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Any additional functionality that can be added through a plugin should be done
|
||||
|
|
|
@ -88,10 +88,11 @@ Shows the current time and date in a view with large text
|
|||
Underlines matching pair for bracket under the caret
|
||||
|
||||
**codesets**
|
||||
This plugin is exclusive for AmigaOS 4 and uses the codesets.library to
|
||||
translate ISO encoded files to unicode. 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 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
|
||||
|
||||
|
@ -115,7 +116,10 @@ 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.
|
||||
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
|
||||
|
@ -174,6 +178,9 @@ 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
|
||||
|
||||
|
@ -223,6 +230,46 @@ 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).
|
||||
|
|
503
changelog.md
503
changelog.md
|
@ -1,5 +1,505 @@
|
|||
# Changes Log
|
||||
|
||||
## [2.1.4] - 2024-04-16
|
||||
|
||||
This release addresses severe bugs not found in previous releases,
|
||||
and improves the usability of the program.
|
||||
|
||||
### Features
|
||||
|
||||
* Add `.pyi` extension to `language_python`.
|
||||
([#1728](https://github.com/lite-xl/lite-xl/pull/1728))
|
||||
|
||||
* Improve autocomplete suggestions box behavior with long text
|
||||
([#1734](https://github.com/lite-xl/lite-xl/pull/1734))
|
||||
|
||||
* Improve `CommandView` and autocomplete scroll behavior
|
||||
([#1732](https://github.com/lite-xl/lite-xl/pull/1732))
|
||||
|
||||
* Add `from` symbol to support ESM
|
||||
([#1754](https://github.com/lite-xl/lite-xl/pull/1754))
|
||||
|
||||
* Add Arduino syntax highlighting support in `language_cpp`
|
||||
([#1767](https://github.com/lite-xl/lite-xl/pull/1767))
|
||||
|
||||
* Skip patterns matching nothing in tokenizer
|
||||
([#1743](https://github.com/lite-xl/lite-xl/pull/1743))
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix uninitialized variables in `src/api/process.c`
|
||||
([#1719](https://github.com/lite-xl/lite-xl/pull/1719))
|
||||
|
||||
* Fix `language_js` regex/comment distinction
|
||||
([#1731](https://github.com/lite-xl/lite-xl/pull/1731))
|
||||
|
||||
* Fix compilation on non-MINGW64 platforms
|
||||
([#1739](https://github.com/lite-xl/lite-xl/pull/1739))
|
||||
|
||||
* Limit `language_js` regex avoidance to numbers, and fix starting `/*` comments
|
||||
([#1744](https://github.com/lite-xl/lite-xl/pull/1744))
|
||||
|
||||
* Fix `buffer_size` in `g_read` for Windows
|
||||
([#1722](https://github.com/lite-xl/lite-xl/pull/1722))
|
||||
|
||||
* Fix missing permission for creating releases
|
||||
([#1770](https://github.com/lite-xl/lite-xl/pull/1770))
|
||||
|
||||
### Other Changes
|
||||
|
||||
* Rectify LICENSE dates and owners
|
||||
([#1748](https://github.com/lite-xl/lite-xl/pull/1748))
|
||||
|
||||
* Fix some typos in `core.init`
|
||||
([#1755](https://github.com/lite-xl/lite-xl/pull/1755))
|
||||
|
||||
## [2.1.3] - 2024-01-29
|
||||
|
||||
This release addresses severe bugs not found in previous releases.
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix `doc:create-cursor-{previous,next}-line` with tabs
|
||||
([#1697](https://github.com/lite-xl/lite-xl/pull/1697))
|
||||
|
||||
* Fix heap buffer overflow and memory leaks in process and renderer API
|
||||
([#1705](https://github.com/lite-xl/lite-xl/pull/1705))
|
||||
|
||||
* Improve Python number syntax highlighting
|
||||
([#1704](https://github.com/lite-xl/lite-xl/pull/1704))
|
||||
|
||||
* Fix inconsistent NagView options on `doc:save`
|
||||
([#1696](https://github.com/lite-xl/lite-xl/pull/1696))
|
||||
|
||||
* Fix crashes with autoreload when files are deleted externally and replaced with a directory.
|
||||
([#1698](https://github.com/lite-xl/lite-xl/pull/1698))
|
||||
|
||||
* Improve JavaScript number syntax highlighting
|
||||
([#1710](https://github.com/lite-xl/lite-xl/pull/1710))
|
||||
|
||||
### Other Changes
|
||||
|
||||
* Process API style changes
|
||||
([#1709](https://github.com/lite-xl/lite-xl/pull/1709))
|
||||
|
||||
## [2.1.2] - 2023-12-29
|
||||
|
||||
This release addresses some issues present in the previous release,
|
||||
and improves the performance and stability of Lite XL.
|
||||
|
||||
### New Features
|
||||
|
||||
* The context menu in TreeView is now navigable with a keyboard.
|
||||
([#1338](https://github.com/lite-xl/lite-xl/pull/1338))
|
||||
|
||||
* A universal build of Lite XL is now available for macOS.
|
||||
This build runs natively on both Intel and Apple Silicon macs.
|
||||
([#1458](https://github.com/lite-xl/lite-xl/pull/1458))
|
||||
|
||||
* Most Unicode characters should be displayed properly
|
||||
if your fonts support them.
|
||||
([#1524](https://github.com/lite-xl/lite-xl/pull/1524))
|
||||
|
||||
* LogView will no longer scroll automatically if the user had scrolled.
|
||||
The LogView will only scroll automatically when the user scrolls up
|
||||
to the last entry.
|
||||
([#1546](https://github.com/lite-xl/lite-xl/pull/1546))
|
||||
|
||||
* When using different fonts (especially fonts that render different scripts),
|
||||
the letters will be aligned vertically.
|
||||
([#1560](https://github.com/lite-xl/lite-xl/pull/1560))
|
||||
|
||||
* Unsaved named files are now saved in the workspace.
|
||||
([#1597](https://github.com/lite-xl/lite-xl/pull/1597))
|
||||
|
||||
* macOS builds are now signed with a developer certificate.
|
||||
This allows the user to right click the application in Finder and execute
|
||||
it directly.
|
||||
([#1656](https://github.com/lite-xl/lite-xl/pull/1656))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* Allow command buffer to be expanded.
|
||||
([#1297](https://github.com/lite-xl/lite-xl/pull/1297))
|
||||
|
||||
* Use table.move to implement `common.splice`.
|
||||
([#1324](https://github.com/lite-xl/lite-xl/pull/1324))
|
||||
|
||||
* Create renderer only when it doesn't exist.
|
||||
([#1315](https://github.com/lite-xl/lite-xl/pull/1315))
|
||||
|
||||
* Avoid drawing hidden text in `DocView:draw_line_text`.
|
||||
([#1298](https://github.com/lite-xl/lite-xl/pull/1298))
|
||||
|
||||
* Don't calculate widths per-uft8-char when not needed.
|
||||
([#1409](https://github.com/lite-xl/lite-xl/pull/1409))
|
||||
|
||||
* Allow tokenizer to pause and resume in the middle of a line.
|
||||
([#1444](https://github.com/lite-xl/lite-xl/pull/1444))
|
||||
|
||||
* Optimize CI build times on MSYS2.
|
||||
([#1435](https://github.com/lite-xl/lite-xl/pull/1435))
|
||||
|
||||
* Significant memory usage improvements when using huge fonts on Windows.
|
||||
([#1555](https://github.com/lite-xl/lite-xl/pull/1555))
|
||||
|
||||
* Optimize background tasks response time.
|
||||
([#1601](https://github.com/lite-xl/lite-xl/pull/1601))
|
||||
|
||||
### Backward Incompatible Changes
|
||||
|
||||
* The native plugin API is now usable on multiple source files,
|
||||
without causing any duplicated symbol errors during compilation.
|
||||
Plugins using the new plugin API header must define `LITE_XL_PLUGIN_ENTRYPOINT`
|
||||
before importing the header, in one of their source files.
|
||||
([#1335](https://github.com/lite-xl/lite-xl/pull/1335))
|
||||
|
||||
* The native plugin API header now follows the Lua 5.4 API.
|
||||
Previously, the plugin API header followed the Lua 5.2 API.
|
||||
([#1436](https://github.com/lite-xl/lite-xl/pull/1436))
|
||||
|
||||
* On Linux, `process.start()` will now throw an error if `execv()` fails.
|
||||
([#1363](https://github.com/lite-xl/lite-xl/pull/1363))
|
||||
|
||||
* Lite XL will use the default `SCALE` of 1 due to unreliable display
|
||||
scale detection. This may be fixed in a later version of Lite XL.
|
||||
Set the `LITE_SCALE` environment variable to override this value.
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix minor typos in user module
|
||||
([#1289](https://github.com/lite-xl/lite-xl/pull/1289))
|
||||
|
||||
* Do not allow users to create an empty font group
|
||||
([#1303](https://github.com/lite-xl/lite-xl/pull/1303))
|
||||
|
||||
* Fix a memory leak
|
||||
([#1305](https://github.com/lite-xl/lite-xl/pull/1305))
|
||||
|
||||
* Make dirwatch sorting compatible with what file_bisect expects
|
||||
([#1300](https://github.com/lite-xl/lite-xl/pull/1300))
|
||||
|
||||
* Handle readlink errors
|
||||
([#1292](https://github.com/lite-xl/lite-xl/pull/1292))
|
||||
|
||||
* Disable horizontal scrolling when linewrapping is enabled
|
||||
([#1309](https://github.com/lite-xl/lite-xl/pull/1309))
|
||||
|
||||
* Update widgets install location
|
||||
|
||||
* Add missing luaL_typeerror symbol to plugin API
|
||||
([#1313](https://github.com/lite-xl/lite-xl/pull/1313))
|
||||
|
||||
* Defer lua error until after cleanup
|
||||
([#1310](https://github.com/lite-xl/lite-xl/pull/1310))
|
||||
|
||||
* Make empty groups in regex.gmatch return their offset
|
||||
([#1325](https://github.com/lite-xl/lite-xl/pull/1325))
|
||||
|
||||
* Add missing header declaration
|
||||
|
||||
* Fix msys build now requiring ca-certificates
|
||||
([#1348](https://github.com/lite-xl/lite-xl/pull/1348))
|
||||
|
||||
* Fix path to macOS arm64 cross file in GitHub workflows
|
||||
|
||||
* Fix Doc contextmenu not registering commands if scale plugin is not found
|
||||
([#1338](https://github.com/lite-xl/lite-xl/pull/1338))
|
||||
|
||||
* Fix TreeView contextmenu commands not working if the mouse hovers DocView
|
||||
([#1338](https://github.com/lite-xl/lite-xl/pull/1338))
|
||||
|
||||
* Fix incorrect contextmenu predicate
|
||||
([#1338](https://github.com/lite-xl/lite-xl/pull/1338))
|
||||
|
||||
* Properly rescale NagView on scale change
|
||||
([#1379](https://github.com/lite-xl/lite-xl/pull/1379))
|
||||
|
||||
* Scale plugin also rescales `style.expanded_scrollbar_size`
|
||||
([#1380](https://github.com/lite-xl/lite-xl/pull/1380))
|
||||
|
||||
* Improve DocView:get_visible_line_range precision
|
||||
([#1382](https://github.com/lite-xl/lite-xl/pull/1382))
|
||||
|
||||
* Fix up some post 5.1/JIT Symbols
|
||||
([#1385](https://github.com/lite-xl/lite-xl/pull/1385))
|
||||
|
||||
* Fix incorrect x_offset if opened docs have different tab sizes
|
||||
([#1383](https://github.com/lite-xl/lite-xl/pull/1383))
|
||||
|
||||
* Use correct view for scrolling to find-replace:repeat-find results
|
||||
([#1400](https://github.com/lite-xl/lite-xl/pull/1400))
|
||||
|
||||
* Improve text width calculation precision
|
||||
([#1408](https://github.com/lite-xl/lite-xl/pull/1408))
|
||||
|
||||
* Add asynchronous process reaping
|
||||
([#1412](https://github.com/lite-xl/lite-xl/pull/1412))
|
||||
|
||||
* Fix cursors positions when deleting multiple selections
|
||||
([#1393](https://github.com/lite-xl/lite-xl/pull/1393),
|
||||
[#1463](https://github.com/lite-xl/lite-xl/pull/1463))
|
||||
|
||||
* Fix invalid EXEFILE and EXEDIR on Windows
|
||||
([#1396](https://github.com/lite-xl/lite-xl/pull/1396))
|
||||
|
||||
* Fix `os.getenv()` not supporting UTF-8 output
|
||||
([#1397](https://github.com/lite-xl/lite-xl/pull/1397))
|
||||
|
||||
* Fix differing stacktrace on stdout and file
|
||||
([#1404](https://github.com/lite-xl/lite-xl/pull/1404))
|
||||
|
||||
* Update api_require to expose more symbols
|
||||
([#1437](https://github.com/lite-xl/lite-xl/pull/1437))
|
||||
|
||||
* Make system.path_compare more case-aware
|
||||
([#1457](https://github.com/lite-xl/lite-xl/pull/1457))
|
||||
|
||||
* Fix for api_require wrong macro && conditions
|
||||
([#1465](https://github.com/lite-xl/lite-xl/pull/1465))
|
||||
|
||||
* Merge carets after doc:move-to-{previous,next}-char
|
||||
([#1462](https://github.com/lite-xl/lite-xl/pull/1462))
|
||||
|
||||
* Process API improvements (again)
|
||||
([#1370](https://github.com/lite-xl/lite-xl/pull/1370))
|
||||
|
||||
* Make system.path_compare more digit-aware
|
||||
([#1474](https://github.com/lite-xl/lite-xl/pull/1474))
|
||||
|
||||
* Check for HANDLE_INVALID in Process API
|
||||
([#1475](https://github.com/lite-xl/lite-xl/pull/1475))
|
||||
|
||||
* Fix linewrapping bug to do with wordwrapping
|
||||
|
||||
* Fix compiler warning for printing size_t in rencache.c
|
||||
|
||||
* Return error string from C searcher
|
||||
|
||||
* Restore horizontal scroll position after scale change
|
||||
([#494](https://github.com/lite-xl/lite-xl/pull/494))
|
||||
|
||||
* Fix memory leak in renderer.c when freeing glyphsets
|
||||
|
||||
* Move lineguide below blinking cursor
|
||||
([#1511](https://github.com/lite-xl/lite-xl/pull/1511))
|
||||
|
||||
* Close lua state when exiting on a runtime error
|
||||
([#1487](https://github.com/lite-xl/lite-xl/pull/1487))
|
||||
|
||||
* Mark linewrapping open_files table as weak
|
||||
|
||||
* Don't use core.status_view if not yet initialized when logging
|
||||
|
||||
* Revert "core syntax: strip the path from filename on syntax.get ([#1168](https://github.com/lite-xl/lite-xl/pull/1168))"
|
||||
([#1322](https://github.com/lite-xl/lite-xl/pull/1322))
|
||||
|
||||
* Make Doc:sanitize_position return a more appropriate col
|
||||
([#1469](https://github.com/lite-xl/lite-xl/pull/1469))
|
||||
|
||||
* Skip checking files if no filename was provided to syntax.get
|
||||
|
||||
* Normalize stroke before adding keybind
|
||||
([#1334](https://github.com/lite-xl/lite-xl/pull/1334))
|
||||
|
||||
* Make DocView aware of scrollbars sizes
|
||||
([#1177](https://github.com/lite-xl/lite-xl/pull/1177))
|
||||
|
||||
* Normalize strokes in fixed order
|
||||
([#1572](https://github.com/lite-xl/lite-xl/pull/1572))
|
||||
|
||||
* Defer core:open-log until everything is loaded
|
||||
([#1585](https://github.com/lite-xl/lite-xl/pull/1585))
|
||||
|
||||
* Fix returned percent when clicking the Scrollbar track
|
||||
|
||||
* Fix C++14 digit separators
|
||||
([#1593](https://github.com/lite-xl/lite-xl/pull/1593))
|
||||
|
||||
* Make linewrapping consider the expanded Scrollbar size
|
||||
|
||||
* Fix dimmed text when antialiasing is turned off
|
||||
([#1641](https://github.com/lite-xl/lite-xl/pull/1641))
|
||||
|
||||
* Mark unsaved named files as dirty
|
||||
([#1598](https://github.com/lite-xl/lite-xl/pull/1598))
|
||||
|
||||
* Make `common.serialize()` locale-independent and nan/inf compatible
|
||||
([#1640](https://github.com/lite-xl/lite-xl/pull/1640))
|
||||
|
||||
* Ignore keypresses during IME composition
|
||||
([#1573](https://github.com/lite-xl/lite-xl/pull/1573))
|
||||
|
||||
* Fix deadlock if error handler jumps somewhere else
|
||||
([#1647](https://github.com/lite-xl/lite-xl/pull/1647))
|
||||
|
||||
* Avoid considering single spaces in detectindent
|
||||
([#1595](https://github.com/lite-xl/lite-xl/pull/1595))
|
||||
|
||||
* Fix deleting indentation with multiple cursors
|
||||
([#1670](https://github.com/lite-xl/lite-xl/pull/1670))
|
||||
|
||||
* Fix `set_target_size` passing the wrong value to plugins
|
||||
([#1657](https://github.com/lite-xl/lite-xl/pull/1657))
|
||||
|
||||
* Limit `system.{sleep,wait_event}` to `timeouts >= 0`
|
||||
([#1666](https://github.com/lite-xl/lite-xl/pull/1666))
|
||||
|
||||
* Fix running core.step when receiving an event while not waiting
|
||||
([#1667](https://github.com/lite-xl/lite-xl/pull/1667))
|
||||
|
||||
* Fix dirmonitor sorting issues
|
||||
([#1599](https://github.com/lite-xl/lite-xl/pull/1599))
|
||||
|
||||
* Scale mouse coordinates by window scale
|
||||
([#1630](https://github.com/lite-xl/lite-xl/pull/1630))
|
||||
|
||||
* Made coroutines make more sense, and fixed a bug
|
||||
([#1381](https://github.com/lite-xl/lite-xl/pull/1381))
|
||||
|
||||
* Fix selecting newlines with `find-replace:select-add-{next,all}`
|
||||
([#1608](https://github.com/lite-xl/lite-xl/pull/1608))
|
||||
|
||||
* Fix editing after undo not clearing the change id
|
||||
([#1574](https://github.com/lite-xl/lite-xl/pull/1574))
|
||||
|
||||
* Fix language_js regex constant detection
|
||||
([#1581](https://github.com/lite-xl/lite-xl/pull/1581))
|
||||
|
||||
* Fix patterns starting with `^` in tokenizer
|
||||
([#1645](https://github.com/lite-xl/lite-xl/pull/1645))
|
||||
|
||||
* Use x offset to define render command rect in rencache_draw_text
|
||||
([#1618](https://github.com/lite-xl/lite-xl/pull/1618))
|
||||
|
||||
* Improve font/color change detection in `language_md`
|
||||
([#1614](https://github.com/lite-xl/lite-xl/pull/1614))
|
||||
|
||||
* Allow long commands and envs on process_start
|
||||
([#1477](https://github.com/lite-xl/lite-xl/pull/1477))
|
||||
|
||||
* Fix typo in `drawwhitespace.lua`
|
||||
|
||||
* Fix NagBar save failed message
|
||||
([#1678](https://github.com/lite-xl/lite-xl/pull/1678))
|
||||
|
||||
* Fix typo in `drawwhitespace.lua`
|
||||
|
||||
* Add autocompletion to multicursor
|
||||
([#1394](https://github.com/lite-xl/lite-xl/pull/1394))
|
||||
|
||||
### Other Changes
|
||||
|
||||
* Make api_require's nodes const
|
||||
([#1296](https://github.com/lite-xl/lite-xl/pull/1296))
|
||||
|
||||
* Don't set a value twice
|
||||
([#1306](https://github.com/lite-xl/lite-xl/pull/1306))
|
||||
|
||||
* Center title and version in emptyview
|
||||
([#1311](https://github.com/lite-xl/lite-xl/pull/1311))
|
||||
|
||||
* Use master branch for packaging plugins for addons release
|
||||
|
||||
* Reorganize resources folder and add wasm target
|
||||
([#1244](https://github.com/lite-xl/lite-xl/pull/1244))
|
||||
|
||||
* Replace uses of SDL_Window with RenWindow
|
||||
([#1319](https://github.com/lite-xl/lite-xl/pull/1319))
|
||||
|
||||
* Update dummy dirmonitor method signature to match prototypes
|
||||
|
||||
* Remove static libgcc from meson
|
||||
([#1290](https://github.com/lite-xl/lite-xl/pull/1290))
|
||||
|
||||
* Pass RenWindow by argument
|
||||
([#1321](https://github.com/lite-xl/lite-xl/pull/1321))
|
||||
|
||||
* Get rid of annoying forward slash on windows
|
||||
([#1345](https://github.com/lite-xl/lite-xl/pull/1345))
|
||||
|
||||
* Improve plugins config table handling
|
||||
([#1356](https://github.com/lite-xl/lite-xl/pull/1356))
|
||||
|
||||
* Add manifest on Windows
|
||||
([#1405](https://github.com/lite-xl/lite-xl/pull/1405))
|
||||
|
||||
* Split Command struct into different structs for each command type
|
||||
([#1407](https://github.com/lite-xl/lite-xl/pull/1407))
|
||||
|
||||
* Move SetProcessDPIAware to manifests
|
||||
([#1413](https://github.com/lite-xl/lite-xl/pull/1413))
|
||||
|
||||
* Use clipping functions provided by SDL
|
||||
([#1426](https://github.com/lite-xl/lite-xl/pull/1426))
|
||||
|
||||
* Aggregate SDL_Surfaces and their scale in RenSurface
|
||||
([#1429](https://github.com/lite-xl/lite-xl/pull/1429))
|
||||
|
||||
* Disable trimwhitespace and drawwhitespace via their configs
|
||||
([#1446](https://github.com/lite-xl/lite-xl/pull/1446))
|
||||
|
||||
* Bump dependency versions
|
||||
([#1434](https://github.com/lite-xl/lite-xl/pull/1434))
|
||||
|
||||
* Improvements to cross-compilation
|
||||
([#1458](https://github.com/lite-xl/lite-xl/pull/1458))
|
||||
|
||||
* Move native plugin API header into include/
|
||||
([#1440](https://github.com/lite-xl/lite-xl/pull/1440))
|
||||
|
||||
* Build releases with Ubuntu 18.04 container
|
||||
([#1460](https://github.com/lite-xl/lite-xl/pull/1460))
|
||||
|
||||
* Update GitHub Actions dependencies
|
||||
|
||||
* Make all parameters for set_window_hit_test optional in documentation
|
||||
|
||||
* Attach command buffer to Renderer Window
|
||||
([#1472](https://github.com/lite-xl/lite-xl/pull/1472))
|
||||
|
||||
* Fix comment typo in object.lua
|
||||
([#1541](https://github.com/lite-xl/lite-xl/pull/1541))
|
||||
|
||||
* Allow setting custom glyphset size
|
||||
([#1542](https://github.com/lite-xl/lite-xl/pull/1542))
|
||||
|
||||
* Use FreeType header names in renderer.c
|
||||
([#1554](https://github.com/lite-xl/lite-xl/pull/1554))
|
||||
|
||||
* Add documentation for core.common
|
||||
([#1510](https://github.com/lite-xl/lite-xl/pull/1510))
|
||||
|
||||
* Document missing parameter for system.path_compare
|
||||
([#1566](https://github.com/lite-xl/lite-xl/pull/1566))
|
||||
|
||||
* Add documentation for core.command
|
||||
([#1564](https://github.com/lite-xl/lite-xl/pull/1564))
|
||||
|
||||
* Update the *Installing prebuild* section in README.md
|
||||
([#1548](https://github.com/lite-xl/lite-xl/pull/1548))
|
||||
|
||||
* Update README.md to remove previously installed files
|
||||
prior to installing a new version
|
||||
|
||||
* Use lite-xl Build Box to build releases
|
||||
([#1571](https://github.com/lite-xl/lite-xl/pull/1571))
|
||||
|
||||
* Use Lua wrap by default
|
||||
([#1481](https://github.com/lite-xl/lite-xl/pull/1481))
|
||||
|
||||
* Add documentation for contextmenu
|
||||
([#1567](https://github.com/lite-xl/lite-xl/pull/1567))
|
||||
|
||||
* Use dmgbuild to create DMGs
|
||||
([#1664](https://github.com/lite-xl/lite-xl/pull/1664))
|
||||
|
||||
* Un-hardcode lua subproject detection and update dependencies
|
||||
([#1676](https://github.com/lite-xl/lite-xl/pull/1676))
|
||||
|
||||
* Make license time-independent
|
||||
([#1655](https://github.com/lite-xl/lite-xl/pull/1655))
|
||||
|
||||
## [2.1.1] - 2022-12-29
|
||||
|
||||
### New Features
|
||||
|
@ -1004,6 +1504,9 @@ A new global variable `USERDIR` is exposed to point to the user's directory.
|
|||
|
||||
- subpixel font rendering with gamma correction
|
||||
|
||||
[2.1.4]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.4
|
||||
[2.1.3]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.3
|
||||
[2.1.2]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.2
|
||||
[2.1.1]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.1
|
||||
[2.1.0]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.0
|
||||
[2.0.5]: https://github.com/lite-xl/lite-xl/releases/tag/v2.0.5
|
||||
|
|
|
@ -185,7 +185,7 @@ command.add(nil, {
|
|||
local dirname = common.dirname(core.project_dir)
|
||||
local text
|
||||
if dirname then
|
||||
text = common.home_encode(dirname) .. PATHSEP
|
||||
text = common.basepath(common.home_encode(dirname))
|
||||
end
|
||||
core.command_view:enter("Change Project Folder", {
|
||||
text = text,
|
||||
|
|
|
@ -44,8 +44,8 @@ local function save(filename)
|
|||
else
|
||||
core.error(err)
|
||||
core.nag_view:show("Saving failed", string.format("Couldn't save file \"%s\". Do you want to save to another location?", doc().filename), {
|
||||
{ text = "No", default_no = true },
|
||||
{ text = "Yes", default_yes = true }
|
||||
{ text = "Yes", default_yes = true },
|
||||
{ text = "No", default_no = true }
|
||||
}, function(item)
|
||||
if item.text == "Yes" then
|
||||
core.add_thread(function()
|
||||
|
@ -93,11 +93,14 @@ local function cut_or_copy(delete)
|
|||
system.set_clipboard(full_text)
|
||||
end
|
||||
|
||||
local function split_cursor(direction)
|
||||
local function split_cursor(dv, direction)
|
||||
local new_cursors = {}
|
||||
for _, line1, col1 in doc():get_selections() do
|
||||
if line1 + direction >= 1 and line1 + direction <= #doc().lines then
|
||||
table.insert(new_cursors, { line1 + direction, col1 })
|
||||
local dv_translate = direction < 0
|
||||
and DocView.translate.previous_line
|
||||
or DocView.translate.next_line
|
||||
for _, line1, col1 in dv.doc:get_selections() do
|
||||
if line1 + direction >= 1 and line1 + direction <= #dv.doc.lines then
|
||||
table.insert(new_cursors, { dv_translate(dv.doc, line1, col1, dv) })
|
||||
end
|
||||
end
|
||||
-- add selections in the order that will leave the "last" added one as doc.last_selection
|
||||
|
@ -107,7 +110,7 @@ local function split_cursor(direction)
|
|||
end
|
||||
for i = start, stop, direction do
|
||||
local v = new_cursors[i]
|
||||
doc():add_selection(v[1], v[2])
|
||||
dv.doc:add_selection(v[1], v[2])
|
||||
end
|
||||
core.blink_reset()
|
||||
end
|
||||
|
@ -360,7 +363,7 @@ local commands = {
|
|||
["doc:select-lines"] = function(dv)
|
||||
for idx, line1, _, line2 in dv.doc:get_selections(true) do
|
||||
append_line_if_last_line(line2)
|
||||
dv.doc:set_selections(idx, line2 + 1, 1, line1, 1)
|
||||
dv.doc:set_selections(idx, line1, 1, line2 + 1, 1)
|
||||
end
|
||||
end,
|
||||
|
||||
|
@ -545,11 +548,6 @@ local commands = {
|
|||
dv.doc.crlf = not dv.doc.crlf
|
||||
end,
|
||||
|
||||
["doc:toggle-overwrite"] = function(dv)
|
||||
dv.doc.overwrite = not dv.doc.overwrite
|
||||
core.blink_reset() -- to show the cursor has changed edit modes
|
||||
end,
|
||||
|
||||
["doc:save-as"] = function(dv)
|
||||
local last_doc = core.last_active_view and core.last_active_view.doc
|
||||
local text
|
||||
|
@ -626,12 +624,12 @@ local commands = {
|
|||
end,
|
||||
|
||||
["doc:create-cursor-previous-line"] = function(dv)
|
||||
split_cursor(-1)
|
||||
split_cursor(dv, -1)
|
||||
dv.doc:merge_cursors()
|
||||
end,
|
||||
|
||||
["doc:create-cursor-next-line"] = function(dv)
|
||||
split_cursor(1)
|
||||
split_cursor(dv, 1)
|
||||
dv.doc:merge_cursors()
|
||||
end
|
||||
|
||||
|
|
|
@ -196,23 +196,6 @@ local function select_next(reverse)
|
|||
if l2 then doc():set_selection(l2, c2, l1, c1) end
|
||||
end
|
||||
|
||||
---@param in_selection? boolean whether to replace in the selections only, or in the whole file.
|
||||
local function find_replace(in_selection)
|
||||
local l1, c1, l2, c2 = doc():get_selection()
|
||||
local selected_text = ""
|
||||
if not in_selection then
|
||||
selected_text = doc():get_text(l1, c1, l2, c2)
|
||||
doc():set_selection(l2, c2, l2, c2)
|
||||
end
|
||||
replace("Text", l1 == l2 and selected_text or "", function(text, old, new)
|
||||
if not find_regex then
|
||||
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
|
||||
end
|
||||
local result, matches = regex.gsub(regex.compile(old, "m"), text, new)
|
||||
return result, matches
|
||||
end)
|
||||
end
|
||||
|
||||
command.add(has_unique_selection, {
|
||||
["find-replace:select-next"] = select_next,
|
||||
["find-replace:select-previous"] = function() select_next(true) end,
|
||||
|
@ -229,11 +212,15 @@ command.add("core.docview!", {
|
|||
end,
|
||||
|
||||
["find-replace:replace"] = function()
|
||||
find_replace()
|
||||
end,
|
||||
|
||||
["find-replace:replace-in-selection"] = function()
|
||||
find_replace(true)
|
||||
local l1, c1, l2, c2 = doc():get_selection()
|
||||
local selected_text = doc():get_text(l1, c1, l2, c2)
|
||||
replace("Text", l1 == l2 and selected_text or "", function(text, old, new)
|
||||
if not find_regex then
|
||||
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
|
||||
end
|
||||
local result, matches = regex.gsub(regex.compile(old, "m"), text, new)
|
||||
return result, matches
|
||||
end)
|
||||
end,
|
||||
|
||||
["find-replace:replace-symbol"] = function()
|
||||
|
|
|
@ -4,7 +4,6 @@ local DocView = require "core.docview"
|
|||
local command = require "core.command"
|
||||
local common = require "core.common"
|
||||
local config = require "core.config"
|
||||
local Node = require "core.node"
|
||||
|
||||
|
||||
local t = {
|
||||
|
@ -30,6 +29,20 @@ local t = {
|
|||
core.confirm_close_docs(docs, core.root_view.close_all_docviews, core.root_view, true)
|
||||
end,
|
||||
|
||||
["root:switch-to-previous-tab"] = function(node)
|
||||
local idx = node:get_view_idx(core.active_view)
|
||||
idx = idx - 1
|
||||
if idx < 1 then idx = #node.views end
|
||||
node:set_active_view(node.views[idx])
|
||||
end,
|
||||
|
||||
["root:switch-to-next-tab"] = function(node)
|
||||
local idx = node:get_view_idx(core.active_view)
|
||||
idx = idx + 1
|
||||
if idx > #node.views then idx = 1 end
|
||||
node:set_active_view(node.views[idx])
|
||||
end,
|
||||
|
||||
["root:move-tab-left"] = function(node)
|
||||
local idx = node:get_view_idx(core.active_view)
|
||||
if idx > 1 then
|
||||
|
@ -104,7 +117,7 @@ end, t)
|
|||
|
||||
command.add(nil, {
|
||||
["root:scroll"] = function(delta)
|
||||
local view = core.root_view.overlapping_view or core.active_view
|
||||
local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view
|
||||
if view and view.scrollable then
|
||||
view.scroll.to.y = view.scroll.to.y + delta * -config.mouse_wheel_scroll
|
||||
return true
|
||||
|
@ -112,7 +125,7 @@ command.add(nil, {
|
|||
return false
|
||||
end,
|
||||
["root:horizontal-scroll"] = function(delta)
|
||||
local view = core.root_view.overlapping_view or core.active_view
|
||||
local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view
|
||||
if view and view.scrollable then
|
||||
view.scroll.to.x = view.scroll.to.x + delta * -config.mouse_wheel_scroll
|
||||
return true
|
||||
|
@ -120,74 +133,3 @@ command.add(nil, {
|
|||
return false
|
||||
end
|
||||
})
|
||||
|
||||
command.add(function(node)
|
||||
if not Node:is_extended_by(node) then node = nil end
|
||||
-- No node was specified, use the active one
|
||||
node = node or core.root_view:get_active_node()
|
||||
if not node then return false end
|
||||
return true, node
|
||||
end,
|
||||
{
|
||||
["root:switch-to-previous-tab"] = function(node)
|
||||
local idx = node:get_view_idx(node.active_view)
|
||||
idx = idx - 1
|
||||
if idx < 1 then idx = #node.views end
|
||||
node:set_active_view(node.views[idx])
|
||||
end,
|
||||
|
||||
["root:switch-to-next-tab"] = function(node)
|
||||
local idx = node:get_view_idx(node.active_view)
|
||||
idx = idx + 1
|
||||
if idx > #node.views then idx = 1 end
|
||||
node:set_active_view(node.views[idx])
|
||||
end,
|
||||
|
||||
["root:scroll-tabs-backward"] = function(node)
|
||||
node:scroll_tabs(1)
|
||||
end,
|
||||
|
||||
["root:scroll-tabs-forward"] = function(node)
|
||||
node:scroll_tabs(2)
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
command.add(function()
|
||||
local node = core.root_view.root_node:get_child_overlapping_point(core.root_view.mouse.x, core.root_view.mouse.y)
|
||||
if not node then return false end
|
||||
return (node.hovered_tab or node.hovered_scroll_button > 0) and true, node
|
||||
end,
|
||||
{
|
||||
["root:switch-to-hovered-previous-tab"] = function(node)
|
||||
command.perform("root:switch-to-previous-tab", node)
|
||||
end,
|
||||
|
||||
["root:switch-to-hovered-next-tab"] = function(node)
|
||||
command.perform("root:switch-to-next-tab", node)
|
||||
end,
|
||||
|
||||
["root:scroll-hovered-tabs-backward"] = function(node)
|
||||
command.perform("root:scroll-tabs-backward", node)
|
||||
end,
|
||||
|
||||
["root:scroll-hovered-tabs-forward"] = function(node)
|
||||
command.perform("root:scroll-tabs-forward", node)
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
-- double clicking the tab bar, or on the emptyview should open a new doc
|
||||
command.add(function(x, y)
|
||||
local node = x and y and core.root_view.root_node:get_child_overlapping_point(x, y)
|
||||
return node and node:is_in_tab_area(x, y)
|
||||
end, {
|
||||
["tabbar:new-doc"] = function()
|
||||
command.perform("core:new-doc")
|
||||
end
|
||||
})
|
||||
command.add("core.emptyview", {
|
||||
["emptyview:new-doc"] = function()
|
||||
command.perform("core:new-doc")
|
||||
end
|
||||
})
|
||||
|
|
|
@ -50,6 +50,7 @@ local default_state = {
|
|||
function CommandView:new()
|
||||
CommandView.super.new(self, SingleLineDoc())
|
||||
self.suggestion_idx = 1
|
||||
self.suggestions_offset = 1
|
||||
self.suggestions = {}
|
||||
self.suggestions_height = 0
|
||||
self.last_change_id = 0
|
||||
|
@ -84,11 +85,6 @@ function CommandView:get_line_screen_position(line, col)
|
|||
end
|
||||
|
||||
|
||||
function CommandView:supports_text_input()
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function CommandView:get_scrollable_size()
|
||||
return 0
|
||||
end
|
||||
|
@ -128,6 +124,24 @@ function CommandView:move_suggestion_idx(dir)
|
|||
end
|
||||
end
|
||||
|
||||
local function get_suggestions_offset()
|
||||
local max_visible = math.min(max_suggestions, #self.suggestions)
|
||||
if dir > 0 then
|
||||
if self.suggestions_offset + max_visible < self.suggestion_idx + 1 then
|
||||
return self.suggestion_idx - max_visible + 1
|
||||
elseif self.suggestions_offset > self.suggestion_idx then
|
||||
return self.suggestion_idx
|
||||
end
|
||||
else
|
||||
if self.suggestions_offset > self.suggestion_idx then
|
||||
return self.suggestion_idx
|
||||
elseif self.suggestions_offset + max_visible < self.suggestion_idx + 1 then
|
||||
return self.suggestion_idx - max_visible + 1
|
||||
end
|
||||
end
|
||||
return self.suggestions_offset
|
||||
end
|
||||
|
||||
if self.state.show_suggestions then
|
||||
local n = self.suggestion_idx + dir
|
||||
self.suggestion_idx = overflow_suggestion_idx(n, #self.suggestions)
|
||||
|
@ -151,6 +165,8 @@ function CommandView:move_suggestion_idx(dir)
|
|||
self.last_change_id = self.doc:get_change_id()
|
||||
self.state.suggest(self:get_text())
|
||||
end
|
||||
|
||||
self.suggestions_offset = get_suggestions_offset()
|
||||
end
|
||||
|
||||
|
||||
|
@ -261,6 +277,7 @@ function CommandView:update_suggestions()
|
|||
end
|
||||
self.suggestions = res
|
||||
self.suggestion_idx = 1
|
||||
self.suggestions_offset = 1
|
||||
end
|
||||
|
||||
|
||||
|
@ -304,7 +321,7 @@ function CommandView:update()
|
|||
self:move_towards("suggestions_height", dest, nil, "commandview")
|
||||
|
||||
-- update suggestion cursor offset
|
||||
local dest = math.min(self.suggestion_idx, max_suggestions) * self:get_suggestion_line_height()
|
||||
local dest = (self.suggestion_idx - self.suggestions_offset + 1) * self:get_suggestion_line_height()
|
||||
self:move_towards("selection_offset", dest, nil, "commandview")
|
||||
|
||||
-- update size based on whether this is the active_view
|
||||
|
@ -340,6 +357,7 @@ local function draw_suggestions_box(self)
|
|||
local h = math.ceil(self.suggestions_height)
|
||||
local rx, ry, rw, rh = self.position.x, self.position.y - h - dh, self.size.x, h
|
||||
|
||||
core.push_clip_rect(rx, ry, rw, rh)
|
||||
-- draw suggestions background
|
||||
if #self.suggestions > 0 then
|
||||
renderer.draw_rect(rx, ry, rw, rh, style.background3)
|
||||
|
@ -349,14 +367,12 @@ local function draw_suggestions_box(self)
|
|||
end
|
||||
|
||||
-- draw suggestion text
|
||||
local offset = math.max(self.suggestion_idx - max_suggestions, 0)
|
||||
local last = math.min(offset + max_suggestions, #self.suggestions)
|
||||
core.push_clip_rect(rx, ry, rw, rh)
|
||||
local first = 1 + offset
|
||||
local first = math.max(self.suggestions_offset, 1)
|
||||
local last = math.min(self.suggestions_offset + max_suggestions, #self.suggestions)
|
||||
for i=first, last do
|
||||
local item = self.suggestions[i]
|
||||
local color = (i == self.suggestion_idx) and style.accent or style.text
|
||||
local y = self.position.y - (i - offset) * lh - dh
|
||||
local y = self.position.y - (i - first + 1) * lh - dh
|
||||
common.draw_text(self:get_font(), color, item.text, nil, x, y, 0, lh)
|
||||
|
||||
if item.info then
|
||||
|
|
|
@ -226,7 +226,13 @@ function common.path_suggest(text, root)
|
|||
if root and root:sub(-1) ~= PATHSEP then
|
||||
root = root .. PATHSEP
|
||||
end
|
||||
local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
|
||||
local path, name
|
||||
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
|
||||
path, name = text:match("^(.-)([^:"..PATHSEP.."]*)$")
|
||||
else
|
||||
path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
|
||||
end
|
||||
|
||||
local clean_dotslash = false
|
||||
-- ignore root if path is absolute
|
||||
local is_absolute = common.is_absolute_path(text)
|
||||
|
@ -279,7 +285,12 @@ end
|
|||
---@param text string The input path.
|
||||
---@return string[]
|
||||
function common.dir_path_suggest(text)
|
||||
local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
|
||||
local path, name
|
||||
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
|
||||
path, name = text:match("^(.-)([^:"..PATHSEP.."]*)$")
|
||||
else
|
||||
path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
|
||||
end
|
||||
local files = system.list_dir(path == "" and "." or path) or {}
|
||||
local res = {}
|
||||
for _, file in ipairs(files) do
|
||||
|
@ -298,7 +309,13 @@ end
|
|||
---@param dir_list string[] A list of paths to filter.
|
||||
---@return string[]
|
||||
function common.dir_list_suggest(text, dir_list)
|
||||
local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
|
||||
local path, name
|
||||
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
|
||||
path, name = text:match("^(.-)([^:"..PATHSEP.."]*)$")
|
||||
else
|
||||
path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
|
||||
end
|
||||
|
||||
local res = {}
|
||||
for _, dir_path in ipairs(dir_list) do
|
||||
if dir_path:lower():find(text:lower(), nil, true) == 1 then
|
||||
|
@ -465,11 +482,35 @@ 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
|
||||
|
||||
|
@ -519,6 +560,13 @@ 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
|
||||
|
@ -541,6 +589,12 @@ 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
|
||||
|
||||
|
@ -566,6 +620,11 @@ 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
|
||||
|
@ -596,7 +655,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*):')
|
||||
return path:sub(1, 1) == PATHSEP or path:match("^(%a):\\") or path:match('^([%w%s]*):')
|
||||
end
|
||||
|
||||
|
||||
|
@ -605,7 +664,7 @@ end
|
|||
---@param path string The parent path.
|
||||
---@return boolean
|
||||
function common.path_belongs_to(filename, path)
|
||||
return string.find(filename, path .. PATHSEP, 1, true) == 1
|
||||
return string.find(filename, common.basepath(path), 1, true) == 1
|
||||
end
|
||||
|
||||
|
||||
|
@ -615,6 +674,9 @@ 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:\
|
||||
|
@ -657,7 +719,7 @@ function common.mkdirp(path)
|
|||
path = updir
|
||||
end
|
||||
for _, dirname in ipairs(subdirs) do
|
||||
path = path and path .. PATHSEP .. dirname or dirname
|
||||
path = path and common.basepath(path) .. dirname or dirname
|
||||
if not system.mkdir(path) then
|
||||
return false, "cannot create directory", path
|
||||
end
|
||||
|
@ -721,4 +783,3 @@ end
|
|||
|
||||
|
||||
return common
|
||||
|
||||
|
|
|
@ -2,71 +2,15 @@ local common = require "core.common"
|
|||
|
||||
local config = {}
|
||||
|
||||
---The frame rate of Lite XL.
|
||||
---Note that setting this value to the screen's refresh rate
|
||||
---does not eliminate screen tearing.
|
||||
---
|
||||
---Defaults to 60.
|
||||
---@type number
|
||||
config.fps = 60
|
||||
|
||||
---Maximum number of log items that will be stored.
|
||||
---When the number of log items exceed this value, old items will be discarded.
|
||||
---
|
||||
---Defaults to 800.
|
||||
---@type number
|
||||
config.max_log_items = 800
|
||||
|
||||
---The timeout, in seconds, before a message dissapears from StatusView.
|
||||
---
|
||||
---Defaults to 5.
|
||||
---@type number
|
||||
config.message_timeout = 5
|
||||
|
||||
---The number of pixels scrolled per-step.
|
||||
---
|
||||
---Defaults to 50 * SCALE.
|
||||
---@type number
|
||||
config.mouse_wheel_scroll = 50 * SCALE
|
||||
|
||||
---Enables/disables transitions when scrolling with the scrollbar.
|
||||
---When enabled, the scrollbar will have inertia and slowly move towards the cursor.
|
||||
---Otherwise, the scrollbar will immediately follow the cursor.
|
||||
---
|
||||
---Defaults to false.
|
||||
---@type boolean
|
||||
config.animate_drag_scroll = false
|
||||
|
||||
---Enables/disables scrolling past the end of a document.
|
||||
---
|
||||
---Defaults to true.
|
||||
---@type boolean
|
||||
config.scroll_past_end = true
|
||||
|
||||
---@alias config.scrollbartype
|
||||
---| "expanded" # A thicker scrollbar is shown at all times.
|
||||
---| "contracted" # A thinner scrollbar is shown at all times.
|
||||
---| false # The scrollbar expands when the cursor hovers over it.
|
||||
|
||||
---Controls whether the DocView scrollbar is always shown or hidden.
|
||||
---This option does not affect other View's scrollbars.
|
||||
---
|
||||
---Defaults to false.
|
||||
---@type config.scrollbartype
|
||||
---@type "expanded" | "contracted" | false @Force the scrollbar status of the DocView
|
||||
config.force_scrollbar_status = false
|
||||
|
||||
---The file size limit, in megabytes.
|
||||
---Files larger than this size will not be shown in the file picker.
|
||||
---
|
||||
---Defaults to 10.
|
||||
---@type number
|
||||
config.file_size_limit = 10
|
||||
|
||||
---A list of files and directories to ignore.
|
||||
---Each element is a Lua pattern, where patterns ending with a forward slash
|
||||
---are recognized as directories while patterns ending with an anchor ("$") are
|
||||
---recognized as files.
|
||||
---@type string[]
|
||||
config.ignore_files = {
|
||||
-- folders
|
||||
"^%.svn/", "^%.git/", "^%.hg/", "^CVS/", "^%.Trash/", "^%.Trash%-.*/",
|
||||
|
@ -77,194 +21,46 @@ config.ignore_files = {
|
|||
"%.suo$", "%.pdb$", "%.idb$", "%.class$", "%.psd$", "%.db$",
|
||||
"^desktop%.ini$", "^%.DS_Store$", "^%.directory$",
|
||||
}
|
||||
|
||||
---Lua pattern used to find symbols when advanced syntax highlighting
|
||||
---is not available.
|
||||
---This pattern is also used for navigation, e.g. move to next word.
|
||||
---
|
||||
---The default pattern matches all letters, followed by any number
|
||||
---of letters and digits.
|
||||
---@type string
|
||||
config.symbol_pattern = "[%a_][%w_]*"
|
||||
|
||||
---A list of characters that delimits a word.
|
||||
---
|
||||
---The default is ``" \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"``
|
||||
---@type string
|
||||
config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
|
||||
|
||||
---The timeout, in seconds, before several consecutive actions
|
||||
---are merged as a single undo step.
|
||||
---
|
||||
---The default is 0.3 seconds.
|
||||
---@type number
|
||||
config.undo_merge_timeout = 0.3
|
||||
|
||||
---The maximum number of undo steps per-document.
|
||||
---
|
||||
---The default is 10000.
|
||||
---@type number
|
||||
config.max_undos = 10000
|
||||
|
||||
---The maximum number of tabs shown at a time.
|
||||
---
|
||||
---The default is 8.
|
||||
---@type number
|
||||
config.max_tabs = 8
|
||||
|
||||
---Shows/hides the tab bar when there is only one tab open.
|
||||
---
|
||||
---The tab bar is always shown by default.
|
||||
---@type boolean
|
||||
config.always_show_tabs = true
|
||||
|
||||
---@alias config.highlightlinetype
|
||||
---| true # Always highlight the current line.
|
||||
---| false # Never highlight the current line.
|
||||
---| "no_selection" # Highlight the current line if no text is selected.
|
||||
|
||||
---Highlights the current line.
|
||||
---
|
||||
---The default is true.
|
||||
---@type config.highlightlinetype
|
||||
-- Possible values: false, true, "no_selection"
|
||||
config.highlight_current_line = true
|
||||
|
||||
---The spacing between each line of text.
|
||||
---
|
||||
---The default is 120% of the height of the text (1.2).
|
||||
---@type number
|
||||
config.line_height = 1.2
|
||||
|
||||
---The number of spaces each level of indentation represents.
|
||||
---
|
||||
---The default is 2.
|
||||
---@type number
|
||||
config.indent_size = 2
|
||||
|
||||
---The type of indentation.
|
||||
---
|
||||
---The default is "soft" (spaces).
|
||||
---@type "soft" | "hard"
|
||||
config.tab_type = "soft"
|
||||
|
||||
---Do not remove whitespaces when advancing to the next line.
|
||||
---
|
||||
---Defaults to false.
|
||||
---@type boolean
|
||||
config.keep_newline_whitespace = false
|
||||
|
||||
---Default line endings for new files.
|
||||
---
|
||||
---Defaults to `crlf` (`\r\n`) on Windows and `lf` (`\n`) on everything else.
|
||||
---@type "crlf" | "lf"
|
||||
config.line_endings = PLATFORM == "Windows" and "crlf" or "lf"
|
||||
|
||||
---Maximum number of characters per-line for the line guide.
|
||||
---
|
||||
---Defaults to 80.
|
||||
---@type number
|
||||
config.line_limit = 80
|
||||
|
||||
---Maximum number of project files to keep track of.
|
||||
---If the number of files in the project exceeds this number,
|
||||
---Lite XL will not be able to keep track of them.
|
||||
---They will be not be searched when searching for files or text.
|
||||
---
|
||||
---Defaults to 2000.
|
||||
---@type number
|
||||
config.max_project_files = 2000
|
||||
|
||||
---Enables/disables all transitions.
|
||||
---
|
||||
---Defaults to true.
|
||||
---@type boolean
|
||||
config.transitions = true
|
||||
|
||||
---Enable/disable individual transitions.
|
||||
---These values are overriden by `config.transitions`.
|
||||
config.disabled_transitions = {
|
||||
---Disables scrolling transitions.
|
||||
scroll = false,
|
||||
---Disables transitions for CommandView's suggestions list.
|
||||
commandview = false,
|
||||
---Disables transitions for showing/hiding the context menu.
|
||||
contextmenu = false,
|
||||
---Disables transitions when clicking on log items in LogView.
|
||||
logview = false,
|
||||
---Disables transitions for showing/hiding the Nagbar.
|
||||
nagbar = false,
|
||||
---Disables transitions when scrolling the tab bar.
|
||||
tabs = false,
|
||||
---Disables transitions when a tab is being dragged.
|
||||
tab_drag = false,
|
||||
---Disables transitions when a notification is shown.
|
||||
statusbar = false,
|
||||
}
|
||||
|
||||
---The rate of all transitions.
|
||||
---
|
||||
---Defaults to 1.
|
||||
---@type number
|
||||
config.animation_rate = 1.0
|
||||
|
||||
---The caret's blinking period, in seconds.
|
||||
---
|
||||
---Defaults to 0.8.
|
||||
---@type number
|
||||
config.blink_period = 0.8
|
||||
|
||||
---Disables caret blinking.
|
||||
---
|
||||
---Defaults to false.
|
||||
---@type boolean
|
||||
config.disable_blink = false
|
||||
|
||||
---Draws whitespaces as dots.
|
||||
---This option is deprecated.
|
||||
---Please use the drawwhitespace plugin instead.
|
||||
---@deprecated
|
||||
config.draw_whitespace = false
|
||||
|
||||
---Disables system-drawn window borders.
|
||||
---
|
||||
---When set to true, Lite XL draws its own window decorations,
|
||||
---which can be useful for certain setups.
|
||||
---
|
||||
---Defaults to false.
|
||||
---@type boolean
|
||||
config.borderless = false
|
||||
|
||||
---Shows/hides the close buttons on tabs.
|
||||
---When hidden, users can close tabs via keyboard shortcuts or commands.
|
||||
---
|
||||
---Defaults to true.
|
||||
---@type boolean
|
||||
config.tab_close_button = true
|
||||
|
||||
---Maximum number of clicks recognized by Lite XL.
|
||||
---
|
||||
---Defaults to 3.
|
||||
---@type number
|
||||
config.max_clicks = 3
|
||||
|
||||
---Disables plugin version checking.
|
||||
---Do not change this unless you know what you are doing.
|
||||
---
|
||||
---Defaults to false.
|
||||
---@type boolean
|
||||
-- set as true to be able to test non supported plugins
|
||||
config.skip_plugins_version = false
|
||||
|
||||
-- holds the plugins real config table
|
||||
local plugins_config = {}
|
||||
|
||||
---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
|
||||
-- virtual representation of plugins config table
|
||||
config.plugins = {}
|
||||
|
||||
-- allows virtual access to the plugins config table
|
||||
|
|
|
@ -177,7 +177,8 @@ end
|
|||
-- compute a file's info entry completed with "filename" to be used
|
||||
-- in project scan or falsy if it shouldn't appear in the list.
|
||||
local function get_project_file_info(root, file, ignore_compiled)
|
||||
local info = system.get_file_info(root .. PATHSEP .. file)
|
||||
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.
|
||||
if info and info.type then
|
||||
|
@ -200,7 +201,8 @@ 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(root .. PATHSEP .. path)
|
||||
local all = system.list_dir(common.basepath(root) .. path)
|
||||
|
||||
if not all then return nil end
|
||||
local entries = { }
|
||||
for _, file in ipairs(all) do
|
||||
|
|
|
@ -48,7 +48,7 @@ function Highlighter:start()
|
|||
self:update_notify(retokenized_from, max - retokenized_from)
|
||||
end
|
||||
core.redraw = true
|
||||
coroutine.yield(0)
|
||||
coroutine.yield()
|
||||
end
|
||||
self.max_wanted_line = 0
|
||||
self.running = false
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
local Object = require "core.object"
|
||||
local Highlighter = require "core.doc.highlighter"
|
||||
local translate = require "core.doc.translate"
|
||||
local core = require "core"
|
||||
local syntax = require "core.syntax"
|
||||
local config = require "core.config"
|
||||
|
@ -28,11 +27,9 @@ function Doc:new(filename, abs_filename, new_file)
|
|||
self:load(filename)
|
||||
end
|
||||
end
|
||||
if new_file then
|
||||
self.crlf = config.line_endings == "crlf"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:reset()
|
||||
self.lines = { "\n" }
|
||||
self.selections = { 1, 1, 1, 1 }
|
||||
|
@ -41,15 +38,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 = core.project_dir .. PATHSEP .. self.filename
|
||||
path = common.basepath(core.project_dir) .. self.filename
|
||||
end
|
||||
if path then path = common.normalize_path(path) end
|
||||
local syn = syntax.get(path, header)
|
||||
|
@ -59,14 +56,16 @@ function Doc:reset_syntax()
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:set_filename(filename, abs_filename)
|
||||
self.filename = filename
|
||||
self.abs_filename = abs_filename
|
||||
self:reset_syntax()
|
||||
end
|
||||
|
||||
|
||||
function Doc:load(filename)
|
||||
local fp = assert(io.open(filename, "rb"))
|
||||
local fp = assert( io.open(filename, "rb") )
|
||||
self:reset()
|
||||
self.lines = {}
|
||||
local i = 1
|
||||
|
@ -86,6 +85,7 @@ function Doc:load(filename)
|
|||
self:reset_syntax()
|
||||
end
|
||||
|
||||
|
||||
function Doc:reload()
|
||||
if self.filename then
|
||||
local sel = { self:get_selection() }
|
||||
|
@ -95,6 +95,7 @@ function Doc:reload()
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:save(filename, abs_filename)
|
||||
if not filename then
|
||||
assert(self.filename, "no filename set to default to")
|
||||
|
@ -103,7 +104,7 @@ function Doc:save(filename, abs_filename)
|
|||
else
|
||||
assert(self.filename or abs_filename, "calling save on unnamed doc without absolute path")
|
||||
end
|
||||
local fp = assert(io.open(filename, "wb"))
|
||||
local fp = assert( io.open(filename, "wb") )
|
||||
for _, line in ipairs(self.lines) do
|
||||
if self.crlf then line = line:gsub("\n", "\r\n") end
|
||||
fp:write(line)
|
||||
|
@ -114,10 +115,12 @@ function Doc:save(filename, abs_filename)
|
|||
self:clean()
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_name()
|
||||
return self.filename or "unsaved"
|
||||
end
|
||||
|
||||
|
||||
function Doc:is_dirty()
|
||||
if self.new_file then
|
||||
if self.filename then return true end
|
||||
|
@ -127,17 +130,20 @@ 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
|
||||
|
@ -161,14 +167,13 @@ function Doc:get_selection(sort)
|
|||
return line1, col1, line2, col2, swap
|
||||
end
|
||||
|
||||
|
||||
---Get the selection specified by `idx`
|
||||
---@param idx integer @the index of the selection to retrieve
|
||||
---@param sort? boolean @whether to sort the selection returned
|
||||
---@return integer,integer,integer,integer,boolean? @line1, col1, line2, col2, was the selection sorted
|
||||
function Doc:get_selection_idx(idx, sort)
|
||||
local line1, col1, line2, col2 = self.selections[idx * 4 - 3], self.selections[idx * 4 - 2],
|
||||
self.selections[idx * 4 - 1],
|
||||
self.selections[idx * 4]
|
||||
local line1, col1, line2, col2 = self.selections[idx*4-3], self.selections[idx*4-2], self.selections[idx*4-1], self.selections[idx*4]
|
||||
if line1 and sort then
|
||||
return sort_positions(line1, col1, line2, col2)
|
||||
else
|
||||
|
@ -212,7 +217,7 @@ function Doc:set_selections(idx, line1, col1, line2, col2, swap, rm)
|
|||
if swap then line1, col1, line2, col2 = line2, col2, line1, col1 end
|
||||
line1, col1 = self:sanitize_position(line1, col1)
|
||||
line2, col2 = self:sanitize_position(line2 or line1, col2 or col1)
|
||||
common.splice(self.selections, (idx - 1) * 4 + 1, rm == nil and 4 or rm, { line1, col1, line2, col2 })
|
||||
common.splice(self.selections, (idx - 1)*4 + 1, rm == nil and 4 or rm, { line1, col1, line2, col2 })
|
||||
end
|
||||
|
||||
function Doc:add_selection(line1, col1, line2, col2, swap)
|
||||
|
@ -228,6 +233,7 @@ function Doc:add_selection(line1, col1, line2, col2, swap)
|
|||
self.last_selection = target
|
||||
end
|
||||
|
||||
|
||||
function Doc:remove_selection(idx)
|
||||
if self.last_selection >= idx then
|
||||
self.last_selection = self.last_selection - 1
|
||||
|
@ -235,6 +241,7 @@ function Doc:remove_selection(idx)
|
|||
common.splice(self.selections, (idx - 1) * 4 + 1, 4)
|
||||
end
|
||||
|
||||
|
||||
function Doc:set_selection(line1, col1, line2, col2, swap)
|
||||
self.selections = {}
|
||||
self:set_selections(1, line1, col1, line2, col2, swap)
|
||||
|
@ -245,24 +252,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
|
||||
|
||||
|
@ -270,9 +277,8 @@ end
|
|||
-- If a number, runs for exactly that iteration.
|
||||
function Doc:get_selections(sort_intra, idx_reverse)
|
||||
return selection_iterator, { self.selections, sort_intra, idx_reverse },
|
||||
idx_reverse == true and ((#self.selections / 4) + 1) or ((idx_reverse or -1) + 1)
|
||||
idx_reverse == true and ((#self.selections / 4) + 1) or ((idx_reverse or -1)+1)
|
||||
end
|
||||
|
||||
-- End of cursor seciton.
|
||||
|
||||
function Doc:sanitize_position(line, col)
|
||||
|
@ -285,6 +291,7 @@ function Doc:sanitize_position(line, col)
|
|||
return line, common.clamp(col, 1, #self.lines[line])
|
||||
end
|
||||
|
||||
|
||||
local function position_offset_func(self, line, col, fn, ...)
|
||||
line, col = self:sanitize_position(line, col)
|
||||
return fn(self, line, col, ...)
|
||||
|
@ -323,6 +330,7 @@ function Doc:position_offset(line, col, ...)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_text(line1, col1, line2, col2)
|
||||
line1, col1 = self:sanitize_position(line1, col1)
|
||||
line2, col2 = self:sanitize_position(line2, col2)
|
||||
|
@ -338,11 +346,13 @@ function Doc:get_text(line1, col1, line2, col2)
|
|||
return table.concat(lines)
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_char(line, col)
|
||||
line, col = self:sanitize_position(line, col)
|
||||
return self.lines[line]:sub(col, col)
|
||||
end
|
||||
|
||||
|
||||
local function push_undo(undo_stack, time, type, ...)
|
||||
undo_stack[undo_stack.idx] = { type = type, time = time, ... }
|
||||
undo_stack[undo_stack.idx - config.max_undos] = nil
|
||||
|
@ -403,8 +413,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
|
|||
if cline1 < line then break end
|
||||
local line_addition = (line < cline1 or col < ccol1) and #lines - 1 or 0
|
||||
local column_addition = line == cline1 and ccol1 > col and len or 0
|
||||
self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition,
|
||||
ccol2 + column_addition)
|
||||
self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition, ccol2 + column_addition)
|
||||
end
|
||||
|
||||
-- push undo
|
||||
|
@ -417,6 +426,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
|
|||
self:sanitize_selection()
|
||||
end
|
||||
|
||||
|
||||
function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
|
||||
-- push undo
|
||||
local text = self:get_text(line1, col1, line2, col2)
|
||||
|
@ -475,6 +485,7 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
|
|||
self:sanitize_selection()
|
||||
end
|
||||
|
||||
|
||||
function Doc:insert(line, col, text)
|
||||
self.redo_stack = { idx = 1 }
|
||||
-- Reset the clean id when we're pushing something new before it
|
||||
|
@ -486,6 +497,7 @@ function Doc:insert(line, col, text)
|
|||
self:on_text_change("insert")
|
||||
end
|
||||
|
||||
|
||||
function Doc:remove(line1, col1, line2, col2)
|
||||
self.redo_stack = { idx = 1 }
|
||||
line1, col1 = self:sanitize_position(line1, col1)
|
||||
|
@ -495,34 +507,28 @@ function Doc:remove(line1, col1, line2, col2)
|
|||
self:on_text_change("remove")
|
||||
end
|
||||
|
||||
|
||||
function Doc:undo()
|
||||
pop_undo(self, self.undo_stack, self.redo_stack, false)
|
||||
end
|
||||
|
||||
|
||||
function Doc:redo()
|
||||
pop_undo(self, self.redo_stack, self.undo_stack, false)
|
||||
end
|
||||
|
||||
|
||||
function Doc:text_input(text, idx)
|
||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
|
||||
local had_selection = false
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
self:delete_to_cursor(sidx)
|
||||
had_selection = true
|
||||
end
|
||||
|
||||
if self.overwrite
|
||||
and not had_selection
|
||||
and col1 < #self.lines[line1]
|
||||
and text:ulen() == 1 then
|
||||
self:remove(line1, col1, translate.next_char(self, line1, col1))
|
||||
end
|
||||
|
||||
self:insert(line1, col1, text)
|
||||
self:move_to_cursor(sidx, #text)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:ime_text_editing(text, start, length, idx)
|
||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
|
@ -533,6 +539,7 @@ function Doc:ime_text_editing(text, start, length, idx)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
|
||||
local old_text = self:get_text(line1, col1, line2, col2)
|
||||
local new_text, res = fn(old_text)
|
||||
|
@ -548,7 +555,7 @@ function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
|
|||
end
|
||||
|
||||
function Doc:replace(fn)
|
||||
local has_selection, results = false, {}
|
||||
local has_selection, results = false, { }
|
||||
for idx, line1, col1, line2, col2 in self:get_selections(true) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
results[idx] = self:replace_cursor(idx, line1, col1, line2, col2, fn)
|
||||
|
@ -562,6 +569,7 @@ function Doc:replace(fn)
|
|||
return results
|
||||
end
|
||||
|
||||
|
||||
function Doc:delete_to_cursor(idx, ...)
|
||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
|
@ -575,7 +583,6 @@ function Doc:delete_to_cursor(idx, ...)
|
|||
end
|
||||
self:merge_cursors(idx)
|
||||
end
|
||||
|
||||
function Doc:delete_to(...) return self:delete_to_cursor(nil, ...) end
|
||||
|
||||
function Doc:move_to_cursor(idx, ...)
|
||||
|
@ -584,9 +591,9 @@ function Doc:move_to_cursor(idx, ...)
|
|||
end
|
||||
self:merge_cursors(idx)
|
||||
end
|
||||
|
||||
function Doc:move_to(...) return self:move_to_cursor(nil, ...) end
|
||||
|
||||
|
||||
function Doc:select_to_cursor(idx, ...)
|
||||
for sidx, line, col, line2, col2 in self:get_selections(false, idx) do
|
||||
line, col = self:position_offset(line, col, ...)
|
||||
|
@ -594,9 +601,9 @@ function Doc:select_to_cursor(idx, ...)
|
|||
end
|
||||
self:merge_cursors(idx)
|
||||
end
|
||||
|
||||
function Doc:select_to(...) return self:select_to_cursor(nil, ...) end
|
||||
|
||||
|
||||
function Doc:get_indent_string()
|
||||
local indent_type, indent_size = self:get_indent_info()
|
||||
if indent_type == "hard" then
|
||||
|
@ -618,7 +625,7 @@ function Doc:get_line_indent(line, rnd_up)
|
|||
local indent = e and line:sub(1, e):gsub("\t", soft_tab) or ""
|
||||
local number = #indent / #soft_tab
|
||||
return e, indent:sub(1,
|
||||
(rnd_up and math.ceil(number) or math.floor(number)) * #soft_tab)
|
||||
(rnd_up and math.ceil(number) or math.floor(number))*#soft_tab)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -667,4 +674,5 @@ function Doc:on_close()
|
|||
core.log_quiet("Closed doc \"%s\"", self:get_name())
|
||||
end
|
||||
|
||||
|
||||
return Doc
|
||||
|
|
|
@ -254,11 +254,6 @@ function DocView:scroll_to_line(line, ignore_if_visible, instant)
|
|||
end
|
||||
|
||||
|
||||
function DocView:supports_text_input()
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function DocView:scroll_to_make_visible(line, col)
|
||||
local _, oy = self:get_content_offset()
|
||||
local _, ly = self:get_line_screen_position(line, col)
|
||||
|
@ -322,7 +317,7 @@ function DocView:mouse_selection(doc, snap_type, line1, col1, line2, col2)
|
|||
line1, col1 = translate.start_of_word(doc, line1, col1)
|
||||
line2, col2 = translate.end_of_word(doc, line2, col2)
|
||||
elseif snap_type == "lines" then
|
||||
col1, col2, line2 = 1, 1, line2 + 1
|
||||
col1, col2 = 1, math.huge
|
||||
end
|
||||
if swap then
|
||||
return line2, col2, line1, col1
|
||||
|
@ -460,16 +455,9 @@ 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)
|
||||
|
@ -566,12 +554,7 @@ function DocView:draw_overlay()
|
|||
else
|
||||
if config.disable_blink
|
||||
or (core.blink_timer - core.blink_start) % T < T / 2 then
|
||||
local x, y = self:get_line_screen_position(line1, col1)
|
||||
if self.doc.overwrite then
|
||||
self:draw_overwrite_caret(x, y, self:get_font():get_width(self.doc:get_char(line1, col1)))
|
||||
else
|
||||
self:draw_caret(x, y)
|
||||
end
|
||||
self:draw_caret(self:get_line_screen_position(line1, col1))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,13 +18,13 @@ local Doc
|
|||
local core = {}
|
||||
|
||||
local function load_session()
|
||||
local ok, t = pcall(dofile, USERDIR .. PATHSEP .. "session.lua")
|
||||
local ok, t = pcall(dofile, common.basepath(USERDIR) .. "session.lua")
|
||||
return ok and t or {}
|
||||
end
|
||||
|
||||
|
||||
local function save_session()
|
||||
local fp = io.open(USERDIR .. PATHSEP .. "session.lua", "w")
|
||||
local fp = io.open(common.basepath(USERDIR) .. "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(topdir.name .. PATHSEP .. (target or ""))
|
||||
topdir.watch:unwatch(common.basepath(topdir.name) .. (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(topdir.name .. PATHSEP .. old_info.filename)
|
||||
topdir.watch:unwatch(common.basepath(topdir.name) .. 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(topdir.name .. PATHSEP .. v.filename)
|
||||
topdir.watch:watch(common.basepath(topdir.name) .. 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(path .. PATHSEP .. v.filename) end
|
||||
if v.type == "dir" then topdir.watch:watch(common.basepath(path) .. 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 = topdir.name .. PATHSEP .. dirpath
|
||||
local abs_dirpath = common.basepath(topdir.name) .. 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(dir.name .. PATHSEP .. filename)
|
||||
dir.watch:watch(common.basepath(dir.name) .. filename)
|
||||
else
|
||||
dir.watch:unwatch(dir.name .. PATHSEP .. filename)
|
||||
dir.watch:unwatch(common.basepath(dir.name) .. 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 = path .. PATHSEP .. file
|
||||
local file = common.basepath(path) .. 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 = USERDIR .. PATHSEP .. modname
|
||||
local subdirname = common.basepath(USERDIR) .. modname
|
||||
if not system.mkdir(subdirname) then
|
||||
error("cannot create directory: \"" .. subdirname .. "\"")
|
||||
end
|
||||
|
@ -572,22 +572,22 @@ local config = require "core.config"
|
|||
--
|
||||
-- Here some examples:
|
||||
--
|
||||
-- "^%." match any file of directory whose basename begins with a dot.
|
||||
-- "^%." matches any file of directory whose basename begins with a dot.
|
||||
--
|
||||
-- When there is an '/' or a '/$' at the end the pattern it will only match
|
||||
-- When there is an '/' or a '/$' at the end, the pattern will only match
|
||||
-- directories. When using such a pattern a final '/' will be added to the name
|
||||
-- of any directory entry before checking if it matches.
|
||||
--
|
||||
-- "^%.git/" matches any directory named ".git" anywhere in the project.
|
||||
--
|
||||
-- If a "/" appears anywhere in the pattern except if it appears at the end or
|
||||
-- is immediately followed by a '$' then the pattern will be applied to the full
|
||||
-- If a "/" appears anywhere in the pattern (except when it appears at the end or
|
||||
-- is immediately followed by a '$'), then the pattern will be applied to the full
|
||||
-- path of the file or directory. An initial "/" will be prepended to the file's
|
||||
-- or directory's path to indicate the project's root.
|
||||
--
|
||||
-- "^/node_modules/" will match a directory named "node_modules" at the project's root.
|
||||
-- "^/build.*/" match any top level directory whose name begins with "build"
|
||||
-- "^/subprojects/.+/" match any directory inside a top-level folder named "subprojects".
|
||||
-- "^/build.*/" will match any top level directory whose name begins with "build".
|
||||
-- "^/subprojects/.+/" will match any directory inside a top-level folder named "subprojects".
|
||||
|
||||
-- You may activate some plugins on a per-project basis to override the user's settings.
|
||||
-- config.plugins.trimwitespace = true
|
||||
|
@ -602,7 +602,7 @@ function core.load_user_directory()
|
|||
if not stat_dir then
|
||||
create_user_directory()
|
||||
end
|
||||
local init_filename = USERDIR .. PATHSEP .. "init.lua"
|
||||
local init_filename = common.basepath(USERDIR) .. "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(USERDIR .. PATHSEP .. "init.lua")
|
||||
local user_filename = system.absolute_path(common.basepath(USERDIR) .. "init.lua")
|
||||
function Doc:save(filename, abs_filename)
|
||||
local module_filename = system.absolute_path(".lite_project.lua")
|
||||
doc_save(self, filename, abs_filename)
|
||||
|
@ -660,17 +660,14 @@ function core.project_absolute_path(filename)
|
|||
return common.normalize_path(filename)
|
||||
elseif not core.project_dir then
|
||||
local cwd = system.absolute_path(".")
|
||||
return cwd .. PATHSEP .. common.normalize_path(filename)
|
||||
return common.basepath(cwd) .. common.normalize_path(filename)
|
||||
else
|
||||
return core.project_dir .. PATHSEP .. filename
|
||||
return common.basepath(core.project_dir) .. 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"
|
||||
|
@ -716,7 +713,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
|
||||
|
@ -724,6 +721,7 @@ function core.init()
|
|||
|
||||
core.frame_start = 0
|
||||
core.clip_rect_stack = {{ 0,0,0,0 }}
|
||||
core.log_items = {}
|
||||
core.docs = {}
|
||||
core.cursor_clipboard = {}
|
||||
core.cursor_clipboard_whole_line = {}
|
||||
|
@ -825,19 +823,15 @@ function core.init()
|
|||
local msg = {}
|
||||
for _, entry in pairs(plugins_refuse_list) do
|
||||
if #entry.plugins > 0 then
|
||||
local msg_list = {}
|
||||
for _, p in pairs(entry.plugins) do
|
||||
table.insert(msg_list, string.format("%s[%s]", p.file, p.version_string))
|
||||
end
|
||||
msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(msg_list, "\n"))
|
||||
msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(entry.plugins, "\n"))
|
||||
end
|
||||
end
|
||||
core.nag_view:show(
|
||||
"Refused Plugins",
|
||||
string.format(
|
||||
"Some plugins are not loaded due to version mismatch. Expected version %s.\n\n%s.\n\n" ..
|
||||
"Some plugins are not loaded due to version mismatch.\n\n%s.\n\n" ..
|
||||
"Please download a recent version from https://github.com/lite-xl/lite-xl-plugins.",
|
||||
MOD_VERSION_STRING, table.concat(msg, ".\n\n")),
|
||||
table.concat(msg, ".\n\n")),
|
||||
opt, function(item)
|
||||
if item.text == "Exit" then os.exit(1) end
|
||||
end)
|
||||
|
@ -884,7 +878,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(dir .. PATHSEP .. filename)
|
||||
os.remove(common.basepath(dir) .. filename)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -892,7 +886,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 dir .. PATHSEP .. temp_file_prefix
|
||||
return common.basepath(dir) .. temp_file_prefix
|
||||
.. string.format("%06x", temp_file_counter) .. (ext or "")
|
||||
end
|
||||
|
||||
|
@ -926,8 +920,6 @@ function core.restart()
|
|||
end
|
||||
|
||||
|
||||
local mod_version_regex =
|
||||
regex.compile([[--.*mod-version:(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:$|\s)]])
|
||||
local function get_plugin_details(filename)
|
||||
local info = system.get_file_info(filename)
|
||||
if info ~= nil and info.type == "dir" then
|
||||
|
@ -939,32 +931,17 @@ local function get_plugin_details(filename)
|
|||
if not f then return false end
|
||||
local priority = false
|
||||
local version_match = false
|
||||
local major, minor, patch
|
||||
|
||||
for line in f:lines() do
|
||||
if not version_match then
|
||||
local _major, _minor, _patch = mod_version_regex:match(line)
|
||||
if _major then
|
||||
_major = tonumber(_major) or 0
|
||||
_minor = tonumber(_minor) or 0
|
||||
_patch = tonumber(_patch) or 0
|
||||
major, minor, patch = _major, _minor, _patch
|
||||
|
||||
version_match = major == MOD_VERSION_MAJOR
|
||||
if version_match then
|
||||
version_match = minor <= MOD_VERSION_MINOR
|
||||
end
|
||||
if version_match then
|
||||
version_match = patch <= MOD_VERSION_PATCH
|
||||
end
|
||||
local mod_version = line:match('%-%-.*%f[%a]mod%-version%s*:%s*(%d+)')
|
||||
if mod_version then
|
||||
version_match = (mod_version == MOD_VERSION)
|
||||
end
|
||||
end
|
||||
|
||||
if not priority then
|
||||
priority = line:match('%-%-.*%f[%a]priority%s*:%s*(%d+)')
|
||||
if priority then priority = tonumber(priority) end
|
||||
end
|
||||
|
||||
if version_match then
|
||||
break
|
||||
end
|
||||
|
@ -972,7 +949,6 @@ local function get_plugin_details(filename)
|
|||
f:close()
|
||||
return true, {
|
||||
version_match = version_match,
|
||||
version = major and {major, minor, patch} or {},
|
||||
priority = priority or 100
|
||||
}
|
||||
end
|
||||
|
@ -986,7 +962,7 @@ function core.load_plugins()
|
|||
}
|
||||
local files, ordered = {}, {}
|
||||
for _, root_dir in ipairs {DATADIR, USERDIR} do
|
||||
local plugin_dir = root_dir .. PATHSEP .. "plugins"
|
||||
local plugin_dir = common.basepath(root_dir) .. "plugins"
|
||||
for _, filename in ipairs(system.list_dir(plugin_dir) or {}) do
|
||||
if not files[filename] then
|
||||
table.insert(
|
||||
|
@ -1001,15 +977,13 @@ 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(dir .. PATHSEP .. plugin.file)
|
||||
local is_lua_file, details = get_plugin_details(common.basepath(dir) .. 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
|
||||
|
@ -1025,35 +999,27 @@ function core.load_plugins()
|
|||
if plugin.valid then
|
||||
if not config.skip_plugins_version and not plugin.version_match then
|
||||
core.log_quiet(
|
||||
"Version mismatch for plugin %q[%s] from %s",
|
||||
"Version mismatch for plugin %q from %s",
|
||||
plugin.name,
|
||||
plugin.version_string,
|
||||
plugin.dir
|
||||
)
|
||||
local rlist = plugin.dir:find(USERDIR, 1, true) == 1
|
||||
and 'userdir' or 'datadir'
|
||||
local list = refused_list[rlist].plugins
|
||||
table.insert(list, plugin)
|
||||
table.insert(list, plugin.file)
|
||||
elseif config.plugins[plugin.name] ~= false then
|
||||
local start = system.get_time()
|
||||
local ok, loaded_plugin = core.try(require, "plugins." .. plugin.name)
|
||||
local ok = core.try(require, "plugins." .. plugin.name)
|
||||
if ok then
|
||||
local plugin_version = ""
|
||||
if plugin.version_string ~= MOD_VERSION_STRING then
|
||||
plugin_version = "["..plugin.version_string.."]"
|
||||
end
|
||||
core.log_quiet(
|
||||
"Loaded plugin %q%s from %s in %.1fms",
|
||||
"Loaded plugin %q from %s in %.1fms",
|
||||
plugin.name,
|
||||
plugin_version,
|
||||
plugin.dir,
|
||||
(system.get_time() - start) * 1000
|
||||
)
|
||||
end
|
||||
if not ok then
|
||||
no_errors = false
|
||||
elseif config.plugins[plugin.name].onload then
|
||||
core.try(config.plugins[plugin.name].onload, loaded_plugin)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1107,7 +1073,6 @@ function core.set_active_view(view)
|
|||
-- Reset the IME even if the focus didn't change
|
||||
ime.stop()
|
||||
if view ~= core.active_view then
|
||||
system.text_input(view:supports_text_input())
|
||||
if core.active_view and core.active_view.force_focus then
|
||||
core.next_active_view = view
|
||||
return
|
||||
|
@ -1296,12 +1261,6 @@ function core.on_event(type, ...)
|
|||
if not core.root_view:on_mouse_wheel(...) then
|
||||
did_keymap = keymap.on_mouse_wheel(...)
|
||||
end
|
||||
elseif type == "touchpressed" then
|
||||
core.root_view:on_touch_pressed(...)
|
||||
elseif type == "touchreleased" then
|
||||
core.root_view:on_touch_released(...)
|
||||
elseif type == "touchmoved" then
|
||||
core.root_view:on_touch_moved(...)
|
||||
elseif type == "resized" then
|
||||
core.window_mode = system.get_window_mode()
|
||||
elseif type == "minimized" or type == "maximized" or type == "restored" then
|
||||
|
@ -1351,11 +1310,6 @@ function core.step()
|
|||
did_keymap = false
|
||||
elseif type == "mousemoved" then
|
||||
core.try(core.on_event, type, a, b, c, d)
|
||||
elseif type == "enteringforeground" then
|
||||
-- to break our frame refresh in two if we get entering/entered at the same time.
|
||||
-- required to avoid flashing and refresh issues on mobile
|
||||
core.redraw = true
|
||||
break
|
||||
else
|
||||
local _, res = core.try(core.on_event, type, a, b, c, d)
|
||||
did_keymap = res or did_keymap
|
||||
|
@ -1412,10 +1366,11 @@ local run_threads = coroutine.wrap(function()
|
|||
else
|
||||
core.threads[k] = nil
|
||||
end
|
||||
else
|
||||
wait = wait or (1/30)
|
||||
elseif wait then
|
||||
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())
|
||||
|
@ -1496,7 +1451,7 @@ end
|
|||
|
||||
function core.on_error(err)
|
||||
-- write error to file
|
||||
local fp = io.open(USERDIR .. PATHSEP .. "error.txt", "wb")
|
||||
local fp = io.open(common.basepath(USERDIR) .. "error.txt", "wb")
|
||||
fp:write("Error: " .. tostring(err) .. "\n")
|
||||
fp:write(debug.traceback("", 4) .. "\n")
|
||||
fp:close()
|
||||
|
@ -1509,16 +1464,4 @@ function core.on_error(err)
|
|||
end
|
||||
|
||||
|
||||
local alerted_deprecations = {}
|
||||
---Show deprecation notice once per `kind`.
|
||||
---
|
||||
---@param kind string
|
||||
function core.deprecation_log(kind)
|
||||
if alerted_deprecations[kind] then return end
|
||||
alerted_deprecations[kind] = true
|
||||
core.warn("Used deprecated functionality [%s]. Check if your plugins are up to date.", kind)
|
||||
end
|
||||
|
||||
|
||||
return core
|
||||
|
||||
|
|
|
@ -36,8 +36,6 @@ local function keymap_macos(keymap)
|
|||
["wheel"] = "root:scroll",
|
||||
["hwheel"] = "root:horizontal-scroll",
|
||||
["shift+hwheel"] = "root:horizontal-scroll",
|
||||
["wheelup"] = "root:scroll-hovered-tabs-backward",
|
||||
["wheeldown"] = "root:scroll-hovered-tabs-forward",
|
||||
|
||||
["cmd+f"] = "find-replace:find",
|
||||
["cmd+r"] = "find-replace:replace",
|
||||
|
|
|
@ -71,6 +71,7 @@ local function key_to_stroke(key)
|
|||
return normalize_stroke(table.concat(keys, "+"))
|
||||
end
|
||||
|
||||
|
||||
---Remove the given value from an array associated to a key in a table.
|
||||
---@param tbl table<string, string> The table containing the key
|
||||
---@param k string The key containing the array
|
||||
|
@ -124,6 +125,7 @@ end
|
|||
function keymap.add_direct(map)
|
||||
for stroke, commands in pairs(map) do
|
||||
stroke = normalize_stroke(stroke)
|
||||
|
||||
if type(commands) == "string" or type(commands) == "function" then
|
||||
commands = { commands }
|
||||
end
|
||||
|
@ -323,8 +325,6 @@ keymap.add_direct {
|
|||
["wheel"] = "root:scroll",
|
||||
["hwheel"] = "root:horizontal-scroll",
|
||||
["shift+wheel"] = "root:horizontal-scroll",
|
||||
["wheelup"] = "root:scroll-hovered-tabs-backward",
|
||||
["wheeldown"] = "root:scroll-hovered-tabs-forward",
|
||||
|
||||
["ctrl+f"] = "find-replace:find",
|
||||
["ctrl+r"] = "find-replace:replace",
|
||||
|
@ -341,7 +341,6 @@ keymap.add_direct {
|
|||
["ctrl+x"] = "doc:cut",
|
||||
["ctrl+c"] = "doc:copy",
|
||||
["ctrl+v"] = "doc:paste",
|
||||
["insert"] = "doc:toggle-overwrite",
|
||||
["ctrl+insert"] = "doc:copy",
|
||||
["shift+insert"] = "doc:paste",
|
||||
["escape"] = { "command:escape", "doc:select-none", "dialog:select-no" },
|
||||
|
@ -391,7 +390,7 @@ keymap.add_direct {
|
|||
["shift+1lclick"] = "doc:select-to-cursor",
|
||||
["ctrl+1lclick"] = "doc:split-cursor",
|
||||
["1lclick"] = "doc:set-cursor",
|
||||
["2lclick"] = { "doc:set-cursor-word", "emptyview:new-doc", "tabbar:new-doc" },
|
||||
["2lclick"] = "doc:set-cursor-word",
|
||||
["3lclick"] = "doc:set-cursor-line",
|
||||
["shift+left"] = "doc:select-to-previous-char",
|
||||
["shift+right"] = "doc:select-to-next-char",
|
||||
|
@ -412,4 +411,3 @@ keymap.add_direct {
|
|||
}
|
||||
|
||||
return keymap
|
||||
|
||||
|
|
|
@ -7,12 +7,8 @@ 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", "super" }
|
||||
modkeys.keys = { "ctrl", "shift", "alt", "altgr" }
|
||||
|
||||
return modkeys
|
||||
|
|
|
@ -24,7 +24,6 @@ function NagView:new()
|
|||
self.scrollable = true
|
||||
self.target_height = 0
|
||||
self.on_mouse_pressed_root = nil
|
||||
self.dim_alpha = 0
|
||||
end
|
||||
|
||||
function NagView:get_title()
|
||||
|
@ -69,9 +68,7 @@ function NagView:dim_window_content()
|
|||
oy = oy + self.show_height
|
||||
local w, h = core.root_view.size.x, core.root_view.size.y - oy
|
||||
core.root_view:defer_draw(function()
|
||||
local dim_color = { table.unpack(style.nagbar_dim) }
|
||||
dim_color[4] = style.nagbar_dim[4] * self.dim_alpha
|
||||
renderer.draw_rect(ox, oy, w, h, dim_color)
|
||||
renderer.draw_rect(ox, oy, w, h, style.nagbar_dim)
|
||||
end)
|
||||
end
|
||||
|
||||
|
@ -175,13 +172,10 @@ function NagView:update()
|
|||
NagView.super.update(self)
|
||||
|
||||
if self.visible and core.active_view == self and self.title then
|
||||
local target_height = self:get_target_height()
|
||||
self:move_towards(self, "show_height", target_height, nil, "nagbar")
|
||||
self:move_towards(self, "show_height", self:get_target_height(), nil, "nagbar")
|
||||
self:move_towards(self, "underline_progress", 1, nil, "nagbar")
|
||||
self:move_towards(self, "dim_alpha", self.show_height / target_height, nil, "nagbar")
|
||||
else
|
||||
self:move_towards(self, "show_height", 0, nil, "nagbar")
|
||||
self:move_towards(self, "dim_alpha", 0, nil, "nagbar")
|
||||
if self.show_height <= 0 then
|
||||
self.title = nil
|
||||
self.message = nil
|
||||
|
|
|
@ -18,6 +18,7 @@ function Node:new(type)
|
|||
if self.type == "leaf" then
|
||||
self:add_view(EmptyView())
|
||||
end
|
||||
self.hovered = {x = -1, y = -1 }
|
||||
self.hovered_close = 0
|
||||
self.tab_shift = 0
|
||||
self.tab_offset = 1
|
||||
|
@ -32,10 +33,9 @@ function Node:propagate(fn, ...)
|
|||
end
|
||||
|
||||
|
||||
---@deprecated
|
||||
function Node:on_mouse_moved(x, y, ...)
|
||||
core.deprecation_log("Node:on_mouse_moved")
|
||||
if self.type == "leaf" then
|
||||
self.hovered.x, self.hovered.y = x, y
|
||||
self.active_view:on_mouse_moved(x, y, ...)
|
||||
else
|
||||
self:propagate("on_mouse_moved", x, y, ...)
|
||||
|
@ -43,9 +43,7 @@ function Node:on_mouse_moved(x, y, ...)
|
|||
end
|
||||
|
||||
|
||||
---@deprecated
|
||||
function Node:on_mouse_released(...)
|
||||
core.deprecation_log("Node:on_mouse_released")
|
||||
if self.type == "leaf" then
|
||||
self.active_view:on_mouse_released(...)
|
||||
else
|
||||
|
@ -54,9 +52,7 @@ function Node:on_mouse_released(...)
|
|||
end
|
||||
|
||||
|
||||
---@deprecated
|
||||
function Node:on_mouse_left()
|
||||
core.deprecation_log("Node:on_mouse_left")
|
||||
if self.type == "leaf" then
|
||||
self.active_view:on_mouse_left()
|
||||
else
|
||||
|
@ -65,17 +61,6 @@ function Node:on_mouse_left()
|
|||
end
|
||||
|
||||
|
||||
---@deprecated
|
||||
function Node:on_touch_moved(...)
|
||||
core.deprecation_log("Node:on_touch_moved")
|
||||
if self.type == "leaf" then
|
||||
self.active_view:on_touch_moved(...)
|
||||
else
|
||||
self:propagate("on_touch_moved", ...)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Node:consume(node)
|
||||
for k, _ in pairs(self) do self[k] = nil end
|
||||
for k, v in pairs(node) do self[k] = v end
|
||||
|
@ -177,12 +162,8 @@ function Node:add_view(view, idx)
|
|||
assert(not self.locked, "Tried to add view to locked node")
|
||||
if self.views[1] and self.views[1]:is(EmptyView) then
|
||||
table.remove(self.views)
|
||||
if idx and idx > 1 then
|
||||
idx = idx - 1
|
||||
end
|
||||
end
|
||||
idx = common.clamp(idx or (#self.views + 1), 1, (#self.views + 1))
|
||||
table.insert(self.views, idx, view)
|
||||
table.insert(self.views, idx or (#self.views + 1), view)
|
||||
self:set_active_view(view)
|
||||
end
|
||||
|
||||
|
@ -320,7 +301,7 @@ function Node:tab_hovered_update(px, py)
|
|||
if px >= cx and px < cx + cw and py >= y and py < y + h and config.tab_close_button then
|
||||
self.hovered_close = tab_index
|
||||
end
|
||||
elseif #self.views > self:get_visible_tabs_number() then
|
||||
else
|
||||
self.hovered_scroll_button = self:get_scroll_button_index(px, py) or 0
|
||||
end
|
||||
end
|
||||
|
@ -338,17 +319,10 @@ function Node:get_child_overlapping_point(x, y)
|
|||
return child:get_child_overlapping_point(x, y)
|
||||
end
|
||||
|
||||
-- returns: total height, text padding, top margin
|
||||
local function get_tab_y_sizes()
|
||||
local height = style.font:get_height()
|
||||
local padding = style.padding.y
|
||||
local margin = style.margin.tab.top
|
||||
return height + (padding * 2) + margin, padding, margin
|
||||
end
|
||||
|
||||
function Node:get_scroll_button_rect(index)
|
||||
local w, pad = get_scroll_button_width()
|
||||
local h = get_tab_y_sizes()
|
||||
local h = style.font:get_height() + style.padding.y * 2
|
||||
local x = self.position.x + (index == 1 and self.size.x - w * 2 or self.size.x - w)
|
||||
return x, self.position.y, w, h, pad
|
||||
end
|
||||
|
@ -359,8 +333,8 @@ function Node:get_tab_rect(idx)
|
|||
local x0 = self.position.x
|
||||
local x1 = x0 + common.clamp(self.tab_width * (idx - 1) - self.tab_shift, 0, maxw)
|
||||
local x2 = x0 + common.clamp(self.tab_width * idx - self.tab_shift, 0, maxw)
|
||||
local h, pad_y, margin_y = get_tab_y_sizes()
|
||||
return x1, self.position.y, x2 - x1, h, margin_y
|
||||
local h = style.font:get_height() + style.padding.y * 2
|
||||
return x1, self.position.y, x2 - x1, h
|
||||
end
|
||||
|
||||
|
||||
|
@ -508,7 +482,7 @@ function Node:update()
|
|||
for _, view in ipairs(self.views) do
|
||||
view:update()
|
||||
end
|
||||
self:tab_hovered_update(core.root_view.mouse.x, core.root_view.mouse.y)
|
||||
self:tab_hovered_update(self.hovered.x, self.hovered.y)
|
||||
local tab_width = self:target_tab_width()
|
||||
self:move_towards("tab_shift", tab_width * (self.tab_offset - 1), nil, "tabs")
|
||||
self:move_towards("tab_width", tab_width, nil, "tabs")
|
||||
|
@ -551,7 +525,6 @@ function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalo
|
|||
if is_active then
|
||||
color = style.text
|
||||
renderer.draw_rect(x, y, w, h, style.background)
|
||||
renderer.draw_rect(x, y, w, ds, style.divider)
|
||||
renderer.draw_rect(x + w, y, ds, h, style.divider)
|
||||
renderer.draw_rect(x - ds, y, ds, h, style.divider)
|
||||
end
|
||||
|
@ -559,8 +532,7 @@ function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalo
|
|||
end
|
||||
|
||||
function Node:draw_tab(view, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
|
||||
local _, padding_y, margin_y = get_tab_y_sizes()
|
||||
x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y + margin_y, w, h - margin_y, standalone)
|
||||
x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalone)
|
||||
-- Close button
|
||||
local cx, cw, cpad = close_button_location(x, w)
|
||||
local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button)
|
||||
|
@ -636,13 +608,6 @@ function Node:is_empty()
|
|||
end
|
||||
|
||||
|
||||
function Node:is_in_tab_area(x, y)
|
||||
if not self:should_show_tabs() then return false end
|
||||
local _, ty, _, th = self:get_scroll_button_rect(1)
|
||||
return y >= ty and y < ty + th
|
||||
end
|
||||
|
||||
|
||||
function Node:close_all_docviews(keep_active)
|
||||
local node_active_view = self.active_view
|
||||
local lost_active_view = false
|
||||
|
@ -781,7 +746,7 @@ function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
|
|||
tab_index = self:get_visible_tabs_number() + (self.tab_offset - 1 or 0)
|
||||
end
|
||||
end
|
||||
local tab_x, tab_y, tab_w, tab_h, margin_y = self:get_tab_rect(tab_index)
|
||||
local tab_x, tab_y, tab_w, tab_h = self:get_tab_rect(tab_index)
|
||||
if x > tab_x + tab_w / 2 and tab_index <= #self.views then
|
||||
-- use next tab
|
||||
tab_x = tab_x + tab_w
|
||||
|
@ -792,7 +757,7 @@ function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
|
|||
tab_index = tab_index - 1
|
||||
tab_x = tab_x - tab_w
|
||||
end
|
||||
return tab_index, tab_x, tab_y + margin_y, tab_w, tab_h - margin_y
|
||||
return tab_index, tab_x, tab_y, tab_w, tab_h
|
||||
end
|
||||
|
||||
return Node
|
||||
|
|
|
@ -27,13 +27,6 @@ function Object:is(T)
|
|||
return getmetatable(self) == T
|
||||
end
|
||||
|
||||
---Check if the parameter is strictly of the object type.
|
||||
---@param T any
|
||||
---@return boolean
|
||||
function Object:is_class_of(T)
|
||||
return getmetatable(T) == self
|
||||
end
|
||||
|
||||
---Check if the object inherits from the given type.
|
||||
---@param T any
|
||||
---@return boolean
|
||||
|
@ -48,22 +41,6 @@ function Object:extends(T)
|
|||
return false
|
||||
end
|
||||
|
||||
---Check if the parameter inherits from the object.
|
||||
---@param T any
|
||||
---@return boolean
|
||||
function Object:is_extended_by(T)
|
||||
local mt = getmetatable(T)
|
||||
while mt do
|
||||
if mt == self then
|
||||
return true
|
||||
end
|
||||
local _mt = getmetatable(T)
|
||||
if mt == _mt then break end
|
||||
mt = _mt
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---Metamethod to get a string representation of an object.
|
||||
---@return string
|
||||
function Object:__tostring()
|
||||
|
|
|
@ -24,9 +24,6 @@ function RootView:new()
|
|||
base_color = style.drag_overlay_tab,
|
||||
color = { table.unpack(style.drag_overlay_tab) } }
|
||||
self.drag_overlay_tab.to = { x = 0, y = 0, w = 0, h = 0 }
|
||||
self.grab = nil -- = {view = nil, button = nil}
|
||||
self.overlapping_view = nil
|
||||
self.touched_view = nil
|
||||
end
|
||||
|
||||
|
||||
|
@ -119,31 +116,6 @@ function RootView:close_all_docviews(keep_active)
|
|||
end
|
||||
|
||||
|
||||
---Obtain mouse grab.
|
||||
---
|
||||
---This means that mouse movements will be sent to the specified view, even when
|
||||
---those occur outside of it.
|
||||
---There can't be multiple mouse grabs, even for different buttons.
|
||||
---@see RootView:ungrab_mouse
|
||||
---@param button core.view.mousebutton
|
||||
---@param view core.view
|
||||
function RootView:grab_mouse(button, view)
|
||||
assert(self.grab == nil)
|
||||
self.grab = {view = view, button = button}
|
||||
end
|
||||
|
||||
|
||||
---Release mouse grab.
|
||||
---
|
||||
---The specified button *must* be the last button that grabbed the mouse.
|
||||
---@see RootView:grab_mouse
|
||||
---@param button core.view.mousebutton
|
||||
function RootView:ungrab_mouse(button)
|
||||
assert(self.grab and self.grab.button == button)
|
||||
self.grab = nil
|
||||
end
|
||||
|
||||
|
||||
---Function to intercept mouse pressed events on the active view.
|
||||
---Do nothing by default.
|
||||
---@param button core.view.mousebutton
|
||||
|
@ -160,10 +132,6 @@ end
|
|||
---@param clicks integer
|
||||
---@return boolean
|
||||
function RootView:on_mouse_pressed(button, x, y, clicks)
|
||||
-- If there is a grab, release it first
|
||||
if self.grab then
|
||||
self:on_mouse_released(self.grab.button, x, y)
|
||||
end
|
||||
local div = self.root_node:get_divider_overlapping_point(x, y)
|
||||
local node = self.root_node:get_child_overlapping_point(x, y)
|
||||
if div and (node and not node.active_view:scrollbar_overlaps_point(x, y)) then
|
||||
|
@ -188,7 +156,6 @@ function RootView:on_mouse_pressed(button, x, y, clicks)
|
|||
end
|
||||
elseif not self.dragged_node then -- avoid sending on_mouse_pressed events when dragging tabs
|
||||
core.set_active_view(node.active_view)
|
||||
self:grab_mouse(button, node.active_view)
|
||||
return self.on_view_mouse_pressed(button, x, y, clicks) or node.active_view:on_mouse_pressed(button, x, y, clicks)
|
||||
end
|
||||
end
|
||||
|
@ -221,21 +188,6 @@ end
|
|||
---@param x number
|
||||
---@param y number
|
||||
function RootView:on_mouse_released(button, x, y, ...)
|
||||
if self.grab then
|
||||
if self.grab.button == button then
|
||||
local grabbed_view = self.grab.view
|
||||
grabbed_view:on_mouse_released(button, x, y, ...)
|
||||
self:ungrab_mouse(button)
|
||||
|
||||
-- If the mouse was released over a different view, send it the mouse position
|
||||
local hovered_view = self.root_node:get_child_overlapping_point(x, y)
|
||||
if grabbed_view ~= hovered_view then
|
||||
self:on_mouse_moved(x, y, 0, 0)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if self.dragged_divider then
|
||||
self.dragged_divider = nil
|
||||
end
|
||||
|
@ -276,6 +228,8 @@ function RootView:on_mouse_released(button, x, y, ...)
|
|||
end
|
||||
self.dragged_node = nil
|
||||
end
|
||||
else -- avoid sending on_mouse_released events when dragging tabs
|
||||
self.root_node:on_mouse_released(button, x, y, ...)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -296,14 +250,6 @@ end
|
|||
---@param dx number
|
||||
---@param dy number
|
||||
function RootView:on_mouse_moved(x, y, dx, dy)
|
||||
self.mouse.x, self.mouse.y = x, y
|
||||
|
||||
if self.grab then
|
||||
self.grab.view:on_mouse_moved(x, y, dx, dy)
|
||||
core.request_cursor(self.grab.view.cursor)
|
||||
return
|
||||
end
|
||||
|
||||
if core.active_view == core.nag_view then
|
||||
core.request_cursor("arrow")
|
||||
core.active_view:on_mouse_moved(x, y, dx, dy)
|
||||
|
@ -323,6 +269,8 @@ function RootView:on_mouse_moved(x, y, dx, dy)
|
|||
return
|
||||
end
|
||||
|
||||
self.mouse.x, self.mouse.y = x, y
|
||||
|
||||
local dn = self.dragged_node
|
||||
if dn and not dn.dragging then
|
||||
-- start dragging only after enough movement
|
||||
|
@ -335,33 +283,32 @@ function RootView:on_mouse_moved(x, y, dx, dy)
|
|||
-- avoid sending on_mouse_moved events when dragging tabs
|
||||
if dn then return end
|
||||
|
||||
local last_overlapping_view = self.overlapping_view
|
||||
local overlapping_node = self.root_node:get_child_overlapping_point(x, y)
|
||||
self.overlapping_view = overlapping_node and overlapping_node.active_view
|
||||
self.root_node:on_mouse_moved(x, y, dx, dy)
|
||||
|
||||
if last_overlapping_view and last_overlapping_view ~= self.overlapping_view then
|
||||
last_overlapping_view:on_mouse_left()
|
||||
local last_overlapping_node = self.overlapping_node
|
||||
self.overlapping_node = self.root_node:get_child_overlapping_point(x, y)
|
||||
|
||||
if last_overlapping_node and last_overlapping_node ~= self.overlapping_node then
|
||||
last_overlapping_node:on_mouse_left()
|
||||
end
|
||||
|
||||
if not self.overlapping_view then return end
|
||||
|
||||
self.overlapping_view:on_mouse_moved(x, y, dx, dy)
|
||||
core.request_cursor(self.overlapping_view.cursor)
|
||||
|
||||
if not overlapping_node then return end
|
||||
|
||||
local div = self.root_node:get_divider_overlapping_point(x, y)
|
||||
if overlapping_node:get_scroll_button_index(x, y) or overlapping_node:is_in_tab_area(x, y) then
|
||||
local tab_index = self.overlapping_node and self.overlapping_node:get_tab_overlapping_point(x, y)
|
||||
if self.overlapping_node and self.overlapping_node:get_scroll_button_index(x, y) then
|
||||
core.request_cursor("arrow")
|
||||
elseif div and not self.overlapping_view:scrollbar_overlaps_point(x, y) then
|
||||
elseif div and (self.overlapping_node and not self.overlapping_node.active_view:scrollbar_overlaps_point(x, y)) then
|
||||
core.request_cursor(div.type == "hsplit" and "sizeh" or "sizev")
|
||||
elseif tab_index then
|
||||
core.request_cursor("arrow")
|
||||
elseif self.overlapping_node then
|
||||
core.request_cursor(self.overlapping_node.active_view.cursor)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function RootView:on_mouse_left()
|
||||
if self.overlapping_view then
|
||||
self.overlapping_view:on_mouse_left()
|
||||
if self.overlapping_node then
|
||||
self.overlapping_node:on_mouse_left()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -387,50 +334,6 @@ function RootView:on_text_input(...)
|
|||
core.active_view:on_text_input(...)
|
||||
end
|
||||
|
||||
function RootView:on_touch_pressed(x, y, ...)
|
||||
local touched_node = self.root_node:get_child_overlapping_point(x, y)
|
||||
self.touched_view = touched_node and touched_node.active_view
|
||||
end
|
||||
|
||||
function RootView:on_touch_released(x, y, ...)
|
||||
self.touched_view = nil
|
||||
end
|
||||
|
||||
function RootView:on_touch_moved(x, y, dx, dy, ...)
|
||||
if not self.touched_view then return end
|
||||
if core.active_view == core.nag_view then
|
||||
core.active_view:on_touch_moved(x, y, dx, dy, ...)
|
||||
return
|
||||
end
|
||||
|
||||
if self.dragged_divider then
|
||||
local node = self.dragged_divider
|
||||
if node.type == "hsplit" then
|
||||
x = common.clamp(x - node.position.x, 0, self.root_node.size.x * 0.95)
|
||||
resize_child_node(node, "x", x, dx)
|
||||
elseif node.type == "vsplit" then
|
||||
y = common.clamp(y - node.position.y, 0, self.root_node.size.y * 0.95)
|
||||
resize_child_node(node, "y", y, dy)
|
||||
end
|
||||
node.divider = common.clamp(node.divider, 0.01, 0.99)
|
||||
return
|
||||
end
|
||||
|
||||
local dn = self.dragged_node
|
||||
if dn and not dn.dragging then
|
||||
-- start dragging only after enough movement
|
||||
dn.dragging = common.distance(x, y, dn.drag_start_x, dn.drag_start_y) > style.tab_width * .05
|
||||
if dn.dragging then
|
||||
core.request_cursor("hand")
|
||||
end
|
||||
end
|
||||
|
||||
-- avoid sending on_touch_moved events when dragging tabs
|
||||
if dn then return end
|
||||
|
||||
self.touched_view:on_touch_moved(x, y, dx, dy, ...)
|
||||
end
|
||||
|
||||
function RootView:on_ime_text_editing(...)
|
||||
core.active_view:on_ime_text_editing(...)
|
||||
end
|
||||
|
|
|
@ -58,9 +58,9 @@ function Scrollbar:new(options)
|
|||
---@type "expanded" | "contracted" | false @Force the scrollbar status
|
||||
self.force_status = options.force_status
|
||||
self:set_forced_status(options.force_status)
|
||||
---@type number? @Override the default value specified by `style.scrollbar_size`
|
||||
self.contracted_size = options.contracted_size
|
||||
---@type number? @Override the default value specified by `style.expanded_scrollbar_size`
|
||||
self.contracted_size = options.contracted_size
|
||||
---@type number? @Override the default value specified by `style.scrollbar_size`
|
||||
self.expanded_size = options.expanded_size
|
||||
end
|
||||
|
||||
|
@ -121,7 +121,7 @@ function Scrollbar:_get_thumb_rect_normal()
|
|||
across_size = across_size + (expanded_scrollbar_size - scrollbar_size) * self.expand_percent
|
||||
return
|
||||
nr.across + nr.across_size - across_size,
|
||||
nr.along + self.percent * (nr.along_size - along_size),
|
||||
nr.along + self.percent * nr.scrollable * (nr.along_size - along_size) / (sz - nr.along_size),
|
||||
across_size,
|
||||
along_size
|
||||
end
|
||||
|
@ -238,8 +238,7 @@ end
|
|||
function Scrollbar:_on_mouse_moved_normal(x, y, dx, dy)
|
||||
if self.dragging then
|
||||
local nr = self.normal_rect
|
||||
local _, _, _, along_size = self:_get_thumb_rect_normal()
|
||||
return common.clamp((y - nr.along + self.drag_start_offset) / (nr.along_size - along_size), 0, 1)
|
||||
return common.clamp((y - nr.along + self.drag_start_offset) / nr.along_size, 0, 1)
|
||||
end
|
||||
return self:_update_hover_status_normal(x, y)
|
||||
end
|
||||
|
@ -282,7 +281,7 @@ function Scrollbar:set_size(x, y, w, h, scrollable)
|
|||
end
|
||||
|
||||
---Updates the scrollbar location
|
||||
---@param percent number @number between 0 and 1 where 0 means thumb at the top and 1 at the bottom
|
||||
---@param percent number @number between 0 and 1 representing the position of the middle part of the thumb
|
||||
function Scrollbar:set_percent(percent)
|
||||
self.percent = percent
|
||||
end
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
-- this file is used by lite-xl to setup the Lua environment when starting
|
||||
VERSION = "2.1.2r1"
|
||||
MOD_VERSION_MAJOR = 3
|
||||
MOD_VERSION_MINOR = 0
|
||||
MOD_VERSION_PATCH = 0
|
||||
MOD_VERSION_STRING = string.format("%d.%d.%d", MOD_VERSION_MAJOR, MOD_VERSION_MINOR, MOD_VERSION_PATCH)
|
||||
VERSION = "2.1.4r1"
|
||||
MOD_VERSION = "3"
|
||||
|
||||
SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or 1
|
||||
PATHSEP = package.config:sub(1, 1)
|
||||
|
@ -25,7 +22,7 @@ package.path = DATADIR .. '/?/init.lua;' .. package.path
|
|||
package.path = USERDIR .. '/?.lua;' .. package.path
|
||||
package.path = USERDIR .. '/?/init.lua;' .. package.path
|
||||
|
||||
local suffix = PLATFORM == "Windows" and 'dll' or 'so'
|
||||
local suffix = PLATFORM == "Mac OS X" and 'lib' or (PLATFORM == "Windows" and 'dll' or 'so')
|
||||
package.cpath =
|
||||
USERDIR .. '/?.' .. ARCH .. "." .. suffix .. ";" ..
|
||||
USERDIR .. '/?/init.' .. ARCH .. "." .. suffix .. ";" ..
|
||||
|
|
|
@ -232,42 +232,15 @@ function StatusView:register_docview_items()
|
|||
return {
|
||||
style.text, line, ":",
|
||||
col > config.line_limit and style.accent or style.text, col,
|
||||
style.text
|
||||
style.text,
|
||||
self.separator,
|
||||
string.format("%.f%%", line / #dv.doc.lines * 100)
|
||||
}
|
||||
end,
|
||||
command = "doc:go-to-line",
|
||||
tooltip = "line : column"
|
||||
})
|
||||
|
||||
self:add_item({
|
||||
predicate = predicate_docview,
|
||||
name = "doc:position-percent",
|
||||
alignment = StatusView.Item.LEFT,
|
||||
get_item = function()
|
||||
local dv = core.active_view
|
||||
local line = dv.doc:get_selection()
|
||||
return {
|
||||
string.format("%.f%%", line / #dv.doc.lines * 100)
|
||||
}
|
||||
end,
|
||||
tooltip = "caret position"
|
||||
})
|
||||
|
||||
self:add_item({
|
||||
predicate = predicate_docview,
|
||||
name = "doc:selections",
|
||||
alignment = StatusView.Item.LEFT,
|
||||
get_item = function()
|
||||
local dv = core.active_view
|
||||
local nsel = math.floor(#dv.doc.selections / 4)
|
||||
if nsel > 1 then
|
||||
return { style.text, nsel, " selections" }
|
||||
end
|
||||
|
||||
return {}
|
||||
end
|
||||
})
|
||||
|
||||
self:add_item({
|
||||
predicate = predicate_docview,
|
||||
name = "doc:indentation",
|
||||
|
@ -319,19 +292,6 @@ function StatusView:register_docview_items()
|
|||
end,
|
||||
command = "doc:toggle-line-ending"
|
||||
})
|
||||
|
||||
self:add_item {
|
||||
predicate = predicate_docview,
|
||||
name = "doc:overwrite-mode",
|
||||
alignment = StatusView.Item.RIGHT,
|
||||
get_item = function()
|
||||
return {
|
||||
style.text, core.active_view.doc.overwrite and "OVR" or "INS"
|
||||
}
|
||||
end,
|
||||
command = "doc:toggle-overwrite",
|
||||
separator = StatusView.separator2
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
|
@ -1014,12 +974,6 @@ function StatusView:on_mouse_pressed(button, x, y, clicks)
|
|||
end
|
||||
|
||||
|
||||
function StatusView:on_mouse_left()
|
||||
StatusView.super.on_mouse_left(self)
|
||||
self.hovered_item = {}
|
||||
end
|
||||
|
||||
|
||||
function StatusView:on_mouse_moved(x, y, dx, dy)
|
||||
if not self.visible then return end
|
||||
StatusView.super.on_mouse_moved(self, x, y, dx, dy)
|
||||
|
|
|
@ -1,23 +1,13 @@
|
|||
local common = require "core.common"
|
||||
local style = {}
|
||||
|
||||
style.padding = { x = common.round(14 * SCALE), y = common.round(7 * SCALE) }
|
||||
style.divider_size = common.round(1 * SCALE)
|
||||
style.scrollbar_size = common.round(4 * SCALE)
|
||||
style.expanded_scrollbar_size = common.round(12 * SCALE)
|
||||
style.caret_width = common.round(2 * SCALE)
|
||||
style.tab_width = common.round(170 * SCALE)
|
||||
|
||||
style.padding = {
|
||||
x = common.round(14 * SCALE),
|
||||
y = common.round(7 * SCALE),
|
||||
}
|
||||
|
||||
style.margin = {
|
||||
tab = {
|
||||
top = common.round(-style.divider_size * SCALE)
|
||||
}
|
||||
}
|
||||
|
||||
-- The function renderer.font.load can accept an option table as a second optional argument.
|
||||
-- It shoud be like the following:
|
||||
--
|
||||
|
@ -50,4 +40,3 @@ style.syntax_fonts = {}
|
|||
style.log = {}
|
||||
|
||||
return style
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ local common = require "core.common"
|
|||
local syntax = {}
|
||||
syntax.items = {}
|
||||
|
||||
syntax.plain_text_syntax = { name = "Plain Text", patterns = {}, symbols = {} }
|
||||
local plain_text_syntax = { name = "Plain Text", patterns = {}, symbols = {} }
|
||||
|
||||
|
||||
function syntax.add(t)
|
||||
|
@ -46,7 +46,7 @@ end
|
|||
function syntax.get(filename, header)
|
||||
return (filename and find(filename, "files"))
|
||||
or (header and find(header, "headers"))
|
||||
or syntax.plain_text_syntax
|
||||
or plain_text_syntax
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -112,12 +112,6 @@ function TitleView:on_mouse_pressed(button, x, y, clicks)
|
|||
end
|
||||
|
||||
|
||||
function TitleView:on_mouse_left()
|
||||
TitleView.super.on_mouse_left(self)
|
||||
self.hovered_item = nil
|
||||
end
|
||||
|
||||
|
||||
function TitleView:on_mouse_moved(px, py, ...)
|
||||
if self.size.y == 0 then return end
|
||||
TitleView.super.on_mouse_moved(self, px, py, ...)
|
||||
|
|
|
@ -28,8 +28,11 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
|
|||
-- Each position spans characters from i_n to ((i_n+1) - 1), to form
|
||||
-- consecutive spans of text.
|
||||
--
|
||||
-- Insert the start index at i_1 to make iterating easier
|
||||
table.insert(find_results, 3, find_results[1])
|
||||
-- If i_1 is not equal to start, start is automatically inserted at
|
||||
-- that index.
|
||||
if find_results[3] ~= find_results[1] then
|
||||
table.insert(find_results, 3, find_results[1])
|
||||
end
|
||||
-- Copy the ending index to the end of the table, so that an ending index
|
||||
-- always follows a starting index after position 3 in the table.
|
||||
table.insert(find_results, find_results[2] + 1)
|
||||
|
@ -39,10 +42,8 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
|
|||
local fin = find_results[i + 1] - 1
|
||||
local type = pattern.type[i - 2]
|
||||
-- ↑ (i - 2) to convert from [3; n] to [1; n]
|
||||
if fin >= start then
|
||||
local text = full_text:usub(start, fin)
|
||||
push_token(t, syn.symbols[text] or type, text)
|
||||
end
|
||||
local text = full_text:usub(start, fin)
|
||||
push_token(t, syn.symbols[text] or type, text)
|
||||
end
|
||||
else
|
||||
local start, fin = find_results[1], find_results[2]
|
||||
|
@ -122,8 +123,10 @@ local function report_bad_pattern(log_fn, syntax, pattern_idx, msg, ...)
|
|||
end
|
||||
if bad_patterns[syntax][pattern_idx] then return end
|
||||
bad_patterns[syntax][pattern_idx] = true
|
||||
log_fn("Malformed pattern #%d in %s language plugin. " .. msg,
|
||||
pattern_idx, syntax.name or "unnamed", ...)
|
||||
log_fn("Malformed pattern #%d <%s> in %s language plugin.\n" .. msg,
|
||||
pattern_idx,
|
||||
syntax.patterns[pattern_idx].pattern or syntax.patterns[pattern_idx].regex,
|
||||
syntax.name or "unnamed", ...)
|
||||
end
|
||||
|
||||
---@param incoming_syntax table
|
||||
|
@ -133,12 +136,12 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
local res
|
||||
local i = 1
|
||||
|
||||
state = state or string.char(0)
|
||||
|
||||
if #incoming_syntax.patterns == 0 then
|
||||
return { "normal", text }, state
|
||||
return { "normal", text }
|
||||
end
|
||||
|
||||
state = state or string.char(0)
|
||||
|
||||
if resume then
|
||||
res = resume.res
|
||||
-- Remove "incomplete" tokens
|
||||
|
@ -241,7 +244,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
res[1] = char_pos_1
|
||||
res[2] = char_pos_2
|
||||
end
|
||||
if not res[1] then return end
|
||||
if res[1] and target[3] then
|
||||
-- Check to see if the escaped character is there,
|
||||
-- and if it is not itself escaped.
|
||||
|
@ -253,19 +255,18 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
if count % 2 == 0 then
|
||||
-- The match is not escaped, so confirm it
|
||||
break
|
||||
else
|
||||
-- The match is escaped, so avoid it
|
||||
res[1] = false
|
||||
elseif not close then
|
||||
-- The *open* match is escaped, so avoid it
|
||||
return
|
||||
end
|
||||
end
|
||||
until at_start or not close or not target[3]
|
||||
until not res[1] or not close or not target[3]
|
||||
return table.unpack(res)
|
||||
end
|
||||
|
||||
local text_len = text:ulen()
|
||||
local start_time = system.get_time()
|
||||
local starting_i = i
|
||||
|
||||
while text_len ~= nil and i <= text_len do
|
||||
-- Every 200 chars, check if we're out of time
|
||||
if i - starting_i > 200 then
|
||||
|
@ -284,9 +285,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
if current_pattern_idx > 0 then
|
||||
local p = current_syntax.patterns[current_pattern_idx]
|
||||
local s, e = find_text(text, p, i, false, true)
|
||||
-- Use the first token type specified in the type table for the "middle"
|
||||
-- part of the subsyntax.
|
||||
local token_type = type(p.type) == "table" and p.type[1] or p.type
|
||||
|
||||
local cont = true
|
||||
-- If we're in subsyntax mode, always check to see if we end our syntax
|
||||
|
@ -299,7 +297,7 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
-- treat the bit after as a token to be normally parsed
|
||||
-- (as it's the syntax delimiter).
|
||||
if ss and (s == nil or ss < s) then
|
||||
push_token(res, token_type, text:usub(i, ss - 1))
|
||||
push_token(res, p.type, text:usub(i, ss - 1))
|
||||
i = ss
|
||||
cont = false
|
||||
end
|
||||
|
@ -308,11 +306,11 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
-- continue on as normal.
|
||||
if cont then
|
||||
if s then
|
||||
push_token(res, token_type, text:usub(i, e))
|
||||
push_token(res, p.type, text:usub(i, e))
|
||||
set_subsyntax_pattern_idx(0)
|
||||
i = e + 1
|
||||
else
|
||||
push_token(res, token_type, text:usub(i))
|
||||
push_token(res, p.type, text:usub(i))
|
||||
break
|
||||
end
|
||||
end
|
||||
|
@ -321,10 +319,9 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
-- we're ending early in the middle of a delimiter, or
|
||||
-- just normally, upon finding a token.
|
||||
while subsyntax_info do
|
||||
local find_results = { find_text(text, subsyntax_info, i, true, true) }
|
||||
local s, e = find_results[1], find_results[2]
|
||||
local s, e = find_text(text, subsyntax_info, i, true, true)
|
||||
if s then
|
||||
push_tokens(res, current_syntax, subsyntax_info, text, find_results)
|
||||
push_token(res, subsyntax_info.type, text:usub(i, e))
|
||||
-- On finding unescaped delimiter, pop it.
|
||||
pop_subsyntax()
|
||||
i = e + 1
|
||||
|
@ -338,6 +335,14 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
for n, p in ipairs(current_syntax.patterns) do
|
||||
local find_results = { find_text(text, p, i, true, false) }
|
||||
if find_results[1] then
|
||||
-- Check for patterns successfully matching nothing
|
||||
if find_results[1] > find_results[2] then
|
||||
report_bad_pattern(core.warn, current_syntax, n,
|
||||
"Pattern successfully matched, but nothing was captured.")
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- Check for patterns with mismatching number of `types`
|
||||
local type_is_table = type(p.type) == "table"
|
||||
local n_types = type_is_table and #p.type or 1
|
||||
if #find_results == 2 and type_is_table then
|
||||
|
@ -351,6 +356,7 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
report_bad_pattern(core.warn, current_syntax, n,
|
||||
"Too many token types: got %d needed %d.", n_types, #find_results - 1)
|
||||
end
|
||||
|
||||
-- matched pattern; make and add tokens
|
||||
push_tokens(res, current_syntax, p, text, find_results)
|
||||
-- update state if this was a start|end pattern pair
|
||||
|
@ -366,6 +372,7 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
|
|||
i = find_results[2] + 1
|
||||
matched = true
|
||||
break
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -108,10 +108,6 @@ function View:get_h_scrollable_size()
|
|||
end
|
||||
|
||||
|
||||
function View:supports_text_input()
|
||||
return false
|
||||
end
|
||||
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@return boolean
|
||||
|
@ -142,14 +138,14 @@ function View:on_mouse_pressed(button, x, y, clicks)
|
|||
local result = self.v_scrollbar:on_mouse_pressed(button, x, y, clicks)
|
||||
if result then
|
||||
if result ~= true then
|
||||
self.scroll.to.y = result * (self:get_scrollable_size() - self.size.y)
|
||||
self.scroll.to.y = result * self:get_scrollable_size()
|
||||
end
|
||||
return true
|
||||
end
|
||||
result = self.h_scrollbar:on_mouse_pressed(button, x, y, clicks)
|
||||
if result then
|
||||
if result ~= true then
|
||||
self.scroll.to.x = result * (self:get_h_scrollable_size() - self.size.x)
|
||||
self.scroll.to.x = result * self:get_h_scrollable_size()
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
@ -177,7 +173,7 @@ function View:on_mouse_moved(x, y, dx, dy)
|
|||
result = self.v_scrollbar:on_mouse_moved(x, y, dx, dy)
|
||||
if result then
|
||||
if result ~= true then
|
||||
self.scroll.to.y = result * (self:get_scrollable_size() - self.size.y)
|
||||
self.scroll.to.y = result * self:get_scrollable_size()
|
||||
if not config.animate_drag_scroll then
|
||||
self:clamp_scroll_position()
|
||||
self.scroll.y = self.scroll.to.y
|
||||
|
@ -191,7 +187,7 @@ function View:on_mouse_moved(x, y, dx, dy)
|
|||
result = self.h_scrollbar:on_mouse_moved(x, y, dx, dy)
|
||||
if result then
|
||||
if result ~= true then
|
||||
self.scroll.to.x = result * (self:get_h_scrollable_size() - self.size.x)
|
||||
self.scroll.to.x = result * self:get_h_scrollable_size()
|
||||
if not config.animate_drag_scroll then
|
||||
self:clamp_scroll_position()
|
||||
self.scroll.x = self.scroll.to.x
|
||||
|
@ -248,23 +244,6 @@ function View:get_content_bounds()
|
|||
return x, y, x + self.size.x, y + self.size.y
|
||||
end
|
||||
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param dx number
|
||||
---@param dy number
|
||||
---@param i number
|
||||
function View:on_touch_moved(x, y, dx, dy, i)
|
||||
if not self.scrollable then return end
|
||||
if self.dragging_scrollbar then
|
||||
local delta = self:get_scrollable_size() / self.size.y * dy
|
||||
self.scroll.to.y = self.scroll.to.y + delta
|
||||
end
|
||||
self.hovered_scrollbar = self:scrollbar_overlaps_point(x, y)
|
||||
|
||||
self.scroll.to.y = self.scroll.to.y + -dy
|
||||
self.scroll.to.x = self.scroll.to.x + -dx
|
||||
end
|
||||
|
||||
|
||||
---@return number x
|
||||
---@return number y
|
||||
|
@ -287,16 +266,12 @@ end
|
|||
function View:update_scrollbar()
|
||||
local v_scrollable = self:get_scrollable_size()
|
||||
self.v_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, v_scrollable)
|
||||
local v_percent = self.scroll.y/(v_scrollable - self.size.y)
|
||||
-- Avoid setting nan percent
|
||||
self.v_scrollbar:set_percent(v_percent == v_percent and v_percent or 0)
|
||||
self.v_scrollbar:set_percent(self.scroll.y/v_scrollable)
|
||||
self.v_scrollbar:update()
|
||||
|
||||
local h_scrollable = self:get_h_scrollable_size()
|
||||
self.h_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, h_scrollable)
|
||||
local h_percent = self.scroll.x/(h_scrollable - self.size.x)
|
||||
-- Avoid setting nan percent
|
||||
self.h_scrollbar:set_percent(h_percent == h_percent and h_percent or 0)
|
||||
self.h_scrollbar:set_percent(self.scroll.x/h_scrollable)
|
||||
self.h_scrollbar:update()
|
||||
end
|
||||
|
||||
|
|
|
@ -10,10 +10,6 @@ local RootView = require "core.rootview"
|
|||
local DocView = require "core.docview"
|
||||
local Doc = require "core.doc"
|
||||
|
||||
---Symbols cache of all open documents
|
||||
---@type table<core.doc, table>
|
||||
local cache = setmetatable({}, { __mode = "k" })
|
||||
|
||||
config.plugins.autocomplete = common.merge({
|
||||
-- Amount of characters that need to be written for autocomplete
|
||||
min_len = 3,
|
||||
|
@ -23,16 +19,8 @@ config.plugins.autocomplete = common.merge({
|
|||
max_suggestions = 100,
|
||||
-- Maximum amount of symbols to cache per document
|
||||
max_symbols = 4000,
|
||||
-- Which symbols to show on the suggestions list: global, local, related, none
|
||||
suggestions_scope = "global",
|
||||
-- Font size of the description box
|
||||
desc_font_size = 12,
|
||||
-- Do not show the icons associated to the suggestions
|
||||
hide_icons = false,
|
||||
-- Position where icons will be displayed on the suggestions list
|
||||
icon_position = "left",
|
||||
-- Do not show the additional information related to a suggestion
|
||||
hide_info = false,
|
||||
-- The config specification used by gui generators
|
||||
config_spec = {
|
||||
name = "Autocomplete",
|
||||
|
@ -72,26 +60,6 @@ config.plugins.autocomplete = common.merge({
|
|||
min = 1000,
|
||||
max = 10000
|
||||
},
|
||||
{
|
||||
label = "Suggestions Scope",
|
||||
description = "Which symbols to show on the suggestions list.",
|
||||
path = "suggestions_scope",
|
||||
type = "selection",
|
||||
default = "global",
|
||||
values = {
|
||||
{"All Documents", "global"},
|
||||
{"Current Document", "local"},
|
||||
{"Related Documents", "related"},
|
||||
{"Known Symbols", "none"}
|
||||
},
|
||||
on_apply = function(value)
|
||||
if value == "global" then
|
||||
for _, doc in ipairs(core.docs) do
|
||||
if cache[doc] then cache[doc] = nil end
|
||||
end
|
||||
end
|
||||
end
|
||||
},
|
||||
{
|
||||
label = "Description Font Size",
|
||||
description = "Font size of the description box.",
|
||||
|
@ -99,31 +67,6 @@ config.plugins.autocomplete = common.merge({
|
|||
type = "number",
|
||||
default = 12,
|
||||
min = 8
|
||||
},
|
||||
{
|
||||
label = "Hide Icons",
|
||||
description = "Do not show icons on the suggestions list.",
|
||||
path = "hide_icons",
|
||||
type = "toggle",
|
||||
default = false
|
||||
},
|
||||
{
|
||||
label = "Icons Position",
|
||||
description = "Position to display icons on the suggestions list.",
|
||||
path = "icon_position",
|
||||
type = "selection",
|
||||
default = "left",
|
||||
values = {
|
||||
{"Left", "left"},
|
||||
{"Right", "Right"}
|
||||
}
|
||||
},
|
||||
{
|
||||
label = "Hide Items Info",
|
||||
description = "Do not show the additional info related to each suggestion.",
|
||||
path = "hide_info",
|
||||
type = "toggle",
|
||||
default = false
|
||||
}
|
||||
}
|
||||
}, config.plugins.autocomplete)
|
||||
|
@ -133,7 +76,6 @@ local autocomplete = {}
|
|||
autocomplete.map = {}
|
||||
autocomplete.map_manually = {}
|
||||
autocomplete.on_close = nil
|
||||
autocomplete.icons = {}
|
||||
|
||||
-- Flag that indicates if the autocomplete box was manually triggered
|
||||
-- with the autocomplete.complete() function to prevent the suggestions
|
||||
|
@ -153,7 +95,6 @@ function autocomplete.add(t, manually_triggered)
|
|||
{
|
||||
text = text,
|
||||
info = info.info,
|
||||
icon = info.icon, -- Name of icon to show
|
||||
desc = info.desc, -- Description shown on item selected
|
||||
onhover = info.onhover, -- A callback called once when item is hovered
|
||||
onselect = info.onselect, -- A callback called when item is selected
|
||||
|
@ -178,35 +119,28 @@ end
|
|||
--
|
||||
-- Thread that scans open document symbols and cache them
|
||||
--
|
||||
local global_symbols = {}
|
||||
local max_symbols = config.plugins.autocomplete.max_symbols
|
||||
|
||||
core.add_thread(function()
|
||||
local function load_syntax_symbols(doc)
|
||||
if doc.syntax and not autocomplete.map["language_"..doc.syntax.name] then
|
||||
local symbols = {
|
||||
name = "language_"..doc.syntax.name,
|
||||
files = doc.syntax.files,
|
||||
items = {}
|
||||
}
|
||||
for name, type in pairs(doc.syntax.symbols) do
|
||||
symbols.items[name] = type
|
||||
local cache = setmetatable({}, { __mode = "k" })
|
||||
|
||||
local function get_syntax_symbols(symbols, doc)
|
||||
if doc.syntax then
|
||||
for sym in pairs(doc.syntax.symbols) do
|
||||
symbols[sym] = true
|
||||
end
|
||||
autocomplete.add(symbols)
|
||||
return symbols.items
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
local function get_symbols(doc)
|
||||
local s = {}
|
||||
local syntax_symbols = load_syntax_symbols(doc)
|
||||
local max_symbols = config.plugins.autocomplete.max_symbols
|
||||
get_syntax_symbols(s, doc)
|
||||
if doc.disable_symbols then return s end
|
||||
local i = 1
|
||||
local symbols_count = 0
|
||||
while i <= #doc.lines do
|
||||
for sym in doc.lines[i]:gmatch(config.symbol_pattern) do
|
||||
if not s[sym] and not syntax_symbols[sym] then
|
||||
if not s[sym] then
|
||||
symbols_count = symbols_count + 1
|
||||
if symbols_count > max_symbols then
|
||||
s = nil
|
||||
|
@ -252,18 +186,14 @@ core.add_thread(function()
|
|||
}
|
||||
end
|
||||
-- update symbol set with doc's symbol set
|
||||
if config.plugins.autocomplete.suggestions_scope == "global" then
|
||||
for sym in pairs(cache[doc].symbols) do
|
||||
symbols[sym] = true
|
||||
end
|
||||
for sym in pairs(cache[doc].symbols) do
|
||||
symbols[sym] = true
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
|
||||
-- update global symbols list
|
||||
if config.plugins.autocomplete.suggestions_scope == "global" then
|
||||
global_symbols = symbols
|
||||
end
|
||||
-- update symbols list
|
||||
autocomplete.add { name = "open-docs", items = symbols }
|
||||
|
||||
-- wait for next scan
|
||||
local valid = true
|
||||
|
@ -282,12 +212,14 @@ end)
|
|||
|
||||
|
||||
local partial = ""
|
||||
local suggestions_offset = 1
|
||||
local suggestions_idx = 1
|
||||
local suggestions = {}
|
||||
local last_line, last_col
|
||||
|
||||
|
||||
local function reset_suggestions()
|
||||
suggestions_offset = 1
|
||||
suggestions_idx = 1
|
||||
suggestions = {}
|
||||
|
||||
|
@ -310,50 +242,12 @@ local function update_suggestions()
|
|||
map = autocomplete.map_manually
|
||||
end
|
||||
|
||||
local assigned_sym = {}
|
||||
|
||||
-- get all relevant suggestions for given filename
|
||||
local items = {}
|
||||
for _, v in pairs(map) do
|
||||
if common.match_pattern(filename, v.files) then
|
||||
for _, item in pairs(v.items) do
|
||||
table.insert(items, item)
|
||||
assigned_sym[item.text] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Append the global, local or related text symbols if applicable
|
||||
local scope = config.plugins.autocomplete.suggestions_scope
|
||||
|
||||
if not triggered_manually then
|
||||
local text_symbols = nil
|
||||
|
||||
if scope == "global" then
|
||||
text_symbols = global_symbols
|
||||
elseif scope == "local" and cache[doc] and cache[doc].symbols then
|
||||
text_symbols = cache[doc].symbols
|
||||
elseif scope == "related" then
|
||||
for _, d in ipairs(core.docs) do
|
||||
if doc.syntax == d.syntax then
|
||||
if cache[d].symbols then
|
||||
for name in pairs(cache[d].symbols) do
|
||||
if not assigned_sym[name] then
|
||||
table.insert(items, setmetatable(
|
||||
{text = name, info = "normal"}, mt
|
||||
))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if text_symbols then
|
||||
for name in pairs(text_symbols) do
|
||||
if not assigned_sym[name] then
|
||||
table.insert(items, setmetatable({text = name, info = "normal"}, mt))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -369,6 +263,7 @@ local function update_suggestions()
|
|||
end
|
||||
end
|
||||
suggestions_idx = 1
|
||||
suggestions_offset = 1
|
||||
end
|
||||
|
||||
local function get_partial_symbol()
|
||||
|
@ -384,8 +279,10 @@ local function get_active_view()
|
|||
end
|
||||
end
|
||||
|
||||
local last_max_width = 0
|
||||
local function get_suggestions_rect(av)
|
||||
if #suggestions == 0 then
|
||||
last_max_width = 0
|
||||
return 0, 0, 0, 0
|
||||
end
|
||||
|
||||
|
@ -394,51 +291,49 @@ local function get_suggestions_rect(av)
|
|||
y = y + av:get_line_height() + style.padding.y
|
||||
local font = av:get_font()
|
||||
local th = font:get_height()
|
||||
local has_icons = false
|
||||
local hide_info = config.plugins.autocomplete.hide_info
|
||||
local hide_icons = config.plugins.autocomplete.hide_icons
|
||||
|
||||
local max_width = 0
|
||||
for _, s in ipairs(suggestions) do
|
||||
local w = font:get_width(s.text)
|
||||
if s.info and not hide_info then
|
||||
w = w + style.font:get_width(s.info) + style.padding.x
|
||||
end
|
||||
local icon = s.icon or s.info
|
||||
if not hide_icons and icon and autocomplete.icons[icon] then
|
||||
w = w + autocomplete.icons[icon].font:get_width(
|
||||
autocomplete.icons[icon].char
|
||||
) + (style.padding.x / 2)
|
||||
has_icons = true
|
||||
end
|
||||
max_width = math.max(max_width, w)
|
||||
end
|
||||
|
||||
local ah = config.plugins.autocomplete.max_height
|
||||
|
||||
local max_items = #suggestions
|
||||
if max_items > ah then
|
||||
max_items = ah
|
||||
local max_items = math.min(ah, #suggestions)
|
||||
|
||||
local show_count = math.min(#suggestions, ah)
|
||||
local start_index = math.max(suggestions_idx-(ah-1), 1)
|
||||
|
||||
local max_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
|
||||
w = w + style.font:get_width(s.info) + style.padding.x
|
||||
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
|
||||
|
||||
-- additional line to display total items
|
||||
max_items = max_items + 1
|
||||
|
||||
if max_width < 150 then
|
||||
max_width = 150
|
||||
if max_width > core.root_view.size.x then
|
||||
max_width = core.root_view.size.x
|
||||
end
|
||||
if max_width < 150 * SCALE then
|
||||
max_width = 150 * SCALE
|
||||
end
|
||||
|
||||
-- if portion not visiable to right, reposition to DocView right margin
|
||||
if (x - av.position.x) + max_width > av.size.x then
|
||||
x = (av.size.x + av.position.x) - max_width - (style.padding.x * 2)
|
||||
if x + max_width > core.root_view.size.x then
|
||||
x = (av.size.x + av.position.x) - max_width
|
||||
end
|
||||
|
||||
return
|
||||
x - style.padding.x,
|
||||
x,
|
||||
y - style.padding.y,
|
||||
max_width + style.padding.x * 2,
|
||||
max_items * (th + style.padding.y) + style.padding.y,
|
||||
has_icons
|
||||
max_width,
|
||||
max_items * (th + style.padding.y) + style.padding.y
|
||||
end
|
||||
|
||||
local function wrap_line(line, max_chars)
|
||||
|
@ -558,16 +453,15 @@ local function draw_suggestions_box(av)
|
|||
local ah = config.plugins.autocomplete.max_height
|
||||
|
||||
-- draw background rect
|
||||
local rx, ry, rw, rh, has_icons = get_suggestions_rect(av)
|
||||
local rx, ry, rw, rh = get_suggestions_rect(av)
|
||||
renderer.draw_rect(rx, ry, rw, rh, style.background3)
|
||||
|
||||
-- draw text
|
||||
local font = av:get_font()
|
||||
local lh = font:get_height() + style.padding.y
|
||||
local y = ry + style.padding.y / 2
|
||||
local show_count = #suggestions <= ah and #suggestions or ah
|
||||
local start_index = suggestions_idx > ah and (suggestions_idx-(ah-1)) or 1
|
||||
local hide_info = config.plugins.autocomplete.hide_info
|
||||
local show_count = math.min(#suggestions, ah)
|
||||
local start_index = suggestions_offset
|
||||
|
||||
for i=start_index, start_index+show_count-1, 1 do
|
||||
if not suggestions[i] then
|
||||
|
@ -575,44 +469,23 @@ local function draw_suggestions_box(av)
|
|||
end
|
||||
local s = suggestions[i]
|
||||
|
||||
local icon_l_padding, icon_r_padding = 0, 0
|
||||
|
||||
if has_icons then
|
||||
local icon = s.icon or s.info
|
||||
if icon and autocomplete.icons[icon] then
|
||||
local ifont = autocomplete.icons[icon].font
|
||||
local itext = autocomplete.icons[icon].char
|
||||
local icolor = autocomplete.icons[icon].color
|
||||
if i == suggestions_idx then
|
||||
icolor = style.accent
|
||||
elseif type(icolor) == "string" then
|
||||
icolor = style.syntax[icolor]
|
||||
end
|
||||
if config.plugins.autocomplete.icon_position == "left" then
|
||||
common.draw_text(
|
||||
ifont, icolor, itext, "left", rx + style.padding.x, y, rw, lh
|
||||
)
|
||||
icon_l_padding = ifont:get_width(itext) + (style.padding.x / 2)
|
||||
else
|
||||
common.draw_text(
|
||||
ifont, icolor, itext, "right", rx, y, rw - style.padding.x, lh
|
||||
)
|
||||
icon_r_padding = ifont:get_width(itext) + (style.padding.x / 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
local info_size = s.info and (style.font:get_width(s.info) + style.padding.x) or style.padding.x
|
||||
|
||||
local color = (i == suggestions_idx) and style.accent or style.text
|
||||
common.draw_text(
|
||||
font, color, s.text, "left",
|
||||
rx + icon_l_padding + style.padding.x, y, rw, lh
|
||||
)
|
||||
if s.info and not hide_info then
|
||||
-- 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.pop_clip_rect()
|
||||
-- If the text wasn't fully visible, draw an ellipsis
|
||||
if x_adv > rx + rw - info_size then
|
||||
local ellipsis_size = font:get_width("…")
|
||||
local ell_x = rx + rw - info_size - 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
|
||||
color = (i == suggestions_idx) and style.text or style.dim
|
||||
common.draw_text(
|
||||
style.font, color, s.info, "right",
|
||||
rx, y, rw - icon_r_padding - style.padding.x, lh
|
||||
)
|
||||
common.draw_text(style.font, color, s.info, "right", rx, y, rw - style.padding.x, lh)
|
||||
end
|
||||
y = y + lh
|
||||
if suggestions_idx == i then
|
||||
|
@ -773,31 +646,6 @@ function autocomplete.can_complete()
|
|||
return false
|
||||
end
|
||||
|
||||
---Register a font icon that can be assigned to completion items.
|
||||
---@param name string
|
||||
---@param character string
|
||||
---@param font? renderer.font
|
||||
---@param color? string | renderer.color A style.syntax[] name or specific color
|
||||
function autocomplete.add_icon(name, character, font, color)
|
||||
local color_type = type(color)
|
||||
assert(
|
||||
not color or color_type == "table"
|
||||
or (color_type == "string" and style.syntax[color]),
|
||||
"invalid icon color given"
|
||||
)
|
||||
autocomplete.icons[name] = {
|
||||
char = character,
|
||||
font = font or style.code_font,
|
||||
color = color or "keyword"
|
||||
}
|
||||
end
|
||||
|
||||
--
|
||||
-- Register built-in syntax symbol types icon
|
||||
--
|
||||
for name, _ in pairs(style.syntax) do
|
||||
autocomplete.add_icon(name, "M", style.icon_font, name)
|
||||
end
|
||||
|
||||
--
|
||||
-- Commands
|
||||
|
@ -811,6 +659,7 @@ command.add(predicate, {
|
|||
["autocomplete:complete"] = function(dv)
|
||||
local doc = dv.doc
|
||||
local item = suggestions[suggestions_idx]
|
||||
local text = item.text
|
||||
local inserted = false
|
||||
if item.onselect then
|
||||
inserted = item.onselect(suggestions_idx, item)
|
||||
|
@ -819,7 +668,7 @@ command.add(predicate, {
|
|||
local current_partial = get_partial_symbol()
|
||||
local sz = #current_partial
|
||||
|
||||
for idx, line1, col1, line2, col2 in doc:get_selections(true) do
|
||||
for _, line1, col1, line2, _ in doc:get_selections(true) do
|
||||
local n = col1 - 1
|
||||
local line = doc.lines[line1]
|
||||
for i = 1, sz + 1 do
|
||||
|
@ -840,10 +689,24 @@ command.add(predicate, {
|
|||
|
||||
["autocomplete:previous"] = function()
|
||||
suggestions_idx = (suggestions_idx - 2) % #suggestions + 1
|
||||
|
||||
local ah = math.min(config.plugins.autocomplete.max_height, #suggestions)
|
||||
if suggestions_offset > suggestions_idx then
|
||||
suggestions_offset = suggestions_idx
|
||||
elseif suggestions_offset + ah < suggestions_idx + 1 then
|
||||
suggestions_offset = suggestions_idx - ah + 1
|
||||
end
|
||||
end,
|
||||
|
||||
["autocomplete:next"] = function()
|
||||
suggestions_idx = (suggestions_idx % #suggestions) + 1
|
||||
|
||||
local ah = math.min(config.plugins.autocomplete.max_height, #suggestions)
|
||||
if suggestions_offset + ah < suggestions_idx + 1 then
|
||||
suggestions_offset = suggestions_idx - ah + 1
|
||||
elseif suggestions_offset > suggestions_idx then
|
||||
suggestions_offset = suggestions_idx
|
||||
end
|
||||
end,
|
||||
|
||||
["autocomplete:cycle"] = function()
|
||||
|
|
|
@ -69,7 +69,7 @@ function dirwatch:check(change_callback, ...)
|
|||
for _, doc in ipairs(core.docs) do
|
||||
if doc.abs_filename and (dir == common.dirname(doc.abs_filename) or dir == doc.abs_filename) then
|
||||
local info = system.get_file_info(doc.filename or "")
|
||||
if info and times[doc] ~= info.modified then
|
||||
if info and info.type == "file" and times[doc] ~= info.modified then
|
||||
if not doc:is_dirty() and not config.plugins.autoreload.always_show_nagview then
|
||||
reload_doc(doc)
|
||||
else
|
||||
|
|
|
@ -37,11 +37,7 @@ local function optimal_indent_from_stat(stat)
|
|||
elseif
|
||||
indent > stat[y]
|
||||
and
|
||||
(
|
||||
indent_occurrences_more_than_once(stat, y)
|
||||
or
|
||||
(y == count and stat[y] > 1)
|
||||
)
|
||||
indent_occurrences_more_than_once(stat, y)
|
||||
then
|
||||
score = 0
|
||||
break
|
||||
|
@ -122,10 +118,10 @@ local function get_comment_patterns(syntax, _loop)
|
|||
end
|
||||
if type(pattern.regex) == "table" then
|
||||
table.insert(comments, {
|
||||
"r", regex.compile(startp), regex.compile(pattern.regex[2]), r=startp
|
||||
"r", regex.compile(startp), regex.compile(pattern.regex[2])
|
||||
})
|
||||
elseif not_is_string then
|
||||
table.insert(comments, {"r", regex.compile(startp), r=startp})
|
||||
table.insert(comments, {"r", regex.compile(startp)})
|
||||
end
|
||||
end
|
||||
elseif pattern.syntax then
|
||||
|
@ -156,25 +152,6 @@ local function get_comment_patterns(syntax, _loop)
|
|||
table.insert(comments, {"p", "^%s*" .. block_comment[1], block_comment[2]})
|
||||
end
|
||||
end
|
||||
-- Put comments first and strings last
|
||||
table.sort(comments, function(c1, c2)
|
||||
local comment1, comment2 = false, false
|
||||
if
|
||||
(c1[1] == "p" and string.find(c1[2], "^%s*", 1, true))
|
||||
or
|
||||
(c1[1] == "r" and string.find(c1["r"], "^\\s*", 1, true))
|
||||
then
|
||||
comment1 = true
|
||||
end
|
||||
if
|
||||
(c2[1] == "p" and string.find(c2[2], "^%s*", 1, true))
|
||||
or
|
||||
(c2[1] == "r" and string.find(c2["r"], "^\\s*", 1, true))
|
||||
then
|
||||
comment2 = true
|
||||
end
|
||||
return comment1 and not comment2
|
||||
end)
|
||||
comments_cache[syntax] = comments
|
||||
if #comments > 0 then
|
||||
return comments
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
-- mod-version:3
|
||||
|
||||
local core = require "core"
|
||||
local style = require "core.style"
|
||||
local DocView = require "core.docview"
|
||||
local common = require "core.common"
|
||||
|
@ -13,7 +12,6 @@ config.plugins.drawwhitespace = common.merge({
|
|||
show_leading = true,
|
||||
show_trailing = true,
|
||||
show_middle = true,
|
||||
show_selected_only = false,
|
||||
|
||||
show_middle_min = 1,
|
||||
|
||||
|
@ -67,13 +65,6 @@ config.plugins.drawwhitespace = common.merge({
|
|||
type = "toggle",
|
||||
default = true,
|
||||
},
|
||||
{
|
||||
label = "Show Selected Only",
|
||||
description = "Only draw whitespaces if it is within a selection.",
|
||||
path = "show_selected_only",
|
||||
type = "toggle",
|
||||
default = false,
|
||||
},
|
||||
{
|
||||
label = "Show Trailing as Error",
|
||||
description = "Uses an error square to spot them easily, requires 'Show Trailing' enabled.",
|
||||
|
@ -302,41 +293,11 @@ function DocView:draw_line_text(idx, x, y)
|
|||
for i=1,#cache,4 do
|
||||
local tx = cache[i + 1] + x
|
||||
local tw = cache[i + 2]
|
||||
local sub = cache[i]
|
||||
local color = cache[i + 3]
|
||||
local partials = {}
|
||||
if config.plugins.drawwhitespace.show_selected_only and self.doc:has_any_selection() then
|
||||
for _, l1, c1, l2, c2 in self.doc:get_selections(true) do
|
||||
if idx > l1 and idx < l2 then
|
||||
-- Between selection lines, so everything is selected
|
||||
table.insert(partials, false)
|
||||
elseif idx == l1 and idx == l2 then
|
||||
-- Both ends of the selection are on the same line
|
||||
local _x1 = math.max(cache[i + 1], self:get_col_x_offset(idx, c1))
|
||||
local _x2 = math.min((cache[i + 1] + tw), self:get_col_x_offset(idx, c2))
|
||||
if _x1 < _x2 then
|
||||
table.insert(partials, {_x1 + x, 0, _x2 - _x1, math.huge})
|
||||
end
|
||||
elseif idx >= l1 and idx <= l2 then
|
||||
-- On one of the selection ends
|
||||
if idx == l1 then -- Start of the selection
|
||||
local _x = math.max(cache[i + 1], self:get_col_x_offset(idx, c1))
|
||||
table.insert(partials, {_x + x, 0, math.huge, math.huge})
|
||||
else -- End of the selection
|
||||
local _x = math.min((cache[i + 1] + tw), self:get_col_x_offset(idx, c2))
|
||||
table.insert(partials, {0, 0, _x + x, math.huge})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #partials == 0 and not config.plugins.drawwhitespace.show_selected_only then
|
||||
renderer.draw_text(font, sub, tx, ty, color)
|
||||
else
|
||||
for _, p in pairs(partials) do
|
||||
if p then core.push_clip_rect(table.unpack(p)) end
|
||||
renderer.draw_text(font, sub, tx, ty, color)
|
||||
if p then core.pop_clip_rect() end
|
||||
if tx <= 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)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,8 @@ syntax.add {
|
|||
name = "C++",
|
||||
files = {
|
||||
"%.h$", "%.inl$", "%.cpp$", "%.cc$", "%.C$", "%.cxx$",
|
||||
"%.c++$", "%.hh$", "%.H$", "%.hxx$", "%.hpp$", "%.h++$"
|
||||
"%.c++$", "%.hh$", "%.H$", "%.hxx$", "%.hpp$", "%.h++$",
|
||||
"%.ino$"
|
||||
},
|
||||
comment = "//",
|
||||
block_comment = { "/*", "*/" },
|
||||
|
|
|
@ -20,10 +20,10 @@ local syntax = require "core.syntax"
|
|||
-- followed by pattern options, and anything that can
|
||||
-- be after a pattern.
|
||||
--
|
||||
-- Demo with some unit tests (click on the Unit Tests entry): https://regex101.com/r/R0w8Qw/1
|
||||
-- Demo with some unit tests (click on the Unit Tests entry): https://regex101.com/r/Vx5L5V/1
|
||||
-- Note that it has a couple of changes to make it work on that platform.
|
||||
local regex_pattern = {
|
||||
[=[/(?=(?!/)(?:(?>[^\\[\/]++|\\.|\[(?:[^\\\]]++|\\.)*+\])*+)++/[gmiyuvsd]*\s*[\n,;\)\]\}\.])()]=],
|
||||
[=[\/(?=(?!\/)(?:(?>[^\\[\/]++|\\.|\[(?:[^\\\]]++|\\.)*+\])*+)++\/[gmiyuvsd]*\s*(?:[\n,;\)\]\}\.]|\/[\/*]))()]=],
|
||||
"/()[gmiyuvsd]*", "\\"
|
||||
}
|
||||
|
||||
|
@ -57,18 +57,19 @@ syntax.add {
|
|||
comment = "//",
|
||||
block_comment = { "/*", "*/" },
|
||||
patterns = {
|
||||
{ pattern = "//.*", type = "comment" },
|
||||
{ pattern = { "/%*", "%*/" }, type = "comment" },
|
||||
{ regex = regex_pattern, syntax = inner_regex_syntax, type = {"string", "string"} },
|
||||
{ pattern = { '"', '"', '\\' }, type = "string" },
|
||||
{ pattern = { "'", "'", '\\' }, type = "string" },
|
||||
{ pattern = { "`", "`", '\\' }, type = "string" },
|
||||
{ pattern = "0x[%da-fA-F_]+n?()%s*()/?", type = {"number", "normal", "operator"} },
|
||||
{ pattern = "-?%d+[%d%.eE_n]*()%s*()/?", type = {"number", "normal", "operator"} },
|
||||
{ pattern = "-?%.?%d+()%s*()/?", type = {"number", "normal", "operator"} },
|
||||
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
|
||||
{ pattern = "[%a_][%w_]*%f[(]", type = "function" },
|
||||
{ pattern = "[%a_][%w_]*()%s*()/?", type = {"symbol", "normal", "operator"} },
|
||||
{ pattern = "//.*", type = "comment" },
|
||||
{ pattern = { "/%*", "%*/" }, type = "comment" },
|
||||
{ regex = regex_pattern, syntax = inner_regex_syntax, type = {"string", "string"} },
|
||||
{ pattern = { '"', '"', '\\' }, type = "string" },
|
||||
{ pattern = { "'", "'", '\\' }, type = "string" },
|
||||
{ pattern = { "`", "`", '\\' }, type = "string" },
|
||||
-- Use (?:\/(?!\/|\*))? to avoid that a regex can start after a number, while also allowing // and /* comments
|
||||
{ regex = [[-?0[xXbBoO][\da-fA-F_]+n?()\s*()(?:\/(?!\/|\*))?]], type = {"number", "normal", "operator"} },
|
||||
{ regex = [[-?\d+[0-9.eE_n]*()\s*()(?:\/(?!\/|\*))?]], type = {"number", "normal", "operator"} },
|
||||
{ regex = [[-?\.?\d+()\s*()(?:\/(?!\/|\*))?]], type = {"number", "normal", "operator"} },
|
||||
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
|
||||
{ pattern = "[%a_][%w_]*%f[(]", type = "function" },
|
||||
{ pattern = "[%a_][%w_]*", type = "symbol" },
|
||||
},
|
||||
symbols = {
|
||||
["async"] = "keyword",
|
||||
|
@ -92,6 +93,7 @@ syntax.add {
|
|||
["get"] = "keyword",
|
||||
["if"] = "keyword",
|
||||
["import"] = "keyword",
|
||||
["from"] = "keyword",
|
||||
["in"] = "keyword",
|
||||
["of"] = "keyword",
|
||||
["instanceof"] = "keyword",
|
||||
|
|
|
@ -128,7 +128,6 @@ syntax.add {
|
|||
{ pattern = { "```go", "```" }, type = "string", syntax = ".go" },
|
||||
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
|
||||
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
|
||||
{ pattern = { "```nix", "```" }, type = "string", syntax = ".nix" },
|
||||
{ pattern = { "```", "```" }, type = "string" },
|
||||
{ pattern = { "``", "``" }, type = "string" },
|
||||
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" },
|
||||
|
@ -267,4 +266,3 @@ core.add_thread(function()
|
|||
coroutine.yield(1)
|
||||
end
|
||||
end)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ local syntax = require "core.syntax"
|
|||
|
||||
syntax.add {
|
||||
name = "Python",
|
||||
files = { "%.py$", "%.pyw$", "%.rpy$" },
|
||||
files = { "%.py$", "%.pyw$", "%.rpy$", "%.pyi$" },
|
||||
headers = "^#!.*[ /]python",
|
||||
comment = "#",
|
||||
block_comment = { '"""', '"""' },
|
||||
|
@ -16,8 +16,8 @@ syntax.add {
|
|||
{ pattern = { "[ruU]?'''", "'''", '\\' }, type = "string" },
|
||||
{ pattern = { '[ruU]?"', '"', '\\' }, type = "string" },
|
||||
{ pattern = { "[ruU]?'", "'", '\\' }, type = "string" },
|
||||
{ pattern = "0x[%da-fA-F]+", type = "number" },
|
||||
{ pattern = "-?%d+[%d%.eE]*", type = "number" },
|
||||
{ pattern = "-?0[xboXBO][%da-fA-F_]+",type = "number" },
|
||||
{ pattern = "-?%d+[%d%.eE_]*", type = "number" },
|
||||
{ pattern = "-?%.?%d+", type = "number" },
|
||||
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
|
||||
{ pattern = "[%a_][%w_]*%f[(]", type = "function" },
|
||||
|
|
|
@ -15,8 +15,6 @@ config.plugins.lineguide = common.merge({
|
|||
-- 120,
|
||||
config.line_limit
|
||||
},
|
||||
use_custom_color = false,
|
||||
custom_color = style.selection,
|
||||
-- The config specification used by gui generators
|
||||
config_spec = {
|
||||
name = "Line Guide",
|
||||
|
@ -65,21 +63,7 @@ config.plugins.lineguide = common.merge({
|
|||
end
|
||||
return new_rulers
|
||||
end
|
||||
},
|
||||
{
|
||||
label = "Use Custom Color",
|
||||
description = "Enable the utilization of a custom line color.",
|
||||
path = "use_custom_color",
|
||||
type = "toggle",
|
||||
default = false
|
||||
},
|
||||
{
|
||||
label = "Custom Color",
|
||||
description = "Applied when the above toggle is enabled.",
|
||||
path = "custom_color",
|
||||
type = "color",
|
||||
default = style.selection
|
||||
},
|
||||
}
|
||||
}
|
||||
}, config.plugins.lineguide)
|
||||
|
||||
|
@ -102,12 +86,10 @@ function DocView:draw_overlay(...)
|
|||
and
|
||||
self:is(DocView)
|
||||
then
|
||||
local conf = config.plugins.lineguide
|
||||
local line_x = self:get_line_screen_position(1)
|
||||
local character_width = self:get_font():get_width("n")
|
||||
local ruler_width = config.plugins.lineguide.width
|
||||
local ruler_color = conf.use_custom_color and conf.custom_color
|
||||
or (style.guide or style.selection)
|
||||
local ruler_color = style.guide or style.selection
|
||||
|
||||
for k,v in ipairs(config.plugins.lineguide.rulers) do
|
||||
local ruler = get_ruler(v)
|
||||
|
|
|
@ -37,7 +37,7 @@ local function find_all_matches_in_file(t, filename, fn)
|
|||
table.insert(t, { file = filename, text = (start_index > 1 and "..." or "") .. line:sub(start_index, 256 + start_index), line = n, col = s })
|
||||
core.redraw = true
|
||||
end
|
||||
if n % 100 == 0 then coroutine.yield(0) end
|
||||
if n % 100 == 0 then coroutine.yield() end
|
||||
n = n + 1
|
||||
core.redraw = true
|
||||
end
|
||||
|
|
|
@ -48,7 +48,7 @@ end
|
|||
|
||||
function ToolbarView:get_icon_width()
|
||||
local max_width = 0
|
||||
for i,v in ipairs(self.toolbar_commands) do max_width = math.max(max_width, (v.font or self.toolbar_font):get_width(v.symbol)) end
|
||||
for i,v in ipairs(self.toolbar_commands) do max_width = math.max(max_width, self.toolbar_font:get_width(v.symbol)) end
|
||||
return max_width
|
||||
end
|
||||
|
||||
|
@ -83,7 +83,7 @@ function ToolbarView:draw()
|
|||
|
||||
for item, x, y, w, h in self:each_item() do
|
||||
local color = item == self.hovered_item and command.is_valid(item.command) and style.text or style.dim
|
||||
common.draw_text(item.font or self.toolbar_font, color, item.symbol, nil, x, y, 0, h)
|
||||
common.draw_text(self.toolbar_font, color, item.symbol, nil, x, y, 0, h)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -100,16 +100,6 @@ function ToolbarView:on_mouse_pressed(button, x, y, clicks)
|
|||
end
|
||||
|
||||
|
||||
function ToolbarView:on_mouse_left()
|
||||
ToolbarView.super.on_mouse_left(self)
|
||||
if self.tooltip then
|
||||
core.status_view:remove_tooltip()
|
||||
self.tooltip = false
|
||||
end
|
||||
self.hovered_item = nil
|
||||
end
|
||||
|
||||
|
||||
function ToolbarView:on_mouse_moved(px, py, ...)
|
||||
if not self.visible then return end
|
||||
ToolbarView.super.on_mouse_moved(self, px, py, ...)
|
||||
|
|
|
@ -9,15 +9,10 @@ local View = require "core.view"
|
|||
local ContextMenu = require "core.contextmenu"
|
||||
local RootView = require "core.rootview"
|
||||
local CommandView = require "core.commandview"
|
||||
local DocView = require "core.docview"
|
||||
|
||||
config.plugins.treeview = common.merge({
|
||||
-- Default treeview width
|
||||
size = 200 * SCALE,
|
||||
highlight_focused_file = true,
|
||||
expand_dirs_to_focused_file = false,
|
||||
scroll_to_focused_file = false,
|
||||
animate_scroll_to_focused_file = true
|
||||
size = 200 * SCALE
|
||||
}, config.plugins.treeview)
|
||||
|
||||
local tooltip_offset = style.font:get_height()
|
||||
|
@ -51,7 +46,7 @@ function TreeView:new()
|
|||
self.target_size = config.plugins.treeview.size
|
||||
self.cache = {}
|
||||
self.tooltip = { x = 0, y = 0, begin = 0, alpha = 0 }
|
||||
self.last_scroll_y = 0
|
||||
self.cursor_pos = { x = 0, y = 0 }
|
||||
|
||||
self.item_icon_width = 0
|
||||
self.item_text_spacing = 0
|
||||
|
@ -88,7 +83,7 @@ function TreeView:get_cached(dir, item, dirname)
|
|||
else
|
||||
t.filename = item.filename
|
||||
t.depth = get_depth(item.filename)
|
||||
t.abs_filename = dirname .. PATHSEP .. item.filename
|
||||
t.abs_filename = common.basepath(dirname) .. item.filename
|
||||
end
|
||||
t.name = basename
|
||||
t.type = item.type
|
||||
|
@ -174,73 +169,20 @@ function TreeView:each_item()
|
|||
end
|
||||
|
||||
|
||||
function TreeView:set_selection(selection, selection_y, center, instant)
|
||||
function TreeView:set_selection(selection, selection_y)
|
||||
self.selected_item = selection
|
||||
if selection and selection_y
|
||||
and (selection_y <= 0 or selection_y >= self.size.y) then
|
||||
|
||||
local lh = self:get_item_height()
|
||||
if not center and selection_y >= self.size.y - lh then
|
||||
if selection_y >= self.size.y - lh then
|
||||
selection_y = selection_y - self.size.y + lh
|
||||
end
|
||||
if center then
|
||||
selection_y = selection_y - (self.size.y - lh) / 2
|
||||
end
|
||||
local _, y = self:get_content_offset()
|
||||
self.scroll.to.y = selection_y - y
|
||||
self.scroll.to.y = common.clamp(self.scroll.to.y, 0, self:get_scrollable_size() - self.size.y)
|
||||
if instant then
|
||||
self.scroll.y = self.scroll.to.y
|
||||
end
|
||||
self.scroll.to.y = selection and (selection_y - y)
|
||||
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")
|
||||
|
@ -251,9 +193,10 @@ function TreeView:get_text_bounding_box(item, x, y, w, h)
|
|||
end
|
||||
|
||||
|
||||
|
||||
function TreeView:on_mouse_moved(px, py, ...)
|
||||
if not self.visible then return end
|
||||
self.cursor_pos.x = px
|
||||
self.cursor_pos.y = py
|
||||
if TreeView.super.on_mouse_moved(self, px, py, ...) then
|
||||
-- mouse movement handled by the View (scrollbar)
|
||||
self.hovered_item = nil
|
||||
|
@ -280,12 +223,6 @@ function TreeView:on_mouse_moved(px, py, ...)
|
|||
end
|
||||
|
||||
|
||||
function TreeView:on_mouse_left()
|
||||
TreeView.super.on_mouse_left(self)
|
||||
self.hovered_item = nil
|
||||
end
|
||||
|
||||
|
||||
function TreeView:update()
|
||||
-- update width
|
||||
local dest = self.visible and self.target_size or 0
|
||||
|
@ -296,7 +233,7 @@ function TreeView:update()
|
|||
self:move_towards(self.size, "x", dest, nil, "treeview")
|
||||
end
|
||||
|
||||
if self.size.x == 0 or self.size.y == 0 or not self.visible then return end
|
||||
if not self.visible then return end
|
||||
|
||||
local duration = system.get_time() - self.tooltip.begin
|
||||
if self.hovered_item and self.tooltip.x and duration > tooltip_delay then
|
||||
|
@ -309,30 +246,10 @@ function TreeView:update()
|
|||
self.item_text_spacing = style.icon_font:get_width("f") / 2
|
||||
|
||||
-- this will make sure hovered_item is updated
|
||||
local dy = math.abs(self.last_scroll_y - self.scroll.y)
|
||||
if dy > 0 then
|
||||
self:on_mouse_moved(core.root_view.mouse.x, core.root_view.mouse.y, 0, 0)
|
||||
self.last_scroll_y = self.scroll.y
|
||||
end
|
||||
|
||||
local config = config.plugins.treeview
|
||||
if config.highlight_focused_file then
|
||||
-- Try to only highlight when we actually change tabs
|
||||
local current_node = core.root_view:get_active_node()
|
||||
local current_active_view = core.active_view
|
||||
if current_node and not current_node.locked
|
||||
and current_active_view ~= self and current_active_view ~= self.last_active_view then
|
||||
self.selected_item = nil
|
||||
self.last_active_view = current_active_view
|
||||
if DocView:is_extended_by(current_active_view) then
|
||||
local abs_filename = current_active_view.doc
|
||||
and current_active_view.doc.abs_filename or ""
|
||||
self:set_selection_to_path(abs_filename,
|
||||
config.expand_dirs_to_focused_file,
|
||||
config.scroll_to_focused_file,
|
||||
not config.animate_scroll_to_focused_file)
|
||||
end
|
||||
end
|
||||
-- we don't want events when the thing is scrolling fast
|
||||
local dy = math.abs(self.scroll.to.y - self.scroll.y)
|
||||
if self.scroll.to.y ~= 0 and dy < self:get_item_height() then
|
||||
self:on_mouse_moved(self.cursor_pos.x, self.cursor_pos.y, 0, 0)
|
||||
end
|
||||
|
||||
TreeView.super.update(self)
|
||||
|
@ -505,8 +422,8 @@ function TreeView:get_previous(item)
|
|||
end
|
||||
|
||||
|
||||
function TreeView:toggle_expand(toggle, item)
|
||||
item = item or self.selected_item
|
||||
function TreeView:toggle_expand(toggle)
|
||||
local item = self.selected_item
|
||||
|
||||
if not item then return end
|
||||
|
||||
|
@ -524,11 +441,6 @@ function TreeView:toggle_expand(toggle, item)
|
|||
end
|
||||
|
||||
|
||||
function TreeView:open_doc(filename)
|
||||
core.root_view:open_doc(core.open_doc(filename))
|
||||
end
|
||||
|
||||
|
||||
-- init
|
||||
local view = TreeView()
|
||||
local node = core.root_view:get_active_node()
|
||||
|
@ -707,7 +619,8 @@ command.add(
|
|||
if core.last_active_view and core.active_view == view then
|
||||
core.set_active_view(core.last_active_view)
|
||||
end
|
||||
view:open_doc(core.normalize_to_project_dir(item.abs_filename))
|
||||
local doc_filename = core.normalize_to_project_dir(item.abs_filename)
|
||||
core.root_view:open_doc(core.open_doc(doc_filename))
|
||||
end)
|
||||
end
|
||||
end,
|
||||
|
@ -753,26 +666,6 @@ command.add(
|
|||
view:toggle_expand(true)
|
||||
end
|
||||
end,
|
||||
|
||||
["treeview-context:show"] = function()
|
||||
if view.hovered_item then
|
||||
menu:show(core.root_view.mouse.x, core.root_view.mouse.y)
|
||||
return
|
||||
end
|
||||
|
||||
local item = view.selected_item
|
||||
if not item then return end
|
||||
|
||||
local x, y
|
||||
for _i, _x, _y, _w, _h in view:each_item() do
|
||||
if _i == item then
|
||||
x = _x + _w / 2
|
||||
y = _y + _h / 2
|
||||
break
|
||||
end
|
||||
end
|
||||
menu:show(x, y)
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
|
@ -786,7 +679,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.basename(item.dir_name) .. PATHSEP .. relfilename
|
||||
relfilename = common.basepath(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"
|
||||
|
@ -831,7 +724,7 @@ command.add(
|
|||
submit = function(filename)
|
||||
local abs_filename = filename
|
||||
if not common.is_absolute_path(filename) then
|
||||
abs_filename = item.dir_name .. PATHSEP .. filename
|
||||
abs_filename = common.basepath(item.dir_name) .. filename
|
||||
end
|
||||
local res, err = os.rename(old_abs_filename, abs_filename)
|
||||
if res then -- successfully renamed
|
||||
|
@ -861,12 +754,12 @@ command.add(
|
|||
core.command_view:enter("Filename", {
|
||||
text = text,
|
||||
submit = function(filename)
|
||||
local doc_filename = item.dir_name .. PATHSEP .. filename
|
||||
local doc_filename = common.basepath(item.dir_name) .. filename
|
||||
core.log(doc_filename)
|
||||
local file = io.open(doc_filename, "a+")
|
||||
file:write("")
|
||||
file:close()
|
||||
view:open_doc(doc_filename)
|
||||
core.root_view:open_doc(core.open_doc(doc_filename))
|
||||
core.log("Created %s", doc_filename)
|
||||
end,
|
||||
suggest = function(text)
|
||||
|
@ -883,7 +776,7 @@ command.add(
|
|||
core.command_view:enter("Folder Name", {
|
||||
text = text,
|
||||
submit = function(filename)
|
||||
local dir_path = item.dir_name .. PATHSEP .. filename
|
||||
local dir_path = common.basepath(item.dir_name) .. filename
|
||||
common.mkdirp(dir_path)
|
||||
core.log("Created %s", dir_path)
|
||||
end,
|
||||
|
@ -935,25 +828,6 @@ command.add(function()
|
|||
})
|
||||
|
||||
|
||||
command.add(
|
||||
function()
|
||||
return menu.show_context_menu == true and core.active_view:is(TreeView)
|
||||
end, {
|
||||
["treeview-context:focus-previous"] = function()
|
||||
menu:focus_previous()
|
||||
end,
|
||||
["treeview-context:focus-next"] = function()
|
||||
menu:focus_next()
|
||||
end,
|
||||
["treeview-context:hide"] = function()
|
||||
menu:hide()
|
||||
end,
|
||||
["treeview-context:on-selected"] = function()
|
||||
menu:call_selected_item()
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
keymap.add {
|
||||
["ctrl+\\"] = "treeview:toggle",
|
||||
["up"] = "treeview:previous",
|
||||
|
@ -969,15 +843,6 @@ keymap.add {
|
|||
["ctrl+lclick"] = "treeview:new-folder"
|
||||
}
|
||||
|
||||
keymap.add {
|
||||
["menu"] = "treeview-context:show",
|
||||
["return"] = "treeview-context:on-selected",
|
||||
["up"] = "treeview-context:focus-previous",
|
||||
["down"] = "treeview-context:focus-next",
|
||||
["escape"] = "treeview-context:hide"
|
||||
}
|
||||
|
||||
|
||||
-- The config specification used by gui generators
|
||||
config.plugins.treeview.config_spec = {
|
||||
name = "Treeview",
|
||||
|
|
|
@ -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 = USERDIR .. PATHSEP .. "ws"
|
||||
local workspace_dir = common.basepath(USERDIR) .. "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(workspace_dir .. PATHSEP .. file, id)
|
||||
coroutine.yield(common.basepath(workspace_dir) .. 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 USERDIR .. PATHSEP .. "ws" .. PATHSEP .. basename .. "-" .. tostring(id)
|
||||
return common.basepath(USERDIR) .. "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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
project('lite-xl',
|
||||
['c'],
|
||||
version : '2.1.1',
|
||||
version : '2.1.4',
|
||||
license : 'MIT',
|
||||
meson_version : '>= 0.56',
|
||||
default_options : [
|
||||
|
@ -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=disabled', 'interpreter=false']
|
||||
default_options: default_fallback_options + ['default_library=static', 'line_editing=false', '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/com.lite_xl.LiteXL.desktop',
|
||||
install_data('resources/linux/org.lite_xl.lite_xl.desktop',
|
||||
install_dir : 'share/applications'
|
||||
)
|
||||
install_data('resources/linux/com.lite_xl.LiteXL.appdata.xml',
|
||||
install_data('resources/linux/org.lite_xl.lite_xl.appdata.xml',
|
||||
install_dir : 'share/metainfo'
|
||||
)
|
||||
endif
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
; 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
|
|
@ -1,8 +1,25 @@
|
|||
-- mod-version:3
|
||||
local core = require "core"
|
||||
local command = require "core.command"
|
||||
local common = require "core.common"
|
||||
local config = require "core.config"
|
||||
local keymap = require "core.keymap"
|
||||
|
||||
config.plugins.ghmarkdown = common.merge({
|
||||
-- Find information on how to generate your own token at
|
||||
-- https://docs.github.com/en/rest/markdown/markdown?apiVersion=2022-11-28#render-a-markdown-document-in-raw-mode
|
||||
github_token = "",
|
||||
config_spec = {
|
||||
name = "GHMarkdown",
|
||||
{
|
||||
label = "GitHub token",
|
||||
description = "Enter your personal GitHub token",
|
||||
path = "github_token",
|
||||
type = "string",
|
||||
default = ""
|
||||
}
|
||||
}
|
||||
}, config.plugins.ghmarkdown)
|
||||
|
||||
local html = [[
|
||||
<html>
|
||||
|
@ -31,7 +48,9 @@ local html = [[
|
|||
<script>
|
||||
var xhr = new XMLHttpRequest;
|
||||
xhr.open("POST", "https://api.github.com/markdown/raw");
|
||||
xhr.setRequestHeader("Content-Type", "text/plain");
|
||||
xhr.setRequestHeader("content-type", "text/plain");
|
||||
xhr.setRequestHeader("authorization", "Bearer ${token}");
|
||||
xhr.setRequestHeader("x-github-api-version", "2022-11-28");
|
||||
xhr.onload = function() { document.body.innerHTML = xhr.responseText; };
|
||||
xhr.send("${content}");
|
||||
</script>
|
||||
|
@ -42,11 +61,17 @@ local html = [[
|
|||
|
||||
command.add("core.docview!", {
|
||||
["ghmarkdown:show-preview"] = function(dv)
|
||||
if config.plugins.ghmarkdown.github_token == "" then
|
||||
core.error "You need to provide your own GitHub token"
|
||||
return
|
||||
end
|
||||
|
||||
local content = dv.doc:get_text(1, 1, math.huge, math.huge)
|
||||
local esc = { ['"'] = '\\"', ["\n"] = '\\n' }
|
||||
local text = html:gsub("${(.-)}", {
|
||||
title = dv:get_name(),
|
||||
content = content:gsub(".", esc)
|
||||
content = content:gsub(".", esc),
|
||||
token = config.plugins.ghmarkdown.github_token
|
||||
})
|
||||
|
||||
local htmlfile = core.temp_filename(".html")
|
||||
|
|
|
@ -0,0 +1,414 @@
|
|||
-- mod-version:3
|
||||
|
||||
local core = require "core"
|
||||
local config = require "core.config"
|
||||
local style = require "core.style"
|
||||
local common = require "core.common"
|
||||
local View = require "core.view"
|
||||
local command = require "core.command"
|
||||
local keymap = require "core.keymap"
|
||||
|
||||
local TetrisView = View:extend()
|
||||
|
||||
config.plugins.tetris = common.merge({
|
||||
tick = 0.5, -- The amount of time in seconds it takes for a piece to fall one line at 0 score.
|
||||
height = 30, -- The amount of cells of height.
|
||||
width = 10, -- The amount of cells of width.
|
||||
cell_size = 18, -- The size in pixels of each cell.
|
||||
cell_padding = 2, -- pixels between each cell
|
||||
drop_shadow = true, -- Should cast a drop shadow.
|
||||
lock_delay = 3, -- the multiplier for lock delay over a normal tick. set to 0 to disable
|
||||
down_amount = 1 -- the amount we move a tetronimo down when you hit the down key; change to math.huge for instant.
|
||||
}, config.plugins.tetris)
|
||||
|
||||
function TetrisView:new(options)
|
||||
TetrisView.super.new(self)
|
||||
self.cell_size = options.cell_size
|
||||
self.cell_padding = options.cell_padding
|
||||
self.grid = { x = options.width, y = options.height }
|
||||
self.size.x = self.grid.x * (self.cell_size + self.cell_padding) + style.padding.x * 2
|
||||
self.cells = { }
|
||||
self.score = 0
|
||||
self.paused = false
|
||||
self.initial_tick = options.tick
|
||||
self.lock_delay = options.lock_delay
|
||||
self.drop_shadow = options.drop_shadow
|
||||
self.tick = self:calculate_tick(self.score)
|
||||
self.finished = false
|
||||
self.thread = core.add_thread(function()
|
||||
while not self.finished do
|
||||
self:step()
|
||||
core.redraw = true
|
||||
coroutine.yield(self.tick)
|
||||
end
|
||||
end)
|
||||
-- easier to specify rotations than to start doing matrix multiplication
|
||||
self.tetronimos = {
|
||||
{
|
||||
color = { common.color "#ff0000" },
|
||||
shape = { {
|
||||
0,1,1,0,
|
||||
0,1,1,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0
|
||||
} }
|
||||
},
|
||||
{
|
||||
color = { common.color "#00ff00" },
|
||||
shape = { {
|
||||
1,1,0,0,
|
||||
1,0,0,0,
|
||||
1,0,0,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
1,1,1,0,
|
||||
0,0,1,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
0,0,1,0,
|
||||
0,0,1,0,
|
||||
0,1,1,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
0,0,0,0,
|
||||
1,0,0,0,
|
||||
1,1,1,0,
|
||||
0,0,0,0
|
||||
} }
|
||||
},
|
||||
{
|
||||
color = { common.color "#0000ff" },
|
||||
shape = { {
|
||||
1,0,0,0,
|
||||
1,0,0,0,
|
||||
1,1,0,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
1,1,1,0,
|
||||
1,0,0,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
0,1,1,0,
|
||||
0,0,1,0,
|
||||
0,0,1,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
0,0,0,0,
|
||||
0,0,1,0,
|
||||
1,1,1,0,
|
||||
0,0,0,0
|
||||
} }
|
||||
},
|
||||
{
|
||||
color = { common.color "#00ffff" },
|
||||
shape = { {
|
||||
1,1,0,0,
|
||||
0,1,1,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
0,0,1,0,
|
||||
0,1,1,0,
|
||||
0,1,0,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
0, 0,0,0,
|
||||
1,1,0,0,
|
||||
0,1,1,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
0,1,0,0,
|
||||
1,1,0,0,
|
||||
1,0,0,0,
|
||||
0,0,0,0
|
||||
} }
|
||||
},
|
||||
{
|
||||
color = { common.color "#ffff00" },
|
||||
shape = { {
|
||||
0,1,1,0,
|
||||
1,1,0,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
0,1,0,0,
|
||||
0,1,1,0,
|
||||
0,0,1,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
0,0,0,0,
|
||||
0,1,1,0,
|
||||
1,1,0,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
1,0,0,0,
|
||||
1,1,0,0,
|
||||
0,1,0,0,
|
||||
0,0,0,0
|
||||
} }
|
||||
},
|
||||
{
|
||||
color = { common.color "#ff00ff" },
|
||||
shape = { {
|
||||
0,1,0,0,
|
||||
0,1,0,0,
|
||||
0,1,0,0,
|
||||
0,1,0,0
|
||||
}, {
|
||||
0,0,0,0,
|
||||
1,1,1,1,
|
||||
0,0,0,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
0,0,1,0,
|
||||
0,0,1,0,
|
||||
0,0,1,0,
|
||||
0,0,1,0
|
||||
}, {
|
||||
0,0,0,0,
|
||||
0,0,0,0,
|
||||
1,1,1,1,
|
||||
0,0,0,0
|
||||
} }
|
||||
},
|
||||
{
|
||||
color = { common.color "#ffffff" },
|
||||
shape = { {
|
||||
1,1,1,0,
|
||||
0,1,0,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
0,0,1,0,
|
||||
0,1,1,0,
|
||||
0,0,1,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
0,0,0,0,
|
||||
0,1,0,0,
|
||||
1,1,1,0,
|
||||
0,0,0,0
|
||||
}, {
|
||||
1,0,0,0,
|
||||
1,1,0,0,
|
||||
1,0,0,0,
|
||||
0,0,0,0
|
||||
} }
|
||||
}
|
||||
}
|
||||
self.live_piece = nil
|
||||
self.hold_piece = nil
|
||||
end
|
||||
|
||||
function TetrisView:calculate_tick(score)
|
||||
return self.initial_tick / (math.floor(score / 10) + 1)
|
||||
end
|
||||
|
||||
|
||||
function TetrisView:does_collide(x, y, tetronimo, rot)
|
||||
local shape = tetronimo.shape[rot]
|
||||
for i = 0, 3 do
|
||||
for j = 0, 3 do
|
||||
local ny = y + i
|
||||
local nx = x + j
|
||||
if (nx >= self.grid.x or ny >= self.grid.y or nx < 0 or ny < 0 or self.cells[self.grid.x * ny + nx + 1]) and shape[i * 4 + j + 1] == 1 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function TetrisView:finalize_live_piece()
|
||||
assert(self.live_piece)
|
||||
local shape = self.live_piece.tetronimo.shape[self.live_piece.rot]
|
||||
for i = 0, 3 do
|
||||
for j = 0, 3 do
|
||||
local ny = self.live_piece.y + i
|
||||
local nx = self.live_piece.x + j
|
||||
if shape[(i * 4 + j) + 1] == 1 then
|
||||
self.cells[ny * self.grid.x + nx + 1] = self.live_piece.idx
|
||||
end
|
||||
end
|
||||
end
|
||||
for y = self.live_piece.y, math.min(self.live_piece.y + 4, self.grid.y - 1) do
|
||||
local all_present = true
|
||||
for x = 0, self.grid.x - 1 do
|
||||
if not self.cells[y * self.grid.x + x + 1] then all_present = false end
|
||||
end
|
||||
if all_present then
|
||||
self.score = self.score + 1
|
||||
self.tick = self:calculate_tick(self.score)
|
||||
for ny = y, 2, -1 do
|
||||
for nx = 0, self.grid.x - 1 do
|
||||
self.cells[ny * self.grid.x + nx + 1] = self.cells[(ny - 1) * self.grid.x + nx + 1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.live_piece = nil
|
||||
end
|
||||
|
||||
|
||||
function TetrisView:step()
|
||||
if not self.finished and not self.paused then
|
||||
if not self.live_piece then
|
||||
local idx = self.next_piece or math.floor(math.random() * #self.tetronimos) + 1
|
||||
self.live_piece = { tetronimo = self.tetronimos[idx], idx = idx, x = math.floor(self.grid.x / 2), y = 0, rot = 1 }
|
||||
self.next_piece = math.floor(math.random() * #self.tetronimos) + 1
|
||||
if (self:does_collide(self.live_piece.x, self.live_piece.y + 1, self.live_piece.tetronimo, self.live_piece.rot)) then
|
||||
self:finalize_live_piece()
|
||||
self.finished = true
|
||||
end
|
||||
else
|
||||
if (self:does_collide(self.live_piece.x, self.live_piece.y + 1, self.live_piece.tetronimo, self.live_piece.rot)) then
|
||||
self.live_piece.countup = (self.live_piece.countup or 0) + 1
|
||||
if self.live_piece.countup > self.lock_delay then
|
||||
self:finalize_live_piece()
|
||||
end
|
||||
else
|
||||
self.live_piece.y = self.live_piece.y + 1
|
||||
self.live_piece.countup = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TetrisView:draw_tetronimo(posx, posy, tetronimo, rot, color)
|
||||
local shape = tetronimo.shape[rot]
|
||||
for y = 0, 3 do
|
||||
for x = 0, 3 do
|
||||
if shape[y * 4 + x + 1] == 1 then
|
||||
renderer.draw_rect(posx + x * (self.cell_size + self.cell_padding), posy + y * (self.cell_size + self.cell_padding), self.cell_size, self.cell_size, color or tetronimo.color)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TetrisView:draw()
|
||||
self:draw_background(style.background3)
|
||||
local lh = style.font:get_height()
|
||||
local tx = self.position.x + style.padding.x
|
||||
local ty = self.position.y + style.padding.y
|
||||
|
||||
renderer.draw_text(style.font, "Score: " .. self.score, tx, self.position.y + style.padding.y, style.normal)
|
||||
local w = renderer.draw_text(style.font, "Next Piece", tx, self.position.y + style.padding.y + lh, style.normal)
|
||||
if self.next_piece then
|
||||
self:draw_tetronimo(tx, self.position.y + style.padding.y + lh * 2, self.tetronimos[self.next_piece], 1)
|
||||
end
|
||||
if self.held_piece then
|
||||
self:draw_tetronimo(w + style.padding.x, self.position.y + style.padding.y + lh * 2, self.tetronimos[self.held_piece], 1)
|
||||
end
|
||||
renderer.draw_text(style.font, "Held Piece", w + style.padding.x, self.position.y + style.padding.y + lh, style.normal)
|
||||
ty = ty + lh * 2 + (self.cell_size + self.cell_padding) * 4 + style.padding.y
|
||||
|
||||
renderer.draw_rect(tx, ty, (self.cell_size + self.cell_padding) * self.grid.x, (self.cell_size + self.cell_padding) * self.grid.y, style.background)
|
||||
for y = 0, self.grid.y - 1 do
|
||||
for x = 0, self.grid.x - 1 do
|
||||
if self.cells[y * self.grid.x + x + 1] then
|
||||
local color = self.tetronimos[self.cells[y * self.grid.x + x + 1]].color
|
||||
renderer.draw_rect(tx + x * (self.cell_size + self.cell_padding), ty + y * (self.cell_size + self.cell_padding), self.cell_size, self.cell_size, color)
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.live_piece then
|
||||
self:draw_tetronimo(tx + self.live_piece.x * (self.cell_size + self.cell_padding), ty + self.live_piece.y * (self.cell_size + self.cell_padding), self.live_piece.tetronimo, self.live_piece.rot)
|
||||
if self.drop_shadow then
|
||||
local y = self:get_max_drop(math.huge)
|
||||
if y ~= self.live_piece.y then
|
||||
self:draw_tetronimo(tx + self.live_piece.x * (self.cell_size + self.cell_padding), ty + y * (self.cell_size + self.cell_padding), self.live_piece.tetronimo, self.live_piece.rot, { self.live_piece.tetronimo.color[1], self.live_piece.tetronimo.color[2], self.live_piece.tetronimo.color[3], 50 })
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.finished or self.paused then renderer.draw_rect(tx, ty, self.grid.x * (self.cell_size + self.cell_padding), self.grid.y * (self.cell_size + self.cell_padding), { common.color "rgba(255, 255, 255, 0.5)" }) end
|
||||
if self.finished then common.draw_text(style.font, style.error, "GAME OVER", "center", tx, ty, self.grid.x * (self.cell_size + self.cell_padding), self.grid.y * (self.cell_size + self.cell_padding)) end
|
||||
if self.paused then common.draw_text(style.font, style.warn, "PAUSED", "center", tx, ty, self.grid.x * (self.cell_size + self.cell_padding), self.grid.y * (self.cell_size + self.cell_padding)) end
|
||||
end
|
||||
|
||||
function TetrisView:rotate()
|
||||
if self.live_piece and not self.paused then
|
||||
local new_rot = (self.live_piece.rot % #self.live_piece.tetronimo.shape) + 1
|
||||
if not self:does_collide(self.live_piece.x, self.live_piece.y, self.live_piece.tetronimo, new_rot) then
|
||||
self.live_piece.rot = new_rot
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TetrisView:hold()
|
||||
if self.live_piece and not self.paused then
|
||||
if self.held_piece then
|
||||
if not self:does_collide(self.live_piece.x, self.live_piece.y, self.tetronimos[self.held_piece], 1) then
|
||||
local live_piece = self.live_piece.idx
|
||||
self.live_piece = { x = self.live_piece.x, y = self.live_piece.y, rot = 1, idx = self.held_piece, tetronimo = self.tetronimos[self.held_piece] }
|
||||
self.held_piece = live_piece
|
||||
end
|
||||
else
|
||||
self.held_piece = self.live_piece.idx
|
||||
self.live_piece = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TetrisView:get_max_drop(amount)
|
||||
if self.live_piece then
|
||||
for y = self.live_piece.y, math.min(self.grid.y, self.live_piece.y + amount) do
|
||||
if self:does_collide(self.live_piece.x, y + 1, self.live_piece.tetronimo, self.live_piece.rot) then
|
||||
return y, true
|
||||
end
|
||||
end
|
||||
end
|
||||
return self.live_piece.y + amount, false
|
||||
end
|
||||
|
||||
function TetrisView:drop(amount)
|
||||
if self.live_piece and not self.paused then
|
||||
local y, collides = self:get_max_drop(amount)
|
||||
self.live_piece.y = y
|
||||
if collides then
|
||||
self:finalize_live_piece()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TetrisView:shift(delta)
|
||||
if self.live_piece and not self.paused and not self:does_collide(self.live_piece.x + delta, self.live_piece.y, self.live_piece.tetronimo, self.live_piece.rot) then
|
||||
self.live_piece.x = self.live_piece.x + delta
|
||||
end
|
||||
end
|
||||
|
||||
command.add(TetrisView, {
|
||||
["tetris:rotate"] = function() core.active_view:rotate() end,
|
||||
["tetris:shift-left"] = function() core.active_view:shift(-1) end,
|
||||
["tetris:shift-right"] = function() core.active_view:shift(1) end,
|
||||
["tetris:drop"] = function() core.active_view:drop(config.plugins.tetris.down_amount) end,
|
||||
["tetris:hard-drop"] = function() core.active_view:drop(math.huge) end,
|
||||
["tetris:hold"] = function() core.active_view:hold() end,
|
||||
["tetris:toggle-pause"] = function() core.active_view.paused = not core.active_view.paused end,
|
||||
["tetris:quit"] = function()
|
||||
core.active_view.finished = true
|
||||
core.active_view.node:close_view(core.root_view.root_node, core.active_view)
|
||||
end
|
||||
})
|
||||
command.add(nil, {
|
||||
["tetris:start"] = function()
|
||||
local view = TetrisView(config.plugins.tetris)
|
||||
local node = core.root_view:get_active_node()
|
||||
view.node = node:split("right", view, { x = true }, false)
|
||||
core.set_active_view(view)
|
||||
end
|
||||
})
|
||||
|
||||
keymap.add {
|
||||
["up"] = "tetris:rotate",
|
||||
["left"] = "tetris:shift-left",
|
||||
["right"] = "tetris:shift-right",
|
||||
["down"] = "tetris:drop",
|
||||
["space"] = "tetris:hard-drop",
|
||||
["tab"] = "tetris:hold",
|
||||
["escape"] = "tetris:quit",
|
||||
["ctrl+e"] = { "tetris:quit", "tetris:start" },
|
||||
["p"] = "tetris:toggle-pause"
|
||||
}
|
||||
return { view = TetrisView }
|
|
@ -2,13 +2,13 @@
|
|||
* lite_xl_plugin_api.h
|
||||
* API for writing C extension modules loaded by Lite XL.
|
||||
* This file is licensed under MIT.
|
||||
*
|
||||
*
|
||||
* The Lite XL plugin API is quite simple.
|
||||
* You would write a lua C extension and replace any references to lua.h, lauxlib.h
|
||||
* and lualib.h with lite_xl_plugin_api.h.
|
||||
* In your main file (where your entrypoint resides), define LITE_XL_PLUGIN_ENTRYPOINT.
|
||||
* If you have multiple entrypoints, define LITE_XL_PLUGIN_ENTRYPOINT in one of them.
|
||||
*
|
||||
*
|
||||
* After that, you need to create a Lite XL entrypoint, which is formatted as
|
||||
* luaopen_lite_xl_xxxxx instead of luaopen_xxxxx.
|
||||
* This entrypoint accepts a lua_State and an extra parameter of type void *.
|
||||
|
@ -16,9 +16,9 @@
|
|||
* If you have multiple entrypoints, you must call lite_xl_plugin_init() in
|
||||
* each of them.
|
||||
* This function is not thread safe, so don't try to do anything stupid.
|
||||
*
|
||||
*
|
||||
* An example:
|
||||
*
|
||||
*
|
||||
* #define LITE_XL_PLUGIN_ENTRYPOINT
|
||||
* #include "lite_xl_plugin_api.h"
|
||||
* int luaopen_lite_xl_xxxxx(lua_State* L, void* XL) {
|
||||
|
@ -26,19 +26,14 @@
|
|||
* ...
|
||||
* return 1;
|
||||
* }
|
||||
*
|
||||
*
|
||||
* You can compile the library just like any Lua library without linking to Lua.
|
||||
* An example command would be: gcc -shared -o xxxxx.so xxxxx.c
|
||||
* You must not link to ANY lua library to avoid symbol collision.
|
||||
*
|
||||
* This file contains stock configuration for a typical installation of Lua 5.4.6.
|
||||
*
|
||||
* This file contains stock configuration for a typical installation of Lua 5.4.
|
||||
* DO NOT MODIFY ANYTHING. MODIFYING STUFFS IN HERE WILL BREAK
|
||||
* COMPATIBILITY WITH LITE XL AND CAUSE UNDEBUGGABLE BUGS.
|
||||
*
|
||||
* For reference, here are a list of permalinks to previous version of this file that targets an older version of Lua.
|
||||
* If you don't need functionalities offered by the new version, use the OLDEST FILE for backwards compatibility.
|
||||
*
|
||||
* - Lua 5.4.4: https://github.com/lite-xl/lite-xl/blob/397973067f14420b26e3b20a238a50016c0b75e2/resources/include/lite_xl_plugin_api.h
|
||||
**/
|
||||
#ifndef LITE_XL_PLUGIN_API
|
||||
#define LITE_XL_PLUGIN_API
|
||||
|
@ -65,8 +60,8 @@
|
|||
#define FE_7(what, x, ...) what x,FE_6(what, __VA_ARGS__)
|
||||
#define FE_8(what, x, ...) what x,FE_7(what, __VA_ARGS__)
|
||||
#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
|
||||
#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__)
|
||||
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
|
||||
#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__)
|
||||
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
|
||||
#define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0
|
||||
#define FOR_EACH_(N, what, ...) CONCAT(FE_, N)(what, __VA_ARGS__)
|
||||
#define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__)
|
||||
|
@ -1033,7 +1028,6 @@ extern const char lua_ident[];
|
|||
SYMBOL_DECLARE(lua_State *, lua_newstate, lua_Alloc f, void *ud)
|
||||
SYMBOL_DECLARE(void, lua_close, lua_State *L)
|
||||
SYMBOL_DECLARE(lua_State *, lua_newthread, lua_State *L)
|
||||
SYMBOL_DECLARE(int, lua_closethread, lua_State *L, lua_State *from)
|
||||
SYMBOL_DECLARE(int, lua_resetthread, lua_State *L)
|
||||
|
||||
SYMBOL_DECLARE(lua_CFunction, lua_atpanic, lua_State *L, lua_CFunction panicf)
|
||||
|
@ -1745,9 +1739,6 @@ SYMBOL_WRAP_DECL(void, lua_close, lua_State *L) {
|
|||
SYMBOL_WRAP_DECL(lua_State *, lua_newthread, lua_State *L) {
|
||||
return SYMBOL_WRAP_CALL(lua_newthread, L);
|
||||
}
|
||||
SYMBOL_WRAP_DECL(int, lua_closethread, lua_State *L, lua_State *from) {
|
||||
return SYMBOL_WRAP_CALL(lua_closethread, L, from);
|
||||
}
|
||||
SYMBOL_WRAP_DECL(int, lua_resetthread, lua_State *L) {
|
||||
return SYMBOL_WRAP_CALL(lua_resetthread, L);
|
||||
}
|
||||
|
@ -2360,7 +2351,6 @@ void lite_xl_plugin_init(void *XL) {
|
|||
IMPORT_SYMBOL(lua_newstate, lua_State *, lua_Alloc f, void *ud);
|
||||
IMPORT_SYMBOL(lua_close, void, lua_State *L);
|
||||
IMPORT_SYMBOL(lua_newthread, lua_State *, lua_State *L);
|
||||
IMPORT_SYMBOL(lua_closethread, int, lua_State *L, lua_State *from);
|
||||
IMPORT_SYMBOL(lua_resetthread, int, lua_State *L);
|
||||
IMPORT_SYMBOL(lua_atpanic, lua_CFunction, lua_State *L, lua_CFunction panicf);
|
||||
IMPORT_SYMBOL(lua_version, lua_Number, lua_State *L);
|
||||
|
@ -2570,4 +2560,4 @@ void lite_xl_plugin_init(void *XL);
|
|||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
******************************************************************************/
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop">
|
||||
<id>com.lite_xl.LiteXL</id>
|
||||
<id>org.lite_xl.lite_xl</id>
|
||||
<metadata_license>MIT</metadata_license>
|
||||
<project_license>MIT</project_license>
|
||||
<name>Lite XL</name>
|
||||
<summary>A lightweight text editor written in Lua</summary>
|
||||
<content_rating type="oars-1.0" />
|
||||
<launchable type="desktop-id">com.lite_xl.LiteXL.desktop</launchable>
|
||||
<launchable type="desktop-id">org.lite_xl.lite_xl.desktop</launchable>
|
||||
|
||||
<description>
|
||||
<p>
|
||||
|
@ -29,6 +29,6 @@
|
|||
</provides>
|
||||
|
||||
<releases>
|
||||
<release version="2.1.1" date="2022-12-29" />
|
||||
<release version="2.1.4" date="2024-04-16" />
|
||||
</releases>
|
||||
</component>
|
|
@ -1,6 +1,6 @@
|
|||
diff -ruN lua-5.4.4/meson.build lua-5.4.4-patched/meson.build
|
||||
--- lua-5.4.4/meson.build Wed Feb 22 18:16:56 2023
|
||||
+++ lua-5.4.4-patched/meson.build Wed Feb 22 04:10:01 2023
|
||||
diff -ruN lua-5.4.4\meson.build lua-5.4.4-patched\meson.build
|
||||
--- lua-5.4.4\meson.build Wed Feb 22 18:16:56 2023
|
||||
+++ lua-5.4.4-patched\meson.build Wed Feb 22 04:10:01 2023
|
||||
@@ -85,6 +85,7 @@
|
||||
'src/lutf8lib.c',
|
||||
'src/lvm.c',
|
||||
|
@ -9,13 +9,13 @@ diff -ruN lua-5.4.4/meson.build lua-5.4.4-patched/meson.build
|
|||
dependencies: lua_lib_deps,
|
||||
version: meson.project_version(),
|
||||
soversion: lua_versions[0] + '.' + lua_versions[1],
|
||||
diff -ruN lua-5.4.4/src/luaconf.h lua-5.4.4-patched/src/luaconf.h
|
||||
--- lua-5.4.4/src/luaconf.h Thu Jan 13 19:24:43 2022
|
||||
+++ lua-5.4.4-patched/src/luaconf.h Wed Feb 22 04:10:02 2023
|
||||
diff -ruN lua-5.4.4\src\luaconf.h lua-5.4.4-patched\src\luaconf.h
|
||||
--- lua-5.4.4\src\luaconf.h Thu Jan 13 19:24:43 2022
|
||||
+++ lua-5.4.4-patched\src\luaconf.h Wed Feb 22 04:10:02 2023
|
||||
@@ -782,5 +782,15 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+#if defined(lua_c) || defined(luac_c) || (defined(LUA_LIB) && \
|
||||
+ (defined(lauxlib_c) || defined(liolib_c) || \
|
||||
+ defined(loadlib_c) || defined(loslib_c)))
|
||||
|
@ -27,22 +27,22 @@ diff -ruN lua-5.4.4/src/luaconf.h lua-5.4.4-patched/src/luaconf.h
|
|||
+
|
||||
+
|
||||
#endif
|
||||
|
||||
diff -ruN lua-5.4.4/src/Makefile lua-5.4.4-patched/src/Makefile
|
||||
--- lua-5.4.4/src/Makefile Thu Jul 15 22:01:52 2021
|
||||
+++ lua-5.4.4-patched/src/Makefile Wed Feb 22 04:10:02 2023
|
||||
|
||||
diff -ruN lua-5.4.4\src\Makefile lua-5.4.4-patched\src\Makefile
|
||||
--- lua-5.4.4\src\Makefile Thu Jul 15 22:01:52 2021
|
||||
+++ lua-5.4.4-patched\src\Makefile Wed Feb 22 04:10:02 2023
|
||||
@@ -33,7 +33,7 @@
|
||||
PLATS= guess aix bsd c89 freebsd generic linux linux-readline macosx mingw posix solaris
|
||||
|
||||
|
||||
LUA_A= liblua.a
|
||||
-CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o
|
||||
+CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o utf8_wrappers.o
|
||||
LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
|
||||
BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)
|
||||
|
||||
diff -ruN lua-5.4.4/src/utf8_wrappers.c lua-5.4.4-patched/src/utf8_wrappers.c
|
||||
--- lua-5.4.4/src/utf8_wrappers.c Thu Jan 01 08:00:00 1970
|
||||
+++ lua-5.4.4-patched/src/utf8_wrappers.c Wed Feb 22 18:13:45 2023
|
||||
|
||||
diff -ruN lua-5.4.4\src\utf8_wrappers.c lua-5.4.4-patched\src\utf8_wrappers.c
|
||||
--- lua-5.4.4\src\utf8_wrappers.c Thu Jan 01 08:00:00 1970
|
||||
+++ lua-5.4.4-patched\src\utf8_wrappers.c Wed Feb 22 18:13:45 2023
|
||||
@@ -0,0 +1,129 @@
|
||||
+/**
|
||||
+ * Wrappers to provide Unicode (UTF-8) support on Windows.
|
||||
|
@ -173,9 +173,9 @@ diff -ruN lua-5.4.4/src/utf8_wrappers.c lua-5.4.4-patched/src/utf8_wrappers.c
|
|||
+ return env_value;
|
||||
+}
|
||||
+#endif
|
||||
diff -ruN lua-5.4.4/src/utf8_wrappers.h lua-5.4.4-patched/src/utf8_wrappers.h
|
||||
--- lua-5.4.4/src/utf8_wrappers.h Thu Jan 01 08:00:00 1970
|
||||
+++ lua-5.4.4-patched/src/utf8_wrappers.h Wed Feb 22 18:09:48 2023
|
||||
diff -ruN lua-5.4.4\src\utf8_wrappers.h lua-5.4.4-patched\src\utf8_wrappers.h
|
||||
--- lua-5.4.4\src\utf8_wrappers.h Thu Jan 01 08:00:00 1970
|
||||
+++ lua-5.4.4-patched\src\utf8_wrappers.h Wed Feb 22 18:09:48 2023
|
||||
@@ -0,0 +1,46 @@
|
||||
+/**
|
||||
+ * Wrappers to provide Unicode (UTF-8) support on Windows.
|
||||
|
|
|
@ -138,7 +138,7 @@ generate_appimage() {
|
|||
mv AppRun LiteXL.AppDir/
|
||||
# These could be symlinks but it seems they doesn't work with AppimageLauncher
|
||||
cp resources/icons/lite-xl.svg LiteXL.AppDir/
|
||||
cp resources/linux/com.lite_xl.LiteXL.desktop LiteXL.AppDir/
|
||||
cp resources/linux/org.lite_xl.lite_xl.desktop LiteXL.AppDir/
|
||||
|
||||
if [[ $ADDONS == true ]]; then
|
||||
addons_download "${BUILD_DIR}"
|
||||
|
|
|
@ -77,11 +77,17 @@ get_platform_arch() {
|
|||
platform=$(get_platform_name)
|
||||
arch=${CROSS_ARCH:-$(uname -m)}
|
||||
if [[ $MSYSTEM != "" ]]; then
|
||||
if [[ $MSYSTEM == "MINGW64" ]]; then
|
||||
case "$MSYSTEM" in
|
||||
MINGW64|UCRT64|CLANG64)
|
||||
arch=x86_64
|
||||
else
|
||||
;;
|
||||
MINGW32|CLANG32)
|
||||
arch=i686
|
||||
fi
|
||||
;;
|
||||
CLANGARM64)
|
||||
arch=aarch64
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
echo "$arch"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#define MyAppName "Lite XL"
|
||||
#define MyAppVersion "@PROJECT_VERSION@"
|
||||
#define MyAppPublisher "Lite XL Team"
|
||||
#define MyAppURL "https://lite-xl.github.io"
|
||||
#define MyAppURL "https://lite-xl.com"
|
||||
#define MyAppExeName "lite-xl.exe"
|
||||
#define BuildDir "@PROJECT_BUILD_DIR@"
|
||||
#define SourceDir "@PROJECT_SOURCE_DIR@"
|
||||
|
@ -26,12 +26,16 @@ AppPublisherURL={#MyAppURL}
|
|||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
|
||||
#if Arch=="x64"
|
||||
ArchitecturesAllowed=x64
|
||||
ArchitecturesInstallIn64BitMode=x64
|
||||
#define ArchInternal "x86_64"
|
||||
#else
|
||||
#if Arch=="x86"
|
||||
#define ArchInternal "i686"
|
||||
#else
|
||||
ArchitecturesAllowed={#Arch}
|
||||
ArchitecturesInstallIn64BitMode={#Arch}
|
||||
#if Arch=="x64"
|
||||
#define ArchInternal "x86_64"
|
||||
#elif Arch=="arm64"
|
||||
#define ArchInternal "aarch64"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
AllowNoIcons=yes
|
||||
|
@ -69,9 +73,7 @@ Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescrip
|
|||
Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked
|
||||
|
||||
[Files]
|
||||
Source: "{#BuildDir}/src/lite-xl.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "{#BuildDir}/mingwLibs{#Arch}/*"; DestDir: "{app}"; Flags: ignoreversion ; Check: DirExists(ExpandConstant('{#BuildDir}/mingwLibs{#Arch}'))
|
||||
Source: "{#SourceDir}/data/*"; DestDir: "{app}/data"; Flags: ignoreversion recursesubdirs
|
||||
Source: "{#SourceDir}/lite-xl/*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
|
||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||
|
||||
[Icons]
|
||||
|
|
|
@ -29,13 +29,24 @@ main() {
|
|||
local version
|
||||
local output
|
||||
|
||||
if [[ $MSYSTEM == "MINGW64" ]]; then
|
||||
case "$MSYSTEM" in
|
||||
MINGW64|UCRT64|CLANG64)
|
||||
arch=x64
|
||||
arch_file=x86_64
|
||||
else
|
||||
arch=i686;
|
||||
;;
|
||||
MINGW32|CLANG32)
|
||||
arch=x86
|
||||
arch_file=i686
|
||||
fi
|
||||
;;
|
||||
CLANGARM64)
|
||||
arch=arm64
|
||||
arch_file=aarch64
|
||||
;;
|
||||
*)
|
||||
echo "error: unsupported MSYSTEM type: $MSYSTEM"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
initial_arg_count=$#
|
||||
|
||||
|
|
|
@ -213,14 +213,18 @@ main() {
|
|||
if [[ $platform == "windows" ]]; then
|
||||
exe_file="${exe_file}.exe"
|
||||
stripcmd="strip --strip-all"
|
||||
# Copy MinGW libraries dependencies.
|
||||
# MSYS2 ldd command seems to be only 64bit, so use ntldd
|
||||
# see https://github.com/msys2/MINGW-packages/issues/4164
|
||||
ntldd -R "${exe_file}" \
|
||||
| grep mingw \
|
||||
| awk '{print $3}' \
|
||||
| sed 's#\\#/#g' \
|
||||
| xargs -I '{}' cp -v '{}' "$(pwd)/${dest_dir}/"
|
||||
if command -v ntldd >/dev/null 2>&1; then
|
||||
# Copy MinGW libraries dependencies.
|
||||
# MSYS2 ldd command seems to be only 64bit, so use ntldd
|
||||
# see https://github.com/msys2/MINGW-packages/issues/4164
|
||||
ntldd -R "${exe_file}" \
|
||||
| grep mingw \
|
||||
| awk '{print $3}' \
|
||||
| sed 's#\\#/#g' \
|
||||
| xargs -I '{}' cp -v '{}' "$(pwd)/${dest_dir}/"
|
||||
else
|
||||
echo "WARNING: ntldd not found; assuming program is static"
|
||||
fi
|
||||
else
|
||||
# Windows archive is always portable
|
||||
package_name+="-portable"
|
||||
|
|
|
@ -7,7 +7,7 @@ int luaopen_regex(lua_State *L);
|
|||
int luaopen_dirmonitor(lua_State* L);
|
||||
int luaopen_utf8extra(lua_State* L);
|
||||
|
||||
#if defined(__amigaos4__)
|
||||
#if defined(__amigaos4__) || defined(__morphos__)
|
||||
int luaopen_codesets(lua_State* L);
|
||||
#endif
|
||||
|
||||
|
@ -18,7 +18,7 @@ static const luaL_Reg libs[] = {
|
|||
// { "process", luaopen_process },
|
||||
{ "dirmonitor", luaopen_dirmonitor },
|
||||
{ "utf8extra", luaopen_utf8extra },
|
||||
#if defined(__amigaos4__)
|
||||
#if defined(__amigaos4__) || defined(__morphos__)
|
||||
{ "codesetsextra", luaopen_codesets },
|
||||
#endif
|
||||
{ NULL, NULL }
|
||||
|
@ -29,3 +29,4 @@ void api_load_libs(lua_State *L) {
|
|||
for (int i = 0; libs[i].name; i++)
|
||||
luaL_requiref(L, libs[i].name, libs[i].func, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ typedef wchar_t *process_env_t;
|
|||
|
||||
#define HANDLE_INVALID (INVALID_HANDLE_VALUE)
|
||||
#define PROCESS_GET_HANDLE(P) ((P)->process_information.hProcess)
|
||||
#define PROCESS_ARGLIST_INITIALIZER { 0 }
|
||||
|
||||
static volatile long PipeSerialNumber;
|
||||
|
||||
|
@ -52,6 +53,7 @@ typedef char **process_env_t;
|
|||
|
||||
#define HANDLE_INVALID (0)
|
||||
#define PROCESS_GET_HANDLE(P) ((P)->pid)
|
||||
#define PROCESS_ARGLIST_INITIALIZER NULL
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -440,6 +442,7 @@ static int process_arglist_add(process_arglist_t *list, size_t *list_len, const
|
|||
|
||||
|
||||
static void process_arglist_free(process_arglist_t *list) {
|
||||
if (!*list) return;
|
||||
#ifndef _WIN32
|
||||
char **cmd = *list;
|
||||
for (int i = 0; cmd[i]; i++)
|
||||
|
@ -563,14 +566,13 @@ static int process_env_add(process_env_t *env_list, size_t *env_len, const char
|
|||
}
|
||||
|
||||
|
||||
static void process_env_free(process_env_t *list) {
|
||||
static void process_env_free(process_env_t *list, size_t list_len) {
|
||||
if (!*list) return;
|
||||
#ifdef _WIN32
|
||||
free(*list);
|
||||
#else
|
||||
for (size_t i = 0; (*list)[i]; i++) free((*list)[i]);
|
||||
free(*list);
|
||||
#ifndef _WIN32
|
||||
for (size_t i = 0; i < list_len; i++)
|
||||
free((*list)[i]);
|
||||
#endif
|
||||
free(*list);
|
||||
*list = NULL;
|
||||
}
|
||||
|
||||
|
@ -578,7 +580,8 @@ static void process_env_free(process_env_t *list) {
|
|||
static int process_start(lua_State* L) {
|
||||
int r, retval = 1;
|
||||
size_t env_len = 0, cmd_len = 0, arglist_len = 0, env_vars_len = 0;
|
||||
process_arglist_t arglist;
|
||||
process_t *self = NULL;
|
||||
process_arglist_t arglist = PROCESS_ARGLIST_INITIALIZER;
|
||||
process_env_t env_vars = NULL;
|
||||
const char *cwd = NULL;
|
||||
bool detach = false, escape = true;
|
||||
|
@ -668,7 +671,7 @@ static int process_start(lua_State* L) {
|
|||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
process_t* self = lua_newuserdata(L, sizeof(process_t));
|
||||
self = lua_newuserdata(L, sizeof(process_t));
|
||||
memset(self, 0, sizeof(process_t));
|
||||
luaL_setmetatable(L, API_TYPE_PROCESS);
|
||||
self->deadline = deadline;
|
||||
|
@ -826,14 +829,16 @@ static int process_start(lua_State* L) {
|
|||
if (control_pipe[0]) close(control_pipe[0]);
|
||||
if (control_pipe[1]) close(control_pipe[1]);
|
||||
#endif
|
||||
for (int stream = 0; stream < 3; ++stream) {
|
||||
process_stream_t* pipe = &self->child_pipes[stream][stream == STDIN_FD ? 0 : 1];
|
||||
if (*pipe) {
|
||||
close_fd(pipe);
|
||||
if (self) {
|
||||
for (int stream = 0; stream < 3; ++stream) {
|
||||
process_stream_t* pipe = &self->child_pipes[stream][stream == STDIN_FD ? 0 : 1];
|
||||
if (*pipe) {
|
||||
close_fd(pipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
process_arglist_free(&arglist);
|
||||
process_env_free(&env_vars);
|
||||
process_env_free(&env_vars, env_vars_len);
|
||||
|
||||
if (retval == -1)
|
||||
return lua_error(L);
|
||||
|
@ -849,7 +854,7 @@ static int g_read(lua_State* L, int stream, unsigned long read_size) {
|
|||
return luaL_error(L, "error: redirect to handles, FILE* and paths are not supported");
|
||||
#if _WIN32
|
||||
int writable_stream_idx = stream - 1;
|
||||
if (self->reading[writable_stream_idx] || !ReadFile(self->child_pipes[stream][0], self->buffer[writable_stream_idx], READ_BUF_SIZE, NULL, &self->overlapped[writable_stream_idx])) {
|
||||
if (self->reading[writable_stream_idx] || !ReadFile(self->child_pipes[stream][0], self->buffer[writable_stream_idx], read_size > READ_BUF_SIZE ? READ_BUF_SIZE : read_size, NULL, &self->overlapped[writable_stream_idx])) {
|
||||
if (self->reading[writable_stream_idx] || GetLastError() == ERROR_IO_PENDING) {
|
||||
self->reading[writable_stream_idx] = true;
|
||||
DWORD bytesTransferred = 0;
|
||||
|
@ -975,10 +980,7 @@ static int self_signal(lua_State* L, signal_e sig) {
|
|||
static int f_terminate(lua_State* L) { return self_signal(L, SIGNAL_TERM); }
|
||||
static int f_kill(lua_State* L) { return self_signal(L, SIGNAL_KILL); }
|
||||
static int f_interrupt(lua_State* L) { return self_signal(L, SIGNAL_INTERRUPT); }
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
>>>>>>> master
|
||||
static int f_gc(lua_State* L) {
|
||||
process_kill_list_t *list = NULL;
|
||||
process_kill_t *p = NULL;
|
||||
|
|
|
@ -90,7 +90,7 @@ static int f_font_load(lua_State *L) {
|
|||
return ret_code;
|
||||
|
||||
RenFont** font = lua_newuserdata(L, sizeof(RenFont*));
|
||||
*font = ren_font_load(window_renderer, filename, size, antialiasing, hinting, style);
|
||||
*font = ren_font_load(&window_renderer, filename, size, antialiasing, hinting, style);
|
||||
if (!*font)
|
||||
return luaL_error(L, "failed to load font");
|
||||
luaL_setmetatable(L, API_TYPE_FONT);
|
||||
|
@ -130,7 +130,7 @@ static int f_font_copy(lua_State *L) {
|
|||
}
|
||||
for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) {
|
||||
RenFont** font = lua_newuserdata(L, sizeof(RenFont*));
|
||||
*font = ren_font_copy(window_renderer, fonts[i], size, antialiasing, hinting, style);
|
||||
*font = ren_font_copy(&window_renderer, fonts[i], size, antialiasing, hinting, style);
|
||||
if (!*font)
|
||||
return luaL_error(L, "failed to copy font");
|
||||
luaL_setmetatable(L, API_TYPE_FONT);
|
||||
|
@ -198,7 +198,7 @@ static int f_font_get_width(lua_State *L) {
|
|||
size_t len;
|
||||
const char *text = luaL_checklstring(L, 2, &len);
|
||||
|
||||
lua_pushnumber(L, ren_font_group_get_width(window_renderer, fonts, text, len, NULL));
|
||||
lua_pushnumber(L, ren_font_group_get_width(&window_renderer, fonts, text, len, NULL));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -217,7 +217,7 @@ static int f_font_get_size(lua_State *L) {
|
|||
static int f_font_set_size(lua_State *L) {
|
||||
RenFont* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1);
|
||||
float size = luaL_checknumber(L, 2);
|
||||
ren_font_group_set_size(window_renderer, fonts, size);
|
||||
ren_font_group_set_size(&window_renderer, fonts, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -276,7 +276,7 @@ static int f_show_debug(lua_State *L) {
|
|||
|
||||
static int f_get_size(lua_State *L) {
|
||||
int w, h;
|
||||
ren_get_size(window_renderer, &w, &h);
|
||||
ren_get_size(&window_renderer, &w, &h);
|
||||
lua_pushnumber(L, w);
|
||||
lua_pushnumber(L, h);
|
||||
return 2;
|
||||
|
@ -284,13 +284,13 @@ static int f_get_size(lua_State *L) {
|
|||
|
||||
|
||||
static int f_begin_frame(UNUSED lua_State *L) {
|
||||
rencache_begin_frame(window_renderer);
|
||||
rencache_begin_frame(&window_renderer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_end_frame(UNUSED lua_State *L) {
|
||||
rencache_end_frame(window_renderer);
|
||||
rencache_end_frame(&window_renderer);
|
||||
// clear the font reference table
|
||||
lua_newtable(L);
|
||||
lua_rawseti(L, LUA_REGISTRYINDEX, RENDERER_FONT_REF);
|
||||
|
@ -311,7 +311,7 @@ static int f_set_clip_rect(lua_State *L) {
|
|||
lua_Number w = luaL_checknumber(L, 3);
|
||||
lua_Number h = luaL_checknumber(L, 4);
|
||||
RenRect rect = rect_to_grid(x, y, w, h);
|
||||
rencache_set_clip_rect(window_renderer, rect);
|
||||
rencache_set_clip_rect(&window_renderer, rect);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -323,7 +323,7 @@ static int f_draw_rect(lua_State *L) {
|
|||
lua_Number h = luaL_checknumber(L, 4);
|
||||
RenRect rect = rect_to_grid(x, y, w, h);
|
||||
RenColor color = checkcolor(L, 5, 255);
|
||||
rencache_draw_rect(window_renderer, rect, color);
|
||||
rencache_draw_rect(&window_renderer, rect, color);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -348,7 +348,7 @@ static int f_draw_text(lua_State *L) {
|
|||
double x = luaL_checknumber(L, 3);
|
||||
int y = luaL_checknumber(L, 4);
|
||||
RenColor color = checkcolor(L, 5, 255);
|
||||
x = rencache_draw_text(window_renderer, fonts, text, len, x, y, color);
|
||||
x = rencache_draw_text(&window_renderer, fonts, text, len, x, y, color);
|
||||
lua_pushnumber(L, x);
|
||||
return 1;
|
||||
}
|
||||
|
@ -391,4 +391,3 @@ int luaopen_renderer(lua_State *L) {
|
|||
lua_setfield(L, -2, "font");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
100
src/api/system.c
100
src/api/system.c
|
@ -78,7 +78,7 @@ static SDL_HitTestResult SDLCALL hit_test(SDL_Window *window, const SDL_Point *p
|
|||
const int controls_width = hit_info->controls_width;
|
||||
int w, h;
|
||||
|
||||
SDL_GetWindowSize(window_renderer->window, &w, &h);
|
||||
SDL_GetWindowSize(window_renderer.window, &w, &h);
|
||||
|
||||
if (pt->y < hit_info->title_height &&
|
||||
#if RESIZE_FROM_TOP
|
||||
|
@ -197,7 +197,7 @@ top:
|
|||
|
||||
case SDL_WINDOWEVENT:
|
||||
if (e.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
ren_resize_window(window_renderer);
|
||||
ren_resize_window(&window_renderer);
|
||||
lua_pushstring(L, "resized");
|
||||
/* The size below will be in points. */
|
||||
lua_pushinteger(L, e.window.data1);
|
||||
|
@ -236,8 +236,8 @@ top:
|
|||
SDL_GetMouseState(&mx, &my);
|
||||
lua_pushstring(L, "filedropped");
|
||||
lua_pushstring(L, e.drop.file);
|
||||
lua_pushinteger(L, mx * window_renderer->scale_x);
|
||||
lua_pushinteger(L, my * window_renderer->scale_y);
|
||||
lua_pushinteger(L, mx * window_renderer.scale_x);
|
||||
lua_pushinteger(L, my * window_renderer.scale_y);
|
||||
SDL_free(e.drop.file);
|
||||
return 4;
|
||||
|
||||
|
@ -294,8 +294,8 @@ top:
|
|||
if (e.button.button == 1) { SDL_CaptureMouse(1); }
|
||||
lua_pushstring(L, "mousepressed");
|
||||
lua_pushstring(L, button_name(e.button.button));
|
||||
lua_pushinteger(L, e.button.x * window_renderer->scale_x);
|
||||
lua_pushinteger(L, e.button.y * window_renderer->scale_y);
|
||||
lua_pushinteger(L, e.button.x * window_renderer.scale_x);
|
||||
lua_pushinteger(L, e.button.y * window_renderer.scale_y);
|
||||
lua_pushinteger(L, e.button.clicks);
|
||||
return 5;
|
||||
|
||||
|
@ -303,8 +303,8 @@ top:
|
|||
if (e.button.button == 1) { SDL_CaptureMouse(0); }
|
||||
lua_pushstring(L, "mousereleased");
|
||||
lua_pushstring(L, button_name(e.button.button));
|
||||
lua_pushinteger(L, e.button.x * window_renderer->scale_x);
|
||||
lua_pushinteger(L, e.button.y * window_renderer->scale_y);
|
||||
lua_pushinteger(L, e.button.x * window_renderer.scale_x);
|
||||
lua_pushinteger(L, e.button.y * window_renderer.scale_y);
|
||||
return 4;
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
|
@ -316,10 +316,10 @@ top:
|
|||
e.motion.yrel += event_plus.motion.yrel;
|
||||
}
|
||||
lua_pushstring(L, "mousemoved");
|
||||
lua_pushinteger(L, e.motion.x * window_renderer->scale_x);
|
||||
lua_pushinteger(L, e.motion.y * window_renderer->scale_y);
|
||||
lua_pushinteger(L, e.motion.xrel * window_renderer->scale_x);
|
||||
lua_pushinteger(L, e.motion.yrel * window_renderer->scale_y);
|
||||
lua_pushinteger(L, e.motion.x * window_renderer.scale_x);
|
||||
lua_pushinteger(L, e.motion.y * window_renderer.scale_y);
|
||||
lua_pushinteger(L, e.motion.xrel * window_renderer.scale_x);
|
||||
lua_pushinteger(L, e.motion.yrel * window_renderer.scale_y);
|
||||
return 5;
|
||||
|
||||
case SDL_MOUSEWHEEL:
|
||||
|
@ -335,7 +335,7 @@ top:
|
|||
return 3;
|
||||
|
||||
case SDL_FINGERDOWN:
|
||||
SDL_GetWindowSize(window_renderer->window, &w, &h);
|
||||
SDL_GetWindowSize(window_renderer.window, &w, &h);
|
||||
|
||||
lua_pushstring(L, "touchpressed");
|
||||
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
|
||||
|
@ -344,7 +344,7 @@ top:
|
|||
return 4;
|
||||
|
||||
case SDL_FINGERUP:
|
||||
SDL_GetWindowSize(window_renderer->window, &w, &h);
|
||||
SDL_GetWindowSize(window_renderer.window, &w, &h);
|
||||
|
||||
lua_pushstring(L, "touchreleased");
|
||||
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
|
||||
|
@ -360,7 +360,7 @@ top:
|
|||
e.tfinger.dx += event_plus.tfinger.dx;
|
||||
e.tfinger.dy += event_plus.tfinger.dy;
|
||||
}
|
||||
SDL_GetWindowSize(window_renderer->window, &w, &h);
|
||||
SDL_GetWindowSize(window_renderer.window, &w, &h);
|
||||
|
||||
lua_pushstring(L, "touchmoved");
|
||||
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
|
||||
|
@ -369,21 +369,6 @@ top:
|
|||
lua_pushinteger(L, (lua_Integer)(e.tfinger.dy * h));
|
||||
lua_pushinteger(L, e.tfinger.fingerId);
|
||||
return 6;
|
||||
case SDL_APP_WILLENTERFOREGROUND:
|
||||
case SDL_APP_DIDENTERFOREGROUND:
|
||||
#ifdef LITE_USE_SDL_RENDERER
|
||||
rencache_invalidate();
|
||||
#else
|
||||
SDL_UpdateWindowSurface(window_renderer->window);
|
||||
#endif
|
||||
lua_pushstring(L, e.type == SDL_APP_WILLENTERFOREGROUND ? "enteringforeground" : "enteredforeground");
|
||||
return 1;
|
||||
case SDL_APP_WILLENTERBACKGROUND:
|
||||
lua_pushstring(L, "enteringbackground");
|
||||
return 1;
|
||||
case SDL_APP_DIDENTERBACKGROUND:
|
||||
lua_pushstring(L, "enteredbackground");
|
||||
return 1;
|
||||
|
||||
default:
|
||||
goto top;
|
||||
|
@ -440,7 +425,7 @@ static int f_set_cursor(lua_State *L) {
|
|||
|
||||
static int f_set_window_title(lua_State *L) {
|
||||
const char *title = luaL_checkstring(L, 1);
|
||||
SDL_SetWindowTitle(window_renderer->window, title);
|
||||
SDL_SetWindowTitle(window_renderer.window, title);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -450,39 +435,39 @@ enum { WIN_NORMAL, WIN_MINIMIZED, WIN_MAXIMIZED, WIN_FULLSCREEN };
|
|||
|
||||
static int f_set_window_mode(lua_State *L) {
|
||||
int n = luaL_checkoption(L, 1, "normal", window_opts);
|
||||
SDL_SetWindowFullscreen(window_renderer->window,
|
||||
SDL_SetWindowFullscreen(window_renderer.window,
|
||||
n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
if (n == WIN_NORMAL) { SDL_RestoreWindow(window_renderer->window); }
|
||||
if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window_renderer->window); }
|
||||
if (n == WIN_MINIMIZED) { SDL_MinimizeWindow(window_renderer->window); }
|
||||
if (n == WIN_NORMAL) { SDL_RestoreWindow(window_renderer.window); }
|
||||
if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window_renderer.window); }
|
||||
if (n == WIN_MINIMIZED) { SDL_MinimizeWindow(window_renderer.window); }
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_set_window_bordered(lua_State *L) {
|
||||
int bordered = lua_toboolean(L, 1);
|
||||
SDL_SetWindowBordered(window_renderer->window, bordered);
|
||||
SDL_SetWindowBordered(window_renderer.window, bordered);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_set_window_hit_test(lua_State *L) {
|
||||
if (lua_gettop(L) == 0) {
|
||||
SDL_SetWindowHitTest(window_renderer->window, NULL, NULL);
|
||||
SDL_SetWindowHitTest(window_renderer.window, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
window_hit_info->title_height = luaL_checknumber(L, 1);
|
||||
window_hit_info->controls_width = luaL_checknumber(L, 2);
|
||||
window_hit_info->resize_border = luaL_checknumber(L, 3);
|
||||
SDL_SetWindowHitTest(window_renderer->window, hit_test, window_hit_info);
|
||||
SDL_SetWindowHitTest(window_renderer.window, hit_test, window_hit_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_get_window_size(lua_State *L) {
|
||||
int x, y, w, h;
|
||||
SDL_GetWindowSize(window_renderer->window, &w, &h);
|
||||
SDL_GetWindowPosition(window_renderer->window, &x, &y);
|
||||
SDL_GetWindowSize(window_renderer.window, &w, &h);
|
||||
SDL_GetWindowPosition(window_renderer.window, &x, &y);
|
||||
lua_pushinteger(L, w);
|
||||
lua_pushinteger(L, h);
|
||||
lua_pushinteger(L, x);
|
||||
|
@ -496,22 +481,22 @@ static int f_set_window_size(lua_State *L) {
|
|||
double h = luaL_checknumber(L, 2);
|
||||
double x = luaL_checknumber(L, 3);
|
||||
double y = luaL_checknumber(L, 4);
|
||||
SDL_SetWindowSize(window_renderer->window, w, h);
|
||||
SDL_SetWindowPosition(window_renderer->window, x, y);
|
||||
ren_resize_window(window_renderer);
|
||||
SDL_SetWindowSize(window_renderer.window, w, h);
|
||||
SDL_SetWindowPosition(window_renderer.window, x, y);
|
||||
ren_resize_window(&window_renderer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_window_has_focus(lua_State *L) {
|
||||
unsigned flags = SDL_GetWindowFlags(window_renderer->window);
|
||||
unsigned flags = SDL_GetWindowFlags(window_renderer.window);
|
||||
lua_pushboolean(L, flags & SDL_WINDOW_INPUT_FOCUS);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int f_get_window_mode(lua_State *L) {
|
||||
unsigned flags = SDL_GetWindowFlags(window_renderer->window);
|
||||
unsigned flags = SDL_GetWindowFlags(window_renderer.window);
|
||||
if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) {
|
||||
lua_pushstring(L, "fullscreen");
|
||||
} else if (flags & SDL_WINDOW_MINIMIZED) {
|
||||
|
@ -549,8 +534,8 @@ static int f_raise_window(lua_State *L) {
|
|||
to allow the window to be focused. Also on wayland the raise window event
|
||||
may not always be obeyed.
|
||||
*/
|
||||
SDL_SetWindowInputFocus(window_renderer->window);
|
||||
SDL_RaiseWindow(window_renderer->window);
|
||||
SDL_SetWindowInputFocus(window_renderer.window);
|
||||
SDL_RaiseWindow(window_renderer.window);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -891,6 +876,13 @@ static int f_exec(lua_State *L) {
|
|||
#if _WIN32
|
||||
sprintf(buf, "cmd /c \"%s\"", cmd);
|
||||
WinExec(buf, SW_HIDE);
|
||||
#elif defined(__amigaos4__)
|
||||
SystemTags( cmd,
|
||||
SYS_Input, ZERO,
|
||||
SYS_Output, NULL,
|
||||
SYS_Error, ZERO,
|
||||
SYS_Asynch, TRUE,
|
||||
TAG_DONE);
|
||||
#else
|
||||
sprintf(buf, "%s &", cmd);
|
||||
int res = system(buf);
|
||||
|
@ -931,7 +923,7 @@ static int f_fuzzy_match(lua_State *L) {
|
|||
|
||||
static int f_set_window_opacity(lua_State *L) {
|
||||
double n = luaL_checknumber(L, 1);
|
||||
int r = SDL_SetWindowOpacity(window_renderer->window, n);
|
||||
int r = SDL_SetWindowOpacity(window_renderer.window, n);
|
||||
lua_pushboolean(L, r > -1);
|
||||
return 1;
|
||||
}
|
||||
|
@ -1087,7 +1079,6 @@ static int f_path_compare(lua_State *L) {
|
|||
size_t offset = 0, i, j;
|
||||
for (i = 0; i < len1 && i < len2; i++) {
|
||||
if (path1[i] != path2[i]) break;
|
||||
if (isdigit(path1[i])) break;
|
||||
if (path1[i] == PATHSEP) {
|
||||
offset = i + 1;
|
||||
}
|
||||
|
@ -1158,15 +1149,6 @@ static int f_path_compare(lua_State *L) {
|
|||
}
|
||||
|
||||
|
||||
static int f_text_input(lua_State* L) {
|
||||
if (lua_toboolean(L, 1))
|
||||
SDL_StartTextInput();
|
||||
else
|
||||
SDL_StopTextInput();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg lib[] = {
|
||||
{ "poll_event", f_poll_event },
|
||||
{ "wait_event", f_wait_event },
|
||||
|
@ -1200,7 +1182,6 @@ static const luaL_Reg lib[] = {
|
|||
{ "load_native_plugin", f_load_native_plugin },
|
||||
{ "path_compare", f_path_compare },
|
||||
{ "get_fs_type", f_get_fs_type },
|
||||
{ "text_input", f_text_input },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
@ -1212,4 +1193,3 @@ int luaopen_system(lua_State *L) {
|
|||
luaL_newlib(L, lib);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
57
src/main.c
57
src/main.c
|
@ -8,7 +8,7 @@
|
|||
#include <signal.h>
|
||||
|
||||
#if defined(__amigaos4__) || defined(__morphos__)
|
||||
#define VSTRING "Lite XL 2.1.2r1 (28.12.2023)"
|
||||
#define VSTRING "Lite XL 2.1.3r2 (18.03.2024)"
|
||||
#define VERSTAG "\0$VER: " VSTRING
|
||||
#endif
|
||||
|
||||
|
@ -20,11 +20,13 @@
|
|||
#include <mach-o/dyld.h>
|
||||
#elif defined(__amigaos4__)
|
||||
#include <locale.h>
|
||||
#include <workbench/startup.h>
|
||||
#include "platform/codesets.h"
|
||||
#include "platform/amigaos4.h"
|
||||
static CONST_STRPTR stack USED = "$STACK:102400";
|
||||
static CONST_STRPTR version USED = VERSTAG;
|
||||
#elif defined(__morphos__)
|
||||
#include "platform/codesets.h"
|
||||
#include "platform/morphos.h"
|
||||
unsigned long __stack = 1000000;
|
||||
UBYTE VString[] = VERSTAG;
|
||||
|
@ -111,8 +113,6 @@ void set_macos_bundle_resources(lua_State *L);
|
|||
// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-140
|
||||
#if defined(__x86_64__) || defined(_M_AMD64) || defined(__MINGW64__)
|
||||
#define ARCH_PROCESSOR "x86_64"
|
||||
#elif __arm__
|
||||
#define ARCH_PROCESSOR "arm"
|
||||
#elif defined(__amigaos4__) || defined(__morphos__)
|
||||
#define ARCH_PROCESSOR "ppc"
|
||||
#elif defined(__i386__) || defined(_M_IX86) || defined(__MINGW32__)
|
||||
|
@ -148,16 +148,17 @@ int main(int argc, char **argv) {
|
|||
|
||||
#if defined(__amigaos4__) || defined(__morphos__)
|
||||
setlocale(LC_ALL, "C");
|
||||
#endif
|
||||
#if defined(__amigaos4__)
|
||||
OpenLibs();
|
||||
int libsOpened = OpenLibs();
|
||||
if (libsOpened != RETURN_OK)
|
||||
{
|
||||
return libsOpened;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||||
fprintf(stderr, "Error initializing sdl: %s", SDL_GetError());
|
||||
exit(1);
|
||||
|
@ -209,7 +210,7 @@ int main(int argc, char **argv) {
|
|||
fprintf(stderr, "Error creating lite-xl window: %s", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
window_renderer = ren_init(window);
|
||||
ren_init(window);
|
||||
|
||||
lua_State *L;
|
||||
init_lua:
|
||||
|
@ -223,6 +224,27 @@ init_lua:
|
|||
lua_pushstring(L, argv[i]);
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
}
|
||||
#if defined(__amigaos4__)
|
||||
if (!argc)
|
||||
{
|
||||
struct WBStartup *wbs = (struct WBStartup *)argv;
|
||||
if (wbs->sm_NumArgs > 1)
|
||||
{
|
||||
char result[MAX_DOS_PATH];
|
||||
for (int i = 0; i < wbs->sm_NumArgs; i++) {
|
||||
getFullPathFromLock(wbs->sm_ArgList[i].wa_Lock, result);
|
||||
if (result[strlen(result) -1] == ':')
|
||||
strcat(result, wbs->sm_ArgList[i].wa_Name);
|
||||
else
|
||||
sprintf(result, "%s/%s", result, wbs->sm_ArgList[i].wa_Name);
|
||||
|
||||
lua_pushstring(L, result);
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
lua_setglobal(L, "ARGS");
|
||||
|
||||
lua_pushstring(L, SDL_GetPlatform());
|
||||
|
@ -269,23 +291,22 @@ init_lua:
|
|||
" core.init()\n"
|
||||
" core.run()\n"
|
||||
"end, function(err)\n"
|
||||
" local error_path = 'error.txt'\n"
|
||||
" local error_dir\n"
|
||||
" io.stdout:write('Error: '..tostring(err)..'\\n')\n"
|
||||
" io.stdout:write(debug.traceback(nil, 2)..'\\n')\n"
|
||||
" if core and core.on_error then\n"
|
||||
" error_path = USERDIR .. PATHSEP .. error_path\n"
|
||||
" error_dir=USERDIR\n"
|
||||
" pcall(core.on_error, err)\n"
|
||||
" else\n"
|
||||
" local fp = io.open(error_path, 'wb')\n"
|
||||
" error_dir=system.absolute_path(error_dir)\n"
|
||||
" local fp = io.open('error.txt', 'wb')\n"
|
||||
" fp:write('Error: ' .. tostring(err) .. '\\n')\n"
|
||||
" fp:write(debug.traceback(nil, 2)..'\\n')\n"
|
||||
" fp:write(debug.traceback(nil, 4)..'\\n')\n"
|
||||
" fp:close()\n"
|
||||
" error_path = system.absolute_path(error_path)\n"
|
||||
" end\n"
|
||||
" system.show_fatal_error('Lite XL internal error',\n"
|
||||
" 'An internal error occurred in a critical part of the application.\\n\\n'..\n"
|
||||
" 'Error: '..tostring(err)..'\\n\\n'..\n"
|
||||
" 'Details can be found in \\\"'..error_path..'\\\"')\n"
|
||||
" 'Please verify the file \\\"error.txt\\\" in the directory '..error_dir)\n"
|
||||
" os.exit(1)\n"
|
||||
"end)\n"
|
||||
"return core and core.restart_request\n";
|
||||
|
@ -303,11 +324,11 @@ init_lua:
|
|||
|
||||
// This allows the window to be destroyed before lite-xl is done with
|
||||
// reaping child processes
|
||||
ren_free(window_renderer);
|
||||
ren_free_window_resources(&window_renderer);
|
||||
lua_close(L);
|
||||
|
||||
#if defined(__amigaos4__)
|
||||
CleanExit("JustExit");
|
||||
#if defined(__amigaos4__) || defined(__morphos__)
|
||||
CleanExit("FineExit");
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -70,3 +70,12 @@ char *_fullpath(const char *path)
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void getFullPathFromLock(BPTR lock, char *result)
|
||||
{
|
||||
if (lock)
|
||||
{
|
||||
DevNameFromLock(lock, result, MAX_DOS_PATH, DN_FULLPATH);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
|
||||
|
||||
char *_fullpath(const char *);
|
||||
|
||||
void getFullPathFromLock(BPTR, char *);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Heavily inspired from the encoding plugin
|
||||
* https://github.com/jgmdev/lite-xl-encoding
|
||||
*/
|
||||
#include <SDL2/SDL_stdinc.h>
|
||||
#include <SDL_stdinc.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <lua.h>
|
||||
|
@ -14,8 +14,14 @@
|
|||
|
||||
#include "codesets.h"
|
||||
|
||||
#if defined(__amigaos4__)
|
||||
struct Library *CodesetsBase = NULL;
|
||||
struct CodesetsIFace *ICodesets = NULL;
|
||||
#endif
|
||||
|
||||
#if defined(__morphos__)
|
||||
struct Library *CharsetsBase = NULL;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
const char* charset;
|
||||
|
@ -87,6 +93,7 @@ static const char* encoding_charset_from_bom(
|
|||
// Lua methods for codesets
|
||||
|
||||
int Lcodesets_detect(lua_State *L) {
|
||||
#if defined(__amigaos4__)
|
||||
const char* filename = luaL_checkstring(L, 1);
|
||||
|
||||
BPTR fileHandle = FOpen(filename, MODE_OLDFILE, 0);
|
||||
|
@ -134,12 +141,25 @@ int Lcodesets_detect(lua_State *L) {
|
|||
}
|
||||
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
#if defined(__morphos__)
|
||||
lua_pushstring(L, "could not detect the file encoding");
|
||||
return 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
int Lcodesets_systemCodeset(lua_State *L) {
|
||||
#if defined(__amigaos4__)
|
||||
struct codeset *systemCodeset;
|
||||
systemCodeset = CodesetsFindA(NULL, NULL);
|
||||
lua_pushstring(L, systemCodeset->name);
|
||||
#endif
|
||||
#if defined(__morphos__)
|
||||
char buf[16];
|
||||
GetSystemCharset(buf, 16);
|
||||
lua_pushstring(L, buf);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -148,7 +168,13 @@ int Lcodesets_convert(lua_State *L) {
|
|||
const char* to = luaL_checkstring(L, 1);
|
||||
const char* from = luaL_checkstring(L, 2);
|
||||
size_t text_len = 0;
|
||||
#if defined(__amigaos4__)
|
||||
const char* text = luaL_checklstring(L, 3, &text_len);
|
||||
#endif
|
||||
#if defined(__morphos__)
|
||||
APTR text = luaL_checklstring(L, 3, &text_len);
|
||||
#endif
|
||||
|
||||
/* conversion options */
|
||||
bool strict = false;
|
||||
bool handle_to_bom = false;
|
||||
|
@ -156,6 +182,12 @@ int Lcodesets_convert(lua_State *L) {
|
|||
const unsigned char* bom;
|
||||
size_t bom_len = 0;
|
||||
|
||||
if (text_len == 0)
|
||||
{
|
||||
lua_pushlstring(L, text, text_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lua_gettop(L) > 3 && lua_istable(L, 4)) {
|
||||
lua_getfield(L, 4, "handle_to_bom");
|
||||
if (lua_isboolean(L, -1)) {
|
||||
|
@ -176,12 +208,13 @@ int Lcodesets_convert(lua_State *L) {
|
|||
encoding_charset_from_bom(text, text_len, &bom_len);
|
||||
}
|
||||
|
||||
#if defined(__amigaos4__)
|
||||
char *output;
|
||||
ULONG output_len;
|
||||
struct codeset *srcCodeset;
|
||||
struct codeset *destCodeset;
|
||||
ULONG errNum = 0;
|
||||
|
||||
struct codeset *srcCodeset;
|
||||
struct codeset *destCodeset;
|
||||
srcCodeset = CodesetsFind(from, CSA_FallbackToDefault, FALSE, TAG_DONE);
|
||||
// srcCodeset = CodesetsFindBest(CSA_Source, text,
|
||||
// CSA_ErrPtr, &errNum,
|
||||
|
@ -206,6 +239,39 @@ int Lcodesets_convert(lua_State *L) {
|
|||
CSA_Source, text,
|
||||
CSA_DestLenPtr, &output_len,
|
||||
TAG_DONE);
|
||||
#endif
|
||||
|
||||
#if defined(__morphos__)
|
||||
// LONG output_len = 0;
|
||||
|
||||
ULONG fromMib = GetCharsetNumber(from, CSF_IANA_MIMENAME);
|
||||
if (fromMib == 0)
|
||||
fromMib = GetCharsetNumber(from, CSF_IANA_NAME);
|
||||
if (fromMib == 0)
|
||||
fromMib = GetCharsetNumber(from, CSF_IANA_ALIAS);
|
||||
|
||||
ULONG toMib = GetCharsetNumber(to, CSF_IANA_MIMENAME);
|
||||
if (toMib == 0)
|
||||
toMib = GetCharsetNumber(to, CSF_IANA_NAME);
|
||||
if (toMib == 0)
|
||||
toMib = GetCharsetNumber(to, CSF_IANA_ALIAS);
|
||||
|
||||
LONG output_len = GetByteSize((APTR)text, text_len, fromMib, toMib);
|
||||
char *output = calloc(output_len, sizeof(char) + 1);
|
||||
LONG dstEnc = 0;
|
||||
struct TagItem tags[] = { { CST_DoNotTerminate, FALSE }, { CST_GetDestEncoding, &dstEnc }, { TAG_DONE, 0 } };
|
||||
LONG result = ConvertTagList((APTR)text, text_len, (APTR)output, output_len, fromMib, toMib, tags);
|
||||
|
||||
if (result <= 0)
|
||||
{
|
||||
lua_pushfstring(L, "failed converting from '%s' to '%s'", from, to);
|
||||
free(output);
|
||||
return 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// if (!output)
|
||||
// {
|
||||
// lua_pushnil(L);
|
||||
|
@ -245,12 +311,22 @@ int Lcodesets_convert(lua_State *L) {
|
|||
} else if (!output) {
|
||||
lua_pushnil(L);
|
||||
lua_pushfstring(L, "failed converting from '%s' to '%s'", from, to);
|
||||
#if defined(__amigaos4__)
|
||||
CodesetsFreeA(output, NULL);
|
||||
#endif
|
||||
#if defined(__morphos__)
|
||||
free(output);
|
||||
#endif
|
||||
return 2;
|
||||
}
|
||||
|
||||
lua_pushlstring(L, output, output_len);
|
||||
#if defined(__amigaos4__)
|
||||
CodesetsFreeA(output, NULL);
|
||||
#endif
|
||||
#if defined(__morphos__)
|
||||
free(output);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -354,21 +430,32 @@ int luaopen_codesets (lua_State *L) {
|
|||
|
||||
int OpenLibs(void)
|
||||
{
|
||||
#if defined(__amigaos4__)
|
||||
if ((CodesetsBase = OpenLibrary( "codesets.library", 6 )))
|
||||
{
|
||||
ICodesets = (struct CodesetsIFace *)GetInterface( CodesetsBase, "main", 1L, NULL );
|
||||
if(!ICodesets) return CleanExit("Can't open codesets.library Interface");
|
||||
}
|
||||
else return CleanExit("Can't open codesets.library version 6");
|
||||
#endif
|
||||
#if defined(__morphos__)
|
||||
if ((CharsetsBase = OpenLibrary( "charsets.library", 53 )) == NULL)
|
||||
return CleanExit("Can't open charsets.library version 53");
|
||||
#endif
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
int CleanExit(const char *str)
|
||||
{
|
||||
#if defined(__amigaos4__)
|
||||
if(ICodesets) DropInterface((struct Interface *) ICodesets);
|
||||
if(CodesetsBase) CloseLibrary(CodesetsBase);
|
||||
#endif
|
||||
#if defined(__morphos__)
|
||||
if(CharsetsBase) CloseLibrary(CharsetsBase);
|
||||
#endif
|
||||
|
||||
if(strcmp(str, "JustExit"))
|
||||
if(strcmp(str, "FineExit"))
|
||||
{
|
||||
printf("Error::%s\n", str);
|
||||
return RETURN_ERROR;
|
||||
|
|
|
@ -3,10 +3,14 @@
|
|||
|
||||
#include <proto/dos.h>
|
||||
#include <proto/exec.h>
|
||||
#if defined(__amigaos4__)
|
||||
#include <proto/codesets.h>
|
||||
#endif
|
||||
#if defined(__morphos__)
|
||||
#include <proto/charsets.h>
|
||||
#endif
|
||||
|
||||
int OpenLibs(void);
|
||||
int CleanExit(const char *);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -14,4 +14,3 @@ void rencache_begin_frame(RenWindow *window_renderer);
|
|||
void rencache_end_frame(RenWindow *window_renderer);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#define MAX_LOADABLE_GLYPHSETS (MAX_UNICODE / GLYPHSET_SIZE)
|
||||
#define SUBPIXEL_BITMAPS_CACHED 3
|
||||
|
||||
RenWindow* window_renderer = NULL;
|
||||
RenWindow window_renderer = {0};
|
||||
static FT_Library library;
|
||||
|
||||
// draw_rect_surface is used as a 1x1 surface to simplify ren_draw_rect with blending
|
||||
|
@ -236,7 +236,7 @@ static void font_file_close(FT_Stream stream) {
|
|||
RenFont* ren_font_load(RenWindow *window_renderer, const char* path, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style) {
|
||||
RenFont *font = NULL;
|
||||
FT_Face face = NULL;
|
||||
|
||||
|
||||
SDL_RWops *file = SDL_RWFromFile(path, "rb");
|
||||
if (!file)
|
||||
goto rwops_failure;
|
||||
|
@ -501,35 +501,31 @@ void ren_draw_rect(RenSurface *rs, RenRect rect, RenColor color) {
|
|||
}
|
||||
|
||||
/*************** Window Management ****************/
|
||||
RenWindow* ren_init(SDL_Window *win) {
|
||||
assert(win);
|
||||
int error = FT_Init_FreeType( &library );
|
||||
if ( error ) {
|
||||
fprintf(stderr, "internal font error when starting the application\n");
|
||||
return NULL;
|
||||
}
|
||||
RenWindow* window_renderer = malloc(sizeof(RenWindow));
|
||||
|
||||
window_renderer->window = win;
|
||||
renwin_init_surface(window_renderer);
|
||||
renwin_init_command_buf(window_renderer);
|
||||
renwin_clip_to_surface(window_renderer);
|
||||
draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32,
|
||||
0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
|
||||
|
||||
return window_renderer;
|
||||
}
|
||||
|
||||
void ren_free(RenWindow* window_renderer) {
|
||||
assert(window_renderer);
|
||||
void ren_free_window_resources(RenWindow *window_renderer) {
|
||||
renwin_free(window_renderer);
|
||||
SDL_FreeSurface(draw_rect_surface);
|
||||
free(window_renderer->command_buf);
|
||||
window_renderer->command_buf = NULL;
|
||||
window_renderer->command_buf_size = 0;
|
||||
free(window_renderer);
|
||||
}
|
||||
|
||||
// TODO remove global and return RenWindow*
|
||||
void ren_init(SDL_Window *win) {
|
||||
assert(win);
|
||||
int error = FT_Init_FreeType( &library );
|
||||
if ( error ) {
|
||||
fprintf(stderr, "internal font error when starting the application\n");
|
||||
return;
|
||||
}
|
||||
window_renderer.window = win;
|
||||
renwin_init_surface(&window_renderer);
|
||||
renwin_init_command_buf(&window_renderer);
|
||||
renwin_clip_to_surface(&window_renderer);
|
||||
draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32,
|
||||
0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
|
||||
}
|
||||
|
||||
|
||||
void ren_resize_window(RenWindow *window_renderer) {
|
||||
renwin_resize_surface(window_renderer);
|
||||
renwin_update_scale(window_renderer);
|
||||
|
|
|
@ -23,7 +23,7 @@ typedef struct { SDL_Surface *surface; int scale; } RenSurface;
|
|||
|
||||
struct RenWindow;
|
||||
typedef struct RenWindow RenWindow;
|
||||
extern RenWindow* window_renderer;
|
||||
extern RenWindow window_renderer;
|
||||
|
||||
RenFont* ren_font_load(RenWindow *window_renderer, const char *filename, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style);
|
||||
RenFont* ren_font_copy(RenWindow *window_renderer, RenFont* font, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, int style);
|
||||
|
@ -39,12 +39,12 @@ double ren_draw_text(RenSurface *rs, RenFont **font, const char *text, size_t le
|
|||
|
||||
void ren_draw_rect(RenSurface *rs, RenRect rect, RenColor color);
|
||||
|
||||
RenWindow* ren_init(SDL_Window *win);
|
||||
void ren_free(RenWindow* window_renderer);
|
||||
void ren_init(SDL_Window *win);
|
||||
void ren_resize_window(RenWindow *window_renderer);
|
||||
void ren_update_rects(RenWindow *window_renderer, RenRect *rects, int count);
|
||||
void ren_set_clip_rect(RenWindow *window_renderer, RenRect rect);
|
||||
void ren_get_size(RenWindow *window_renderer, int *x, int *y); /* Reports the size in points. */
|
||||
void ren_free_window_resources(RenWindow *window_renderer);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -149,4 +149,3 @@ void renwin_free(RenWindow *ren) {
|
|||
SDL_FreeSurface(ren->rensurface.surface);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
[wrap-file]
|
||||
directory = lua-5.4.6
|
||||
source_url = https://www.lua.org/ftp/lua-5.4.6.tar.gz
|
||||
source_filename = lua-5.4.6.tar.gz
|
||||
source_hash = 7d5ea1b9cb6aa0b59ca3dde1c6adcb57ef83a1ba8e5432c0ecd06bf439b3ad88
|
||||
patch_filename = lua_5.4.6-3_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.6-3/get_patch
|
||||
patch_hash = 9b72a95422fd47f79f969d9abdb589ee95712d5512a5246f94e7e4f63d2cb7b7
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/lua_5.4.6-3/lua-5.4.6.tar.gz
|
||||
wrapdb_version = 5.4.6-3
|
||||
directory = lua-5.4.4
|
||||
source_url = https://www.lua.org/ftp/lua-5.4.4.tar.gz
|
||||
source_filename = lua-5.4.4.tar.gz
|
||||
source_hash = 164c7849653b80ae67bec4b7473b884bf5cc8d2dca05653475ec2ed27b9ebf61
|
||||
patch_filename = lua_5.4.4-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.4-1/get_patch
|
||||
patch_hash = e61cd965c629d6543176f41a9f1cb9050edfd1566cf00ce768ff211086e40bdc
|
||||
|
||||
[provide]
|
||||
lua-5.4 = lua_dep
|
||||
lua = lua_dep
|
||||
|
||||
|
|
Loading…
Reference in New Issue