Compare commits

..

1 Commits

Author SHA1 Message Date
Francesco Abbate 1f276c0bcb Fix inactive divider intercepting mouse clicks
In the function Node:get_divider_overlapping_point() we check if we
hit a divider (separator between two nodes). If yes the event is
intercepted and used to set the cursor and drag the separator if
appropriate.

In reality, on mouse move events, when one of the node is a split
and one of its child is not resizable we don't set the cursor to
and we don't intercept the event. However on a mouse pressed event
the event was intercepted regardless of the fact that the child
nodes are resizable or not. This latter behavior was unwanted as it
prevents mouse clicks to be processed because of a divided that is
inactive.

In addition it prevented processing of mouse clicks when the child
node was invisible leading to issue #363. For this latter the issue
was the invisible NagView in the upper part of the window.

To fix the problem we provide a divider with
Node:get_divider_overlapping_point() only if its child node are
resizable. In this way the mouse clicks or movements are intercepted
only if the divider is actually active.
2021-08-04 22:13:56 +02:00
132 changed files with 5547 additions and 9903 deletions

View File

@ -6,6 +6,3 @@ indent_size = 2
indent_style = space indent_style = space
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[meson.build]
indent_size = 4

35
.github/labeler.yml vendored
View File

@ -1,35 +0,0 @@
"Category: CI":
- .github/workflows/*
"Category: Meta":
- ./*
- .github/*
- .github/ISSUE_TEMPLATE/*
- .github/PULL_REQUEST_TEMPLATE/*
- .gitignore
"Category: Build System":
- meson.build
- meson_options.txt
- subprojects/*
"Category: Documentation":
- docs/**/*
"Category: Resources":
- resources/**/*
"Category: Themes":
- data/colors/*
"Category: Lua Core":
- data/core/**/*
"Category: Fonts":
- data/fonts/*
"Category: Plugins":
- data/plugins/*
"Category: C Core":
- src/**/*

View File

@ -1,16 +0,0 @@
name: "Pull Request Labeler"
on:
- pull_request_target
permissions:
pull-requests: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Apply Type Label
uses: actions/labeler@v3
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: "" # works around actions/labeler#104

View File

@ -1,44 +1,16 @@
name: CI name: CI
# All builds use lhelper only for releases,
# otherwise for normal builds dependencies are dynamically linked.
on: on:
push: push:
branches: branches:
- '*' - '*'
# tags:
# - 'v[0-9]*'
pull_request: pull_request:
branches: branches:
- '*' - '*'
jobs: jobs:
archive_source_code: build-linux:
name: Source Code Tarball name: Build Linux
runs-on: ubuntu-18.04
# Only on tags/releases
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v2
- name: Python Setup
uses: actions/setup-python@v2
with:
python-version: 3.6
- name: Install Dependencies
run: |
sudo apt-get install -qq ninja-build
pip3 install meson
- name: Package
shell: bash
run: bash scripts/package.sh --version ${GITHUB_REF##*/} --debug --source
- uses: actions/upload-artifact@v2
with:
name: Source Code Tarball
path: "lite-xl-*-src.tar.gz"
build_linux:
name: Linux
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
strategy: strategy:
matrix: matrix:
@ -49,204 +21,48 @@ jobs:
CC: ${{ matrix.config.cc }} CC: ${{ matrix.config.cc }}
CXX: ${{ matrix.config.cxx }} CXX: ${{ matrix.config.cxx }}
steps: steps:
- name: Set Environment Variables - uses: actions/checkout@v2
if: ${{ matrix.config.cc == 'gcc' }} - name: Set up Python
run: | uses: actions/setup-python@v2
echo "$HOME/.local/bin" >> "$GITHUB_PATH" with:
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV" python-version: 3.6
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-linux-$(uname -m)" >> "$GITHUB_ENV" - name: Install dependencies
- uses: actions/checkout@v2 run: |
- name: Python Setup sudo apt-get install -qq libsdl2-dev libfreetype6 ninja-build
uses: actions/setup-python@v2 pip3 install meson
with: - name: Build package
python-version: 3.6 run: bash build-packages.sh x86-64
- name: Update Packages - name: upload packages
run: sudo apt-get update uses: actions/upload-artifact@v2
- name: Install Dependencies with:
if: ${{ !startsWith(github.ref, 'refs/tags/') }} name: Ubuntu Package
run: bash scripts/install-dependencies.sh --debug path: lite-xl-linux-*.tar.gz
- name: Install Release Dependencies
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: |
bash scripts/install-dependencies.sh --debug --lhelper
bash scripts/lhelper.sh --debug
- name: Build
run: |
bash --version
bash scripts/build.sh --debug --forcefallback
- name: Package
if: ${{ matrix.config.cc == 'gcc' }}
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --binary
- name: AppImage
if: ${{ matrix.config.cc == 'gcc' && startsWith(github.ref, 'refs/tags/') }}
run: bash scripts/appimage.sh --nobuild --version ${INSTALL_REF}
- name: Upload Artifacts
uses: actions/upload-artifact@v2
if: ${{ matrix.config.cc == 'gcc' }}
with:
name: Linux Artifacts
path: |
${{ env.INSTALL_NAME }}.tar.gz
LiteXL-${{ env.INSTALL_REF }}-x86_64.AppImage
build_macos: build-macox:
name: macOS (x86_64) name: Build Mac OS X
runs-on: macos-10.15 runs-on: macos-10.15
env:
CC: clang
CXX: clang++
steps:
- name: System Information
run: |
system_profiler SPSoftwareDataType
bash --version
gcc -v
xcodebuild -version
- name: Set Environment Variables
run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-macos-$(uname -m)" >> "$GITHUB_ENV"
- uses: actions/checkout@v2
- name: Python Setup
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install Dependencies
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
run: bash scripts/install-dependencies.sh --debug
- name: Install Release Dependencies
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: |
bash scripts/install-dependencies.sh --debug --lhelper
bash scripts/lhelper.sh --debug
- name: Build
run: |
bash --version
bash scripts/build.sh --bundle --debug --forcefallback
- name: Error Logs
if: failure()
run: |
mkdir ${INSTALL_NAME}
cp /usr/var/lhenv/lite-xl/logs/* ${INSTALL_NAME}
tar czvf ${INSTALL_NAME}.tar.gz ${INSTALL_NAME}
# - name: Package
# if: ${{ !startsWith(github.ref, 'refs/tags/') }}
# run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons
- name: Create DMG Image
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --dmg
- name: Upload DMG Image
uses: actions/upload-artifact@v2
with:
name: macOS DMG Image
path: ${{ env.INSTALL_NAME }}.dmg
- name: Upload Error Logs
uses: actions/upload-artifact@v2
if: failure()
with:
name: Error Logs
path: ${{ env.INSTALL_NAME }}.tar.gz
build_windows_msys2:
name: Windows
runs-on: windows-2019
strategy: strategy:
matrix: matrix:
msystem: [MINGW32, MINGW64] config:
defaults: # - { name: "GCC", cc: gcc-10, cxx: g++-10 }
run: - { name: "clang", cc: clang, cxx: clang++ }
shell: msys2 {0} env:
CC: ${{ matrix.config.cc }}
CXX: ${{ matrix.config.cxx }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: msys2/setup-msys2@v2 - name: Set up Python
with: uses: actions/setup-python@v2
#msystem: MINGW64 with:
msystem: ${{ matrix.msystem }} python-version: 3.9
update: true - name: Install dependencies
install: >- run: |
base-devel pip3 install meson
git brew install ninja sdl2
zip - name: Build package
- name: Set Environment Variables run: bash build-packages.sh x86-64
run: | - name: upload packages
echo "$HOME/.local/bin" >> "$GITHUB_PATH" uses: actions/upload-artifact@v2
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-windows-$(uname -m)" >> "$GITHUB_ENV" with:
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV" name: Mac OS X Package
- name: Install Dependencies path: lite-xl-macosx-*.zip
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
run: bash scripts/install-dependencies.sh --debug
- name: Install Release Dependencies
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: bash scripts/install-dependencies.sh --debug --lhelper
- name: Build
run: |
bash --version
bash scripts/build.sh --debug --forcefallback
- name: Error Logs
if: failure()
run: |
mkdir ${INSTALL_NAME}
cp /usr/var/lhenv/lite-xl/logs/* ${INSTALL_NAME}
tar czvf ${INSTALL_NAME}.tar.gz ${INSTALL_NAME}
- name: Package
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --binary
- name: Build Installer
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: bash scripts/innosetup/innosetup.sh --debug
- name: Upload Artifacts
uses: actions/upload-artifact@v2
with:
name: Windows Artifacts
path: |
LiteXL*.exe
${{ env.INSTALL_NAME }}.zip
- name: Upload Error Logs
uses: actions/upload-artifact@v2
if: failure()
with:
name: Error Logs
path: ${{ env.INSTALL_NAME }}.tar.gz
deploy:
name: Deployment
runs-on: ubuntu-18.04
# if: startsWith(github.ref, 'refs/tags/')
if: false
needs:
- archive_source_code
- build_linux
- build_macos
- build_windows_msys2
steps:
- name: Set Environment Variables
run: echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
- uses: actions/download-artifact@v2
with:
name: Linux Artifacts
- uses: actions/download-artifact@v2
with:
name: macOS DMG Image
- uses: actions/download-artifact@v2
with:
name: Source Code Tarball
- uses: actions/download-artifact@v2
with:
name: Windows Artifacts
- name: Display File Information
shell: bash
run: ls -lR
# Note: not using `actions/create-release@v1`
# because it cannot update an existing release
# see https://github.com/actions/create-release/issues/29
- uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ env.INSTALL_REF }}
name: Release ${{ env.INSTALL_REF }}
draft: false
prerelease: false
files: |
lite-xl-${{ env.INSTALL_REF }}-*
LiteXL*.AppImage
LiteXL*.exe

11
.gitignore vendored
View File

@ -3,24 +3,17 @@ build*/
lhelper/ lhelper/
submodules/ submodules/
subprojects/lua/ subprojects/lua/
subprojects/libagg/
subprojects/reproc/ subprojects/reproc/
/appimage*
.ccls-cache .ccls-cache
.lite-debug.log .lite-debug.log
.run* .run*
*.diff *.diff
*.exe
*.tar.gz *.tar.gz
*.zip *.zip
*.DS_Store *.DS_Store
*App* *App*
appimage*
compile_commands.json compile_commands.json
error.txt error.txt
lite-xl* lite-xl*
LiteXL*
lite
.config/
*.lha
release_files
*.o

View File

@ -1,82 +0,0 @@
#
# Project: Lite XL
#
# Created on: 26-12-2021
#
LiteXL_OBJ := \
src/dirmonitor.o src/main.o src/rencache.o src/renderer.o \
src/renwindow.o src/api/api.o src/api/regex.o \
src/api/renderer.o src/api/system.o src/platform/amigaos4.o
outfile := lite
compiler := gcc
cxxcompiler := g++
INCPATH := -Isrc -Ilib/dmon -I/sdk/local/newlib/include/SDL2 -I/sdk/local/common/include/freetype2
DFLAGS := -D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR
# -DLITE_USE_SDL_RENDERER
# -Wextra -Wall
CFLAGS := -Werror -Wwrite-strings -O3 -g -std=gnu11 -fno-strict-aliasing
# "-gstabs -finstrument-functions -fno-inline -DPROFILING"
LFLAGS := -mcrt=newlib -static-libgcc -static-libstdc++ -lauto -lpcre2 -lSDL2 -llua -lagg -lfreetype -lm -lunix -lpthread -athread=native
# " -lprofyle"
.PHONY: LiteXL clean release
default: LiteXL
clean:
@echo "Cleaning compiler objects..."
@rm -f $(LiteXL_OBJ)
LiteXL: $(LiteXL_OBJ)
@echo "Linking LiteXL"
@$(cxxcompiler) -o $(outfile) $(LiteXL_OBJ) $(LFLAGS)
.c.o:
@echo "Compiling $<"
@$(compiler) -c $< -o $*.o $(CFLAGS) $(INCPATH) $(DFLAGS)
src/dirmonitor.o: src/dirmonitor.c src/platform/amigaos4.h
src/main.o: src/main.c src/api/api.h src/rencache.h \
src/renderer.h src/platform/amigaos4.h src/dirmonitor.h
src/rencache.o: src/rencache.c
src/renderer.o: src/renderer.c
src/renwindow.o: src/renwindow.c
src/api/api.o: src/api/api.c
src/api/regex.o: src/api/regex.c
src/api/renderer.o: src/api/renderer.c
src/api/system.o: src/api/system.c
src/platform/amigaos4.o: src/platform/amigaos4.c
release:
mkdir -p release/LiteXL2
cp release_files/* release/LiteXL2/ -r
mv release/LiteXL2/LiteXL2.info release/
cp data release/LiteXL2/ -r
cp changelog.md release/LiteXL2/
cp lite release/LiteXL2/
strip release/LiteXL2/lite
cp README.md release/LiteXL2/
cp README_OS4.md release/LiteXL2/
cp LICENSE release/LiteXL2/
lha -aeqr3 a LiteXL2_OS4.lha release/

117
README.md
View File

@ -1,26 +1,25 @@
# Lite XL # Lite XL
[![CI]](https://github.com/lite-xl/lite-xl/actions/workflows/build.yml)
[![Discord Badge Image]](https://discord.gg/RWzqC3nx7K) [![Discord Badge Image]](https://discord.gg/RWzqC3nx7K)
![screenshot-dark] ![screenshot-dark]
A lightweight text editor written in Lua, adapted from [lite]. A lightweight text editor written in Lua, adapted from [lite].
* **[Get Lite XL]** — Download for Windows, Linux and Mac OS. * **[Get Lite XL]** — Download for Windows, Linux and Mac OS (notarized app).
* **[Get plugins]** — Add additional functionality, adapted for Lite XL. * **[Get plugins]** — Add additional functionality, adapted for Lite XL.
* **[Get color themes]** — Add additional colors themes. * **[Get color themes]** — Add additional colors themes.
Please refer to our [website] for the user and developer documentation, Please refer to our [website] for the user and developer documentation,
including [build] instructions details. A quick build guide is described below. including [build] instructions.
Lite XL has support for high DPI display on Windows and Linux and, Lite XL has support for high DPI display on Windows and Linux and,
since 1.16.7 release, it supports **retina displays** on macOS. since 1.16.7 release, it supports **retina displays** on macOS.
Please note that Lite XL is compatible with lite for most plugins and all color themes. Please note that Lite XL is compatible with lite for most plugins and all color themes.
We provide a separate lite-xl-plugins repository for Lite XL, because in some cases We provide a separate lite-plugins repository for Lite XL, because in some cases
some adaptations may be needed to make them work better with Lite XL. some adaptations may be needed to make them work better with Lite XL.
The repository with modified plugins is https://github.com/lite-xl/lite-xl-plugins. The repository with modified plugins is https://github.com/franko/lite-plugins.
The changes and differences between Lite XL and rxi/lite are listed in the The changes and differences between Lite XL and rxi/lite are listed in the
[changelog]. [changelog].
@ -43,95 +42,13 @@ the [plugins repository] or in the [Lite XL plugins repository].
Additional color themes can be found in the [colors repository]. Additional color themes can be found in the [colors repository].
These color themes are bundled with all releases of Lite XL by default. These color themes are bundled with all releases of Lite XL by default.
## Quick Build Guide
If you compile Lite XL yourself, it is recommended to use the script
`build-packages.sh`:
```sh
bash build-packages.sh -h
```
The script will run Meson and create a tar compressed archive with the application or,
for Windows, a zip file. Lite XL can be easily installed
by unpacking the archive in any directory of your choice.
Otherwise the following is an example of basic commands if you want to customize
the build:
```sh
meson setup --buildtype=release --prefix <prefix> build
meson compile -C build
DESTDIR="$(pwd)/lite-xl" meson install --skip-subprojects -C build
```
where `<prefix>` might be one of `/`, `/usr` or `/opt`, the default is `/`.
To build a bundle application on macOS:
```sh
meson setup --buildtype=release --Dbundle=true --prefix / build
meson compile -C build
DESTDIR="$(pwd)/Lite XL.app" meson install --skip-subprojects -C build
```
Please note that the package is relocatable to any prefix and the option prefix
affects only the place where the application is actually installed.
## Installing Prebuilt
Head over to [releases](https://github.com/lite-xl/lite-xl/releases) and download the version for your operating system.
### Linux
Unzip the file and `cd` into the `lite-xl` directory:
```sh
tar -xzf <file>
cd lite-xl
```
To run lite-xl without installing:
```sh
cd bin
./lite-xl
```
To install lite-xl copy files over into appropriate directories:
```sh
mkdir -p $HOME/.local/bin && cp bin/lite-xl $HOME/.local/bin
cp -r share $HOME/.local
```
If `$HOME/.local/bin` is not in PATH:
```sh
echo -e 'export PATH=$PATH:$HOME/.local/bin' >> $HOME/.bashrc
```
To get the icon to show up in app launcher:
```sh
xdg-desktop-menu forceupdate
```
You may need to logout and login again to see icon in app launcher.
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/org.lite_xl.lite_xl.desktop \
$HOME/.local/share/metainfo/org.lite_xl.lite_xl.appdata.xml \
$HOME/.local/share/lite-xl
```
## Contributing ## Contributing
Any additional functionality that can be added through a plugin should be done Any additional functionality that can be added through a plugin should be done
as a plugin, after which a pull request to the [Lite XL plugins repository] can be made. as a plugin, after which a pull request to the [plugins repository] can be made.
If the plugin uses any Lite XL-specific functionality,
please open a pull request to the [Lite XL plugins repository].
Pull requests to improve or modify the editor itself are welcome. Pull requests to improve or modify the editor itself are welcome.
@ -143,17 +60,17 @@ the terms of the MIT license. See [LICENSE] for details.
See the [licenses] file for details on licenses used by the required dependencies. See the [licenses] file for details on licenses used by the required dependencies.
[CI]: https://github.com/lite-xl/lite-xl/actions/workflows/build.yml/badge.svg
[Discord Badge Image]: https://img.shields.io/discord/847122429742809208?label=discord&logo=discord [Discord Badge Image]: https://img.shields.io/discord/847122429742809208?label=discord&logo=discord
[screenshot-dark]: https://user-images.githubusercontent.com/433545/111063905-66943980-84b1-11eb-9040-3876f1133b20.png [screenshot-dark]: https://user-images.githubusercontent.com/433545/111063905-66943980-84b1-11eb-9040-3876f1133b20.png
[lite]: https://github.com/rxi/lite [lite]: https://github.com/rxi/lite
[website]: https://lite-xl.com [website]: https://lite-xl.github.io
[build]: https://lite-xl.com/en/documentation/build/ [build]: https://lite-xl.github.io/en/build
[Get Lite XL]: https://github.com/lite-xl/lite-xl/releases/latest [Get Lite XL]: https://github.com/franko/lite-xl/releases/latest
[Get plugins]: https://github.com/lite-xl/lite-xl-plugins [Get plugins]: https://github.com/franko/lite-plugins
[Get color themes]: https://github.com/lite-xl/lite-xl-colors [Get color themes]: https://github.com/rxi/lite-colors
[changelog]: https://github.com/lite-xl/lite-xl/blob/master/changelog.md [changelog]: https://github.com/franko/lite-xl/blob/master/changelog.md
[Lite XL plugins repository]: https://github.com/lite-xl/lite-xl-plugins [Lite XL plugins repository]: https://github.com/franko/lite-plugins
[colors repository]: https://github.com/lite-xl/lite-xl-colors [plugins repository]: https://github.com/rxi/lite-plugins
[colors repository]: https://github.com/rxi/lite-colors
[LICENSE]: LICENSE [LICENSE]: LICENSE
[licenses]: licenses/licenses.md [licenses]: licenses/licenses.md

View File

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

Binary file not shown.

View File

@ -1,164 +1,216 @@
#!/bin/bash #!/bin/bash
set -e
if [ ! -e "src/api/api.h" ]; then # strip-components is normally set to 1 to strip the initial "data" from the
echo "Please run this script from the root directory of Lite XL."; exit 1 # directory path.
fi copy_directory_from_repo () {
local tar_options=()
source scripts/common.sh if [[ $1 == --strip-components=* ]]; then
tar_options+=($1)
show_help() { shift
echo fi
echo "Usage: $0 <OPTIONS>" local dirname="$1"
echo local destdir="$2"
echo "Common options:" git archive "$use_branch" "$dirname" --format=tar | tar xf - -C "$destdir" "${tar_options[@]}"
echo
echo "-h --help Show this help and exit."
echo "-b --builddir DIRNAME Set the name of the build directory (not path)."
echo " Default: '$(get_default_build_dir)'."
echo "-p --prefix PREFIX Install directory prefix."
echo " Default: '/'."
echo " --debug Debug this script."
echo
echo "Build options:"
echo
echo "-f --forcefallback Force to build subprojects dependencies statically."
echo "-B --bundle Create an App bundle (macOS only)"
echo "-P --portable Create a portable package."
echo "-O --pgo Use profile guided optimizations (pgo)."
echo " Requires running the application iteractively."
echo
echo "Package options:"
echo
echo "-d --destdir DIRNAME Set the name of the package directory (not path)."
echo " Default: 'lite-xl'."
echo "-v --version VERSION Sets the version on the package name."
echo "-A --appimage Create an AppImage (Linux only)."
echo "-D --dmg Create a DMG disk image (macOS only)."
echo " Requires NPM and AppDMG."
echo "-I --innosetup Create an InnoSetup installer (Windows only)."
echo "-S --source Create a source code package,"
echo " including subprojects dependencies."
echo
} }
main() { # Check if build directory is ok to be used to build.
local build_dir build_dir_is_usable () {
local build_dir_option=() local build="$1"
local dest_dir if [[ $build == */* || -z "$build" ]]; then
local dest_dir_option=() echo "invalid build directory, no path allowed: \"$build\""
local prefix return 1
local prefix_option=() fi
local version git ls-files --error-unmatch "$build" &> /dev/null
local version_option=() if [ $? == 0 ]; then
local debug echo "invalid path, \"$build\" is under revision control"
local force_fallback return 1
local appimage fi
local bundle }
local innosetup
local portable
local pgo
for i in "$@"; do # Ordinary release build
case $i in lite_build () {
-h|--help) local build="$1"
show_help build_dir_is_usable "$build" || exit 1
exit 0 rm -fr "$build"
;; meson setup --buildtype=release "$build" || exit 1
-b|--builddir) ninja -C "$build" || exit 1
build_dir="$2" }
shift
shift # Build using Profile Guided Optimizations (PGO)
;; lite_build_pgo () {
-d|--destdir) local build="$1"
dest_dir="$2" build_dir_is_usable "$build" || exit 1
shift rm -fr "$build"
shift meson setup --buildtype=release -Db_pgo=generate "$build" || exit 1
;; ninja -C "$build" || exit 1
-f|--forcefallback) copy_directory_from_repo data "$build/src"
force_fallback="--forcefallback" "$build/src/lite-xl"
shift meson configure -Db_pgo=use "$build"
;; ninja -C "$build" || exit 1
-p|--prefix) }
prefix="$2"
shift lite_build_package_windows () {
shift local portable="-msys"
;; if [ "$1" == "-portable" ]; then
-v|--version) portable=""
version="$2" shift
shift fi
shift local build="$1"
;; local arch="$2"
-A|--appimage) local os="win"
appimage="--appimage" local pdir=".package-build/lite-xl"
shift if [ -z "$portable" ]; then
;; local bindir="$pdir"
-B|--bundle) local datadir="$pdir/data"
bundle="--bundle" else
shift local bindir="$pdir/bin"
;; local datadir="$pdir/share/lite-xl"
-D|--dmg) fi
dmg="--dmg" mkdir -p "$bindir"
shift mkdir -p "$datadir"
;; for module_name in core plugins colors fonts; do
-I|--innosetup) copy_directory_from_repo --strip-components=1 "data/$module_name" "$datadir"
innosetup="--innosetup"
shift
;;
-P|--portable)
portable="--portable"
shift
;;
-S|--source)
source="--source"
shift
;;
-O|--pgo)
pgo="--pgo"
shift
;;
--debug)
debug="--debug"
set -x
shift
;;
*)
# unknown option
;;
esac
done done
for module_name in plugins colors; do
cp -r "$build/third/data/$module_name" "$datadir"
done
cp "$build/src/lite-xl.exe" "$bindir"
strip --strip-all "$bindir/lite-xl.exe"
pushd ".package-build"
local package_name="lite-xl-$os-$arch$portable.zip"
zip "$package_name" -r "lite-xl"
mv "$package_name" ..
popd
rm -fr ".package-build"
echo "created package $package_name"
}
if [[ -n $1 ]]; then lite_build_package_macos () {
show_help local build="$1"
local arch="$2"
local os="macos"
local appdir=".package-build/lite-xl.app"
local bindir="$appdir/Contents/MacOS"
local datadir="$appdir/Contents/Resources"
mkdir -p "$bindir" "$datadir"
for module_name in core plugins colors fonts; do
copy_directory_from_repo --strip-components=1 "data/$module_name" "$datadir"
done
for module_name in plugins colors; do
cp -r "$build/third/data/$module_name" "$datadir"
done
cp resources/icons/icon.icns "$appdir/Contents/Resources/icon.icns"
cp resources/macos/Info.plist "$appdir/Contents/Info.plist"
cp "$build/src/lite-xl" "$bindir/lite-xl"
strip "$bindir/lite-xl"
pushd ".package-build"
local package_name="lite-xl-$os-$arch.zip"
zip "$package_name" -r "lite-xl.app"
mv "$package_name" ..
popd
rm -fr ".package-build"
echo "created package $package_name"
}
lite_build_package_linux () {
local portable=""
if [ "$1" == "-portable" ]; then
portable="-portable"
shift
fi
local build="$1"
local arch="$2"
local os="linux"
local pdir=".package-build/lite-xl"
if [ "$portable" == "-portable" ]; then
local bindir="$pdir"
local datadir="$pdir/data"
else
local bindir="$pdir/bin"
local datadir="$pdir/share/lite-xl"
fi
mkdir -p "$bindir"
mkdir -p "$datadir"
for module_name in core plugins colors fonts; do
copy_directory_from_repo --strip-components=1 "data/$module_name" "$datadir"
done
for module_name in plugins colors; do
cp -r "$build/third/data/$module_name" "$datadir"
done
cp "$build/src/lite-xl" "$bindir"
strip "$bindir/lite-xl"
if [ -z "$portable" ]; then
mkdir -p "$pdir/share/applications" "$pdir/share/icons/hicolor/scalable/apps"
cp "resources/linux/lite-xl.desktop" "$pdir/share/applications"
cp "resources/icons/lite-xl.svg" "$pdir/share/icons/hicolor/scalable/apps/lite-xl.svg"
fi
pushd ".package-build"
local package_name="lite-xl-$os-$arch$portable.tar.gz"
tar czf "$package_name" "lite-xl"
mv "$package_name" ..
popd
rm -fr ".package-build"
echo "created package $package_name"
}
lite_build_package () {
if [[ "$OSTYPE" == msys || "$OSTYPE" == win32 ]]; then
lite_build_package_windows "$@"
elif [[ "$OSTYPE" == "darwin"* ]]; then
lite_build_package_macos "$@"
elif [[ "$OSTYPE" == "linux"* || "$OSTYPE" == "freebsd"* ]]; then
lite_build_package_linux "$@"
else
echo "Unknown OS type \"$OSTYPE\""
exit 1 exit 1
fi fi
if [[ -n $build_dir ]]; then build_dir_option=("--builddir" "${build_dir}"); fi
if [[ -n $dest_dir ]]; then dest_dir_option=("--destdir" "${dest_dir}"); fi
if [[ -n $prefix ]]; then prefix_option=("--prefix" "${prefix}"); fi
if [[ -n $version ]]; then version_option=("--version" "${version}"); fi
source scripts/build.sh \
${build_dir_option[@]} \
${prefix_option[@]} \
$debug \
$force_fallback \
$bundle \
$portable \
$pgo
source scripts/package.sh \
${build_dir_option[@]} \
${dest_dir_option[@]} \
${prefix_option[@]} \
${version_option[@]} \
--binary \
--addons \
$debug \
$appimage \
$dmg \
$innosetup \
$source
} }
main "$@" lite_copy_third_party_modules () {
local build="$1"
curl --insecure -L "https://github.com/rxi/lite-colors/archive/master.zip" -o "$build/rxi-lite-colors.zip"
mkdir -p "$build/third/data/colors" "$build/third/data/plugins"
unzip "$build/rxi-lite-colors.zip" -d "$build"
mv "$build/lite-colors-master/colors" "$build/third/data"
rm -fr "$build/lite-colors-master"
}
unset arch
while [ ! -z {$1+x} ]; do
case $1 in
-pgo)
pgo=true
shift
;;
-branch=*)
use_branch="${1#-branch=}"
shift
;;
*)
arch="$1"
break
esac
done
if [ -z ${arch+set} ]; then
echo "usage: $0 [options] <arch>"
exit 1
fi
if [ -z ${use_branch+set} ]; then
use_branch="$(git rev-parse --abbrev-ref HEAD)"
fi
build_dir=".build-$arch"
if [ -z ${pgo+set} ]; then
lite_build "$build_dir"
else
lite_build_pgo "$build_dir"
fi
lite_copy_third_party_modules "$build_dir"
lite_build_package "$build_dir" "$arch"
if [[ ! ( "$OSTYPE" == "linux"* || "$OSTYPE" == "freebsd"* || "$OSTYPE" == "darwin"* ) ]]; then
lite_build_package -portable "$build_dir" "$arch"
fi

View File

@ -1,111 +1,5 @@
This files document the changes done in Lite XL for each release. This files document the changes done in Lite XL for each release.
### 2.0.3
Replace periodic rescan of project folder with a notification based system using the
[dmon library](https://github.com/septag/dmon). Improves performance especially for
large project folders since the application no longer needs to rescan.
The application also reports immediatly any change in the project directory even
when the application is unfocused.
Improved find-replace reverse and forward search.
Fixed a bug in incremental syntax highlighting affecting documents with multiple-lines
comments or strings.
The application now always shows the tabs in the documents' view even when a single
document is opened. Can be changed with the option `config.always_show_tabs`.
Fix problem with numeric keypad function keys not properly working.
Fix problem with pixel not correctly drawn at the window's right edge.
Treat correctly and open network paths on Windows.
Add some improvements for very slow network filesystems.
Fix problem with python syntax highliting, contributed by @dflock.
### 2.0.2
Fix problem project directory when starting the application from Launcher on macOS.
Improved LogView. Entries can now be expanded and there is a context menu to copy the item's content.
Change the behavior of `ctrl+d` to add a multi-cursor selection to the next occurrence.
The old behavior to move the selection to the next occurrence is now done using the shortcut `ctrl+f3`.
Added a command to create a multi-cursor with all the occurrences of the current selection.
Activated with the shortcut `ctrl+shift+l`.
Fix problem when trying to close an unsaved new document.
No longer shows an error for the `-psn` argument passed to the application on macOS.
Fix `treeview:open-in-system` command on Windows.
Fix rename command to update name of document if opened.
Improve the find and replace dialog so that previously used expressions can be recalled
using "up" and "down" keys.
Build package script rewrite with many improvements.
Use bigger fonts by default.
Other minor improvements and fixes.
With many thanks to the contributors: @adamharrison, @takase1121, @Guldoman, @redtide, @Timofffee, @boppyt, @Jan200101.
### 2.0.1
Fix a few bugs and we mandate the mod-version 2 for plugins.
This means that users should ensure they have up-to-date plugins for Lite XL 2.0.
Here some details about the bug fixes:
- fix a bug that created a fatal error when using the command to change project folder or when closing all the active documents
- add a limit to avoid scaling fonts too much and fix a related invalid memory access for very small fonts
- fix focus problem with NagView when switching project directory
- fix error that prevented the verification of plugins versions
- fix error on X11 that caused a bug window event on exit
### 2.0
The 2.0 version of lite contains *breaking changes* to lite, in terms of how plugin settings are structured;
any custom plugins may need to be adjusted accordingly (see note below about plugin namespacing).
Contains the following new features:
Full PCRE (regex) support for find and replace, as well as in language syntax definitions. Can be accessed
programatically via the lua `regex` module.
A full, finalized subprocess API, using libreproc. Subprocess can be started and interacted with using
`Process.new`.
Support for multi-cursor editing. Cursors can be created by either ctrl+clicking on the screen, or by using
the keyboard shortcuts ctrl+shift+up/down to create an additional cursor on the previous/next line.
All build systems other than meson removed.
A more organized directory structure has been implemented; in particular a docs folder which contains C api
documentation, and a resource folder which houses all build resources.
Plugin config namespacing has been implemented. This means that instead of using `config.myplugin.a`,
to read settings, and `config.myplugin = false` to disable plugins, this has been changed to
`config.plugins.myplugin.a`, and `config.plugins.myplugin = false` repsectively. This may require changes to
your user plugin, or to any custom plugins you have.
A context menu on right click has been added.
Changes to how we deal with indentation have been implemented; in particular, hitting home no longer brings you
to the start of a line, it'll bring you to the start of indentation, which is more in line with other editors.
Lineguide, and scale plugins moved into the core, and removed from `lite-plugins`. This may also require you to
adjust your personal plugin folder to remove these if they're present.
In addition, there have been many other small fixes and improvements, too numerous to list here.
### 1.16.11 ### 1.16.11
When opening directories with too many files lite-xl now keep diplaying files and directories in the treeview. When opening directories with too many files lite-xl now keep diplaying files and directories in the treeview.

View File

@ -1,51 +0,0 @@
local b05 = 'rgba(0,0,0,0.5)' local red = '#994D4D'
local b80 = '#333333' local orange = '#B3661A'
local b60 = '#808080' local green = '#52994D'
local b40 = '#ADADAD' local teal = '#4D9999'
local b20 = '#CECECE' local blue = '#1A66B3'
local b00 = '#E6E6E6' local magenta = '#994D99'
--------------------------=--------------------------
local style = require 'core.style'
local common = require 'core.common'
--------------------------=--------------------------
style.line_highlight = { common.color(b20) }
style.background = { common.color(b00) }
style.background2 = { common.color(b20) }
style.background3 = { common.color(b20) }
style.text = { common.color(b60) }
style.caret = { common.color(b80) }
style.accent = { common.color(b80) }
style.dim = { common.color(b60) }
style.divider = { common.color(b40) }
style.selection = { common.color(b40) }
style.line_number = { common.color(b60) }
style.line_number2 = { common.color(b80) }
style.scrollbar = { common.color(b40) }
style.scrollbar2 = { common.color(b60) }
style.nagbar = { common.color(red) }
style.nagbar_text = { common.color(b00) }
style.nagbar_dim = { common.color(b05) }
--------------------------=--------------------------
style.syntax = {}
style.syntax['normal'] = { common.color(b80) }
style.syntax['symbol'] = { common.color(b80) }
style.syntax['comment'] = { common.color(b60) }
style.syntax['keyword'] = { common.color(blue) }
style.syntax['keyword2'] = { common.color(red) }
style.syntax['number'] = { common.color(teal) }
style.syntax['literal'] = { common.color(blue) }
style.syntax['string'] = { common.color(green) }
style.syntax['operator'] = { common.color(magenta) }
style.syntax['function'] = { common.color(blue) }
--------------------------=--------------------------
style.syntax.paren1 = { common.color(magenta) }
style.syntax.paren2 = { common.color(orange) }
style.syntax.paren3 = { common.color(teal) }
style.syntax.paren4 = { common.color(blue) }
style.syntax.paren5 = { common.color(red) }
--------------------------=--------------------------
style.lint = {}
style.lint.info = { common.color(blue) }
style.lint.hint = { common.color(green) }
style.lint.warning = { common.color(red) }
style.lint.error = { common.color(orange) }

View File

@ -41,14 +41,11 @@ function command.get_all_valid()
return res return res
end end
function command.is_valid(name, ...)
return command.map[name] and command.map[name].predicate(...)
end
local function perform(name, ...) local function perform(name)
local cmd = command.map[name] local cmd = command.map[name]
if cmd and cmd.predicate(...) then if cmd and cmd.predicate() then
cmd.perform(...) cmd.perform()
return true return true
end end
return false return false

View File

@ -6,12 +6,10 @@ local LogView = require "core.logview"
local fullscreen = false local fullscreen = false
local restore_title_view = false
local function suggest_directory(text) local function suggest_directory(text)
text = common.home_expand(text) text = common.home_expand(text)
return common.home_encode_list((text == "" or text == common.home_expand(common.dirname(core.project_dir))) return common.home_encode_list(text == "" and core.recent_projects or common.dir_path_suggest(text))
and core.recent_projects or common.dir_path_suggest(text))
end end
command.add(nil, { command.add(nil, {
@ -29,12 +27,9 @@ command.add(nil, {
["core:toggle-fullscreen"] = function() ["core:toggle-fullscreen"] = function()
fullscreen = not fullscreen fullscreen = not fullscreen
if fullscreen then
restore_title_view = core.title_view.visible
end
system.set_window_mode(fullscreen and "fullscreen" or "normal") system.set_window_mode(fullscreen and "fullscreen" or "normal")
core.show_title_bar(not fullscreen and restore_title_view) core.show_title_bar(not fullscreen)
core.title_view:configure_hit_test(not fullscreen and restore_title_view) core.title_view:configure_hit_test(not fullscreen)
end, end,
["core:reload-module"] = function() ["core:reload-module"] = function()
@ -71,8 +66,8 @@ command.add(nil, {
end, end,
["core:find-file"] = function() ["core:find-file"] = function()
if not core.project_files_number() then if core.project_files_limit then
return command.perform "core:open-file" return command.perform "core:open-file"
end end
local files = {} local files = {}
for dir, item in core.get_project_files() do for dir, item in core.get_project_files() do
@ -152,10 +147,6 @@ command.add(nil, {
end, end,
["core:change-project-folder"] = function() ["core:change-project-folder"] = function()
local dirname = common.dirname(core.project_dir)
if dirname then
core.command_view:set_text(common.home_encode(dirname))
end
core.command_view:enter("Change Project Folder", function(text, item) core.command_view:enter("Change Project Folder", function(text, item)
text = system.absolute_path(common.home_expand(item and item.text or text)) text = system.absolute_path(common.home_expand(item and item.text or text))
if text == core.project_dir then return end if text == core.project_dir then return end
@ -164,15 +155,11 @@ command.add(nil, {
core.error("Cannot open folder %q", text) core.error("Cannot open folder %q", text)
return return
end end
core.confirm_close_docs(core.docs, core.open_folder_project, text) core.confirm_close_all(core.open_folder_project, text)
end, suggest_directory) end, suggest_directory)
end, end,
["core:open-project-folder"] = function() ["core:open-project-folder"] = function()
local dirname = common.dirname(core.project_dir)
if dirname then
core.command_view:set_text(common.home_encode(dirname))
end
core.command_view:enter("Open Project", function(text, item) core.command_view:enter("Open Project", function(text, item)
text = common.home_expand(item and item.text or text) text = common.home_expand(item and item.text or text)
local path_stat = system.get_file_info(text) local path_stat = system.get_file_info(text)
@ -196,6 +183,8 @@ command.add(nil, {
return return
end end
core.add_project_directory(system.absolute_path(text)) core.add_project_directory(system.absolute_path(text))
-- TODO: add the name of directory to prioritize
core.reschedule_project_scan()
end, suggest_directory) end, suggest_directory)
end, end,

View File

@ -16,6 +16,14 @@ local function doc()
end end
local function get_indent_string()
if config.tab_type == "hard" then
return "\t"
end
return string.rep(" ", config.indent_size)
end
local function doc_multiline_selections(sort) local function doc_multiline_selections(sort)
local iter, state, idx, line1, col1, line2, col2 = doc():get_selections(sort) local iter, state, idx, line1, col1, line2, col2 = doc():get_selections(sort)
return function() return function()
@ -59,14 +67,13 @@ local function cut_or_copy(delete)
doc().cursor_clipboard[idx] = "" doc().cursor_clipboard[idx] = ""
end end
end end
doc().cursor_clipboard["full"] = full_text
system.set_clipboard(full_text) system.set_clipboard(full_text)
end end
local function split_cursor(direction) local function split_cursor(direction)
local new_cursors = {} local new_cursors = {}
for _, line1, col1 in doc():get_selections() do for _, line1, col1 in doc():get_selections() do
if line1 + direction >= 1 and line1 + direction <= #doc().lines then if line1 > 1 and line1 < #doc().lines then
table.insert(new_cursors, { line1 + direction, col1 }) table.insert(new_cursors, { line1 + direction, col1 })
end end
end end
@ -74,31 +81,6 @@ local function split_cursor(direction)
core.blink_reset() core.blink_reset()
end end
local function set_cursor(x, y, snap_type)
local line, col = dv():resolve_screen_position(x, y)
doc():set_selection(line, col, line, col)
if snap_type == "word" or snap_type == "lines" then
command.perform("doc:select-" .. snap_type)
end
dv().mouse_selecting = { line, col, snap_type }
core.blink_reset()
end
local selection_commands = {
["doc:cut"] = function()
cut_or_copy(true)
end,
["doc:copy"] = function()
cut_or_copy(false)
end,
["doc:select-none"] = function()
local line, col = doc():get_selection()
doc():set_selection(line, col)
end
}
local commands = { local commands = {
["doc:undo"] = function() ["doc:undo"] = function()
doc():undo() doc():undo()
@ -108,14 +90,17 @@ local commands = {
doc():redo() doc():redo()
end, end,
["doc:cut"] = function()
cut_or_copy(true)
end,
["doc:copy"] = function()
cut_or_copy(false)
end,
["doc:paste"] = function() ["doc:paste"] = function()
local clipboard = system.get_clipboard()
-- If the clipboard has changed since our last look, use that instead
if doc().cursor_clipboard["full"] ~= clipboard then
doc().cursor_clipboard = {}
end
for idx, line1, col1, line2, col2 in doc():get_selections() do for idx, line1, col1, line2, col2 in doc():get_selections() do
local value = doc().cursor_clipboard[idx] or clipboard local value = doc().cursor_clipboard[idx] or system.get_clipboard()
doc():text_input(value:gsub("\r", ""), idx) doc():text_input(value:gsub("\r", ""), idx)
end end
end, end,
@ -156,12 +141,11 @@ local commands = {
end, end,
["doc:backspace"] = function() ["doc:backspace"] = function()
local _, indent_size = doc():get_indent_info()
for idx, line1, col1, line2, col2 in doc():get_selections() do for idx, line1, col1, line2, col2 in doc():get_selections() do
if line1 == line2 and col1 == col2 then if line1 == line2 and col1 == col2 then
local text = doc():get_text(line1, 1, line1, col1) local text = doc():get_text(line1, 1, line1, col1)
if #text >= indent_size and text:find("^ *$") then if #text >= config.indent_size and text:find("^ *$") then
doc():delete_to_cursor(idx, 0, -indent_size) doc():delete_to_cursor(idx, 0, -config.indent_size)
return return
end end
end end
@ -173,6 +157,21 @@ local commands = {
doc():set_selection(1, 1, math.huge, math.huge) doc():set_selection(1, 1, math.huge, math.huge)
end, end,
["doc:select-none"] = function()
local line, col = doc():get_selection()
doc():set_selection(line, col)
end,
["doc:indent"] = function()
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
local l1, c1, l2, c2 = doc():indent_text(false, line1, col1, line2, col2)
if l1 then
doc():set_selections(idx, l1, c1, l2, c2)
end
end
end,
["doc:select-lines"] = function() ["doc:select-lines"] = function()
for idx, line1, _, line2 in doc():get_selections(true) do for idx, line1, _, line2 in doc():get_selections(true) do
append_line_if_last_line(line2) append_line_if_last_line(line2)
@ -266,7 +265,7 @@ local commands = {
["doc:toggle-line-comments"] = function() ["doc:toggle-line-comments"] = function()
local comment = doc().syntax.comment local comment = doc().syntax.comment
if not comment then return end if not comment then return end
local indentation = doc():get_indent_string() local indentation = get_indent_string()
local comment_text = comment .. " " local comment_text = comment .. " "
for idx, line1, _, line2 in doc_multiline_selections(true) do for idx, line1, _, line2 in doc_multiline_selections(true) do
local uncomment = true local uncomment = true
@ -393,30 +392,6 @@ local commands = {
os.remove(filename) os.remove(filename)
core.log("Removed \"%s\"", filename) core.log("Removed \"%s\"", filename)
end, end,
["doc:select-to-cursor"] = function(x, y, clicks)
local line1, col1 = select(3, doc():get_selection())
local line2, col2 = dv():resolve_screen_position(x, y)
dv().mouse_selecting = { line1, col1, nil }
doc():set_selection(line2, col2, line1, col1)
end,
["doc:set-cursor"] = function(x, y)
set_cursor(x, y, "set")
end,
["doc:set-cursor-word"] = function(x, y)
set_cursor(x, y, "word")
end,
["doc:set-cursor-line"] = function(x, y, clicks)
set_cursor(x, y, "lines")
end,
["doc:split-cursor"] = function(x, y, clicks)
local line, col = dv():resolve_screen_position(x, y)
doc():add_selection(line, col, line, col)
end,
["doc:create-cursor-previous-line"] = function() ["doc:create-cursor-previous-line"] = function()
split_cursor(-1) split_cursor(-1)
@ -443,7 +418,6 @@ local translations = {
["start-of-line"] = translate.start_of_line, ["start-of-line"] = translate.start_of_line,
["end-of-line"] = translate.end_of_line, ["end-of-line"] = translate.end_of_line,
["start-of-word"] = translate.start_of_word, ["start-of-word"] = translate.start_of_word,
["start-of-indentation"] = translate.start_of_indentation,
["end-of-word"] = translate.end_of_word, ["end-of-word"] = translate.end_of_word,
["previous-line"] = DocView.translate.previous_line, ["previous-line"] = DocView.translate.previous_line,
["next-line"] = DocView.translate.next_line, ["next-line"] = DocView.translate.next_line,
@ -476,6 +450,3 @@ commands["doc:move-to-next-char"] = function()
end end
command.add("core.docview", commands) command.add("core.docview", commands)
command.add(function()
return core.active_view:is(DocView) and core.active_view.doc:has_any_selection()
end ,selection_commands)

View File

@ -7,15 +7,14 @@ local DocView = require "core.docview"
local CommandView = require "core.commandview" local CommandView = require "core.commandview"
local StatusView = require "core.statusview" local StatusView = require "core.statusview"
local last_view, last_fn, last_text, last_sel local max_last_finds = 50
local last_finds, last_view, last_fn, last_text, last_sel
local case_sensitive = config.find_case_sensitive or false local case_sensitive = config.find_case_sensitive or false
local find_regex = config.find_regex or false local find_regex = config.find_regex or false
local found_expression
local function doc() local function doc()
local is_DocView = core.active_view:is(DocView) and not core.active_view:is(CommandView) return core.active_view:is(DocView) and core.active_view.doc or last_view.doc
return is_DocView and core.active_view.doc or (last_view and last_view.doc)
end end
local function get_find_tooltip() local function get_find_tooltip()
@ -30,57 +29,43 @@ local function get_find_tooltip()
end end
local function update_preview(sel, search_fn, text) local function update_preview(sel, search_fn, text)
local ok, line1, col1, line2, col2 = pcall(search_fn, last_view.doc, local ok, line1, col1, line2, col2 =
sel[1], sel[2], text, case_sensitive, find_regex) pcall(search_fn, last_view.doc, sel[1], sel[2], text, case_sensitive, find_regex)
if ok and line1 and text ~= "" then if ok and line1 and text ~= "" then
last_view.doc:set_selection(line2, col2, line1, col1) last_view.doc:set_selection(line2, col2, line1, col1)
last_view:scroll_to_line(line2, true) last_view:scroll_to_line(line2, true)
found_expression = true return true
else else
last_view.doc:set_selection(table.unpack(sel)) last_view.doc:set_selection(unpack(sel))
found_expression = false return false
end end
end end
local function insert_unique(t, v)
local n = #t
for i = 1, n do
if t[i] == v then return end
end
t[n + 1] = v
end
local function find(label, search_fn) local function find(label, search_fn)
last_view, last_sel = core.active_view, last_view, last_sel, last_finds = core.active_view,
{ core.active_view.doc:get_selection() } { core.active_view.doc:get_selection() }, {}
local text = last_view.doc:get_text(unpack(last_sel)) local text, found = last_view.doc:get_text(unpack(last_sel)), false
found_expression = false
core.command_view:set_text(text, true) core.command_view:set_text(text, true)
core.status_view:show_tooltip(get_find_tooltip()) core.status_view:show_tooltip(get_find_tooltip())
core.command_view:set_hidden_suggestions() core.command_view:enter(label, function(text)
core.command_view:enter(label, function(text, item)
insert_unique(core.previous_find, text)
core.status_view:remove_tooltip() core.status_view:remove_tooltip()
if found_expression then if found then
last_fn, last_text = search_fn, text last_fn, last_text = search_fn, text
else else
core.error("Couldn't find %q", text) core.error("Couldn't find %q", text)
last_view.doc:set_selection(table.unpack(last_sel)) last_view.doc:set_selection(unpack(last_sel))
last_view:scroll_to_make_visible(table.unpack(last_sel)) last_view:scroll_to_make_visible(unpack(last_sel))
end end
end, function(text) end, function(text)
update_preview(last_sel, search_fn, text) found = update_preview(last_sel, search_fn, text)
last_fn, last_text = search_fn, text last_fn, last_text = search_fn, text
return core.previous_find
end, function(explicit) end, function(explicit)
core.status_view:remove_tooltip() core.status_view:remove_tooltip()
if explicit then if explicit then
last_view.doc:set_selection(table.unpack(last_sel)) last_view.doc:set_selection(unpack(last_sel))
last_view:scroll_to_make_visible(table.unpack(last_sel)) last_view:scroll_to_make_visible(unpack(last_sel))
end end
end) end)
end end
@ -90,25 +75,18 @@ local function replace(kind, default, fn)
core.command_view:set_text(default, true) core.command_view:set_text(default, true)
core.status_view:show_tooltip(get_find_tooltip()) core.status_view:show_tooltip(get_find_tooltip())
core.command_view:set_hidden_suggestions()
core.command_view:enter("Find To Replace " .. kind, function(old) core.command_view:enter("Find To Replace " .. kind, function(old)
insert_unique(core.previous_find, old)
core.command_view:set_text(old, true) core.command_view:set_text(old, true)
local s = string.format("Replace %s %q With", kind, old) local s = string.format("Replace %s %q With", kind, old)
core.command_view:set_hidden_suggestions()
core.command_view:enter(s, function(new) core.command_view:enter(s, function(new)
core.status_view:remove_tooltip()
insert_unique(core.previous_replace, new)
local n = doc():replace(function(text) local n = doc():replace(function(text)
return fn(text, old, new) return fn(text, old, new)
end) end)
core.log("Replaced %d instance(s) of %s %q with %q", n, kind, old, new) core.log("Replaced %d instance(s) of %s %q with %q", n, kind, old, new)
end, function() return core.previous_replace end, function() end, function() end, function()
core.status_view:remove_tooltip() core.status_view:remove_tooltip()
end) end)
end, function() return core.previous_find end, function()
core.status_view:remove_tooltip()
end) end)
end end
@ -116,85 +94,29 @@ local function has_selection()
return core.active_view:is(DocView) and core.active_view.doc:has_selection() return core.active_view:is(DocView) and core.active_view.doc:has_selection()
end end
local function has_unique_selection() command.add(has_selection, {
if not doc() then return false end ["find-replace:select-next"] = function()
local text = nil local l1, c1, l2, c2 = doc():get_selection(true)
for idx, line1, col1, line2, col2 in doc():get_selections(true, true) do
if line1 == line2 and col1 == col2 then return false end
local selection = doc():get_text(line1, col1, line2, col2)
if text ~= nil and text ~= selection then return false end
text = selection
end
return text ~= nil
end
local function is_in_selection(line, col, l1, c1, l2, c2)
if line < l1 or line > l2 then return false end
if line == l1 and col <= c1 then return false end
if line == l2 and col > c2 then return false end
return true
end
local function is_in_any_selection(line, col)
for idx, l1, c1, l2, c2 in doc():get_selections(true, false) do
if is_in_selection(line, col, l1, c1, l2, c2) then return true end
end
return false
end
local function select_add_next(all)
local il1, ic1 = doc():get_selection(true)
for idx, l1, c1, l2, c2 in doc():get_selections(true, true) do
local text = doc():get_text(l1, c1, l2, c2) local text = doc():get_text(l1, c1, l2, c2)
repeat
l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true })
if l1 == il1 and c1 == ic1 then break end
if l2 and (all or not is_in_any_selection(l2, c2)) then
doc():add_selection(l2, c2, l1, c1)
if not all then
core.active_view:scroll_to_make_visible(l2, c2)
return
end
end
until not all or not l2
if all then break end
end
end
local function select_next(reverse)
local l1, c1, l2, c2 = doc():get_selection(true)
local text = doc():get_text(l1, c1, l2, c2)
if reverse then
l1, c1, l2, c2 = search.find(doc(), l1, c1, text, { wrap = true, reverse = true })
else
l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true }) l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true })
if l2 then doc():set_selection(l2, c2, l1, c1) end
end end
if l2 then doc():set_selection(l2, c2, l1, c1) end
end
command.add(has_unique_selection, {
["find-replace:select-next"] = select_next,
["find-replace:select-previous"] = function() select_next(true) end,
["find-replace:select-add-next"] = select_add_next,
["find-replace:select-add-all"] = function() select_add_next(true) end
}) })
command.add("core.docview", { command.add("core.docview", {
["find-replace:find"] = function() ["find-replace:find"] = function()
find("Find Text", function(doc, line, col, text, case_sensitive, find_regex, find_reverse) find("Find Text", function(doc, line, col, text, case_sensitive, find_regex)
local opt = { wrap = true, no_case = not case_sensitive, regex = find_regex, reverse = find_reverse } local opt = { wrap = true, no_case = not case_sensitive, regex = find_regex }
return search.find(doc, line, col, text, opt) return search.find(doc, line, col, text, opt)
end) end)
end, end,
["find-replace:replace"] = function() ["find-replace:replace"] = function()
local l1, c1, l2, c2 = doc():get_selection() replace("Text", doc():get_text(doc():get_selection(true)), function(text, old, new)
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 if not find_regex then
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil) return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
end end
local result, matches = regex.gsub(regex.compile(old, "m"), text, new) local result, matches = regex.gsub(regex.compile(old), text, new)
return result, #matches return result, #matches
end) end)
end, end,
@ -228,29 +150,29 @@ command.add(valid_for_finding, {
core.error("No find to continue from") core.error("No find to continue from")
else else
local sl1, sc1, sl2, sc2 = doc():get_selection(true) local sl1, sc1, sl2, sc2 = doc():get_selection(true)
local line1, col1, line2, col2 = last_fn(doc(), sl1, sc2, last_text, case_sensitive, find_regex, false) local line1, col1, line2, col2 = last_fn(doc(), sl1, sc2, last_text, case_sensitive, find_regex)
if line1 then if line1 then
if last_view.doc ~= doc() then
last_finds = {}
end
if #last_finds >= max_last_finds then
table.remove(last_finds, 1)
end
table.insert(last_finds, { sl1, sc1, sl2, sc2 })
doc():set_selection(line2, col2, line1, col1) doc():set_selection(line2, col2, line1, col1)
last_view:scroll_to_line(line2, true) last_view:scroll_to_line(line2, true)
else
core.error("Couldn't find %q", last_text)
end end
end end
end, end,
["find-replace:previous-find"] = function() ["find-replace:previous-find"] = function()
if not last_fn then local sel = table.remove(last_finds)
core.error("No find to continue from") if not sel or doc() ~= last_view.doc then
else core.error("No previous finds")
local sl1, sc1, sl2, sc2 = doc():get_selection(true) return
local line1, col1, line2, col2 = last_fn(doc(), sl1, sc1, last_text, case_sensitive, find_regex, true)
if line1 then
doc():set_selection(line2, col2, line1, col1)
last_view:scroll_to_line(line2, true)
else
core.error("Couldn't find %q", last_text)
end
end end
doc():set_selection(table.unpack(sel))
last_view:scroll_to_line(sel[3], true)
end, end,
}) })
@ -258,12 +180,12 @@ command.add("core.commandview", {
["find-replace:toggle-sensitivity"] = function() ["find-replace:toggle-sensitivity"] = function()
case_sensitive = not case_sensitive case_sensitive = not case_sensitive
core.status_view:show_tooltip(get_find_tooltip()) core.status_view:show_tooltip(get_find_tooltip())
if last_sel then update_preview(last_sel, last_fn, last_text) end update_preview(last_sel, last_fn, last_text)
end, end,
["find-replace:toggle-regex"] = function() ["find-replace:toggle-regex"] = function()
find_regex = not find_regex find_regex = not find_regex
core.status_view:show_tooltip(get_find_tooltip()) core.status_view:show_tooltip(get_find_tooltip())
if last_sel then update_preview(last_sel, last_fn, last_text) end update_preview(last_sel, last_fn, last_text)
end end
}) })

View File

@ -3,7 +3,6 @@ local style = require "core.style"
local DocView = require "core.docview" local DocView = require "core.docview"
local command = require "core.command" local command = require "core.command"
local common = require "core.common" local common = require "core.common"
local config = require "core.config"
local t = { local t = {
@ -22,15 +21,9 @@ local t = {
end, end,
["root:close-all"] = function() ["root:close-all"] = function()
core.confirm_close_docs(core.docs, core.root_view.close_all_docviews, core.root_view) core.confirm_close_all(core.root_view.close_all_docviews, core.root_view)
end, end,
["root:close-all-others"] = function()
local active_doc, docs = core.active_view and core.active_view.doc, {}
for i, v in ipairs(core.docs) do if v ~= active_doc then table.insert(docs, v) end end
core.confirm_close_docs(docs, core.root_view.close_all_docviews, core.root_view, true)
end,
["root:switch-to-previous-tab"] = function() ["root:switch-to-previous-tab"] = function()
local node = core.root_view:get_active_node() local node = core.root_view:get_active_node()
local idx = node:get_view_idx(core.active_view) local idx = node:get_view_idx(core.active_view)
@ -64,7 +57,7 @@ local t = {
table.insert(node.views, idx + 1, core.active_view) table.insert(node.views, idx + 1, core.active_view)
end end
end, end,
["root:shrink"] = function() ["root:shrink"] = function()
local node = core.root_view:get_active_node() local node = core.root_view:get_active_node()
local parent = node:get_parent_node(core.root_view.root_node) local parent = node:get_parent_node(core.root_view.root_node)
@ -77,7 +70,7 @@ local t = {
local parent = node:get_parent_node(core.root_view.root_node) local parent = node:get_parent_node(core.root_view.root_node)
local n = (parent.a == node) and 0.1 or -0.1 local n = (parent.a == node) and 0.1 or -0.1
parent.divider = common.clamp(parent.divider + n, 0.1, 0.9) parent.divider = common.clamp(parent.divider + n, 0.1, 0.9)
end end,
} }
@ -113,8 +106,7 @@ for _, dir in ipairs { "left", "right", "up", "down" } do
y = node.position.y + (dir == "up" and -1 or node.size.y + style.divider_size) y = node.position.y + (dir == "up" and -1 or node.size.y + style.divider_size)
end end
local node = core.root_view.root_node:get_child_overlapping_point(x, y) local node = core.root_view.root_node:get_child_overlapping_point(x, y)
local sx, sy = node:get_locked_size() if not node:get_locked_size() then
if not sx and not sy then
core.set_active_view(node.active_view) core.set_active_view(node.active_view)
end end
end end
@ -122,17 +114,5 @@ end
command.add(function() command.add(function()
local node = core.root_view:get_active_node() local node = core.root_view:get_active_node()
local sx, sy = node:get_locked_size() return not node:get_locked_size()
return not sx and not sy
end, t) end, t)
command.add(nil, {
["root:scroll"] = function(delta)
local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view
if view and view.scrollable then
view.scroll.to.y = view.scroll.to.y + delta * -config.mouse_wheel_scroll
return true
end
return false
end
})

View File

@ -15,8 +15,6 @@ end
local CommandView = DocView:extend() local CommandView = DocView:extend()
CommandView.context = "application"
local max_suggestions = 10 local max_suggestions = 10
local noop = function() end local noop = function() end
@ -34,7 +32,6 @@ function CommandView:new()
self.suggestion_idx = 1 self.suggestion_idx = 1
self.suggestions = {} self.suggestions = {}
self.suggestions_height = 0 self.suggestions_height = 0
self.show_suggestions = true
self.last_change_id = 0 self.last_change_id = 0
self.gutter_width = 0 self.gutter_width = 0
self.gutter_text_brightness = 0 self.gutter_text_brightness = 0
@ -46,11 +43,6 @@ function CommandView:new()
end end
function CommandView:set_hidden_suggestions()
self.show_suggestions = false
end
function CommandView:get_name() function CommandView:get_name()
return View.get_name(self) return View.get_name(self)
end end
@ -89,29 +81,10 @@ end
function CommandView:move_suggestion_idx(dir) function CommandView:move_suggestion_idx(dir)
if self.show_suggestions then local n = self.suggestion_idx + dir
local n = self.suggestion_idx + dir self.suggestion_idx = common.clamp(n, 1, #self.suggestions)
self.suggestion_idx = common.clamp(n, 1, #self.suggestions) self:complete()
self:complete() self.last_change_id = self.doc:get_change_id()
self.last_change_id = self.doc:get_change_id()
else
local current_suggestion = #self.suggestions > 0 and self.suggestions[self.suggestion_idx].text
local text = self:get_text()
if text == current_suggestion then
local n = self.suggestion_idx + dir
if n == 0 and self.save_suggestion then
self:set_text(self.save_suggestion)
else
self.suggestion_idx = common.clamp(n, 1, #self.suggestions)
self:complete()
end
else
self.save_suggestion = text
self:complete()
end
self.last_change_id = self.doc:get_change_id()
self.state.suggest(self:get_text())
end
end end
@ -159,8 +132,6 @@ function CommandView:exit(submitted, inexplicit)
self.doc:reset() self.doc:reset()
self.suggestions = {} self.suggestions = {}
if not submitted then cancel(not inexplicit) end if not submitted then cancel(not inexplicit) end
self.show_suggestions = true
self.save_suggestion = nil
end end
@ -214,7 +185,7 @@ function CommandView:update()
-- update suggestions box height -- update suggestions box height
local lh = self:get_suggestion_line_height() local lh = self:get_suggestion_line_height()
local dest = self.show_suggestions and math.min(#self.suggestions, max_suggestions) * lh or 0 local dest = math.min(#self.suggestions, max_suggestions) * lh
self:move_towards("suggestions_height", dest) self:move_towards("suggestions_height", dest)
-- update suggestion cursor offset -- update suggestion cursor offset
@ -283,9 +254,7 @@ end
function CommandView:draw() function CommandView:draw()
CommandView.super.draw(self) CommandView.super.draw(self)
if self.show_suggestions then core.root_view:defer_draw(draw_suggestions_box, self)
core.root_view:defer_draw(draw_suggestions_box, self)
end
end end

View File

@ -1,8 +1,8 @@
local common = {} local common = {}
function common.is_utf8_cont(s, offset) function common.is_utf8_cont(char)
local byte = s:byte(offset or 1) local byte = char:byte()
return byte >= 0x80 and byte < 0xc0 return byte >= 0x80 and byte < 0xc0
end end
@ -41,28 +41,23 @@ function common.lerp(a, b, t)
end end
function common.distance(x1, y1, x2, y2)
return math.sqrt(math.pow(x2-x1, 2)+math.pow(y2-y1, 2))
end
function common.color(str) function common.color(str)
local r, g, b, a = str:match("^#(%x%x)(%x%x)(%x%x)(%x?%x?)$") local r, g, b, a = str:match("#(%x%x)(%x%x)(%x%x)")
if r then if r then
r = tonumber(r, 16) r = tonumber(r, 16)
g = tonumber(g, 16) g = tonumber(g, 16)
b = tonumber(b, 16) b = tonumber(b, 16)
a = tonumber(a, 16) or 0xff a = 1
elseif str:match("rgba?%s*%([%d%s%.,]+%)") then elseif str:match("rgba?%s*%([%d%s%.,]+%)") then
local f = str:gmatch("[%d.]+") local f = str:gmatch("[%d.]+")
r = (f() or 0) r = (f() or 0)
g = (f() or 0) g = (f() or 0)
b = (f() or 0) b = (f() or 0)
a = (f() or 1) * 0xff a = f() or 1
else else
error(string.format("bad color string '%s'", str)) error(string.format("bad color string '%s'", str))
end end
return r, g, b, a return r, g, b, a * 0xff
end end
@ -280,61 +275,22 @@ local function split_on_slash(s, sep_pattern)
end end
-- The filename argument given to the function is supposed to
-- come from system.absolute_path and as such should be an
-- absolute path without . or .. elements.
-- This function exists because on Windows the drive letter returned
-- by system.absolute_path is sometimes with a lower case and sometimes
-- with an upper case to we normalize to upper case.
function common.normalize_volume(filename)
if not filename then return end
if PATHSEP == '\\' then
local drive, rem = filename:match('^([a-zA-Z]:\\)(.*)')
if drive then
return drive:upper() .. rem
end
end
return filename
end
function common.normalize_path(filename) function common.normalize_path(filename)
if not filename then return end
local volume
if PATHSEP == '\\' then if PATHSEP == '\\' then
filename = filename:gsub('[/\\]', '\\') filename = filename:gsub('[/\\]', '\\')
local drive, rem = filename:match('^([a-zA-Z]:\\)(.*)') local drive, rem = filename:match('^([a-zA-Z])(:.*)')
if drive then filename = drive and drive:upper() .. rem or filename
volume, filename = drive:upper(), rem
else
drive, rem = filename:match('^(\\\\[^\\]+\\[^\\]+\\)(.*)')
if drive then
volume, filename = drive, rem
end
end
else
local relpath = filename:match('^/(.+)')
if relpath then
volume, filename = "/", relpath
end
end end
local parts = split_on_slash(filename, PATHSEP) local parts = split_on_slash(filename, PATHSEP)
local accu = {} local accu = {}
for _, part in ipairs(parts) do for _, part in ipairs(parts) do
if part == '..' then if part == '..' then
if #accu > 0 and accu[#accu] ~= ".." then table.remove(accu)
table.remove(accu)
elseif volume then
error("invalid path " .. volume .. filename)
else
table.insert(accu, part)
end
elseif part ~= '.' then elseif part ~= '.' then
table.insert(accu, part) table.insert(accu, part)
end end
end end
local npath = table.concat(accu, PATHSEP) return table.concat(accu, PATHSEP)
return (volume or "") .. (npath == "" and PATHSEP or npath)
end end
@ -349,7 +305,7 @@ function common.relative_path(ref_dir, dir)
if drive and ref_drive and drive ~= ref_drive then if drive and ref_drive and drive ~= ref_drive then
-- Windows, different drives, system.absolute_path fails for C:\..\D:\ -- Windows, different drives, system.absolute_path fails for C:\..\D:\
return dir return dir
end end
local ref_ls = split_on_slash(ref_dir) local ref_ls = split_on_slash(ref_dir)
local dir_ls = split_on_slash(dir) local dir_ls = split_on_slash(dir)
local i = 1 local i = 1

View File

@ -1,18 +1,17 @@
local config = {} local config = {}
config.project_scan_rate = 5
config.fps = 60 config.fps = 60
config.max_log_items = 80 config.max_log_items = 80
config.message_timeout = 5 config.message_timeout = 5
config.mouse_wheel_scroll = 50 * SCALE config.mouse_wheel_scroll = 50 * SCALE
config.scroll_past_end = true
config.file_size_limit = 10 config.file_size_limit = 10
config.ignore_files = "^%." config.ignore_files = "^%."
config.symbol_pattern = "[%a_][%w_]*" config.symbol_pattern = "[%a_][%w_]*"
config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-" config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
config.undo_merge_timeout = 0.3 config.undo_merge_timeout = 0.3
config.max_undos = 10000 config.max_undos = 10000
config.max_tabs = 8 config.max_tabs = 10
config.always_show_tabs = true
config.highlight_current_line = true config.highlight_current_line = true
config.line_height = 1.2 config.line_height = 1.2
config.indent_size = 2 config.indent_size = 2
@ -23,11 +22,9 @@ config.max_project_files = 2000
config.transitions = true config.transitions = true
config.animation_rate = 1.0 config.animation_rate = 1.0
config.blink_period = 0.8 config.blink_period = 0.8
config.disable_blink = false
config.draw_whitespace = false config.draw_whitespace = false
config.borderless = false config.borderless = false
config.tab_close_button = true config.tab_close_button = true
config.max_clicks = 3
-- Disable plugin loading setting to false the config entry -- Disable plugin loading setting to false the config entry
-- of the same name. -- of the same name.
@ -35,6 +32,5 @@ config.plugins = {}
config.plugins.trimwhitespace = false config.plugins.trimwhitespace = false
config.plugins.lineguide = false config.plugins.lineguide = false
config.plugins.drawwhitespace = false
return config return config

View File

@ -49,7 +49,7 @@ function ContextMenu:register(predicate, items)
local width, height = 0, 0 --precalculate the size of context menu local width, height = 0, 0 --precalculate the size of context menu
for i, item in ipairs(items) do for i, item in ipairs(items) do
if item ~= DIVIDER then if item ~= DIVIDER then
item.info = keymap.get_binding(item.command) item.info = keymap.reverse_map[item.command]
end end
local lw, lh = get_item_size(item) local lw, lh = get_item_size(item)
width = math.max(width, lw) width = math.max(width, lw)
@ -66,13 +66,9 @@ function ContextMenu:show(x, y)
for _, items in ipairs(self.itemset) do for _, items in ipairs(self.itemset) do
if items.predicate(x, y) then if items.predicate(x, y) then
items_list.width = math.max(items_list.width, items.items.width) items_list.width = math.max(items_list.width, items.items.width)
items_list.height = items_list.height items_list.height = items_list.height + items.items.height
for _, subitems in ipairs(items.items) do for _, subitems in ipairs(items.items) do
if not subitems.command or command.is_valid(subitems.command) then table.insert(items_list, subitems)
local lw, lh = get_item_size(subitems)
items_list.height = items_list.height + lh
table.insert(items_list, subitems)
end
end end
end end
end end

View File

@ -1,5 +1,4 @@
local core = require "core" local core = require "core"
local common = require "core.common"
local config = require "core.config" local config = require "core.config"
local tokenizer = require "core.tokenizer" local tokenizer = require "core.tokenizer"
local Object = require "core.object" local Object = require "core.object"
@ -25,7 +24,7 @@ function Highlighter:new(doc)
for i = self.first_invalid_line, max do for i = self.first_invalid_line, max do
local state = (i > 1) and self.lines[i - 1].state local state = (i > 1) and self.lines[i - 1].state
local line = self.lines[i] local line = self.lines[i]
if not (line and line.init_state == state and line.text == self.doc.lines[i]) then if not (line and line.init_state == state) then
self.lines[i] = self:tokenize_line(i, state) self.lines[i] = self:tokenize_line(i, state)
end end
end end
@ -41,36 +40,16 @@ end
function Highlighter:reset() function Highlighter:reset()
self.lines = {} self.lines = {}
self:soft_reset()
end
function Highlighter:soft_reset()
for i=1,#self.lines do
self.lines[i] = false
end
self.first_invalid_line = 1 self.first_invalid_line = 1
self.max_wanted_line = 0 self.max_wanted_line = 0
end end
function Highlighter:invalidate(idx) function Highlighter:invalidate(idx)
self.first_invalid_line = math.min(self.first_invalid_line, idx) self.first_invalid_line = math.min(self.first_invalid_line, idx)
self.max_wanted_line = math.min(self.max_wanted_line, #self.doc.lines) self.max_wanted_line = math.min(self.max_wanted_line, #self.doc.lines)
end end
function Highlighter:insert_notify(line, n)
self:invalidate(line)
local blanks = { }
for i = 1, n do
blanks[i] = false
end
common.splice(self.lines, line, 0, blanks)
end
function Highlighter:remove_notify(line, n)
self:invalidate(line)
common.splice(self.lines, line, n)
end
function Highlighter:tokenize_line(idx, state) function Highlighter:tokenize_line(idx, state)
local res = {} local res = {}

View File

@ -47,7 +47,7 @@ function Doc:reset_syntax()
local syn = syntax.get(self.filename or "", header) local syn = syntax.get(self.filename or "", header)
if self.syntax ~= syn then if self.syntax ~= syn then
self.syntax = syn self.syntax = syn
self.highlighter:soft_reset() self.highlighter:reset()
end end
end end
@ -62,15 +62,12 @@ function Doc:load(filename)
local fp = assert( io.open(filename, "rb") ) local fp = assert( io.open(filename, "rb") )
self:reset() self:reset()
self.lines = {} self.lines = {}
local i = 1
for line in fp:lines() do for line in fp:lines() do
if line:byte(-1) == 13 then if line:byte(-1) == 13 then
line = line:sub(1, -2) line = line:sub(1, -2)
self.crlf = true self.crlf = true
end end
table.insert(self.lines, line .. "\n") table.insert(self.lines, line .. "\n")
self.highlighter.lines[i] = false
i = i + 1
end end
if #self.lines == 0 then if #self.lines == 0 then
table.insert(self.lines, "\n") table.insert(self.lines, "\n")
@ -105,11 +102,7 @@ end
function Doc:is_dirty() function Doc:is_dirty()
if self.new_file then return self.clean_change_id ~= self:get_change_id() or self.new_file
return #self.lines > 1 or #self.lines[1] > 1
else
return self.clean_change_id ~= self:get_change_id()
end
end end
@ -118,14 +111,6 @@ function Doc:clean()
end 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
end
function Doc:get_change_id() function Doc:get_change_id()
return self.undo_stack.idx return self.undo_stack.idx
end end
@ -139,31 +124,11 @@ function Doc:get_selection(sort)
return line1, col1, line2, col2, sort return line1, col1, line2, col2, sort
end end
function Doc:get_selection_text(limit)
limit = limit or math.huge
local result = {}
for idx, line1, col1, line2, col2 in self:get_selections() do
if idx > limit then break end
if line1 ~= line2 or col1 ~= col2 then
local text = self:get_text(line1, col1, line2, col2)
if text ~= "" then result[#result + 1] = text end
end
end
return table.concat(result, "\n")
end
function Doc:has_selection() function Doc:has_selection()
local line1, col1, line2, col2 = self:get_selection(false) local line1, col1, line2, col2 = self:get_selection(false)
return line1 ~= line2 or col1 ~= col2 return line1 ~= line2 or col1 ~= col2
end end
function Doc:has_any_selection()
for idx, line1, col1, line2, col2 in self:get_selections() do
if line1 ~= line2 or col1 ~= col2 then return true end
end
return false
end
function Doc:sanitize_selection() function Doc:sanitize_selection()
for idx, line1, col1, line2, col2 in self:get_selections() do for idx, line1, col1, line2, col2 in self:get_selections() do
self:set_selections(idx, line1, col1, line2, col2) self:set_selections(idx, line1, col1, line2, col2)
@ -208,21 +173,19 @@ function Doc:merge_cursors(idx)
if self.selections[i] == self.selections[j] and if self.selections[i] == self.selections[j] and
self.selections[i+1] == self.selections[j+1] then self.selections[i+1] == self.selections[j+1] then
common.splice(self.selections, i, 4) common.splice(self.selections, i, 4)
common.splice(self.cursor_clipboard, i, 1)
break break
end end
end end
end end
if #self.selections <= 4 then self.cursor_clipboard = {} end
end end
local function selection_iterator(invariant, idx) local function selection_iterator(invariant, idx)
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 target > #invariant[1] or target <= 0 or (type(invariant[3]) == "number" and invariant[3] ~= idx - 1) then return end
if invariant[2] then 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(unpack(invariant[1], target, target+4))
else else
return idx+(invariant[3] and -1 or 1), table.unpack(invariant[1], target, target+4) return idx+(invariant[3] and -1 or 1), unpack(invariant[1], target, target+4)
end end
end end
@ -323,8 +286,7 @@ local function pop_undo(self, undo_stack, redo_stack, modified)
local line1, col1, line2, col2 = table.unpack(cmd) local line1, col1, line2, col2 = table.unpack(cmd)
self:raw_remove(line1, col1, line2, col2, redo_stack, cmd.time) self:raw_remove(line1, col1, line2, col2, redo_stack, cmd.time)
elseif cmd.type == "selection" then elseif cmd.type == "selection" then
self.selections = { table.unpack(cmd) } self.selections = { unpack(cmd) }
self:sanitize_selection()
end end
modified = modified or (cmd.type ~= "selection") modified = modified or (cmd.type ~= "selection")
@ -345,7 +307,6 @@ end
function Doc:raw_insert(line, col, text, undo_stack, time) function Doc:raw_insert(line, col, text, undo_stack, time)
-- split text into lines and merge with line at insertion point -- split text into lines and merge with line at insertion point
local lines = split_lines(text) local lines = split_lines(text)
local len = #lines[#lines]
local before = self.lines[line]:sub(1, col - 1) local before = self.lines[line]:sub(1, col - 1)
local after = self.lines[line]:sub(col) local after = self.lines[line]:sub(col)
for i = 1, #lines - 1 do for i = 1, #lines - 1 do
@ -356,22 +317,14 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
-- splice lines into line array -- splice lines into line array
common.splice(self.lines, line, 1, lines) common.splice(self.lines, line, 1, lines)
-- keep cursors where they should be
for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do
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)
end
-- push undo -- push undo
local line2, col2 = self:position_offset(line, col, #text) local line2, col2 = self:position_offset(line, col, #text)
push_undo(undo_stack, time, "selection", table.unpack(self.selections)) push_undo(undo_stack, time, "selection", unpack(self.selections))
push_undo(undo_stack, time, "remove", line, col, line2, col2) push_undo(undo_stack, time, "remove", line, col, line2, col2)
-- update highlighter and assure selection is in bounds -- update highlighter and assure selection is in bounds
self.highlighter:insert_notify(line, #lines - 1) self.highlighter:invalidate(line)
self:sanitize_selection() self:sanitize_selection()
end end
@ -379,7 +332,7 @@ end
function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time) function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
-- push undo -- push undo
local text = self:get_text(line1, col1, line2, col2) local text = self:get_text(line1, col1, line2, col2)
push_undo(undo_stack, time, "selection", table.unpack(self.selections)) push_undo(undo_stack, time, "selection", unpack(self.selections))
push_undo(undo_stack, time, "insert", line1, col1, text) push_undo(undo_stack, time, "insert", line1, col1, text)
-- get line content before/after removed text -- get line content before/after removed text
@ -388,17 +341,9 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
-- splice line into line array -- splice line into line array
common.splice(self.lines, line1, line2 - line1 + 1, { before .. after }) common.splice(self.lines, line1, line2 - line1 + 1, { before .. after })
-- move all cursors back if they share a line with the removed text
for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do
if cline1 < line2 then break end
local line_removal = line2 - line1
local column_removal = line2 == cline2 and col2 < ccol1 and (line2 == line1 and col2 - col1 or col2) or 0
self:set_selections(idx, cline1 - line_removal, ccol1 - column_removal, cline2 - line_removal, ccol2 - column_removal)
end
-- update highlighter and assure selection is in bounds -- update highlighter and assure selection is in bounds
self.highlighter:remove_notify(line1, line2 - line1) self.highlighter:invalidate(line1)
self:sanitize_selection() self:sanitize_selection()
end end
@ -432,7 +377,7 @@ end
function Doc:text_input(text, idx) function Doc:text_input(text, idx)
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do
if line1 ~= line2 or col1 ~= col2 then if line1 ~= line2 or col1 ~= col2 then
self:delete_to_cursor(sidx) self:delete_to_cursor(sidx)
end end
@ -441,7 +386,12 @@ function Doc:text_input(text, idx)
end end
end end
function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
function Doc:replace(fn)
local line1, col1, line2, col2 = self:get_selection(true)
if line1 == line2 and col1 == col2 then
line1, col1, line2, col2 = 1, 1, #self.lines, #self.lines[#self.lines]
end
local old_text = self:get_text(line1, col1, line2, col2) local old_text = self:get_text(line1, col1, line2, col2)
local new_text, n = fn(old_text) local new_text, n = fn(old_text)
if old_text ~= new_text then if old_text ~= new_text then
@ -449,27 +399,12 @@ function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
self:remove(line1, col1, line2, col2) self:remove(line1, col1, line2, col2)
if line1 == line2 and col1 == col2 then if line1 == line2 and col1 == col2 then
line2, col2 = self:position_offset(line1, col1, #new_text) line2, col2 = self:position_offset(line1, col1, #new_text)
self:set_selections(idx, line1, col1, line2, col2) self:set_selection(line1, col1, line2, col2)
end end
end end
return n return n
end end
function Doc:replace(fn)
local has_selection, n = false, 0
for idx, line1, col1, line2, col2 in self:get_selections(true) do
if line1 ~= line2 or col1 ~= col2 then
n = n + self:replace_cursor(idx, line1, col1, line2, col2, fn)
has_selection = true
end
end
if not has_selection then
self:set_selection(table.unpack(self.selections))
n = n + self:replace_cursor(1, 1, 1, #self.lines, #self.lines[#self.lines], fn)
end
return n
end
function Doc:delete_to_cursor(idx, ...) function Doc:delete_to_cursor(idx, ...)
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do
@ -505,21 +440,19 @@ end
function Doc:select_to(...) return self:select_to_cursor(nil, ...) end function Doc:select_to(...) return self:select_to_cursor(nil, ...) end
function Doc:get_indent_string() local function get_indent_string()
local indent_type, indent_size = self:get_indent_info() if config.tab_type == "hard" then
if indent_type == "hard" then
return "\t" return "\t"
end end
return string.rep(" ", indent_size) return string.rep(" ", config.indent_size)
end end
-- returns the size of the original indent, and the indent -- returns the size of the original indent, and the indent
-- in your config format, rounded either up or down -- in your config format, rounded either up or down
function Doc:get_line_indent(line, rnd_up) local function get_line_indent(line, rnd_up)
local _, e = line:find("^[ \t]+") local _, e = line:find("^[ \t]+")
local indent_type, indent_size = self:get_indent_info() local soft_tab = string.rep(" ", config.indent_size)
local soft_tab = string.rep(" ", indent_size) if config.tab_type == "hard" then
if indent_type == "hard" then
local indent = e and line:sub(1, e):gsub(soft_tab, "\t") or "" local indent = e and line:sub(1, e):gsub(soft_tab, "\t") or ""
return e, indent:gsub(" +", rnd_up and "\t" or "") return e, indent:gsub(" +", rnd_up and "\t" or "")
else else
@ -541,14 +474,14 @@ end
-- * if you are unindenting, the cursor will jump to the start of the line, -- * if you are unindenting, the cursor will jump to the start of the line,
-- and remove the appropriate amount of spaces (or a tab). -- and remove the appropriate amount of spaces (or a tab).
function Doc:indent_text(unindent, line1, col1, line2, col2) function Doc:indent_text(unindent, line1, col1, line2, col2)
local text = self:get_indent_string() local text = get_indent_string()
local _, se = self.lines[line1]:find("^[ \t]+") local _, se = self.lines[line1]:find("^[ \t]+")
local in_beginning_whitespace = col1 == 1 or (se and col1 <= se + 1) local in_beginning_whitespace = col1 == 1 or (se and col1 <= se + 1)
local has_selection = line1 ~= line2 or col1 ~= col2 local has_selection = line1 ~= line2 or col1 ~= col2
if unindent or has_selection or in_beginning_whitespace then if unindent or has_selection or in_beginning_whitespace then
local l1d, l2d = #self.lines[line1], #self.lines[line2] local l1d, l2d = #self.lines[line1], #self.lines[line2]
for line = line1, line2 do for line = line1, line2 do
local e, rnded = self:get_line_indent(self.lines[line], unindent) local e, rnded = get_line_indent(self.lines[line], unindent)
self:remove(line, 1, line, (e or 0) + 1) self:remove(line, 1, line, (e or 0) + 1)
self:insert(line, 1, self:insert(line, 1,
unindent and rnded:sub(1, #rnded - #text) or rnded .. text) unindent and rnded:sub(1, #rnded - #text) or rnded .. text)

View File

@ -22,62 +22,37 @@ local function init_args(doc, line, col, text, opt)
return doc, line, col, text, opt return doc, line, col, text, opt
end end
-- This function is needed to uniform the behavior of
-- `regex:cmatch` and `string.find`.
local function regex_func(text, re, index, _)
local s, e = re:cmatch(text, index)
return s, e and e - 1
end
local function rfind(func, text, pattern, index, plain)
local s, e = func(text, pattern, 1, plain)
local last_s, last_e
if index < 0 then index = #text - index + 1 end
while e and e <= index do
last_s, last_e = s, e
s, e = func(text, pattern, s + 1, plain)
end
return last_s, last_e
end
function search.find(doc, line, col, text, opt) function search.find(doc, line, col, text, opt)
doc, line, col, text, opt = init_args(doc, line, col, text, opt) doc, line, col, text, opt = init_args(doc, line, col, text, opt)
local plain = not opt.pattern
local pattern = text local re
local search_func = string.find
if opt.regex then if opt.regex then
pattern = regex.compile(text, opt.no_case and "i" or "") re = regex.compile(text, opt.no_case and "i" or "")
search_func = regex_func
end end
local start, finish, step = line, #doc.lines, 1 for line = line, #doc.lines do
if opt.reverse then
start, finish, step = line, 1, -1
end
for line = start, finish, step do
local line_text = doc.lines[line] local line_text = doc.lines[line]
if opt.no_case and not opt.regex then if opt.regex then
line_text = line_text:lower() local s, e = re:cmatch(line_text, col)
end if s then
local s, e return line, s, line, e
if opt.reverse then end
s, e = rfind(search_func, line_text, pattern, col - 1, plain) col = 1
else else
s, e = search_func(line_text, pattern, col, plain) if opt.no_case then
line_text = line_text:lower()
end
local s, e = line_text:find(text, col, true)
if s then
return line, s, line, e + 1
end
col = 1
end end
if s then
return line, s, line, e + 1
end
col = opt.reverse and -1 or 1
end end
if opt.wrap then if opt.wrap then
opt = { no_case = opt.no_case, regex = opt.regex, reverse = opt.reverse } opt = { no_case = opt.no_case, regex = opt.regex }
if opt.reverse then return search.find(doc, 1, 1, text, opt)
return search.find(doc, #doc.lines, #doc.lines[#doc.lines], text, opt)
else
return search.find(doc, 1, 1, text, opt)
end
end end
end end

View File

@ -117,10 +117,6 @@ function translate.start_of_line(doc, line, col)
return line, 1 return line, 1
end end
function translate.start_of_indentation(doc, line, col)
local s, e = doc.lines[line]:find("^%s*")
return line, col > e + 1 and e + 1 or 1
end
function translate.end_of_line(doc, line, col) function translate.end_of_line(doc, line, col)
return line, math.huge return line, math.huge

View File

@ -9,7 +9,6 @@ local View = require "core.view"
local DocView = View:extend() local DocView = View:extend()
DocView.context = "session"
local function move_to_line_offset(dv, line, col, offset) local function move_to_line_offset(dv, line, col, offset)
local xo = dv.last_x_offset local xo = dv.last_x_offset
@ -98,9 +97,6 @@ end
function DocView:get_scrollable_size() function DocView:get_scrollable_size()
if not config.scroll_past_end then
return self:get_line_height() * (#self.doc.lines) + style.padding.y * 2
end
return self:get_line_height() * (#self.doc.lines - 1) + self.size.y return self:get_line_height() * (#self.doc.lines - 1) + self.size.y
end end
@ -153,14 +149,14 @@ function DocView:get_col_x_offset(line, col)
local font = style.syntax_fonts[type] or default_font local font = style.syntax_fonts[type] or default_font
for char in common.utf8_chars(text) do for char in common.utf8_chars(text) do
if column == col then if column == col then
return xoffset return xoffset / font:subpixel_scale()
end end
xoffset = xoffset + font:get_width(char) xoffset = xoffset + font:get_width_subpixel(char)
column = column + #char column = column + #char
end end
end end
return xoffset return xoffset / default_font:subpixel_scale()
end end
@ -169,12 +165,14 @@ function DocView:get_x_offset_col(line, x)
local xoffset, last_i, i = 0, 1, 1 local xoffset, last_i, i = 0, 1, 1
local default_font = self:get_font() local default_font = self:get_font()
local subpixel_scale = default_font:subpixel_scale()
local x_subpixel = subpixel_scale * x + subpixel_scale / 2
for _, type, text in self.doc.highlighter:each_token(line) do for _, type, text in self.doc.highlighter:each_token(line) do
local font = style.syntax_fonts[type] or default_font local font = style.syntax_fonts[type] or default_font
for char in common.utf8_chars(text) do for char in common.utf8_chars(text) do
local w = font:get_width(char) local w = font:get_width_subpixel(char)
if xoffset >= x then if xoffset >= subpixel_scale * x then
return (xoffset - x > w / 2) and last_i or i return (xoffset - x_subpixel > w / 2) and last_i or i
end end
xoffset = xoffset + w xoffset = xoffset + w
last_i = i last_i = i
@ -225,6 +223,51 @@ function DocView:scroll_to_make_visible(line, col)
end end
local function mouse_selection(doc, clicks, line1, col1, line2, col2)
local swap = line2 < line1 or line2 == line1 and col2 <= col1
if swap then
line1, col1, line2, col2 = line2, col2, line1, col1
end
if clicks % 4 == 2 then
line1, col1 = translate.start_of_word(doc, line1, col1)
line2, col2 = translate.end_of_word(doc, line2, col2)
elseif clicks % 4 == 3 then
if line2 == #doc.lines and doc.lines[#doc.lines] ~= "\n" then
doc:insert(math.huge, math.huge, "\n")
end
line1, col1, line2, col2 = line1, 1, line2 + 1, 1
end
if swap then
return line2, col2, line1, col1
end
return line1, col1, line2, col2
end
function DocView:on_mouse_pressed(button, x, y, clicks)
local caught = DocView.super.on_mouse_pressed(self, button, x, y, clicks)
if caught then
return
end
if keymap.modkeys["shift"] then
if clicks % 2 == 1 then
local line1, col1 = select(3, self.doc:get_selection())
local line2, col2 = self:resolve_screen_position(x, y)
self.doc:set_selection(line2, col2, line1, col1)
end
else
local line, col = self:resolve_screen_position(x, y)
if keymap.modkeys["ctrl"] then
self.doc:add_selection(mouse_selection(self.doc, clicks, line, col, line, col))
else
self.doc:set_selection(mouse_selection(self.doc, clicks, line, col, line, col))
end
self.mouse_selecting = { line, col, clicks = clicks }
end
core.blink_reset()
end
function DocView:on_mouse_moved(x, y, ...) function DocView:on_mouse_moved(x, y, ...)
DocView.super.on_mouse_moved(self, x, y, ...) DocView.super.on_mouse_moved(self, x, y, ...)
@ -236,7 +279,8 @@ function DocView:on_mouse_moved(x, y, ...)
if self.mouse_selecting then if self.mouse_selecting then
local l1, c1 = self:resolve_screen_position(x, y) local l1, c1 = self:resolve_screen_position(x, y)
local l2, c2, snap_type = table.unpack(self.mouse_selecting) local l2, c2 = table.unpack(self.mouse_selecting)
local clicks = self.mouse_selecting.clicks
if keymap.modkeys["ctrl"] then if keymap.modkeys["ctrl"] then
if l1 > l2 then l1, l2 = l2, l1 end if l1 > l2 then l1, l2 = l2, l1 end
self.doc.selections = { } self.doc.selections = { }
@ -244,33 +288,12 @@ function DocView:on_mouse_moved(x, y, ...)
self.doc:set_selections(i - l1 + 1, i, math.min(c1, #self.doc.lines[i]), i, math.min(c2, #self.doc.lines[i])) self.doc:set_selections(i - l1 + 1, i, math.min(c1, #self.doc.lines[i]), i, math.min(c2, #self.doc.lines[i]))
end end
else else
if snap_type then self.doc:set_selection(mouse_selection(self.doc, clicks, l1, c1, l2, c2))
l1, c1, l2, c2 = self:mouse_selection(self.doc, snap_type, l1, c1, l2, c2)
end
self.doc:set_selection(l1, c1, l2, c2)
end end
end end
end end
function DocView:mouse_selection(doc, snap_type, line1, col1, line2, col2)
local swap = line2 < line1 or line2 == line1 and col2 <= col1
if swap then
line1, col1, line2, col2 = line2, col2, line1, col1
end
if snap_type == "word" then
line1, col1 = translate.start_of_word(doc, line1, col1)
line2, col2 = translate.end_of_word(doc, line2, col2)
elseif snap_type == "lines" then
col1, col2 = 1, math.huge
end
if swap then
return line2, col2, line1, col1
end
return line1, col1, line2, col2
end
function DocView:on_mouse_released(button) function DocView:on_mouse_released(button)
DocView.super.on_mouse_released(self, button) DocView.super.on_mouse_released(self, button)
self.mouse_selecting = nil self.mouse_selecting = nil
@ -315,11 +338,16 @@ end
function DocView:draw_line_text(idx, x, y) function DocView:draw_line_text(idx, x, y)
local default_font = self:get_font() local default_font = self:get_font()
local tx, ty = x, y + self:get_line_text_y_offset() local subpixel_scale = default_font:subpixel_scale()
local tx, ty = subpixel_scale * x, y + self:get_line_text_y_offset()
for _, type, text in self.doc.highlighter:each_token(idx) do for _, type, text in self.doc.highlighter:each_token(idx) do
local color = style.syntax[type] local color = style.syntax[type]
local font = style.syntax_fonts[type] or default_font local font = style.syntax_fonts[type] or default_font
tx = renderer.draw_text(font, text, tx, ty, color) if config.draw_whitespace then
tx = renderer.draw_text_subpixel(font, text, tx, ty, color, core.replacements, style.syntax.comment)
else
tx = renderer.draw_text_subpixel(font, text, tx, ty, color)
end
end end
end end
@ -329,18 +357,6 @@ function DocView:draw_caret(x, y)
end end
function DocView:draw_line_body(idx, x, y) function DocView:draw_line_body(idx, x, y)
-- draw highlight if any selection ends on this line
local draw_highlight = false
for lidx, line1, col1, line2, col2 in self.doc:get_selections(false) do
if line1 == idx then
draw_highlight = true
break
end
end
if draw_highlight and config.highlight_current_line and core.active_view == self then
self:draw_line_highlight(x + self.scroll.x, y)
end
-- draw selection if it overlaps this line -- draw selection if it overlaps this line
for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
if idx >= line1 and idx <= line2 then if idx >= line1 and idx <= line2 then
@ -350,9 +366,14 @@ function DocView:draw_line_body(idx, x, y)
local x1 = x + self:get_col_x_offset(idx, col1) local x1 = x + self:get_col_x_offset(idx, col1)
local x2 = x + self:get_col_x_offset(idx, col2) local x2 = x + self:get_col_x_offset(idx, col2)
local lh = self:get_line_height() local lh = self:get_line_height()
if x1 ~= x2 then renderer.draw_rect(x1, y, x2 - x1, lh, style.selection)
renderer.draw_rect(x1, y, x2 - x1, lh, style.selection) end
end end
for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
-- draw line highlight if caret is on this line
if config.highlight_current_line and (line1 == line2 and col1 == col2)
and line1 == idx and core.active_view == self then
self:draw_line_highlight(x + self.scroll.x, y)
end end
end end
@ -382,12 +403,10 @@ function DocView:draw_overlay()
local T = config.blink_period local T = config.blink_period
for _, line, col in self.doc:get_selections() do for _, line, col in self.doc:get_selections() do
if line >= minline and line <= maxline if line >= minline and line <= maxline
and (core.blink_timer - core.blink_start) % T < T / 2
and system.window_has_focus() then and system.window_has_focus() then
if config.disable_blink local x, y = self:get_line_screen_position(line)
or (core.blink_timer - core.blink_start) % T < T / 2 then self:draw_caret(x + self:get_col_x_offset(line, col), y)
local x, y = self:get_line_screen_position(line)
self:draw_caret(x + self:get_col_x_offset(line, col), y)
end
end end
end end
end end
@ -395,8 +414,8 @@ end
function DocView:draw() function DocView:draw()
self:draw_background(style.background) self:draw_background(style.background)
local _, indent_size = self.doc:get_indent_info()
self:get_font():set_tab_size(indent_size) self:get_font():set_tab_size(config.indent_size)
local minline, maxline = self:get_visible_line_range() local minline, maxline = self:get_visible_line_range()
local lh = self:get_line_height() local lh = self:get_line_height()
@ -410,9 +429,7 @@ function DocView:draw()
local pos = self.position local pos = self.position
x, y = self:get_line_screen_position(minline) x, y = self:get_line_screen_position(minline)
-- the clip below ensure we don't write on the gutter region. On the core.push_clip_rect(pos.x + gw, pos.y, self.size.x, self.size.y)
-- right side it is redundant with the Node's clip.
core.push_clip_rect(pos.x + gw, pos.y, self.size.x - gw, self.size.y)
for i = minline, maxline do for i = minline, maxline do
self:draw_line_body(i, x, y) self:draw_line_body(i, x, y)
y = y + lh y = y + lh

View File

@ -1,40 +0,0 @@
local style = require "core.style"
local keymap = require "core.keymap"
local View = require "core.view"
local EmptyView = View:extend()
local function draw_text(x, y, color)
local th = style.big_font:get_height()
local dh = 2 * th + style.padding.y * 2
local x1, y1 = x, y + (dh - th) / 2
x = renderer.draw_text(style.big_font, "Lite XL", x1, y1, color)
renderer.draw_text(style.font, "version " .. VERSION, x1, y1 + th, color)
x = x + style.padding.x
renderer.draw_rect(x, y, math.ceil(1 * SCALE), dh, color)
local lines = {
{ fmt = "%s to run a command", cmd = "core:find-command" },
{ fmt = "%s to open a file from the project", cmd = "core:find-file" },
{ fmt = "%s to change project folder", cmd = "core:change-project-folder" },
{ fmt = "%s to open a project folder", cmd = "core:open-project-folder" },
}
th = style.font:get_height()
y = y + (dh - (th + style.padding.y) * #lines) / 2
local w = 0
for _, line in ipairs(lines) do
local text = string.format(line.fmt, keymap.get_binding(line.cmd))
w = math.max(w, renderer.draw_text(style.font, text, x + style.padding.x, y, color))
y = y + th + style.padding.y
end
return w, dh
end
function EmptyView:draw()
self:draw_background(style.background)
local w, h = draw_text(0, 0, { 0, 0, 0, 0 })
local x = self.position.x + math.max(style.padding.x, (self.size.x - w) / 2)
local y = self.position.y + (self.size.y - h) / 2
draw_text(x, y, style.dim)
end
return EmptyView

View File

@ -17,7 +17,10 @@ local core = {}
local function load_session() local function load_session()
local ok, t = pcall(dofile, USERDIR .. "/session.lua") local ok, t = pcall(dofile, USERDIR .. "/session.lua")
return ok and t or {} if ok then
return t.recents, t.window, t.window_mode
end
return {}
end end
@ -27,8 +30,6 @@ local function save_session()
fp:write("return {recents=", common.serialize(core.recent_projects), fp:write("return {recents=", common.serialize(core.recent_projects),
", window=", common.serialize(table.pack(system.get_window_size())), ", window=", common.serialize(table.pack(system.get_window_size())),
", window_mode=", common.serialize(system.get_window_mode()), ", window_mode=", common.serialize(system.get_window_mode()),
", previous_find=", common.serialize(core.previous_find),
", previous_replace=", common.serialize(core.previous_replace),
"}\n") "}\n")
fp:close() fp:close()
end end
@ -36,7 +37,7 @@ end
local function update_recents_project(action, dir_path_abs) local function update_recents_project(action, dir_path_abs)
local dirname = common.normalize_volume(dir_path_abs) local dirname = common.normalize_path(dir_path_abs)
if not dirname then return end if not dirname then return end
local recents = core.recent_projects local recents = core.recent_projects
local n = #recents local n = #recents
@ -52,13 +53,23 @@ local function update_recents_project(action, dir_path_abs)
end end
function core.reschedule_project_scan()
if core.project_scan_thread_id then
core.threads[core.project_scan_thread_id].wake = 0
end
end
function core.set_project_dir(new_dir, change_project_fn) function core.set_project_dir(new_dir, change_project_fn)
local chdir_ok = pcall(system.chdir, new_dir) local chdir_ok = pcall(system.chdir, new_dir)
if chdir_ok then if chdir_ok then
if change_project_fn then change_project_fn() end if change_project_fn then change_project_fn() end
core.project_dir = common.normalize_volume(new_dir) core.project_dir = common.normalize_path(new_dir)
core.project_directories = {} core.project_directories = {}
core.add_project_directory(new_dir) core.add_project_directory(new_dir)
core.project_files = {}
core.project_files_limit = false
core.reschedule_project_scan()
return true return true
end end
return false return false
@ -69,9 +80,6 @@ function core.open_folder_project(dir_path_abs)
if core.set_project_dir(dir_path_abs, core.on_quit_project) then if core.set_project_dir(dir_path_abs, core.on_quit_project) then
core.root_view:close_all_docviews() core.root_view:close_all_docviews()
update_recents_project("add", dir_path_abs) update_recents_project("add", dir_path_abs)
if not core.load_project_module() then
command.perform("core:open-log")
end
core.on_enter_project(dir_path_abs) core.on_enter_project(dir_path_abs)
end end
end end
@ -92,29 +100,6 @@ local function compare_file(a, b)
return a.filename < b.filename return a.filename < b.filename
end 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)
local info = system.get_file_info(root .. file)
if info then
info.filename = strip_leading_path(file)
return (info.size < config.file_size_limit * 1e6 and
not common.match_pattern(common.basename(info.filename), config.ignore_files)
and info)
end
end
-- Predicate function to inhibit directory recursion in get_directory_files
-- based on a time limit and the number of files.
local function timed_max_files_pred(dir, filename, entries_count, t_elapsed)
local n_limit = entries_count <= config.max_project_files
local t_limit = t_elapsed < 20 / config.fps
return n_limit and t_limit and core.project_subdir_is_shown(dir, filename)
end
-- "root" will by an absolute path without trailing '/' -- "root" will by an absolute path without trailing '/'
-- "path" will be a path starting with '/' and without trailing '/' -- "path" will be a path starting with '/' and without trailing '/'
-- or the empty string. -- or the empty string.
@ -123,31 +108,34 @@ end
-- When recursing "root" will always be the same, only "path" will change. -- When recursing "root" will always be the same, only "path" will change.
-- Returns a list of file "items". In eash item the "filename" will be the -- Returns a list of file "items". In eash item the "filename" will be the
-- complete file path relative to "root" *without* the trailing '/'. -- complete file path relative to "root" *without* the trailing '/'.
local function get_directory_files(dir, root, path, t, entries_count, recurse_pred, begin_hook) local function get_directory_files(root, path, t, recursive, begin_hook)
if begin_hook then begin_hook() end if begin_hook then begin_hook() end
local t0 = system.get_time() local size_limit = config.file_size_limit * 10e5
local all = system.list_dir(root .. path) or {} local all = system.list_dir(root .. path) or {}
local t_elapsed = system.get_time() - t0
local dirs, files = {}, {} local dirs, files = {}, {}
local entries_count = 0
local max_entries = config.max_project_files
for _, file in ipairs(all) do for _, file in ipairs(all) do
local info = get_project_file_info(root, path .. PATHSEP .. file) if not common.match_pattern(file, config.ignore_files) then
if info then local file = path .. PATHSEP .. file
table.insert(info.type == "dir" and dirs or files, info) local info = system.get_file_info(root .. file)
entries_count = entries_count + 1 if info and info.size < size_limit then
info.filename = strip_leading_path(file)
table.insert(info.type == "dir" and dirs or files, info)
entries_count = entries_count + 1
if recursive and entries_count > max_entries then return nil, entries_count end
end
end end
end end
local recurse_complete = true
table.sort(dirs, compare_file) table.sort(dirs, compare_file)
for _, f in ipairs(dirs) do for _, f in ipairs(dirs) do
table.insert(t, f) table.insert(t, f)
if recurse_pred(dir, f.filename, entries_count, t_elapsed) then if recursive and entries_count <= max_entries then
local _, complete, n = get_directory_files(dir, root, PATHSEP .. f.filename, t, entries_count, recurse_pred, begin_hook) local subdir_t, subdir_count = get_directory_files(root, PATHSEP .. f.filename, t, recursive)
recurse_complete = recurse_complete and complete entries_count = entries_count + subdir_count
entries_count = n f.scanned = true
else
recurse_complete = false
end end
end end
@ -156,319 +144,135 @@ local function get_directory_files(dir, root, path, t, entries_count, recurse_pr
table.insert(t, f) table.insert(t, f)
end end
return t, recurse_complete, entries_count return t, entries_count
end end
local function project_scan_thread()
function core.project_subdir_set_show(dir, filename, show) local function diff_files(a, b)
dir.shown_subdir[filename] = show if #a ~= #b then return true end
if dir.files_limit and PLATFORM == "Linux" then for i, v in ipairs(a) do
local fullpath = dir.name .. PATHSEP .. filename if b[i].filename ~= v.filename
local watch_fn = show and system.watch_dir_add or system.watch_dir_rm or b[i].modified ~= v.modified then
local success = watch_fn(dir.watch_id, fullpath) return true
if not success then end
core.log("Internal warning: error calling system.watch_dir_%s", show and "add" or "rm")
end end
end end
while true do
-- get project files and replace previous table if the new table is
-- different
local i = 1
while not core.project_files_limit and i <= #core.project_directories do
local dir = core.project_directories[i]
local t, entries_count = get_directory_files(dir.name, "", {}, true)
if diff_files(dir.files, t) then
if entries_count > config.max_project_files then
core.project_files_limit = true
core.status_view:show_message("!", style.accent,
"Too many files in project directory: stopped reading at "..
config.max_project_files.." files. For more information see "..
"usage.md at github.com/franko/lite-xl."
)
end
dir.files = t
core.redraw = true
end
if dir.name == core.project_dir then
core.project_files = dir.files
end
i = i + 1
end
-- wait for next scan
coroutine.yield(config.project_scan_rate)
end
end end
function core.project_subdir_is_shown(dir, filename) function core.is_project_folder(dirname)
return not dir.files_limit or dir.shown_subdir[filename] for _, dir in ipairs(core.project_directories) do
end if dir.name == dirname then
return true
local function show_max_files_warning(dir)
local message = dir.slow_filesystem and
"Filesystem is too slow: project files will not be indexed." or
"Too many files in project directory: stopped reading at "..
config.max_project_files.." files. For more information see "..
"usage.md at github.com/lite-xl/lite-xl."
core.status_view:show_message("!", style.accent, message)
end
local function file_search(files, info)
local filename, type = info.filename, info.type
local inf, sup = 1, #files
while sup - inf > 8 do
local curr = math.floor((inf + sup) / 2)
if system.path_compare(filename, type, files[curr].filename, files[curr].type) then
sup = curr - 1
else
inf = curr
end end
end end
while inf <= sup and not system.path_compare(filename, type, files[inf].filename, files[inf].type) do return false
if files[inf].filename == filename then
return inf, true
end
inf = inf + 1
end
return inf, false
end end
local function project_scan_add_entry(dir, fileinfo) function core.scan_project_folder(dirname, filename)
local index, match = file_search(dir.files, fileinfo) for _, dir in ipairs(core.project_directories) do
if not match then if dir.name == dirname then
table.insert(dir.files, index, fileinfo) for i, file in ipairs(dir.files) do
dir.is_dirty = true local file = dir.files[i]
end if file.filename == filename then
end if file.scanned then return end
local new_files = get_directory_files(dirname, PATHSEP .. filename, {})
for j, new_file in ipairs(new_files) do
local function files_info_equal(a, b) table.insert(dir.files, i + j, new_file)
return a.filename == b.filename and a.type == b.type end
end file.scanned = true
return
-- for "a" inclusive from i1 + 1 and i1 + n
local function files_list_match(a, i1, n, b)
if n ~= #b then return false end
for i = 1, n do
if not files_info_equal(a[i1 + i], b[i]) then
return false
end
end
return true
end
-- arguments like for files_list_match
local function files_list_replace(as, i1, n, bs)
local m = #bs
local i, j = 1, 1
while i <= m or i <= n do
local a, b = as[i1 + i], bs[j]
if i > n or (j <= m and not files_info_equal(a, b) and
not system.path_compare(a.filename, a.type, b.filename, b.type))
then
table.insert(as, i1 + i, b)
i, j, n = i + 1, j + 1, n + 1
elseif j > m or system.path_compare(a.filename, a.type, b.filename, b.type) then
table.remove(as, i1 + i)
n = n - 1
else
i, j = i + 1, j + 1
end
end
end
local function project_subdir_bounds(dir, filename)
local index, n = 0, #dir.files
for i, file in ipairs(dir.files) do
local file = dir.files[i]
if file.filename == filename then
index, n = i, #dir.files - i
for j = 1, #dir.files - i do
if not common.path_belongs_to(dir.files[i + j].filename, filename) then
n = j - 1
break
end end
end end
return index, n, file
end end
end end
end end
local function rescan_project_subdir(dir, filename_rooted)
local new_files = get_directory_files(dir, dir.name, filename_rooted, {}, 0, core.project_subdir_is_shown, coroutine.yield)
local index, n = 0, #dir.files
if filename_rooted ~= "" then
local filename = strip_leading_path(filename_rooted)
index, n = project_subdir_bounds(dir, filename)
end
if not files_list_match(dir.files, index, n, new_files) then local function find_project_files_co(root, path)
files_list_replace(dir.files, index, n, new_files) local size_limit = config.file_size_limit * 10e5
dir.is_dirty = true
return true
end
end
local function add_dir_scan_thread(dir)
core.add_thread(function()
while true do
local has_changes = rescan_project_subdir(dir, "")
if has_changes then
core.redraw = true -- we run without an event, from a thread
end
coroutine.yield(5)
end
end)
end
-- Populate a project folder top directory by scanning the filesystem.
local function scan_project_folder(index)
local dir = core.project_directories[index]
if PLATFORM == "Linux" then
local fstype = system.get_fs_type(dir.name)
dir.force_rescan = (fstype == "nfs" or fstype == "fuse")
end
local t, complete, entries_count = get_directory_files(dir, dir.name, "", {}, 0, timed_max_files_pred)
if not complete then
dir.slow_filesystem = not complete and (entries_count <= config.max_project_files)
dir.files_limit = true
if not dir.force_rescan then
-- Watch non-recursively on Linux only.
-- The reason is recursively watching with dmon on linux
-- doesn't work on very large directories.
dir.watch_id = system.watch_dir(dir.name, PLATFORM ~= "Linux")
end
if core.status_view then -- May be not yet initialized.
show_max_files_warning(dir)
end
else
if not dir.force_rescan then
dir.watch_id = system.watch_dir(dir.name, true)
end
end
dir.files = t
if dir.force_rescan then
add_dir_scan_thread(dir)
else
core.dir_rescan_add_job(dir, ".")
end
end
function core.add_project_directory(path)
-- top directories has a file-like "item" but the item.filename
-- will be simply the name of the directory, without its path.
-- The field item.topdir will identify it as a top level directory.
path = common.normalize_volume(path)
local dir = {
name = path,
item = {filename = common.basename(path), type = "dir", topdir = true},
files_limit = false,
is_dirty = true,
shown_subdir = {},
}
table.insert(core.project_directories, dir)
scan_project_folder(#core.project_directories)
if path == core.project_dir then
core.project_files = dir.files
end
core.redraw = true
end
function core.update_project_subdir(dir, filename, expanded)
local index, n, file = project_subdir_bounds(dir, filename)
if index then
local new_files = expanded and get_directory_files(dir, dir.name, PATHSEP .. filename, {}, 0, core.project_subdir_is_shown) or {}
files_list_replace(dir.files, index, n, new_files)
dir.is_dirty = true
return true
end
end
-- Find files and directories recursively reading from the filesystem.
-- Filter files and yields file's directory and info table. This latter
-- is filled to be like required by project directories "files" list.
local function find_files_rec(root, path)
local all = system.list_dir(root .. path) or {} local all = system.list_dir(root .. path) or {}
for _, file in ipairs(all) do for _, file in ipairs(all) do
local file = path .. PATHSEP .. file if not common.match_pattern(file, config.ignore_files) then
local info = system.get_file_info(root .. file) local file = path .. PATHSEP .. file
if info then local info = system.get_file_info(root .. file)
info.filename = strip_leading_path(file) if info and info.size < size_limit then
if info.type == "file" then info.filename = strip_leading_path(file)
coroutine.yield(root, info) if info.type == "file" then
else coroutine.yield(root, info)
find_files_rec(root, PATHSEP .. info.filename) else
find_project_files_co(root, PATHSEP .. info.filename)
end
end end
end end
end end
end end
-- Iterator function to list all project files
local function project_files_iter(state) local function project_files_iter(state)
local dir = core.project_directories[state.dir_index] local dir = core.project_directories[state.dir_index]
if state.co then state.file_index = state.file_index + 1
-- We have a coroutine to fetch for files, use the coroutine. while dir and state.file_index > #dir.files do
-- Used for directories that exceeds the files nuumber limit. state.dir_index = state.dir_index + 1
local ok, name, file = coroutine.resume(state.co, dir.name, "") state.file_index = 1
if ok and name then dir = core.project_directories[state.dir_index]
return name, file
else
-- The coroutine terminated, increment file/dir counter to scan
-- next project directory.
state.co = false
state.file_index = 1
state.dir_index = state.dir_index + 1
dir = core.project_directories[state.dir_index]
end
else
-- Increase file/dir counter
state.file_index = state.file_index + 1
while dir and state.file_index > #dir.files do
state.dir_index = state.dir_index + 1
state.file_index = 1
dir = core.project_directories[state.dir_index]
end
end end
if not dir then return end if not dir then return end
if dir.files_limit then
-- The current project directory is files limited: create a couroutine
-- to read files from the filesystem.
state.co = coroutine.create(find_files_rec)
return project_files_iter(state)
end
return dir.name, dir.files[state.file_index] return dir.name, dir.files[state.file_index]
end end
function core.get_project_files() function core.get_project_files()
local state = { dir_index = 1, file_index = 0 } if core.project_files_limit then
return project_files_iter, state return coroutine.wrap(function()
for _, dir in ipairs(core.project_directories) do
find_project_files_co(dir.name, "")
end
end)
else
local state = { dir_index = 1, file_index = 0 }
return project_files_iter, state
end
end end
function core.project_files_number() function core.project_files_number()
local n = 0 if not core.project_files_limit then
for i = 1, #core.project_directories do local n = 0
if core.project_directories[i].files_limit then return end for i = 1, #core.project_directories do
n = n + #core.project_directories[i].files n = n + #core.project_directories[i].files
end
return n
end
local function project_dir_by_watch_id(watch_id)
for i = 1, #core.project_directories do
if core.project_directories[i].watch_id == watch_id then
return core.project_directories[i]
end end
end return n
end
local function project_scan_remove_file(dir, filepath)
local fileinfo = { filename = filepath }
for _, filetype in ipairs {"dir", "file"} do
fileinfo.type = filetype
local index, match = file_search(dir.files, fileinfo)
if match then
table.remove(dir.files, index)
dir.is_dirty = true
return
end
end
end
local function project_scan_add_file(dir, filepath)
for fragment in string.gmatch(filepath, "([^/\\]+)") do
if common.match_pattern(fragment, config.ignore_files) then
return
end
end
local fileinfo = get_project_file_info(dir.name, PATHSEP .. filepath)
if fileinfo then
project_scan_add_entry(dir, fileinfo)
end end
end end
@ -516,8 +320,8 @@ local style = require "core.style"
------------------------------- Fonts ---------------------------------------- ------------------------------- Fonts ----------------------------------------
-- customize fonts: -- customize fonts:
-- style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 14 * SCALE) -- style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 13 * SCALE)
-- style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 14 * SCALE) -- style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 13 * SCALE)
-- --
-- font names used by lite: -- font names used by lite:
-- style.font : user interface -- style.font : user interface
@ -565,6 +369,19 @@ function core.load_user_directory()
end end
function core.add_project_directory(path)
-- top directories has a file-like "item" but the item.filename
-- will be simply the name of the directory, without its path.
-- The field item.topdir will identify it as a top level directory.
path = common.normalize_path(path)
table.insert(core.project_directories, {
name = path,
item = {filename = common.basename(path), type = "dir", topdir = true},
files = {}
})
end
function core.remove_project_directory(path) function core.remove_project_directory(path)
-- skip the fist directory because it is the project's directory -- skip the fist directory because it is the project's directory
for i = 2, #core.project_directories do for i = 2, #core.project_directories do
@ -577,6 +394,15 @@ function core.remove_project_directory(path)
return false return false
end end
local function whitespace_replacements()
local r = renderer.replacements.new()
r:add(" ", "·")
r:add("\t", "»")
return r
end
local function reload_on_user_module_save() local function reload_on_user_module_save()
-- auto-realod style when user's module is saved by overriding Doc:Save() -- auto-realod style when user's module is saved by overriding Doc:Save()
local doc_save = Doc.save local doc_save = Doc.save
@ -603,21 +429,19 @@ function core.init()
Doc = require "core.doc" Doc = require "core.doc"
if PATHSEP == '\\' then if PATHSEP == '\\' then
USERDIR = common.normalize_volume(USERDIR) USERDIR = common.normalize_path(USERDIR)
DATADIR = common.normalize_volume(DATADIR) DATADIR = common.normalize_path(DATADIR)
EXEDIR = common.normalize_volume(EXEDIR) EXEDIR = common.normalize_path(EXEDIR)
end end
do do
local session = load_session() local recent_projects, window_position, window_mode = load_session()
if session.window_mode == "normal" then if window_mode == "normal" then
system.set_window_size(table.unpack(session.window)) system.set_window_size(table.unpack(window_position))
elseif session.window_mode == "maximized" then elseif window_mode == "maximized" then
system.set_window_mode("maximized") system.set_window_mode("maximized")
end end
core.recent_projects = session.recents or {} core.recent_projects = recent_projects
core.previous_find = session.previous_find or {}
core.previous_replace = session.previous_replace or {}
end end
local project_dir = core.recent_projects[1] or "." local project_dir = core.recent_projects[1] or "."
@ -637,10 +461,7 @@ function core.init()
project_dir = arg_filename project_dir = arg_filename
project_dir_explicit = true project_dir_explicit = true
else else
-- on macOS we can get an argument like "-psn_0_52353" that we just ignore. delayed_error = string.format("error: invalid file or directory %q", ARGS[i])
if not ARGS[i]:match("^-psn") then
delayed_error = string.format("error: invalid file or directory %q", ARGS[i])
end
end end
end end
@ -673,7 +494,7 @@ function core.init()
core.redraw = true core.redraw = true
core.visited_files = {} core.visited_files = {}
core.restart_request = false core.restart_request = false
core.quit_request = false core.replacements = whitespace_replacements()
core.root_view = RootView() core.root_view = RootView()
core.command_view = CommandView() core.command_view = CommandView()
@ -690,22 +511,17 @@ function core.init()
cur_node = cur_node:split("down", core.command_view, {y = true}) cur_node = cur_node:split("down", core.command_view, {y = true})
cur_node = cur_node:split("down", core.status_view, {y = true}) cur_node = cur_node:split("down", core.status_view, {y = true})
core.project_scan_thread_id = core.add_thread(project_scan_thread)
command.add_defaults() command.add_defaults()
local got_user_error = not core.load_user_directory() local got_user_error = not core.load_user_directory()
local plugins_success, plugins_refuse_list = core.load_plugins() local plugins_success, plugins_refuse_list = core.load_plugins()
do do
local pdir, pname = project_dir_abs:match("(.*)[:/\\\\](.*)") local pdir, pname = project_dir_abs:match("(.*)[/\\\\](.*)")
core.log("Opening project %q from directory %s", pname, pdir) core.log("Opening project %q from directory %s", pname, pdir)
end end
local got_project_error = not core.load_project_module() local got_project_error = not core.load_project_module()
-- We assume we have just a single project directory here. Now that StatusView
-- is there show max files warning if needed.
if core.project_directories[1].files_limit then
show_max_files_warning(core.project_directories[1])
end
for _, filename in ipairs(files) do for _, filename in ipairs(files) do
core.root_view:open_doc(core.open_doc(filename)) core.root_view:open_doc(core.open_doc(filename))
end end
@ -737,7 +553,7 @@ function core.init()
"Refused Plugins", "Refused Plugins",
string.format( string.format(
"Some plugins are not loaded due to version mismatch.\n\n%s.\n\n" .. "Some plugins are not loaded due to version mismatch.\n\n%s.\n\n" ..
"Please download a recent version from https://github.com/lite-xl/lite-xl-plugins.", "Please download a recent version from https://github.com/franko/lite-plugins.",
table.concat(msg, ".\n\n")), table.concat(msg, ".\n\n")),
opt, function(item) opt, function(item)
if item.text == "Exit" then os.exit(1) end if item.text == "Exit" then os.exit(1) end
@ -748,10 +564,10 @@ function core.init()
end end
function core.confirm_close_docs(docs, close_fn, ...) function core.confirm_close_all(close_fn, ...)
local dirty_count = 0 local dirty_count = 0
local dirty_name local dirty_name
for _, doc in ipairs(docs or core.docs) do for _, doc in ipairs(core.docs) do
if doc:is_dirty() then if doc:is_dirty() then
dirty_count = dirty_count + 1 dirty_count = dirty_count + 1
dirty_name = doc:get_name() dirty_name = doc:get_name()
@ -811,12 +627,12 @@ local function quit_with_function(quit_fn, force)
save_session() save_session()
quit_fn() quit_fn()
else else
core.confirm_close_docs(core.docs, quit_with_function, quit_fn, true) core.confirm_close_all(quit_with_function, quit_fn, true)
end end
end end
function core.quit(force) function core.quit(force)
quit_with_function(function() core.quit_request = true end, force) quit_with_function(os.exit, force)
end end
@ -845,8 +661,8 @@ local function check_plugin_version(filename)
-- Future versions will look only at the mod-version tag. -- Future versions will look only at the mod-version tag.
local version = line:match('%-%-%s*lite%-xl%s*(%d+%.%d+)$') local version = line:match('%-%-%s*lite%-xl%s*(%d+%.%d+)$')
if version then if version then
-- we consider the version tag 2.0 equivalent to mod-version:2 -- we consider the version tag 1.16 equivalent to mod-version:1
version_match = (version == '2.0' and MOD_VERSION == "2") version_match = (version == '1.16' and MOD_VERSION == "1")
break break
end end
end end
@ -861,31 +677,27 @@ function core.load_plugins()
userdir = {dir = USERDIR, plugins = {}}, userdir = {dir = USERDIR, plugins = {}},
datadir = {dir = DATADIR, plugins = {}}, datadir = {dir = DATADIR, plugins = {}},
} }
local files, ordered = {}, {} local files = {}
for _, root_dir in ipairs {DATADIR, USERDIR} do for _, root_dir in ipairs {DATADIR, USERDIR} do
local plugin_dir = root_dir .. "/plugins" local plugin_dir = root_dir .. "/plugins"
for _, filename in ipairs(system.list_dir(plugin_dir) or {}) do for _, filename in ipairs(system.list_dir(plugin_dir) or {}) do
if not files[filename] then table.insert(ordered, filename) end
files[filename] = plugin_dir -- user plugins will always replace system plugins files[filename] = plugin_dir -- user plugins will always replace system plugins
end end
end end
table.sort(ordered)
for _, filename in ipairs(ordered) do for filename, plugin_dir in pairs(files) do
local plugin_dir, basename = files[filename], filename:match("(.-)%.lua$") or filename local basename = filename:match("(.-)%.lua$") or filename
local is_lua_file, version_match = check_plugin_version(plugin_dir .. '/' .. filename) local version_match = check_plugin_version(plugin_dir .. '/' .. filename)
if is_lua_file then if not version_match then
if not version_match then core.log_quiet("Version mismatch for plugin %q from %s", basename, plugin_dir)
core.log_quiet("Version mismatch for plugin %q from %s", basename, plugin_dir) local list = refused_list[plugin_dir:find(USERDIR) == 1 and 'userdir' or 'datadir'].plugins
local list = refused_list[plugin_dir:find(USERDIR, 1, true) == 1 and 'userdir' or 'datadir'].plugins table.insert(list, filename)
table.insert(list, filename) end
end if version_match and config.plugins[basename] ~= false then
if version_match and config.plugins[basename] ~= false then local ok = core.try(require, "plugins." .. basename)
local ok = core.try(require, "plugins." .. basename) if ok then core.log_quiet("Loaded plugin %q from %s", basename, plugin_dir) end
if ok then core.log_quiet("Loaded plugin %q from %s", basename, plugin_dir) end if not ok then
if not ok then no_errors = false
no_errors = false
end
end end
end end
end end
@ -931,12 +743,8 @@ end
function core.set_active_view(view) function core.set_active_view(view)
assert(view, "Tried to set active view to nil") assert(view, "Tried to set active view to nil")
if core.active_view and core.active_view.force_focus then return end
if view ~= core.active_view then if view ~= core.active_view then
if core.active_view and core.active_view.force_focus then
core.next_active_view = view
return
end
core.next_active_view = nil
if view.doc and view.doc.filename then if view.doc and view.doc.filename then
core.set_visited(view.doc.filename) core.set_visited(view.doc.filename)
end end
@ -994,7 +802,7 @@ end
-- This function should get only filenames normalized using -- This function should get only filenames normalized using
-- common.normalize_path function. -- common.normalize_path function.
function core.project_absolute_path(filename) function core.project_absolute_path(filename)
if filename:match('^%a:\\') or filename:find('/', 1, true) == 1 then if filename:match('^%a:\\') or filename:find('/', 1, true) then
return filename return filename
else else
return core.project_dir .. PATHSEP .. filename return core.project_dir .. PATHSEP .. filename
@ -1066,23 +874,6 @@ function core.error(...)
end end
function core.get_log(i)
if i == nil then
local r = {}
for _, item in ipairs(core.log_items) do
table.insert(r, core.get_log(item))
end
return table.concat(r, "\n")
end
local item = type(i) == "number" and core.log_items[i] or i
local text = string.format("[%s] %s at %s", os.date(nil, item.time), item.text, item.at)
if item.info then
text = string.format("%s\n%s\n", text, item.info)
end
return text
end
function core.try(fn, ...) function core.try(fn, ...)
local err local err
local ok, res = xpcall(fn, function(msg) local ok, res = xpcall(fn, function(msg)
@ -1096,84 +887,6 @@ function core.try(fn, ...)
return false, err return false, err
end end
local scheduled_rescan = {}
function core.has_pending_rescan()
for _ in pairs(scheduled_rescan) do
return true
end
end
function core.dir_rescan_add_job(dir, filepath)
local dirpath = filepath:match("^(.+)[/\\].+$")
local dirpath_rooted = dirpath and PATHSEP .. dirpath or ""
local abs_dirpath = dir.name .. dirpath_rooted
if dirpath then
-- check if the directory is in the project files list, if not exit
local dir_index, dir_match = file_search(dir.files, {filename = dirpath, type = "dir"})
-- Note that is dir_match is false dir_index greaten than the last valid index.
-- We use dir_index to index dir.files below only if dir_match is true.
if not dir_match or not core.project_subdir_is_shown(dir, dir.files[dir_index].filename) then return end
end
local new_time = system.get_time() + 1
-- evaluate new rescan request versus existing rescan
local remove_list = {}
for _, rescan in pairs(scheduled_rescan) do
if abs_dirpath == rescan.abs_path or common.path_belongs_to(abs_dirpath, rescan.abs_path) then
-- abs_dirpath is a subpath of a scan already ongoing: skip
rescan.time_limit = new_time
return
elseif common.path_belongs_to(rescan.abs_path, abs_dirpath) then
-- abs_dirpath already cover this rescan: add to the list of rescan to be removed
table.insert(remove_list, rescan.abs_path)
end
end
for _, key_path in ipairs(remove_list) do
scheduled_rescan[key_path] = nil
end
scheduled_rescan[abs_dirpath] = {dir = dir, path = dirpath_rooted, abs_path = abs_dirpath, time_limit = new_time}
core.add_thread(function()
while true do
local rescan = scheduled_rescan[abs_dirpath]
if not rescan then return end
if system.get_time() > rescan.time_limit then
local has_changes = rescan_project_subdir(rescan.dir, rescan.path)
if has_changes then
core.redraw = true -- we run without an event, from a thread
rescan.time_limit = new_time
else
scheduled_rescan[rescan.abs_path] = nil
return
end
end
coroutine.yield(0.2)
end
end)
end
-- no-op but can be overrided by plugins
function core.on_dirmonitor_modify(dir, filepath)
end
function core.on_dir_change(watch_id, action, filepath)
local dir = project_dir_by_watch_id(watch_id)
if not dir then return end
core.dir_rescan_add_job(dir, filepath)
if action == "delete" then
project_scan_remove_file(dir, filepath)
elseif action == "create" then
project_scan_add_file(dir, filepath)
core.on_dirmonitor_modify(dir, filepath);
elseif action == "modify" then
core.on_dirmonitor_modify(dir, filepath);
end
end
function core.on_event(type, ...) function core.on_event(type, ...)
local did_keymap = false local did_keymap = false
@ -1186,15 +899,11 @@ function core.on_event(type, ...)
elseif type == "mousemoved" then elseif type == "mousemoved" then
core.root_view:on_mouse_moved(...) core.root_view:on_mouse_moved(...)
elseif type == "mousepressed" then elseif type == "mousepressed" then
if not core.root_view:on_mouse_pressed(...) then core.root_view:on_mouse_pressed(...)
did_keymap = keymap.on_mouse_pressed(...)
end
elseif type == "mousereleased" then elseif type == "mousereleased" then
core.root_view:on_mouse_released(...) core.root_view:on_mouse_released(...)
elseif type == "mousewheel" then elseif type == "mousewheel" then
if not core.root_view:on_mouse_wheel(...) then core.root_view:on_mouse_wheel(...)
did_keymap = keymap.on_mouse_wheel(...)
end
elseif type == "resized" then elseif type == "resized" then
core.window_mode = system.get_window_mode() core.window_mode = system.get_window_mode()
elseif type == "minimized" or type == "maximized" or type == "restored" then elseif type == "minimized" or type == "maximized" or type == "restored" then
@ -1214,8 +923,6 @@ function core.on_event(type, ...)
end end
elseif type == "focuslost" then elseif type == "focuslost" then
core.root_view:on_focus_lost(...) core.root_view:on_focus_lost(...)
elseif type == "dirchange" then
core.on_dir_change(...)
elseif type == "quit" then elseif type == "quit" then
core.quit() core.quit()
end end
@ -1322,8 +1029,8 @@ function core.run()
while true do while true do
core.frame_start = system.get_time() core.frame_start = system.get_time()
local did_redraw = core.step() local did_redraw = core.step()
local need_more_work = run_threads() or core.has_pending_rescan() local need_more_work = run_threads()
if core.restart_request or core.quit_request then break end if core.restart_request then break end
if not did_redraw and not need_more_work then if not did_redraw and not need_more_work then
idle_iterations = idle_iterations + 1 idle_iterations = idle_iterations + 1
-- do not wait of events at idle_iterations = 1 to give a chance at core.step to run -- do not wait of events at idle_iterations = 1 to give a chance at core.step to run
@ -1374,4 +1081,3 @@ end
return core return core

View File

@ -6,7 +6,6 @@ local function keymap_macos(keymap)
["cmd+n"] = "core:new-doc", ["cmd+n"] = "core:new-doc",
["cmd+shift+c"] = "core:change-project-folder", ["cmd+shift+c"] = "core:change-project-folder",
["cmd+shift+o"] = "core:open-project-folder", ["cmd+shift+o"] = "core:open-project-folder",
["cmd+shift+r"] = "core:restart",
["cmd+ctrl+return"] = "core:toggle-fullscreen", ["cmd+ctrl+return"] = "core:toggle-fullscreen",
["cmd+ctrl+shift+j"] = "root:split-left", ["cmd+ctrl+shift+j"] = "root:split-left",
@ -33,8 +32,6 @@ local function keymap_macos(keymap)
["cmd+7"] = "root:switch-to-tab-7", ["cmd+7"] = "root:switch-to-tab-7",
["cmd+8"] = "root:switch-to-tab-8", ["cmd+8"] = "root:switch-to-tab-8",
["cmd+9"] = "root:switch-to-tab-9", ["cmd+9"] = "root:switch-to-tab-9",
["wheel"] = "root:scroll",
["cmd+f"] = "find-replace:find", ["cmd+f"] = "find-replace:find",
["cmd+r"] = "find-replace:replace", ["cmd+r"] = "find-replace:replace",
["f3"] = "find-replace:repeat-find", ["f3"] = "find-replace:repeat-find",
@ -55,27 +52,23 @@ local function keymap_macos(keymap)
["shift+tab"] = "doc:unindent", ["shift+tab"] = "doc:unindent",
["backspace"] = "doc:backspace", ["backspace"] = "doc:backspace",
["shift+backspace"] = "doc:backspace", ["shift+backspace"] = "doc:backspace",
["option+backspace"] = "doc:delete-to-previous-word-start", ["cmd+backspace"] = "doc:delete-to-previous-word-start",
["cmd+shift+backspace"] = "doc:delete-to-previous-word-start", ["cmd+shift+backspace"] = "doc:delete-to-previous-word-start",
["cmd+backspace"] = "doc:delete-to-start-of-indentation",
["delete"] = "doc:delete", ["delete"] = "doc:delete",
["shift+delete"] = "doc:delete", ["shift+delete"] = "doc:delete",
["option+delete"] = "doc:delete-to-next-word-end", ["cmd+delete"] = "doc:delete-to-next-word-end",
["cmd+shift+delete"] = "doc:delete-to-next-word-end", ["cmd+shift+delete"] = "doc:delete-to-next-word-end",
["cmd+delete"] = "doc:delete-to-end-of-line",
["return"] = { "command:submit", "doc:newline", "dialog:select" }, ["return"] = { "command:submit", "doc:newline", "dialog:select" },
["keypad enter"] = { "command:submit", "doc:newline", "dialog:select" }, ["keypad enter"] = { "command:submit", "doc:newline", "dialog:select" },
["cmd+return"] = "doc:newline-below", ["cmd+return"] = "doc:newline-below",
["cmd+shift+return"] = "doc:newline-above", ["cmd+shift+return"] = "doc:newline-above",
["cmd+j"] = "doc:join-lines", ["cmd+j"] = "doc:join-lines",
["cmd+a"] = "doc:select-all", ["cmd+a"] = "doc:select-all",
["cmd+d"] = { "find-replace:select-add-next", "doc:select-word" }, ["cmd+d"] = { "find-replace:select-next", "doc:select-word" },
["cmd+f3"] = "find-replace:select-next",
["cmd+l"] = "doc:select-lines", ["cmd+l"] = "doc:select-lines",
["cmd+shift+l"] = { "find-replace:select-add-all", "doc:select-word" },
["cmd+/"] = "doc:toggle-line-comments", ["cmd+/"] = "doc:toggle-line-comments",
["option+up"] = "doc:move-lines-up", ["cmd+up"] = "doc:move-lines-up",
["option+down"] = "doc:move-lines-down", ["cmd+down"] = "doc:move-lines-down",
["cmd+shift+d"] = "doc:duplicate-lines", ["cmd+shift+d"] = "doc:duplicate-lines",
["cmd+shift+k"] = "doc:delete-lines", ["cmd+shift+k"] = "doc:delete-lines",
@ -83,42 +76,33 @@ local function keymap_macos(keymap)
["right"] = { "doc:move-to-next-char", "dialog:next-entry"}, ["right"] = { "doc:move-to-next-char", "dialog:next-entry"},
["up"] = { "command:select-previous", "doc:move-to-previous-line" }, ["up"] = { "command:select-previous", "doc:move-to-previous-line" },
["down"] = { "command:select-next", "doc:move-to-next-line" }, ["down"] = { "command:select-next", "doc:move-to-next-line" },
["option+left"] = "doc:move-to-previous-word-start", ["cmd+left"] = "doc:move-to-previous-word-start",
["option+right"] = "doc:move-to-next-word-end", ["cmd+right"] = "doc:move-to-next-word-end",
["cmd+left"] = "doc:move-to-start-of-indentation",
["cmd+right"] = "doc:move-to-end-of-line",
["cmd+["] = "doc:move-to-previous-block-start", ["cmd+["] = "doc:move-to-previous-block-start",
["cmd+]"] = "doc:move-to-next-block-end", ["cmd+]"] = "doc:move-to-next-block-end",
["home"] = "doc:move-to-start-of-indentation", ["home"] = "doc:move-to-start-of-line",
["end"] = "doc:move-to-end-of-line", ["end"] = "doc:move-to-end-of-line",
["cmd+up"] = "doc:move-to-start-of-doc", ["cmd+home"] = "doc:move-to-start-of-doc",
["cmd+down"] = "doc:move-to-end-of-doc", ["cmd+end"] = "doc:move-to-end-of-doc",
["pageup"] = "doc:move-to-previous-page", ["pageup"] = "doc:move-to-previous-page",
["pagedown"] = "doc:move-to-next-page", ["pagedown"] = "doc:move-to-next-page",
["shift+1lclick"] = "doc:select-to-cursor",
["ctrl+1lclick"] = "doc:split-cursor",
["1lclick"] = "doc:set-cursor",
["2lclick"] = "doc:set-cursor-word",
["3lclick"] = "doc:set-cursor-line",
["shift+left"] = "doc:select-to-previous-char", ["shift+left"] = "doc:select-to-previous-char",
["shift+right"] = "doc:select-to-next-char", ["shift+right"] = "doc:select-to-next-char",
["shift+up"] = "doc:select-to-previous-line", ["shift+up"] = "doc:select-to-previous-line",
["shift+down"] = "doc:select-to-next-line", ["shift+down"] = "doc:select-to-next-line",
["option+shift+left"] = "doc:select-to-previous-word-start", ["cmd+shift+left"] = "doc:select-to-previous-word-start",
["option+shift+right"] = "doc:select-to-next-word-end", ["cmd+shift+right"] = "doc:select-to-next-word-end",
["cmd+shift+left"] = "doc:select-to-start-of-indentation",
["cmd+shift+right"] = "doc:select-to-end-of-line",
["cmd+shift+["] = "doc:select-to-previous-block-start", ["cmd+shift+["] = "doc:select-to-previous-block-start",
["cmd+shift+]"] = "doc:select-to-next-block-end", ["cmd+shift+]"] = "doc:select-to-next-block-end",
["shift+home"] = "doc:select-to-start-of-indentation", ["shift+home"] = "doc:select-to-start-of-line",
["shift+end"] = "doc:select-to-end-of-line", ["shift+end"] = "doc:select-to-end-of-line",
["cmd+shift+up"] = "doc:select-to-start-of-doc", ["cmd+shift+home"] = "doc:select-to-start-of-doc",
["cmd+shift+down"] = "doc:select-to-end-of-doc", ["cmd+shift+end"] = "doc:select-to-end-of-doc",
["shift+pageup"] = "doc:select-to-previous-page", ["shift+pageup"] = "doc:select-to-previous-page",
["shift+pagedown"] = "doc:select-to-next-page", ["shift+pagedown"] = "doc:select-to-next-page",
["cmd+option+up"] = "doc:create-cursor-previous-line", ["cmd+shift+up"] = "doc:create-cursor-previous-line",
["cmd+option+down"] = "doc:create-cursor-next-line" ["cmd+shift+down"] = "doc:create-cursor-next-line"
} }
end end

View File

@ -1,16 +1,14 @@
local command = require "core.command" local command = require "core.command"
local config = require "core.config"
local keymap = {} local keymap = {}
keymap.modkeys = {} keymap.modkeys = {}
keymap.map = {} keymap.map = {}
keymap.reverse_map = {} keymap.reverse_map = {}
local macos = PLATFORM == "Mac OS X" local macos = rawget(_G, "MACOS_RESOURCES")
local os4 = PLATFORM == "AmigaOS 4"
-- Thanks to mathewmariani, taken from his lite-macos github repository. -- Thanks to mathewmariani, taken from his lite-macos github repository.
local modkeys_os = require("core.modkeys-" .. (macos and "macos" or os4 and "os4" or "generic")) local modkeys_os = require("core.modkeys-" .. (macos and "macos" or "generic"))
local modkey_map = modkeys_os.map local modkey_map = modkeys_os.map
local modkeys = modkeys_os.keys local modkeys = modkeys_os.keys
@ -32,8 +30,7 @@ function keymap.add_direct(map)
end end
keymap.map[stroke] = commands keymap.map[stroke] = commands
for _, cmd in ipairs(commands) do for _, cmd in ipairs(commands) do
keymap.reverse_map[cmd] = keymap.reverse_map[cmd] or {} keymap.reverse_map[cmd] = stroke
table.insert(keymap.reverse_map[cmd], stroke)
end end
end end
end end
@ -55,43 +52,18 @@ function keymap.add(map, overwrite)
end end
end end
for _, cmd in ipairs(commands) do for _, cmd in ipairs(commands) do
keymap.reverse_map[cmd] = keymap.reverse_map[cmd] or {} keymap.reverse_map[cmd] = stroke
table.insert(keymap.reverse_map[cmd], stroke)
end end
end end
end end
local function remove_only(tbl, k, v)
for key, values in pairs(tbl) do
if key == k then
if v then
for i, value in ipairs(values) do
if value == v then
table.remove(values, i)
end
end
else
tbl[key] = nil
end
break
end
end
end
function keymap.unbind(key, cmd)
remove_only(keymap.map, key, cmd)
remove_only(keymap.reverse_map, cmd, key)
end
function keymap.get_binding(cmd) function keymap.get_binding(cmd)
return table.unpack(keymap.reverse_map[cmd] or {}) return keymap.reverse_map[cmd]
end end
function keymap.on_key_pressed(k, ...) function keymap.on_key_pressed(k)
local mk = modkey_map[k] local mk = modkey_map[k]
if mk then if mk then
keymap.modkeys[mk] = true keymap.modkeys[mk] = true
@ -101,30 +73,18 @@ function keymap.on_key_pressed(k, ...)
end end
else else
local stroke = key_to_stroke(k) local stroke = key_to_stroke(k)
local commands, performed = keymap.map[stroke] local commands = keymap.map[stroke]
if commands then if commands then
for _, cmd in ipairs(commands) do for _, cmd in ipairs(commands) do
performed = command.perform(cmd, ...) local performed = command.perform(cmd)
if performed then break end if performed then break end
end end
return performed return true
end end
end end
return false return false
end end
function keymap.on_mouse_wheel(delta, ...)
return not (keymap.on_key_pressed("wheel" .. (delta > 0 and "up" or "down"), delta, ...)
or keymap.on_key_pressed("wheel", delta, ...))
end
function keymap.on_mouse_pressed(button, x, y, clicks)
local click_number = (((clicks - 1) % config.max_clicks) + 1)
return not (keymap.on_key_pressed(click_number .. button:sub(1,1) .. "click", x, y, clicks) or
keymap.on_key_pressed(button:sub(1,1) .. "click", x, y, clicks) or
keymap.on_key_pressed(click_number .. "click", x, y, clicks) or
keymap.on_key_pressed("click", x, y, clicks))
end
function keymap.on_key_released(k) function keymap.on_key_released(k)
local mk = modkey_map[k] local mk = modkey_map[k]
@ -147,7 +107,6 @@ keymap.add_direct {
["ctrl+n"] = "core:new-doc", ["ctrl+n"] = "core:new-doc",
["ctrl+shift+c"] = "core:change-project-folder", ["ctrl+shift+c"] = "core:change-project-folder",
["ctrl+shift+o"] = "core:open-project-folder", ["ctrl+shift+o"] = "core:open-project-folder",
["ctrl+shift+r"] = "core:restart",
["alt+return"] = "core:toggle-fullscreen", ["alt+return"] = "core:toggle-fullscreen",
["f11"] = "core:toggle-fullscreen", ["f11"] = "core:toggle-fullscreen",
@ -174,7 +133,6 @@ keymap.add_direct {
["alt+7"] = "root:switch-to-tab-7", ["alt+7"] = "root:switch-to-tab-7",
["alt+8"] = "root:switch-to-tab-8", ["alt+8"] = "root:switch-to-tab-8",
["alt+9"] = "root:switch-to-tab-9", ["alt+9"] = "root:switch-to-tab-9",
["wheel"] = "root:scroll",
["ctrl+f"] = "find-replace:find", ["ctrl+f"] = "find-replace:find",
["ctrl+r"] = "find-replace:replace", ["ctrl+r"] = "find-replace:replace",
@ -210,11 +168,8 @@ keymap.add_direct {
["ctrl+shift+return"] = "doc:newline-above", ["ctrl+shift+return"] = "doc:newline-above",
["ctrl+j"] = "doc:join-lines", ["ctrl+j"] = "doc:join-lines",
["ctrl+a"] = "doc:select-all", ["ctrl+a"] = "doc:select-all",
["ctrl+d"] = { "find-replace:select-add-next", "doc:select-word" }, ["ctrl+d"] = { "find-replace:select-next", "doc:select-word" },
["ctrl+f3"] = "find-replace:select-next",
["ctrl+shift+f3"] = "find-replace:select-previous",
["ctrl+l"] = "doc:select-lines", ["ctrl+l"] = "doc:select-lines",
["ctrl+shift+l"] = { "find-replace:select-add-all", "doc:select-word" },
["ctrl+/"] = "doc:toggle-line-comments", ["ctrl+/"] = "doc:toggle-line-comments",
["ctrl+up"] = "doc:move-lines-up", ["ctrl+up"] = "doc:move-lines-up",
["ctrl+down"] = "doc:move-lines-down", ["ctrl+down"] = "doc:move-lines-down",
@ -229,18 +184,13 @@ keymap.add_direct {
["ctrl+right"] = "doc:move-to-next-word-end", ["ctrl+right"] = "doc:move-to-next-word-end",
["ctrl+["] = "doc:move-to-previous-block-start", ["ctrl+["] = "doc:move-to-previous-block-start",
["ctrl+]"] = "doc:move-to-next-block-end", ["ctrl+]"] = "doc:move-to-next-block-end",
["home"] = "doc:move-to-start-of-indentation", ["home"] = "doc:move-to-start-of-line",
["end"] = "doc:move-to-end-of-line", ["end"] = "doc:move-to-end-of-line",
["ctrl+home"] = "doc:move-to-start-of-doc", ["ctrl+home"] = "doc:move-to-start-of-doc",
["ctrl+end"] = "doc:move-to-end-of-doc", ["ctrl+end"] = "doc:move-to-end-of-doc",
["pageup"] = "doc:move-to-previous-page", ["pageup"] = "doc:move-to-previous-page",
["pagedown"] = "doc:move-to-next-page", ["pagedown"] = "doc:move-to-next-page",
["shift+1lclick"] = "doc:select-to-cursor",
["ctrl+1lclick"] = "doc:split-cursor",
["1lclick"] = "doc:set-cursor",
["2lclick"] = "doc:set-cursor-word",
["3lclick"] = "doc:set-cursor-line",
["shift+left"] = "doc:select-to-previous-char", ["shift+left"] = "doc:select-to-previous-char",
["shift+right"] = "doc:select-to-next-char", ["shift+right"] = "doc:select-to-next-char",
["shift+up"] = "doc:select-to-previous-line", ["shift+up"] = "doc:select-to-previous-line",
@ -249,7 +199,7 @@ keymap.add_direct {
["ctrl+shift+right"] = "doc:select-to-next-word-end", ["ctrl+shift+right"] = "doc:select-to-next-word-end",
["ctrl+shift+["] = "doc:select-to-previous-block-start", ["ctrl+shift+["] = "doc:select-to-previous-block-start",
["ctrl+shift+]"] = "doc:select-to-next-block-end", ["ctrl+shift+]"] = "doc:select-to-next-block-end",
["shift+home"] = "doc:select-to-start-of-indentation", ["shift+home"] = "doc:select-to-start-of-line",
["shift+end"] = "doc:select-to-end-of-line", ["shift+end"] = "doc:select-to-end-of-line",
["ctrl+shift+home"] = "doc:select-to-start-of-doc", ["ctrl+shift+home"] = "doc:select-to-start-of-doc",
["ctrl+shift+end"] = "doc:select-to-end-of-doc", ["ctrl+shift+end"] = "doc:select-to-end-of-doc",
@ -260,4 +210,3 @@ keymap.add_direct {
} }
return keymap return keymap

View File

@ -1,45 +1,14 @@
local core = require "core" local core = require "core"
local common = require "core.common"
local style = require "core.style" local style = require "core.style"
local View = require "core.view" local View = require "core.view"
local function lines(text)
if text == "" then return 0 end
local l = 1
for _ in string.gmatch(text, "\n") do
l = l + 1
end
return l
end
local item_height_result = {}
local function get_item_height(item)
local h = item_height_result[item]
if not h then
h = {}
local l = 1 + lines(item.text) + lines(item.info or "")
h.normal = style.font:get_height() + style.padding.y
h.expanded = l * style.font:get_height() + style.padding.y
h.current = h.normal
h.target = h.current
item_height_result[item] = h
end
return h
end
local LogView = View:extend() local LogView = View:extend()
LogView.context = "session"
function LogView:new() function LogView:new()
LogView.super.new(self) LogView.super.new(self)
self.last_item = core.log_items[#core.log_items] self.last_item = core.log_items[#core.log_items]
self.expanding = {}
self.scrollable = true self.scrollable = true
self.yoffset = 0 self.yoffset = 0
end end
@ -50,55 +19,6 @@ function LogView:get_name()
end end
local function is_expanded(item)
local item_height = get_item_height(item)
return item_height.target == item_height.expanded
end
function LogView:expand_item(item)
item = get_item_height(item)
item.target = item.target == item.expanded and item.normal or item.expanded
table.insert(self.expanding, item)
end
function LogView:each_item()
local x, y = self:get_content_offset()
y = y + style.padding.y + self.yoffset
return coroutine.wrap(function()
for i = #core.log_items, 1, -1 do
local item = core.log_items[i]
local h = get_item_height(item).current
coroutine.yield(i, item, x, y, self.size.x, h)
y = y + h
end
end)
end
function LogView:on_mouse_moved(px, py, ...)
LogView.super.on_mouse_moved(self, px, py, ...)
local hovered = false
for _, item, x, y, w, h in self:each_item() do
if px >= x and py >= y and px < x + w and py < y + h then
hovered = true
self.hovered_item = item
break
end
end
if not hovered then self.hovered_item = nil end
end
function LogView:on_mouse_pressed(button, mx, my, clicks)
if LogView.super.on_mouse_pressed(self, button, mx, my, clicks) then return end
if self.hovered_item then
self:expand_item(self.hovered_item)
end
end
function LogView:update() function LogView:update()
local item = core.log_items[#core.log_items] local item = core.log_items[#core.log_items]
if self.last_item ~= item then if self.last_item ~= item then
@ -107,14 +27,6 @@ function LogView:update()
self.yoffset = -(style.font:get_height() + style.padding.y) self.yoffset = -(style.font:get_height() + style.padding.y)
end end
local expanding = self.expanding[1]
if expanding then
self:move_towards(expanding, "current", expanding.target)
if expanding.current == expanding.target then
table.remove(self.expanding, 1)
end
end
self:move_towards("yoffset", 0) self:move_towards("yoffset", 0)
LogView.super.update(self) LogView.super.update(self)
@ -123,48 +35,38 @@ end
local function draw_text_multiline(font, text, x, y, color) local function draw_text_multiline(font, text, x, y, color)
local th = font:get_height() local th = font:get_height()
local resx = x local resx, resy = x, y
for line in text:gmatch("[^\n]+") do for line in text:gmatch("[^\n]+") do
resy = y
resx = renderer.draw_text(style.font, line, x, y, color) resx = renderer.draw_text(style.font, line, x, y, color)
y = y + th y = y + th
end end
return resx, y return resx, resy
end end
function LogView:draw() function LogView:draw()
self:draw_background(style.background) self:draw_background(style.background)
local ox, oy = self:get_content_offset()
local th = style.font:get_height() local th = style.font:get_height()
local lh = th + style.padding.y -- for one line local y = oy + style.padding.y + self.yoffset
for _, item, x, y, w in self:each_item() do
x = x + style.padding.x
for i = #core.log_items, 1, -1 do
local x = ox + style.padding.x
local item = core.log_items[i]
local time = os.date(nil, item.time) local time = os.date(nil, item.time)
x = common.draw_text(style.font, style.dim, time, "left", x, y, w, lh) x = renderer.draw_text(style.font, time, x, y, style.dim)
x = x + style.padding.x x = x + style.padding.x
local subx = x
x = common.draw_text(style.code_font, style.dim, is_expanded(item) and "-" or "+", "left", x, y, w, lh) x, y = draw_text_multiline(style.font, item.text, x, y, style.text)
x = x + style.padding.x renderer.draw_text(style.font, " at " .. item.at, x, y, style.dim)
w = w - (x - self:get_content_offset()) y = y + th
if item.info then
if is_expanded(item) then subx, y = draw_text_multiline(style.font, item.info, subx, y, style.dim)
y = y + common.round(style.padding.y / 2) y = y + th
_, y = draw_text_multiline(style.font, item.text, x, y, style.text)
local at = "at " .. common.home_encode(item.at)
_, y = common.draw_text(style.font, style.dim, at, "left", x, y, w, lh)
if item.info then
_, y = draw_text_multiline(style.font, item.info, x, y, style.dim)
end
else
local line, has_newline = string.match(item.text, "([^\n]+)(\n?)")
if has_newline ~= "" then
line = line .. " ..."
end
_, y = common.draw_text(style.font, style.text, line, "left", x, y, w, lh)
end end
y = y + style.padding.y
end end
end end

View File

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

View File

@ -193,8 +193,7 @@ function NagView:next()
self:change_hovered(common.find_index(self.options, "default_yes")) self:change_hovered(common.find_index(self.options, "default_yes"))
end end
self.force_focus = self.message ~= nil self.force_focus = self.message ~= nil
core.set_active_view(self.message ~= nil and self or core.set_active_view(self.message ~= nil and self or core.last_active_view)
core.next_active_view or core.last_active_view)
end end
function NagView:show(title, message, options, on_select) function NagView:show(title, message, options, on_select)

View File

@ -1,737 +0,0 @@
local core = require "core"
local common = require "core.common"
local config = require "core.config"
local style = require "core.style"
local Object = require "core.object"
local EmptyView = require "core.emptyview"
local View = require "core.view"
local Node = Object:extend()
function Node:new(type)
self.type = type or "leaf"
self.position = { x = 0, y = 0 }
self.size = { x = 0, y = 0 }
self.views = {}
self.divider = 0.5
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
self.tab_width = style.tab_width
self.move_towards = View.move_towards
end
function Node:propagate(fn, ...)
self.a[fn](self.a, ...)
self.b[fn](self.b, ...)
end
function Node:on_mouse_moved(x, y, ...)
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, ...)
end
end
function Node:on_mouse_released(...)
if self.type == "leaf" then
self.active_view:on_mouse_released(...)
else
self:propagate("on_mouse_released", ...)
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
end
local type_map = { up="vsplit", down="vsplit", left="hsplit", right="hsplit" }
-- The "locked" argument below should be in the form {x = <boolean>, y = <boolean>}
-- and it indicates if the node want to have a fixed size along the axis where the
-- boolean is true. If not it will be expanded to take all the available space.
-- The "resizable" flag indicates if, along the "locked" axis the node can be resized
-- by the user. If the node is marked as resizable their view should provide a
-- set_target_size method.
function Node:split(dir, view, locked, resizable)
assert(self.type == "leaf", "Tried to split non-leaf node")
local node_type = assert(type_map[dir], "Invalid direction")
local last_active = core.active_view
local child = Node()
child:consume(self)
self:consume(Node(node_type))
self.a = child
self.b = Node()
if view then self.b:add_view(view) end
if locked then
assert(type(locked) == 'table')
self.b.locked = locked
self.b.resizable = resizable or false
core.set_active_view(last_active)
end
if dir == "up" or dir == "left" then
self.a, self.b = self.b, self.a
return self.a
end
return self.b
end
function Node:remove_view(root, view)
if #self.views > 1 then
local idx = self:get_view_idx(view)
if idx < self.tab_offset then
self.tab_offset = self.tab_offset - 1
end
table.remove(self.views, idx)
if self.active_view == view then
self:set_active_view(self.views[idx] or self.views[#self.views])
end
else
local parent = self:get_parent_node(root)
local is_a = (parent.a == self)
local other = parent[is_a and "b" or "a"]
local locked_size_x, locked_size_y = other:get_locked_size()
local locked_size
if parent.type == "hsplit" then
locked_size = locked_size_x
else
locked_size = locked_size_y
end
local next_primary
if self.is_primary_node then
next_primary = core.root_view:select_next_primary_node()
end
if locked_size or (self.is_primary_node and not next_primary) then
self.views = {}
self:add_view(EmptyView())
else
if other == next_primary then
next_primary = parent
end
parent:consume(other)
local p = parent
while p.type ~= "leaf" do
p = p[is_a and "a" or "b"]
end
p:set_active_view(p.active_view)
if self.is_primary_node then
next_primary.is_primary_node = true
end
end
end
core.last_active_view = nil
end
function Node:close_view(root, view)
local do_close = function()
self:remove_view(root, view)
end
view:try_close(do_close)
end
function Node:close_active_view(root)
self:close_view(root, self.active_view)
end
function Node:add_view(view, idx)
assert(self.type == "leaf", "Tried to add view to non-leaf node")
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)
end
table.insert(self.views, idx or (#self.views + 1), view)
self:set_active_view(view)
end
function Node:set_active_view(view)
assert(self.type == "leaf", "Tried to set active view on non-leaf node")
self.active_view = view
core.set_active_view(view)
end
function Node:get_view_idx(view)
for i, v in ipairs(self.views) do
if v == view then return i end
end
end
function Node:get_node_for_view(view)
for _, v in ipairs(self.views) do
if v == view then return self end
end
if self.type ~= "leaf" then
return self.a:get_node_for_view(view) or self.b:get_node_for_view(view)
end
end
function Node:get_parent_node(root)
if root.a == self or root.b == self then
return root
elseif root.type ~= "leaf" then
return self:get_parent_node(root.a) or self:get_parent_node(root.b)
end
end
function Node:get_children(t)
t = t or {}
for _, view in ipairs(self.views) do
table.insert(t, view)
end
if self.a then self.a:get_children(t) end
if self.b then self.b:get_children(t) end
return t
end
-- return the width including the padding space and separately
-- the padding space itself
local function get_scroll_button_width()
local w = style.icon_font:get_width(">")
local pad = w
return w + 2 * pad, pad
end
function Node:get_divider_overlapping_point(px, py)
if self.type ~= "leaf" then
local axis = self.type == "hsplit" and "x" or "y"
if self.a:is_resizable(axis) and self.b:is_resizable(axis) then
local p = 6
local x, y, w, h = self:get_divider_rect()
x, y = x - p, y - p
w, h = w + p * 2, h + p * 2
if px > x and py > y and px < x + w and py < y + h then
return self
end
end
return self.a:get_divider_overlapping_point(px, py)
or self.b:get_divider_overlapping_point(px, py)
end
end
function Node:get_visible_tabs_number()
return math.min(#self.views - self.tab_offset + 1, config.max_tabs)
end
function Node:get_tab_overlapping_point(px, py)
if not self:should_show_tabs() then return nil end
local tabs_number = self:get_visible_tabs_number()
local x1, y1, w, h = self:get_tab_rect(self.tab_offset)
local x2, y2 = self:get_tab_rect(self.tab_offset + tabs_number)
if px >= x1 and py >= y1 and px < x2 and py < y1 + h then
return math.floor((px - x1) / w) + self.tab_offset
end
end
function Node:should_show_tabs()
if self.locked then return false end
local dn = core.root_view.dragged_node
if #self.views > 1
or (dn and dn.dragging) then -- show tabs while dragging
return true
elseif config.always_show_tabs then
return not self.views[1]:is(EmptyView)
end
return false
end
local function close_button_location(x, w)
local cw = style.icon_font:get_width("C")
local pad = style.padding.y
return x + w - pad - cw, cw, pad
end
function Node:get_scroll_button_index(px, py)
if #self.views == 1 then return end
for i = 1, 2 do
local x, y, w, h = self:get_scroll_button_rect(i)
if px >= x and px < x + w and py >= y and py < y + h then
return i
end
end
end
function Node:tab_hovered_update(px, py)
local tab_index = self:get_tab_overlapping_point(px, py)
self.hovered_tab = tab_index
self.hovered_close = 0
self.hovered_scroll_button = 0
if tab_index then
local x, y, w, h = self:get_tab_rect(tab_index)
local cx, cw = close_button_location(x, w)
if px >= cx and px < cx + cw and py >= y and py < y + h and config.tab_close_button then
self.hovered_close = tab_index
end
else
self.hovered_scroll_button = self:get_scroll_button_index(px, py) or 0
end
end
function Node:get_child_overlapping_point(x, y)
local child
if self.type == "leaf" then
return self
elseif self.type == "hsplit" then
child = (x < self.b.position.x) and self.a or self.b
elseif self.type == "vsplit" then
child = (y < self.b.position.y) and self.a or self.b
end
return child:get_child_overlapping_point(x, y)
end
function Node:get_scroll_button_rect(index)
local w, pad = get_scroll_button_width()
local h = style.font:get_height() + style.padding.y * 2
local x = self.position.x + (index == 1 and 0 or self.size.x - w)
return x, self.position.y, w, h, pad
end
function Node:get_tab_rect(idx)
local sbw = get_scroll_button_width()
local maxw = self.size.x - 2 * sbw
local x0 = self.position.x + sbw
local x1 = x0 + common.clamp(self.tab_width * (idx - 1) - self.tab_shift, 0, maxw)
local x2 = x0 + common.clamp(self.tab_width * idx - self.tab_shift, 0, maxw)
local h = style.font:get_height() + style.padding.y * 2
return x1, self.position.y, x2 - x1, h
end
function Node:get_divider_rect()
local x, y = self.position.x, self.position.y
if self.type == "hsplit" then
return x + self.a.size.x, y, style.divider_size, self.size.y
elseif self.type == "vsplit" then
return x, y + self.a.size.y, self.size.x, style.divider_size
end
end
-- Return two values for x and y axis and each of them is either falsy or a number.
-- A falsy value indicate no fixed size along the corresponding direction.
function Node:get_locked_size()
if self.type == "leaf" then
if self.locked then
local size = self.active_view.size
-- The values below should be either a falsy value or a number
local sx = (self.locked and self.locked.x) and size.x
local sy = (self.locked and self.locked.y) and size.y
return sx, sy
end
else
local x1, y1 = self.a:get_locked_size()
local x2, y2 = self.b:get_locked_size()
-- The values below should be either a falsy value or a number
local sx, sy
if self.type == 'hsplit' then
if x1 and x2 then
local dsx = (x1 < 1 or x2 < 1) and 0 or style.divider_size
sx = x1 + x2 + dsx
end
sy = y1 or y2
else
if y1 and y2 then
local dsy = (y1 < 1 or y2 < 1) and 0 or style.divider_size
sy = y1 + y2 + dsy
end
sx = x1 or x2
end
return sx, sy
end
end
function Node.copy_position_and_size(dst, src)
dst.position.x, dst.position.y = src.position.x, src.position.y
dst.size.x, dst.size.y = src.size.x, src.size.y
end
-- calculating the sizes is the same for hsplits and vsplits, except the x/y
-- axis are swapped; this function lets us use the same code for both
local function calc_split_sizes(self, x, y, x1, x2, y1, y2)
local ds = ((x1 and x1 < 1) or (x2 and x2 < 1)) and 0 or style.divider_size
local n = x1 and x1 + ds or (x2 and self.size[x] - x2 or math.floor(self.size[x] * self.divider))
self.a.position[x] = self.position[x]
self.a.position[y] = self.position[y]
self.a.size[x] = n - ds
self.a.size[y] = self.size[y]
self.b.position[x] = self.position[x] + n
self.b.position[y] = self.position[y]
self.b.size[x] = self.size[x] - n
self.b.size[y] = self.size[y]
end
function Node:update_layout()
if self.type == "leaf" then
local av = self.active_view
if self:should_show_tabs() then
local _, _, _, th = self:get_tab_rect(1)
av.position.x, av.position.y = self.position.x, self.position.y + th
av.size.x, av.size.y = self.size.x, self.size.y - th
else
Node.copy_position_and_size(av, self)
end
else
local x1, y1 = self.a:get_locked_size()
local x2, y2 = self.b:get_locked_size()
if self.type == "hsplit" then
calc_split_sizes(self, "x", "y", x1, x2)
elseif self.type == "vsplit" then
calc_split_sizes(self, "y", "x", y1, y2)
end
self.a:update_layout()
self.b:update_layout()
end
end
function Node:scroll_tabs_to_visible()
local index = self:get_view_idx(self.active_view)
if index then
local tabs_number = self:get_visible_tabs_number()
if self.tab_offset > index then
self.tab_offset = index
elseif self.tab_offset + tabs_number - 1 < index then
self.tab_offset = index - tabs_number + 1
elseif tabs_number < config.max_tabs and self.tab_offset > 1 then
self.tab_offset = #self.views - config.max_tabs + 1
end
end
end
function Node:scroll_tabs(dir)
local view_index = self:get_view_idx(self.active_view)
if dir == 1 then
if self.tab_offset > 1 then
self.tab_offset = self.tab_offset - 1
local last_index = self.tab_offset + self:get_visible_tabs_number() - 1
if view_index > last_index then
self:set_active_view(self.views[last_index])
end
end
elseif dir == 2 then
local tabs_number = self:get_visible_tabs_number()
if self.tab_offset + tabs_number - 1 < #self.views then
self.tab_offset = self.tab_offset + 1
local view_index = self:get_view_idx(self.active_view)
if view_index < self.tab_offset then
self:set_active_view(self.views[self.tab_offset])
end
end
end
end
function Node:target_tab_width()
local n = self:get_visible_tabs_number()
local w = self.size.x - get_scroll_button_width() * 2
return common.clamp(style.tab_width, w / config.max_tabs, w / n)
end
function Node:update()
if self.type == "leaf" then
self:scroll_tabs_to_visible()
for _, view in ipairs(self.views) do
view:update()
end
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))
self:move_towards("tab_width", tab_width)
else
self.a:update()
self.b:update()
end
end
function Node:draw_tab(text, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
local ds = style.divider_size
local dots_width = style.font:get_width("")
local color = style.dim
local padding_y = style.padding.y
renderer.draw_rect(x + w, y + padding_y, ds, h - padding_y * 2, style.dim)
if standalone then
renderer.draw_rect(x-1, y-1, w+2, h+2, style.background2)
end
if is_active then
color = style.text
renderer.draw_rect(x, y, w, h, style.background)
renderer.draw_rect(x + w, y, ds, h, style.divider)
renderer.draw_rect(x - ds, y, ds, h, style.divider)
end
local cx, cw, cspace = close_button_location(x, w)
local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button)
if show_close_button then
local close_style = is_close_hovered and style.text or style.dim
common.draw_text(style.icon_font, close_style, "C", nil, cx, y, 0, h)
end
if is_hovered then
color = style.text
end
local padx = style.padding.x
-- Normally we should substract "cspace" from text_avail_width and from the
-- clipping width. It is the padding space we give to the left and right of the
-- close button. However, since we are using dots to terminate filenames, we
-- choose to ignore "cspace" accepting that the text can possibly "touch" the
-- close button.
local text_avail_width = cx - x - padx
core.push_clip_rect(x, y, cx - x, h)
x, w = x + padx, w - padx * 2
local align = "center"
if style.font:get_width(text) > text_avail_width then
align = "left"
for i = 1, #text do
local reduced_text = text:sub(1, #text - i)
if style.font:get_width(reduced_text) + dots_width <= text_avail_width then
text = reduced_text .. ""
break
end
end
end
common.draw_text(style.font, color, text, align, x, y, w, h)
core.pop_clip_rect()
end
function Node:draw_tabs()
local x, y, w, h, scroll_padding = self:get_scroll_button_rect(1)
local ds = style.divider_size
local dots_width = style.font:get_width("")
core.push_clip_rect(x, y, self.size.x, h)
renderer.draw_rect(x, y, self.size.x, h, style.background2)
renderer.draw_rect(x, y + h - ds, self.size.x, ds, style.divider)
if self.tab_offset > 1 then
local button_style = self.hovered_scroll_button == 1 and style.text or style.dim
common.draw_text(style.icon_font, button_style, "<", nil, x + scroll_padding, y, 0, h)
end
local tabs_number = self:get_visible_tabs_number()
if #self.views > self.tab_offset + tabs_number - 1 then
local xrb, yrb, wrb = self:get_scroll_button_rect(2)
local button_style = self.hovered_scroll_button == 2 and style.text or style.dim
common.draw_text(style.icon_font, button_style, ">", nil, xrb + scroll_padding, yrb, 0, h)
end
for i = self.tab_offset, self.tab_offset + tabs_number - 1 do
local view = self.views[i]
local x, y, w, h = self:get_tab_rect(i)
self:draw_tab(view:get_name(), view == self.active_view,
i == self.hovered_tab, i == self.hovered_close,
x, y, w, h)
end
core.pop_clip_rect()
end
function Node:draw()
if self.type == "leaf" then
if self:should_show_tabs() then
self:draw_tabs()
end
local pos, size = self.active_view.position, self.active_view.size
core.push_clip_rect(pos.x, pos.y, size.x, size.y)
self.active_view:draw()
core.pop_clip_rect()
else
local x, y, w, h = self:get_divider_rect()
renderer.draw_rect(x, y, w, h, style.divider)
self:propagate("draw")
end
end
function Node:is_empty()
if self.type == "leaf" then
return #self.views == 0 or (#self.views == 1 and self.views[1]:is(EmptyView))
else
return self.a:is_empty() and self.b:is_empty()
end
end
function Node:close_all_docviews(keep_active)
local node_active_view = self.active_view
local lost_active_view = false
if self.type == "leaf" then
local i = 1
while i <= #self.views do
local view = self.views[i]
if view.context == "session" and (not keep_active or view ~= self.active_view) then
table.remove(self.views, i)
if view == node_active_view then
lost_active_view = true
end
else
i = i + 1
end
end
self.tab_offset = 1
if #self.views == 0 and self.is_primary_node then
-- if we are not the primary view and we had the active view it doesn't
-- matter to reattribute the active view because, within the close_all_docviews
-- top call, the primary node will take the active view anyway.
-- Set the empty view and takes the active view.
self:add_view(EmptyView())
elseif #self.views > 0 and lost_active_view then
-- In practice we never get there but if a view remain we need
-- to reset the Node's active view.
self:set_active_view(self.views[1])
end
else
self.a:close_all_docviews(keep_active)
self.b:close_all_docviews(keep_active)
if self.a:is_empty() and not self.a.is_primary_node then
self:consume(self.b)
elseif self.b:is_empty() and not self.b.is_primary_node then
self:consume(self.a)
end
end
end
-- Returns true for nodes that accept either "proportional" resizes (based on the
-- node.divider) or "locked" resizable nodes (along the resize axis).
function Node:is_resizable(axis)
if self.type == 'leaf' then
return not self.locked or not self.locked[axis] or self.resizable
else
local a_resizable = self.a:is_resizable(axis)
local b_resizable = self.b:is_resizable(axis)
return a_resizable and b_resizable
end
end
-- Return true iff it is a locked pane along the rezise axis and is
-- declared "resizable".
function Node:is_locked_resizable(axis)
return self.locked and self.locked[axis] and self.resizable
end
function Node:resize(axis, value)
-- the application works fine with non-integer values but to have pixel-perfect
-- placements of view elements, like the scrollbar, we round the value to be
-- an integer.
value = math.floor(value)
if self.type == 'leaf' then
-- If it is not locked we don't accept the
-- resize operation here because for proportional panes the resize is
-- done using the "divider" value of the parent node.
if self:is_locked_resizable(axis) then
return self.active_view:set_target_size(axis, value)
end
else
if self.type == (axis == "x" and "hsplit" or "vsplit") then
-- we are resizing a node that is splitted along the resize axis
if self.a:is_locked_resizable(axis) and self.b:is_locked_resizable(axis) then
local rem_value = value - self.a.size[axis]
if rem_value >= 0 then
return self.b.active_view:set_target_size(axis, rem_value)
else
self.b.active_view:set_target_size(axis, 0)
return self.a.active_view:set_target_size(axis, value)
end
end
else
-- we are resizing a node that is splitted along the axis perpendicular
-- to the resize axis
local a_resizable = self.a:is_resizable(axis)
local b_resizable = self.b:is_resizable(axis)
if a_resizable and b_resizable then
self.a:resize(axis, value)
self.b:resize(axis, value)
end
end
end
end
function Node:get_split_type(mouse_x, mouse_y)
local x, y = self.position.x, self.position.y
local w, h = self.size.x, self.size.y
local _, _, _, tab_h = self:get_scroll_button_rect(1)
y = y + tab_h
h = h - tab_h
local local_mouse_x = mouse_x - x
local local_mouse_y = mouse_y - y
if local_mouse_y < 0 then
return "tab"
else
local left_pct = local_mouse_x * 100 / w
local top_pct = local_mouse_y * 100 / h
if left_pct <= 30 then
return "left"
elseif left_pct >= 70 then
return "right"
elseif top_pct <= 30 then
return "up"
elseif top_pct >= 70 then
return "down"
end
return "middle"
end
end
function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
local tab_index = self:get_tab_overlapping_point(x, y)
if not tab_index then
local first_tab_x = self:get_tab_rect(1)
if x < first_tab_x then
-- mouse before first visible tab
tab_index = self.tab_offset or 1
else
-- mouse after last visible tab
tab_index = self:get_visible_tabs_number() + (self.tab_offset - 1 or 0)
end
end
local tab_x, tab_y, tab_w, tab_h = self:get_tab_rect(tab_index)
if x > tab_x + tab_w / 2 and tab_index <= #self.views then
-- use next tab
tab_x = tab_x + tab_w
tab_index = tab_index + 1
end
if self == dragged_node and dragged_index and tab_index > dragged_index then
-- the tab we are moving is counted in tab_index
tab_index = tab_index - 1
tab_x = tab_x - tab_w
end
return tab_index, tab_x, tab_y, tab_w, tab_h
end
return Node

View File

@ -20,6 +20,17 @@ function Object:extend()
end end
function Object:implement(...)
for _, cls in pairs({...}) do
for k, v in pairs(cls) do
if self[k] == nil and type(v) == "function" then
self[k] = v
end
end
end
end
function Object:is(T) function Object:is(T)
local mt = getmetatable(self) local mt = getmetatable(self)
while mt do while mt do

View File

@ -1,3 +1,4 @@
-- So that in addition to regex.gsub(pattern, string), we can also do -- So that in addition to regex.gsub(pattern, string), we can also do
-- pattern:gsub(string). -- pattern:gsub(string).
regex.__index = function(table, key) return regex[key]; end regex.__index = function(table, key) return regex[key]; end
@ -5,8 +6,7 @@ regex.__index = function(table, key) return regex[key]; end
regex.match = function(pattern_string, string, offset, options) regex.match = function(pattern_string, string, offset, options)
local pattern = type(pattern_string) == "table" and local pattern = type(pattern_string) == "table" and
pattern_string or regex.compile(pattern_string) pattern_string or regex.compile(pattern_string)
local s, e = regex.cmatch(pattern, string, offset or 1, options or 0) return regex.cmatch(pattern, string, offset or 1, options or 0)
return s, e and e - 1
end end
-- Will iterate back through any UTF-8 bytes so that we don't replace bits -- Will iterate back through any UTF-8 bytes so that we don't replace bits
@ -23,7 +23,7 @@ end
-- Moves to the end of the identified character. -- Moves to the end of the identified character.
local function end_character(str, index) local function end_character(str, index)
local byte = string.byte(str, index + 1) local byte = string.byte(str, index + 1)
while byte and byte >= 128 and byte < 192 do while byte >= 128 and byte < 192 do
index = index + 1 index = index + 1
byte = string.byte(str, index + 1) byte = string.byte(str, index + 1)
end end

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
-- this file is used by lite-xl to setup the Lua environment when starting -- this file is used by lite-xl to setup the Lua environment when starting
VERSION = "2.0.3r1" VERSION = "2.0-beta1"
MOD_VERSION = "2" MOD_VERSION = "1"
SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or SCALE SCALE = tonumber(os.getenv("LITE_SCALE")) or SCALE
PATHSEP = package.config:sub(1, 1) PATHSEP = package.config:sub(1, 1)
EXEDIR = EXEFILE:match("^(.+)[/\\][^/\\]+$") EXEDIR = EXEFILE:match("^(.+)[/\\][^/\\]+$")
@ -20,15 +20,3 @@ package.path = DATADIR .. '/?/init.lua;' .. package.path
package.path = USERDIR .. '/?.lua;' .. package.path package.path = USERDIR .. '/?.lua;' .. package.path
package.path = USERDIR .. '/?/init.lua;' .. package.path package.path = USERDIR .. '/?/init.lua;' .. package.path
local dynamic_suffix = PLATFORM == "Mac OS X" and 'lib' or (PLATFORM == "Windows" and 'dll' or 'so')
package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ";" .. USERDIR .. '/?.' .. dynamic_suffix
package.native_plugins = {}
package.searchers = { package.searchers[1], package.searchers[2], function(modname)
local path = package.searchpath(modname, package.cpath)
if not path then return nil end
return system.load_native_plugin, path
end }
table.pack = table.pack or pack or function(...) return {...} end
table.unpack = table.unpack or unpack

View File

@ -6,7 +6,6 @@ local style = require "core.style"
local DocView = require "core.docview" local DocView = require "core.docview"
local LogView = require "core.logview" local LogView = require "core.logview"
local View = require "core.view" local View = require "core.view"
local Object = require "core.object"
local StatusView = View:extend() local StatusView = View:extend()
@ -30,7 +29,6 @@ function StatusView:on_mouse_pressed()
and not core.active_view:is(LogView) then and not core.active_view:is(LogView) then
command.perform "core:open-log" command.perform "core:open-log"
end end
return true
end end
@ -72,7 +70,7 @@ local function draw_items(self, items, x, y, draw_fn)
local color = style.text local color = style.text
for _, item in ipairs(items) do for _, item in ipairs(items) do
if Object.is(item, renderer.font) then if type(item) == "userdata" then
font = item font = item
elseif type(item) == "table" then elseif type(item) == "table" then
color = item color = item
@ -109,9 +107,9 @@ function StatusView:get_items()
local dv = core.active_view local dv = core.active_view
local line, col = dv.doc:get_selection() local line, col = dv.doc:get_selection()
local dirty = dv.doc:is_dirty() local dirty = dv.doc:is_dirty()
local indent_type, indent_size, indent_confirmed = dv.doc:get_indent_info() local indent = dv.doc.indent_info
local indent_label = (indent_type == "hard") and "tabs: " or "spaces: " local indent_label = (indent and indent.type == "hard") and "tabs: " or "spaces: "
local indent_size_str = tostring(indent_size) .. (indent_confirmed and "" or "*") or "unknown" local indent_size = indent and tostring(indent.size) .. (indent.confirmed and "" or "*") or "unknown"
return { return {
dirty and style.accent or style.text, style.icon_font, "f", dirty and style.accent or style.text, style.icon_font, "f",

View File

@ -21,48 +21,40 @@ style.tab_width = common.round(170 * SCALE)
-- --
-- On High DPI monitor or non RGB monitor you may consider using antialiasing grayscale instead. -- On High DPI monitor or non RGB monitor you may consider using antialiasing grayscale instead.
-- The antialiasing grayscale with full hinting is interesting for crisp font rendering. -- The antialiasing grayscale with full hinting is interesting for crisp font rendering.
style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 15 * SCALE) style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 13 * SCALE)
style.big_font = style.font:copy(46 * SCALE) style.big_font = style.font:copy(40 * SCALE)
style.icon_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 16 * SCALE, {antialiasing="grayscale", hinting="full"}) style.icon_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 14 * SCALE, {antialiasing="grayscale", hinting="full"})
style.icon_big_font = style.icon_font:copy(23 * SCALE) style.icon_big_font = style.icon_font:copy(20 * SCALE)
style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 15 * SCALE) style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 13 * SCALE)
style.background = { common.color "#2e2e32" } -- Docview style.background = { common.color "#2e2e32" }
style.background2 = { common.color "#252529" } -- Treeview style.background2 = { common.color "#252529" }
style.background3 = { common.color "#252529" } -- Command view style.background3 = { common.color "#252529" }
style.text = { common.color "#97979c" } style.text = { common.color "#97979c" }
style.caret = { common.color "#93DDFA" } style.caret = { common.color "#93DDFA" }
style.accent = { common.color "#e1e1e6" } style.accent = { common.color "#e1e1e6" }
-- style.dim - text color for nonactive tabs, tabs divider, prefix in log and
-- search result, hotkeys for context menu and command view
style.dim = { common.color "#525257" } style.dim = { common.color "#525257" }
style.divider = { common.color "#202024" } -- Line between nodes style.divider = { common.color "#202024" }
style.selection = { common.color "#48484f" } style.selection = { common.color "#48484f" }
style.line_number = { common.color "#525259" } style.line_number = { common.color "#525259" }
style.line_number2 = { common.color "#83838f" } -- With cursor style.line_number2 = { common.color "#83838f" }
style.line_highlight = { common.color "#343438" } style.line_highlight = { common.color "#343438" }
style.scrollbar = { common.color "#414146" } style.scrollbar = { common.color "#414146" }
style.scrollbar2 = { common.color "#4b4b52" } -- Hovered style.scrollbar2 = { common.color "#4b4b52" }
style.nagbar = { common.color "#FF0000" } style.nagbar = { common.color "#FF0000" }
style.nagbar_text = { common.color "#FFFFFF" } style.nagbar_text = { common.color "#FFFFFF" }
style.nagbar_dim = { common.color "rgba(0, 0, 0, 0.45)" } style.nagbar_dim = { common.color "rgba(0, 0, 0, 0.45)" }
style.drag_overlay = { common.color "rgba(255,255,255,0.1)" }
style.drag_overlay_tab = { common.color "#93DDFA" }
style.good = { common.color "#72b886" }
style.warn = { common.color "#FFA94D" }
style.error = { common.color "#FF3333" }
style.modified = { common.color "#1c7c9c" }
style.syntax = {} style.syntax = {}
style.syntax["normal"] = { common.color "#e1e1e6" } style.syntax["normal"] = { common.color "#e1e1e6" }
style.syntax["symbol"] = { common.color "#e1e1e6" } style.syntax["symbol"] = { common.color "#e1e1e6" }
style.syntax["comment"] = { common.color "#676b6f" } style.syntax["comment"] = { common.color "#676b6f" }
style.syntax["keyword"] = { common.color "#E58AC9" } -- local function end if case style.syntax["keyword"] = { common.color "#E58AC9" }
style.syntax["keyword2"] = { common.color "#F77483" } -- self int float style.syntax["keyword2"] = { common.color "#F77483" }
style.syntax["number"] = { common.color "#FFA94D" } style.syntax["number"] = { common.color "#FFA94D" }
style.syntax["literal"] = { common.color "#FFA94D" } -- true false nil style.syntax["literal"] = { common.color "#FFA94D" }
style.syntax["string"] = { common.color "#f7c95c" } style.syntax["string"] = { common.color "#f7c95c" }
style.syntax["operator"] = { common.color "#93DDFA" } -- = + - / < > style.syntax["operator"] = { common.color "#93DDFA" }
style.syntax["function"] = { common.color "#93DDFA" } style.syntax["function"] = { common.color "#93DDFA" }
-- This can be used to override fonts per syntax group. -- This can be used to override fonts per syntax group.

View File

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

View File

@ -1,5 +1,4 @@
local syntax = require "core.syntax" local syntax = require "core.syntax"
local common = require "core.common"
local tokenizer = {} local tokenizer = {}
@ -143,13 +142,8 @@ function tokenizer.tokenize(incoming_syntax, text, state)
code = p._regex code = p._regex
end end
repeat repeat
local next = res[2] + 1 res = p.pattern and { text:find(at_start and "^" .. code or code, res[2]+1) }
-- go to the start of the next utf-8 character or { regex.match(code, text, res[2]+1, at_start and regex.ANCHORED or 0) }
while text:byte(next) and common.is_utf8_cont(text, next) do
next = next + 1
end
res = p.pattern and { text:find(at_start and "^" .. code or code, next) }
or { regex.match(code, text, next, at_start and regex.ANCHORED or 0) }
if res[1] and close and target[3] then if res[1] and close and target[3] then
local count = 0 local count = 0
for i = res[1] - 1, 1, -1 do for i = res[1] - 1, 1, -1 do
@ -161,7 +155,7 @@ function tokenizer.tokenize(incoming_syntax, text, state)
if count % 2 == 0 then break end if count % 2 == 0 then break end
end end
until not res[1] or not close or not target[3] until not res[1] or not close or not target[3]
return table.unpack(res) return unpack(res)
end end
while i <= #text do while i <= #text do
@ -237,13 +231,8 @@ function tokenizer.tokenize(incoming_syntax, text, state)
-- consume character if we didn't match -- consume character if we didn't match
if not matched then if not matched then
local n = 0 push_token(res, "normal", text:sub(i, i))
-- reach the next character i = i + 1
while text:byte(i + n + 1) and common.is_utf8_cont(text, i + n + 1) do
n = n + 1
end
push_token(res, "normal", text:sub(i, i + n))
i = i + n + 1
end end
end end

View File

@ -7,10 +7,6 @@ local Object = require "core.object"
local View = Object:extend() local View = Object:extend()
-- context can be "application" or "session". The instance of objects
-- with context "session" will be closed when a project session is
-- terminated. The context "application" is for functional UI elements.
View.context = "application"
function View:new() function View:new()
self.position = { x = 0, y = 0 } self.position = { x = 0, y = 0 }
@ -102,10 +98,14 @@ function View:on_text_input(text)
-- no-op -- no-op
end end
function View:on_mouse_wheel(y)
function View:on_mouse_wheel(y)
if self.scrollable then
self.scroll.to.y = self.scroll.to.y + y * -config.mouse_wheel_scroll
end
end end
function View:get_content_bounds() function View:get_content_bounds()
local x = self.scroll.x local x = self.scroll.x
local y = self.scroll.y local y = self.scroll.y
@ -136,7 +136,7 @@ end
function View:draw_background(color) function View:draw_background(color)
local x, y = self.position.x, self.position.y local x, y = self.position.x, self.position.y
local w, h = self.size.x, self.size.y local w, h = self.size.x, self.size.y
renderer.draw_rect(x, y, w, h, color) renderer.draw_rect(x, y, w + x % 1, h + y % 1, color)
end end

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local core = require "core" local core = require "core"
local common = require "core.common" local common = require "core.common"
local config = require "core.config" local config = require "core.config"
@ -10,9 +10,9 @@ local RootView = require "core.rootview"
local DocView = require "core.docview" local DocView = require "core.docview"
local Doc = require "core.doc" local Doc = require "core.doc"
config.plugins.autocomplete = { config.plugins.autocomplete = {
-- Amount of characters that need to be written for autocomplete -- Amount of characters that need to be written for autocomplete
min_len = 3, min_len = 1,
-- The max amount of visible items -- The max amount of visible items
max_height = 6, max_height = 6,
-- The max amount of scrollable items -- The max amount of scrollable items
@ -502,11 +502,6 @@ command.add(predicate, {
suggestions_idx = math.min(suggestions_idx + 1, #suggestions) suggestions_idx = math.min(suggestions_idx + 1, #suggestions)
end, end,
["autocomplete:cycle"] = function()
local newidx = suggestions_idx + 1
suggestions_idx = newidx > #suggestions and 1 or newidx
end,
["autocomplete:cancel"] = function() ["autocomplete:cancel"] = function()
reset_suggestions() reset_suggestions()
end, end,

View File

@ -1,8 +1,9 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local core = require "core" local core = require "core"
local config = require "core.config" local config = require "core.config"
local Doc = require "core.doc" local Doc = require "core.doc"
local times = setmetatable({}, { __mode = "k" }) local times = setmetatable({}, { __mode = "k" })
local function update_time(doc) local function update_time(doc)
@ -10,6 +11,7 @@ local function update_time(doc)
times[doc] = info.modified times[doc] = info.modified
end end
local function reload_doc(doc) local function reload_doc(doc)
local fp = io.open(doc.filename, "r") local fp = io.open(doc.filename, "r")
local text = fp:read("*a") local text = fp:read("*a")
@ -25,19 +27,23 @@ local function reload_doc(doc)
core.log_quiet("Auto-reloaded doc \"%s\"", doc.filename) core.log_quiet("Auto-reloaded doc \"%s\"", doc.filename)
end end
local on_modify = core.on_dirmonitor_modify
core.on_dirmonitor_modify = function(dir, filepath) core.add_thread(function()
local abs_filename = dir.name .. PATHSEP .. filepath while true do
for _, doc in ipairs(core.docs) do -- check all doc modified times
local info = system.get_file_info(doc.filename or "") for _, doc in ipairs(core.docs) do
if doc.abs_filename == abs_filename and info and times[doc] ~= info.modified then local info = system.get_file_info(doc.filename or "")
reload_doc(doc) if info and times[doc] ~= info.modified then
break reload_doc(doc)
end
coroutine.yield()
end end
-- wait for next scan
coroutine.yield(config.project_scan_rate)
end end
on_modify(dir, filepath) end)
end
-- patch `Doc.save|load` to store modified time -- patch `Doc.save|load` to store modified time
local load = Doc.load local load = Doc.load

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local core = require "core" local core = require "core"
local command = require "core.command" local command = require "core.command"
local keymap = require "core.keymap" local keymap = require "core.keymap"
@ -42,35 +42,17 @@ keymap.add {
["menu"] = "context:show" ["menu"] = "context:show"
} }
local function copy_log()
local item = core.active_view.hovered_item
if item then
system.set_clipboard(core.get_log(item))
end
end
local function open_as_doc()
local doc = core.open_doc("logs.txt")
core.root_view:open_doc(doc)
doc:insert(1, 1, core.get_log())
end
menu:register("core.logview", {
{ text = "Copy entry", command = copy_log },
{ text = "Open as file", command = open_as_doc }
})
if require("plugins.scale") then if require("plugins.scale") then
menu:register("core.docview", { menu:register("core.docview", {
{ text = "Cut", command = "doc:cut" }, { text = "Font +", command = "scale:increase" },
{ text = "Copy", command = "doc:copy" }, { text = "Font -", command = "scale:decrease" },
{ text = "Paste", command = "doc:paste" }, { text = "Font Reset", command = "scale:reset" },
{ text = "Font +", command = "scale:increase" },
{ text = "Font -", command = "scale:decrease" },
{ text = "Font Reset", command = "scale:reset" },
ContextMenu.DIVIDER, ContextMenu.DIVIDER,
{ text = "Find", command = "find-replace:find" }, { text = "Find", command = "find-replace:find" },
{ text = "Replace", command = "find-replace:replace" } { text = "Replace", command = "find-replace:replace" },
ContextMenu.DIVIDER,
{ text = "Find Pattern", command = "find-replace:find-pattern" },
{ text = "Replace Pattern", command = "find-replace:replace-pattern" },
}) })
end end

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local core = require "core" local core = require "core"
local command = require "core.command" local command = require "core.command"
local common = require "core.common" local common = require "core.common"
@ -102,11 +102,6 @@ end
local function update_cache(doc) local function update_cache(doc)
local type, size, score = detect_indent_stat(doc) local type, size, score = detect_indent_stat(doc)
local score_threshold = 4 local score_threshold = 4
if score < score_threshold then
-- use default values
type = config.tab_type
size = config.indent_size
end
cache[doc] = { type = type, size = size, confirmed = (score >= score_threshold) } cache[doc] = { type = type, size = size, confirmed = (score >= score_threshold) }
doc.indent_info = cache[doc] doc.indent_info = cache[doc]
end end
@ -116,91 +111,44 @@ local new = Doc.new
function Doc:new(...) function Doc:new(...)
new(self, ...) new(self, ...)
update_cache(self) update_cache(self)
if not cache[self].confirmed then
core.add_thread(function ()
while not cache[self].confirmed do
update_cache(self)
coroutine.yield(1)
end
end, self)
end
end end
local clean = Doc.clean local clean = Doc.clean
function Doc:clean(...) function Doc:clean(...)
clean(self, ...) clean(self, ...)
local _, _, confirmed = self:get_indent_info() update_cache(self)
if not confirmed then end
update_cache(self)
local function with_indent_override(doc, fn, ...)
local c = cache[doc]
if not c then
return fn(...)
end end
local type, size = config.tab_type, config.indent_size
config.tab_type, config.indent_size = c.type, c.size or config.indent_size
local r1, r2, r3 = fn(...)
config.tab_type, config.indent_size = type, size
return r1, r2, r3
end end
local function set_indent_type(doc, type) local perform = command.perform
local _, indent_size = doc:get_indent_info() function command.perform(...)
cache[doc] = {type = type, return with_indent_override(core.active_view.doc, perform, ...)
size = indent_size,
confirmed = true}
doc.indent_info = cache[doc]
end
local function set_indent_type_command()
core.command_view:enter(
"Specify indent style for this file",
function(value) -- submit
local doc = core.active_view.doc
value = value:lower()
set_indent_type(doc, value == "tabs" and "hard" or "soft")
end,
function(text) -- suggest
return common.fuzzy_match({"tabs", "spaces"}, text)
end,
nil, -- cancel
function(text) -- validate
local t = text:lower()
return t == "tabs" or t == "spaces"
end
)
end end
local function set_indent_size(doc, size) local draw = DocView.draw
local indent_type = doc:get_indent_info() function DocView:draw(...)
cache[doc] = {type = indent_type, return with_indent_override(self.doc, draw, self, ...)
size = size,
confirmed = true}
doc.indent_info = cache[doc]
end end
local function set_indent_size_command()
core.command_view:enter(
"Specify indent size for current file",
function(value) -- submit
local value = math.floor(tonumber(value))
local doc = core.active_view.doc
set_indent_size(doc, value)
end,
nil, -- suggest
nil, -- cancel
function(value) -- validate
local value = tonumber(value)
return value ~= nil and value >= 1
end
)
end
command.add("core.docview", {
["indent:set-file-indent-type"] = set_indent_type_command,
["indent:set-file-indent-size"] = set_indent_size_command
})
command.add(function()
return core.active_view:is(DocView)
and cache[core.active_view.doc]
and cache[core.active_view.doc].type == "soft"
end, {
["indent:switch-file-to-tabs-indentation"] = function() set_indent_type(core.active_view.doc, "hard") end
})
command.add(function()
return core.active_view:is(DocView)
and cache[core.active_view.doc]
and cache[core.active_view.doc].type == "hard"
end, {
["indent:switch-file-to-spaces-indentation"] = function() set_indent_type(core.active_view.doc, "soft") end
})

View File

@ -1,36 +0,0 @@
-- mod-version:2 -- lite-xl 2.0
local style = require "core.style"
local DocView = require "core.docview"
local common = require "core.common"
local draw_line_text = DocView.draw_line_text
function DocView:draw_line_text(idx, x, y)
local font = (self:get_font() or style.syntax_fonts["whitespace"] or style.syntax_fonts["comment"])
local color = style.syntax.whitespace or style.syntax.comment
local ty = y + self:get_line_text_y_offset()
local tx
local text, offset, s, e = self.doc.lines[idx], 1
local x1, _, x2, _ = self:get_content_bounds()
local _offset = self:get_x_offset_col(idx, x1)
offset = _offset
while true do
s, e = text:find(" +", offset)
if not s then break end
tx = self:get_col_x_offset(idx, s) + x
renderer.draw_text(font, string.rep("·", e - s + 1), tx, ty, color)
if tx > x + x2 then break end
offset = e + 1
end
offset = _offset
while true do
s, e = text:find("\t", offset)
if not s then break end
tx = self:get_col_x_offset(idx, s) + x
renderer.draw_text(font, "»", tx, ty, color)
if tx > x + x2 then break end
offset = e + 1
end
draw_line_text(self, idx, x, y)
end

View File

@ -1,8 +1,7 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "C",
files = { "%.c$", "%.h$", "%.inl$" }, files = { "%.c$", "%.h$", "%.inl$" },
comment = "//", comment = "//",
patterns = { patterns = {
@ -44,7 +43,7 @@ syntax.add {
["case"] = "keyword", ["case"] = "keyword",
["default"] = "keyword", ["default"] = "keyword",
["auto"] = "keyword", ["auto"] = "keyword",
["void"] = "keyword2", ["void"] = "keyword",
["int"] = "keyword2", ["int"] = "keyword2",
["short"] = "keyword2", ["short"] = "keyword2",
["long"] = "keyword2", ["long"] = "keyword2",

View File

@ -1,10 +1,9 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
pcall(require, "plugins.language_c") pcall(require, "plugins.language_c")
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "C++",
files = { files = {
"%.h$", "%.inl$", "%.cpp$", "%.cc$", "%.C$", "%.cxx$", "%.h$", "%.inl$", "%.cpp$", "%.cc$", "%.C$", "%.cxx$",
"%.c++$", "%.hh$", "%.H$", "%.hxx$", "%.hpp$", "%.h++$" "%.c++$", "%.hh$", "%.H$", "%.hxx$", "%.hpp$", "%.h++$"
@ -96,7 +95,7 @@ syntax.add {
["default"] = "keyword", ["default"] = "keyword",
["auto"] = "keyword", ["auto"] = "keyword",
["const"] = "keyword", ["const"] = "keyword",
["void"] = "keyword2", ["void"] = "keyword",
["int"] = "keyword2", ["int"] = "keyword2",
["short"] = "keyword2", ["short"] = "keyword2",
["long"] = "keyword2", ["long"] = "keyword2",

View File

@ -1,8 +1,7 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "CSS",
files = { "%.css$" }, files = { "%.css$" },
patterns = { patterns = {
{ pattern = "\\.", type = "normal" }, { pattern = "\\.", type = "normal" },

View File

@ -1,8 +1,7 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "HTML",
files = { "%.html?$" }, files = { "%.html?$" },
patterns = { patterns = {
{ {

View File

@ -1,19 +1,18 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "JavaScript",
files = { "%.js$", "%.json$", "%.cson$" }, files = { "%.js$", "%.json$", "%.cson$" },
comment = "//", comment = "//",
patterns = { patterns = {
{ pattern = "//.-\n", type = "comment" }, { pattern = "//.-\n", type = "comment" },
{ pattern = { "/%*", "%*/" }, type = "comment" }, { pattern = { "/%*", "%*/" }, type = "comment" },
{ pattern = { '/[^= ]', '/', '\\' },type = "string" }, { pattern = { '/%g', '/', '\\' }, type = "string" },
{ pattern = { '"', '"', '\\' }, type = "string" }, { pattern = { '"', '"', '\\' }, type = "string" },
{ pattern = { "'", "'", '\\' }, type = "string" }, { pattern = { "'", "'", '\\' }, type = "string" },
{ pattern = { "`", "`", '\\' }, type = "string" }, { pattern = { "`", "`", '\\' }, type = "string" },
{ pattern = "0x[%da-fA-F_]+n?", type = "number" }, { pattern = "0x[%da-fA-F]+", type = "number" },
{ pattern = "-?%d+[%d%.eE_n]*", type = "number" }, { pattern = "-?%d+[%d%.eE]*", type = "number" },
{ pattern = "-?%.?%d+", type = "number" }, { pattern = "-?%.?%d+", type = "number" },
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" }, { pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
{ pattern = "[%a_][%w_]*%f[(]", type = "function" }, { pattern = "[%a_][%w_]*%f[(]", type = "function" },

View File

@ -1,8 +1,7 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "Lua",
files = "%.lua$", files = "%.lua$",
headers = "^#!.*[ /]lua", headers = "^#!.*[ /]lua",
comment = "--", comment = "--",

View File

@ -1,56 +1,22 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "Markdown",
files = { "%.md$", "%.markdown$" }, files = { "%.md$", "%.markdown$" },
patterns = { patterns = {
{ pattern = "\\.", type = "normal" }, { pattern = "\\.", type = "normal" },
{ pattern = { "<!%-%-", "%-%->" }, type = "comment" }, { pattern = { "<!%-%-", "%-%->" }, type = "comment" },
{ pattern = { "```c++", "```" }, type = "string", syntax = ".cpp" }, { pattern = { "```", "```" }, type = "string" },
{ pattern = { "```python", "```" }, type = "string", syntax = ".py" }, { pattern = { "``", "``", "\\" }, type = "string" },
{ pattern = { "```ruby", "```" }, type = "string", syntax = ".rb" }, { pattern = { "`", "`", "\\" }, type = "string" },
{ pattern = { "```perl", "```" }, type = "string", syntax = ".pl" }, { pattern = { "~~", "~~", "\\" }, type = "keyword2" },
{ pattern = { "```php", "```" }, type = "string", syntax = ".php" }, { pattern = "%-%-%-+", type = "comment" },
{ pattern = { "```javascript", "```" }, type = "string", syntax = ".js" }, { pattern = "%*%s+", type = "operator" },
{ pattern = { "```html", "```" }, type = "string", syntax = ".html" }, { pattern = { "%*", "[%*\n]", "\\" }, type = "operator" },
{ pattern = { "```xml", "```" }, type = "string", syntax = ".xml" }, { pattern = { "%_", "[%_\n]", "\\" }, type = "keyword2" },
{ pattern = { "```css", "```" }, type = "string", syntax = ".css" }, { pattern = "#.-\n", type = "keyword" },
{ pattern = { "```lua", "```" }, type = "string", syntax = ".lua" }, { pattern = "!?%[.-%]%(.-%)", type = "function" },
{ pattern = { "```bash", "```" }, type = "string", syntax = ".sh" }, { pattern = "https?://%S+", type = "function" },
{ pattern = { "```java", "```" }, type = "string", syntax = ".java" },
{ pattern = { "```c#", "```" }, type = "string", syntax = ".cs" },
{ pattern = { "```cmake", "```" }, type = "string", syntax = ".cmake" },
{ pattern = { "```d", "```" }, type = "string", syntax = ".d" },
{ pattern = { "```glsl", "```" }, type = "string", syntax = ".glsl" },
{ pattern = { "```c", "```" }, type = "string", syntax = ".c" },
{ pattern = { "```julia", "```" }, type = "string", syntax = ".jl" },
{ pattern = { "```rust", "```" }, type = "string", syntax = ".rs" },
{ pattern = { "```dart", "```" }, type = "string", syntax = ".dart" },
{ pattern = { "```v", "```" }, type = "string", syntax = ".v" },
{ pattern = { "```toml", "```" }, type = "string", syntax = ".toml" },
{ pattern = { "```yaml", "```" }, type = "string", syntax = ".yaml" },
{ pattern = { "```php", "```" }, type = "string", syntax = ".php" },
{ pattern = { "```nim", "```" }, type = "string", syntax = ".nim" },
{ pattern = { "```typescript", "```" }, type = "string", syntax = ".ts" },
{ pattern = { "```rescript", "```" }, type = "string", syntax = ".res" },
{ pattern = { "```moon", "```" }, type = "string", syntax = ".moon" },
{ pattern = { "```go", "```" }, type = "string", syntax = ".go" },
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
{ pattern = { "```", "```" }, type = "string" },
{ pattern = { "``", "``", "\\" }, type = "string" },
{ pattern = { "`", "`", "\\" }, type = "string" },
{ pattern = { "~~", "~~", "\\" }, type = "keyword2" },
{ pattern = "%-%-%-+", type = "comment" },
{ pattern = "%*%s+", type = "operator" },
{ pattern = { "%*", "[%*\n]", "\\" }, type = "operator" },
{ pattern = { "%_", "[%_\n]", "\\" }, type = "keyword2" },
{ pattern = "#.-\n", type = "keyword" },
{ pattern = "!?%[.-%]%(.-%)", type = "function" },
{ pattern = "https?://%S+", type = "function" },
}, },
symbols = { }, symbols = { },
} }

View File

@ -1,22 +1,21 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "Python", files = { "%.py$", "%.pyw$" },
files = { "%.py$", "%.pyw$", "%.rpy$" },
headers = "^#!.*[ /]python", headers = "^#!.*[ /]python",
comment = "#", comment = "#",
patterns = { patterns = {
{ pattern = { "#", "\n" }, type = "comment" }, { pattern = { "#", "\n" }, type = "comment" },
{ pattern = { '[ruU]?"""', '"""'; '\\' }, type = "string" }, { pattern = { '[ruU]?"', '"', '\\' }, type = "string" },
{ pattern = { '[ruU]?"', '"', '\\' }, type = "string" }, { pattern = { "[ruU]?'", "'", '\\' }, type = "string" },
{ pattern = { "[ruU]?'", "'", '\\' }, type = "string" }, { pattern = { '"""', '"""' }, type = "string" },
{ pattern = "0x[%da-fA-F]+", type = "number" }, { pattern = "0x[%da-fA-F]+", type = "number" },
{ pattern = "-?%d+[%d%.eE]*", type = "number" }, { pattern = "-?%d+[%d%.eE]*", type = "number" },
{ pattern = "-?%.?%d+", type = "number" }, { pattern = "-?%.?%d+", type = "number" },
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" }, { pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
{ pattern = "[%a_][%w_]*%f[(]", type = "function" }, { pattern = "[%a_][%w_]*%f[(]", type = "function" },
{ pattern = "[%a_][%w_]*", type = "symbol" }, { pattern = "[%a_][%w_]*", type = "symbol" },
}, },
symbols = { symbols = {
["class"] = "keyword", ["class"] = "keyword",

View File

@ -1,8 +1,7 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local syntax = require "core.syntax" local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "XML",
files = { "%.xml$" }, files = { "%.xml$" },
headers = "<%?xml", headers = "<%?xml",
patterns = { patterns = {

View File

@ -1,21 +1,21 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local config = require "core.config" local config = require "core.config"
local style = require "core.style" local style = require "core.style"
local DocView = require "core.docview" local DocView = require "core.docview"
local CommandView = require "core.commandview"
local draw_overlay = DocView.draw_overlay local draw_overlay = DocView.draw_overlay
function DocView:draw_overlay(...) function DocView:draw_overlay(...)
if not self:is(CommandView) then local ns = ("n"):rep(config.line_limit)
local offset = self:get_font():get_width("n") * config.line_limit local ss = self:get_font():subpixel_scale()
local x = self:get_line_screen_position(1) + offset local offset = self:get_font():get_width_subpixel(ns) / ss
local y = self.position.y local x = self:get_line_screen_position(1) + offset
local w = math.ceil(SCALE * 1) local y = self.position.y
local h = self.size.y local w = math.ceil(SCALE * 1)
local h = self.size.y
local color = style.guide or style.selection
renderer.draw_rect(x, y, w, h, color) local color = style.guide or style.selection
end renderer.draw_rect(x, y, w, h, color)
draw_overlay(self, ...) draw_overlay(self, ...)
end end

View File

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

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local core = require "core" local core = require "core"
local common = require "core.common" local common = require "core.common"
local keymap = require "core.keymap" local keymap = require "core.keymap"
@ -9,7 +9,6 @@ local View = require "core.view"
local ResultsView = View:extend() local ResultsView = View:extend()
ResultsView.context = "session"
function ResultsView:new(text, fn) function ResultsView:new(text, fn)
ResultsView.super.new(self) ResultsView.super.new(self)
@ -92,7 +91,7 @@ end
function ResultsView:on_mouse_pressed(...) function ResultsView:on_mouse_pressed(...)
local caught = ResultsView.super.on_mouse_pressed(self, ...) local caught = ResultsView.super.on_mouse_pressed(self, ...)
if not caught then if not caught then
return self:open_selected_result() self:open_selected_result()
end end
end end
@ -108,7 +107,6 @@ function ResultsView:open_selected_result()
dv.doc:set_selection(res.line, res.col) dv.doc:set_selection(res.line, res.col)
dv:scroll_to_line(res.line, false, true) dv:scroll_to_line(res.line, false, true)
end) end)
return true
end end
@ -172,7 +170,7 @@ function ResultsView:draw()
local ox, oy = self:get_content_offset() local ox, oy = self:get_content_offset()
local x, y = ox + style.padding.x, oy + style.padding.y local x, y = ox + style.padding.x, oy + style.padding.y
local files_number = core.project_files_number() local files_number = core.project_files_number()
local per = common.clamp(files_number and self.last_file_idx / files_number or 1, 0, 1) local per = files_number and self.last_file_idx / files_number or 1
local text local text
if self.searching then if self.searching then
if files_number then if files_number then

View File

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

View File

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

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local core = require "core" local core = require "core"
local common = require "core.common" local common = require "core.common"
local command = require "core.command" local command = require "core.command"
@ -13,6 +13,7 @@ config.plugins.scale = {
use_mousewheel = true use_mousewheel = true
} }
local scale_level = 0
local scale_steps = 0.05 local scale_steps = 0.05
local current_scale = SCALE local current_scale = SCALE
@ -33,6 +34,9 @@ local function set_scale(scale)
local s = scale / current_scale local s = scale / current_scale
current_scale = scale current_scale = scale
-- we set scale_level in case this was called by user
scale_level = (scale - default_scale) / scale_steps
if config.plugins.scale.mode == "ui" then if config.plugins.scale.mode == "ui" then
SCALE = scale SCALE = scale
@ -44,18 +48,10 @@ local function set_scale(scale)
style.tab_width = style.tab_width * s style.tab_width = style.tab_width * s
for _, name in ipairs {"font", "big_font", "icon_font", "icon_big_font", "code_font"} do for _, name in ipairs {"font", "big_font", "icon_font", "icon_big_font", "code_font"} do
style[name] = renderer.font.copy(style[name], s * style[name]:get_size()) renderer.font.set_size(style[name], s * style[name]:get_size())
end end
else else
style.code_font = renderer.font.copy(style.code_font, s * style.code_font:get_size()) renderer.font.set_size(style.code_font, s * style.code_font:get_size())
end
for _, font in pairs(style.syntax_fonts) do
renderer.font.set_size(font, s * font:get_size())
end
for _, font in pairs(style.syntax_fonts) do
renderer.font.set_size(font, s * font:get_size())
end end
-- restore scroll positions -- restore scroll positions
@ -71,16 +67,30 @@ local function get_scale()
return current_scale return current_scale
end end
local on_mouse_wheel = RootView.on_mouse_wheel
function RootView:on_mouse_wheel(d, ...)
if keymap.modkeys["ctrl"] and config.plugins.scale.use_mousewheel then
if d < 0 then command.perform "scale:decrease" end
if d > 0 then command.perform "scale:increase" end
else
return on_mouse_wheel(self, d, ...)
end
end
local function res_scale() local function res_scale()
set_scale(default_scale) scale_level = 0
set_scale(default_scale)
end end
local function inc_scale() local function inc_scale()
set_scale(current_scale + scale_steps) scale_level = scale_level + 1
set_scale(default_scale + scale_level * scale_steps)
end end
local function dec_scale() local function dec_scale()
set_scale(current_scale - scale_steps) scale_level = scale_level - 1
set_scale(default_scale + scale_level * scale_steps)
end end
@ -94,8 +104,6 @@ keymap.add {
["ctrl+0"] = "scale:reset", ["ctrl+0"] = "scale:reset",
["ctrl+-"] = "scale:decrease", ["ctrl+-"] = "scale:decrease",
["ctrl+="] = "scale:increase", ["ctrl+="] = "scale:increase",
["ctrl+wheelup"] = "scale:increase",
["ctrl+wheeldown"] = "scale:decrease"
} }
return { return {

View File

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

View File

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

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0 -- mod-version:1 -- lite-xl 1.16
local core = require "core" local core = require "core"
local common = require "core.common" local common = require "core.common"
local command = require "core.command" local command = require "core.command"
@ -41,18 +41,8 @@ function TreeView:new()
self.init_size = true self.init_size = true
self.target_size = default_treeview_size self.target_size = default_treeview_size
self.cache = {} self.cache = {}
self.last = {}
self.tooltip = { x = 0, y = 0, begin = 0, alpha = 0 } self.tooltip = { x = 0, y = 0, begin = 0, alpha = 0 }
self.item_icon_width = 0
self.item_text_spacing = 0
local on_dirmonitor_modify = core.on_dirmonitor_modify
function core.on_dirmonitor_modify(dir, filepath)
if self.cache[dir.name] then
self.cache[dir.name][filepath] = nil
end
on_dirmonitor_modify(dir, filepath)
end
end end
@ -64,7 +54,7 @@ function TreeView:set_target_size(axis, value)
end end
function TreeView:get_cached(dir, item, dirname) function TreeView:get_cached(item, dirname)
local dir_cache = self.cache[dirname] local dir_cache = self.cache[dirname]
if not dir_cache then if not dir_cache then
dir_cache = {} dir_cache = {}
@ -90,7 +80,6 @@ function TreeView:get_cached(dir, item, dirname)
end end
t.name = basename t.name = basename
t.type = item.type t.type = item.type
t.dir = dir -- points to top level "dir" item
dir_cache[cache_name] = t dir_cache[cache_name] = t
end end
return t return t
@ -115,13 +104,18 @@ end
function TreeView:check_cache() function TreeView:check_cache()
-- invalidate cache's skip values if project_files has changed
for i = 1, #core.project_directories do for i = 1, #core.project_directories do
local dir = core.project_directories[i] local dir = core.project_directories[i]
-- invalidate cache's skip values if directory is declared dirty local last_files = self.last[dir.name]
if dir.is_dirty and self.cache[dir.name] then if not last_files then
self:invalidate_cache(dir.name) self.last[dir.name] = dir.files
else
if dir.files ~= last_files then
self:invalidate_cache(dir.name)
self.last[dir.name] = dir.files
end
end end
dir.is_dirty = false
end end
end end
@ -137,14 +131,14 @@ function TreeView:each_item()
for k = 1, #core.project_directories do for k = 1, #core.project_directories do
local dir = core.project_directories[k] local dir = core.project_directories[k]
local dir_cached = self:get_cached(dir, dir.item, dir.name) local dir_cached = self:get_cached(dir.item, dir.name)
coroutine.yield(dir_cached, ox, y, w, h) coroutine.yield(dir_cached, ox, y, w, h)
count_lines = count_lines + 1 count_lines = count_lines + 1
y = y + h y = y + h
local i = 1 local i = 1
while i <= #dir.files and dir_cached.expanded do while i <= #dir.files and dir_cached.expanded do
local item = dir.files[i] local item = dir.files[i]
local cached = self:get_cached(dir, item, dir.name) local cached = self:get_cached(item, dir.name)
coroutine.yield(cached, ox, y, w, h) coroutine.yield(cached, ox, y, w, h)
count_lines = count_lines + 1 count_lines = count_lines + 1
@ -212,6 +206,7 @@ local function create_directory_in(item)
core.error("cannot create directory %q: %s", dirname, err) core.error("cannot create directory %q: %s", dirname, err)
end end
item.expanded = true item.expanded = true
core.reschedule_project_scan()
end) end)
end end
@ -219,31 +214,39 @@ end
function TreeView:on_mouse_pressed(button, x, y, clicks) function TreeView:on_mouse_pressed(button, x, y, clicks)
local caught = TreeView.super.on_mouse_pressed(self, button, x, y, clicks) local caught = TreeView.super.on_mouse_pressed(self, button, x, y, clicks)
if caught or button ~= "left" then if caught or button ~= "left" then
return true return
end end
local hovered_item = self.hovered_item local hovered_item = self.hovered_item
if not hovered_item then if not hovered_item then
return false return
elseif hovered_item.type == "dir" then elseif hovered_item.type == "dir" then
if keymap.modkeys["ctrl"] and button == "left" then if keymap.modkeys["ctrl"] and button == "left" then
create_directory_in(hovered_item) create_directory_in(hovered_item)
else else
hovered_item.expanded = not hovered_item.expanded if core.project_files_limit and not hovered_item.expanded then
if hovered_item.dir.files_limit then local filename, abs_filename = hovered_item.filename, hovered_item.abs_filename
core.update_project_subdir(hovered_item.dir, hovered_item.filename, hovered_item.expanded) local index = 0
core.project_subdir_set_show(hovered_item.dir, hovered_item.filename, hovered_item.expanded) -- The loop below is used to find the first match starting from the end
-- in case there are multiple matches.
while index and index + #filename < #abs_filename do
index = string.find(abs_filename, filename, index + 1, true)
end
-- we assume here index is not nil because the abs_filename must contain the
-- relative filename
local dirname = string.sub(abs_filename, 1, index - 2)
if core.is_project_folder(dirname) then
core.scan_project_folder(dirname, filename)
self:invalidate_cache(dirname)
end
end end
hovered_item.expanded = not hovered_item.expanded
end end
else else
core.try(function() core.try(function()
if core.last_active_view and core.active_view == self then local doc_filename = common.relative_path(core.project_dir, hovered_item.abs_filename)
core.set_active_view(core.last_active_view)
end
local doc_filename = core.normalize_to_project_dir(hovered_item.abs_filename)
core.root_view:open_doc(core.open_doc(doc_filename)) core.root_view:open_doc(core.open_doc(doc_filename))
end) end)
end end
return true
end end
@ -264,9 +267,6 @@ function TreeView:update()
self.tooltip.alpha = 0 self.tooltip.alpha = 0
end end
self.item_icon_width = style.icon_font:get_width("D")
self.item_text_spacing = style.icon_font:get_width("f") / 2
TreeView.super.update(self) TreeView.super.update(self)
end end
@ -295,90 +295,47 @@ function TreeView:draw_tooltip()
end end
function TreeView:get_item_icon(item, active, hovered)
local character = "f"
if item.type == "dir" then
character = item.expanded and "D" or "d"
end
local font = style.icon_font
local color = style.text
if active or hovered then
color = style.accent
end
return character, font, color
end
function TreeView:get_item_text(item, active, hovered)
local text = item.name
local font = style.font
local color = style.text
if active or hovered then
color = style.accent
end
return text, font, color
end
function TreeView:draw_item_text(item, active, hovered, x, y, w, h)
local item_text, item_font, item_color = self:get_item_text(item, active, hovered)
common.draw_text(item_font, item_color, item_text, nil, x, y, 0, h)
end
function TreeView:draw_item_icon(item, active, hovered, x, y, w, h)
local icon_char, icon_font, icon_color = self:get_item_icon(item, active, hovered)
common.draw_text(icon_font, icon_color, icon_char, nil, x, y, 0, h)
return self.item_icon_width + self.item_text_spacing
end
function TreeView:draw_item_body(item, active, hovered, x, y, w, h)
x = x + self:draw_item_icon(item, active, hovered, x, y, w, h)
self:draw_item_text(item, active, hovered, x, y, w, h)
end
function TreeView:draw_item_chevron(item, active, hovered, x, y, w, h)
if item.type == "dir" then
local chevron_icon = item.expanded and "-" or "+"
local chevron_color = hovered and style.accent or style.text
common.draw_text(style.icon_font, chevron_color, chevron_icon, nil, x, y, 0, h)
end
return style.padding.x
end
function TreeView:draw_item_background(item, active, hovered, x, y, w, h)
if hovered then
renderer.draw_rect(x, y, w, h, style.line_highlight)
end
end
function TreeView:draw_item(item, active, hovered, x, y, w, h)
self:draw_item_background(item, active, hovered, x, y, w, h)
x = x + item.depth * style.padding.x + style.padding.x
x = x + self:draw_item_chevron(item, active, hovered, x, y, w, h)
self:draw_item_body(item, active, hovered, x, y, w, h)
end
function TreeView:draw() function TreeView:draw()
self:draw_background(style.background2) self:draw_background(style.background2)
local _y, _h = self.position.y, self.size.y
local icon_width = style.icon_font:get_width("D")
local spacing = style.icon_font:get_width("f") / 2
local doc = core.active_view.doc local doc = core.active_view.doc
local active_filename = doc and system.absolute_path(doc.filename or "") local active_filename = doc and system.absolute_path(doc.filename or "")
for item, x,y,w,h in self:each_item() do for item, x,y,w,h in self:each_item() do
if y + h >= _y and y < _y + _h then local color = style.text
self:draw_item(item,
item.abs_filename == active_filename, -- highlight active_view doc
item == self.hovered_item, if item.abs_filename == active_filename then
x, y, w, h) color = style.accent
end end
-- hovered item background
if item == self.hovered_item then
renderer.draw_rect(x, y, w, h, style.line_highlight)
color = style.accent
end
-- icons
x = x + item.depth * style.padding.x + style.padding.x
if item.type == "dir" then
local icon1 = item.expanded and "-" or "+"
local icon2 = item.expanded and "D" or "d"
common.draw_text(style.icon_font, color, icon1, nil, x, y, 0, h)
x = x + style.padding.x
common.draw_text(style.icon_font, color, icon2, nil, x, y, 0, h)
x = x + icon_width
else
x = x + style.padding.x
common.draw_text(style.icon_font, color, "f", nil, x, y, 0, h)
x = x + icon_width
end
-- text
x = x + spacing
x = common.draw_text(style.font, color, item.name, nil, x, y, 0, h)
end end
self:draw_scrollbar() self:draw_scrollbar()
@ -447,7 +404,7 @@ function RootView:draw(...)
end end
local function is_project_folder(path) local function is_project_folder(path)
return core.project_dir == path return common.basename(core.project_dir) == path
end end
menu:register(function() return view.hovered_item end, { menu:register(function() return view.hovered_item end, {
@ -458,7 +415,7 @@ menu:register(function() return view.hovered_item end, {
menu:register( menu:register(
function() function()
return view.hovered_item return view.hovered_item
and not is_project_folder(view.hovered_item.abs_filename) and not is_project_folder(view.hovered_item.filename)
end, end,
{ {
{ text = "Rename", command = "treeview:rename" }, { text = "Rename", command = "treeview:rename" },
@ -480,36 +437,22 @@ menu:register(
command.add(nil, { command.add(nil, {
["treeview:toggle"] = function() ["treeview:toggle"] = function()
view.visible = not view.visible view.visible = not view.visible
end}) end,
command.add(function() return view.hovered_item ~= nil end, {
["treeview:rename"] = function() ["treeview:rename"] = function()
local old_filename = view.hovered_item.filename local old_filename = view.hovered_item.filename
local old_abs_filename = view.hovered_item.abs_filename
core.command_view:set_text(old_filename) core.command_view:set_text(old_filename)
core.command_view:enter("Rename", function(filename) core.command_view:enter("Rename", function(filename)
filename = core.normalize_to_project_dir(filename) os.rename(old_filename, filename)
local abs_filename = core.project_absolute_path(filename) core.reschedule_project_scan()
local res, err = os.rename(old_abs_filename, abs_filename) core.log("Renamed \"%s\" to \"%s\"", old_filename, filename)
if res then -- successfully renamed
for _, doc in ipairs(core.docs) do
if doc.abs_filename and old_abs_filename == doc.abs_filename then
doc:set_filename(filename, abs_filename) -- make doc point to the new filename
doc:reset_syntax()
break -- only first needed
end
end
core.log("Renamed \"%s\" to \"%s\"", old_filename, filename)
else
core.error("Error while renaming \"%s\" to \"%s\": %s", old_abs_filename, abs_filename, err)
end
end, common.path_suggest) end, common.path_suggest)
end, end,
["treeview:new-file"] = function() ["treeview:new-file"] = function()
if not is_project_folder(view.hovered_item.abs_filename) then local dir_name = view.hovered_item.filename
core.command_view:set_text(view.hovered_item.filename .. "/") if not is_project_folder(dir_name) then
core.command_view:set_text(dir_name .. "/")
end end
core.command_view:enter("Filename", function(filename) core.command_view:enter("Filename", function(filename)
local doc_filename = core.project_dir .. PATHSEP .. filename local doc_filename = core.project_dir .. PATHSEP .. filename
@ -517,17 +460,20 @@ command.add(function() return view.hovered_item ~= nil end, {
file:write("") file:write("")
file:close() file:close()
core.root_view:open_doc(core.open_doc(doc_filename)) core.root_view:open_doc(core.open_doc(doc_filename))
core.reschedule_project_scan()
core.log("Created %s", doc_filename) core.log("Created %s", doc_filename)
end, common.path_suggest) end, common.path_suggest)
end, end,
["treeview:new-folder"] = function() ["treeview:new-folder"] = function()
if not is_project_folder(view.hovered_item.abs_filename) then local dir_name = view.hovered_item.filename
core.command_view:set_text(view.hovered_item.filename .. "/") if not is_project_folder(dir_name) then
core.command_view:set_text(dir_name .. "/")
end end
core.command_view:enter("Folder Name", function(filename) core.command_view:enter("Folder Name", function(filename)
local dir_path = core.project_dir .. PATHSEP .. filename local dir_path = core.project_dir .. PATHSEP .. filename
common.mkdirp(dir_path) common.mkdirp(dir_path)
core.reschedule_project_scan()
core.log("Created %s", dir_path) core.log("Created %s", dir_path)
end, common.path_suggest) end, common.path_suggest)
end, end,
@ -564,6 +510,7 @@ command.add(function() return view.hovered_item ~= nil end, {
return return
end end
end end
core.reschedule_project_scan()
core.log("Deleted \"%s\"", filename) core.log("Deleted \"%s\"", filename)
end end
end end
@ -574,7 +521,7 @@ command.add(function() return view.hovered_item ~= nil end, {
local hovered_item = view.hovered_item local hovered_item = view.hovered_item
if PLATFORM == "Windows" then if PLATFORM == "Windows" then
system.exec(string.format("start \"\" %q", hovered_item.abs_filename)) system.exec("start " .. hovered_item.abs_filename)
elseif string.find(PLATFORM, "Mac") then elseif string.find(PLATFORM, "Mac") then
system.exec(string.format("open %q", hovered_item.abs_filename)) system.exec(string.format("open %q", hovered_item.abs_filename))
elseif PLATFORM == "Linux" then elseif PLATFORM == "Linux" then

View File

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

View File

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

View File

@ -57,26 +57,23 @@ process.WAIT_INFINITE = -1
---@type integer ---@type integer
process.WAIT_DEADLINE = -2 process.WAIT_DEADLINE = -2
---Default behavior for redirecting streams. ---Used for the process.options stdin, stdout and stderr fields.
---This flag is deprecated and for backwards compatibility with reproc only.
---The behavior of this flag may change in future versions of Lite XL.
---@type integer ---@type integer
process.REDIRECT_DEFAULT = 0 process.REDIRECT_DEFAULT = 0
---Allow Process API to read this stream via process:read functions. ---Used for the process.options stdin, stdout and stderr fields.
---@type integer ---@type integer
process.REDIRECT_PIPE = 1 process.REDIRECT_PIPE = 1
---Redirect this stream to the parent. ---Used for the process.options stdin, stdout and stderr fields.
---@type integer ---@type integer
process.REDIRECT_PARENT = 2 process.REDIRECT_PARENT = 2
---Discard this stream (piping it to /dev/null) ---Used for the process.options stdin, stdout and stderr fields.
---@type integer ---@type integer
process.REDIRECT_DISCARD = 3 process.REDIRECT_DISCARD = 3
---Redirect this stream to stdout. ---Used for the process.options stdin, stdout and stderr fields.
---This flag can only be used on process.options.stderr.
---@type integer ---@type integer
process.REDIRECT_STDOUT = 4 process.REDIRECT_STDOUT = 4

View File

@ -19,9 +19,6 @@ renderer.color = {}
---@class renderer.fontoptions ---@class renderer.fontoptions
---@field public antialiasing "'grayscale'" | "'subpixel'" ---@field public antialiasing "'grayscale'" | "'subpixel'"
---@field public hinting "'slight'" | "'none'" | '"full"' ---@field public hinting "'slight'" | "'none'" | '"full"'
-- @field public bold boolean
-- @field public italic boolean
-- @field public underline boolean
renderer.fontoptions = {} renderer.fontoptions = {}
--- ---
@ -61,6 +58,15 @@ function renderer.font:set_tab_size(chars) end
---@return number ---@return number
function renderer.font:get_width(text) end function renderer.font:get_width(text) end
---
---Get the width in subpixels of the given text when
---rendered with this font.
---
---@param text string
---
---@return number
function renderer.font:get_width_subpixel(text) end
--- ---
---Get the height in pixels that occupies a single character ---Get the height in pixels that occupies a single character
---when rendered with this font. ---when rendered with this font.
@ -68,6 +74,12 @@ function renderer.font:get_width(text) end
---@return number ---@return number
function renderer.font:get_height() end function renderer.font:get_height() end
---
---Gets the font subpixel scale.
---
---@return number
function renderer.font:subpixel_scale() end
--- ---
---Get the current size of the font. ---Get the current size of the font.
--- ---

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,198 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// See implementation agg_font_freetype.cpp
//
//----------------------------------------------------------------------------
#ifndef AGG_FONT_FREETYPE_INCLUDED
#define AGG_FONT_FREETYPE_INCLUDED
#include <ft2build.h>
#include FT_FREETYPE_H
#include "agg_scanline_storage_aa.h"
#include "agg_scanline_storage_bin.h"
#include "agg_scanline_u.h"
#include "agg_scanline_bin.h"
#include "agg_path_storage_integer.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_conv_curve.h"
#include "agg_font_cache_manager.h"
#include "agg_trans_affine.h"
namespace agg
{
//-----------------------------------------------font_engine_freetype_base
class font_engine_freetype_base
{
public:
//--------------------------------------------------------------------
typedef serialized_scanlines_adaptor_aa<int8u> gray8_adaptor_type;
typedef serialized_scanlines_adaptor_bin mono_adaptor_type;
typedef scanline_storage_aa8 scanlines_aa_type;
typedef scanline_storage_bin scanlines_bin_type;
//--------------------------------------------------------------------
~font_engine_freetype_base();
font_engine_freetype_base(bool flag32, unsigned max_faces = 32);
// Set font parameters
//--------------------------------------------------------------------
void resolution(unsigned dpi);
bool load_font(const char* font_name, unsigned face_index, glyph_rendering ren_type,
const char* font_mem = 0, const long font_mem_size = 0);
bool attach(const char* file_name);
bool char_map(FT_Encoding map);
bool height(double h);
bool width(double w);
void hinting(bool h);
void flip_y(bool f);
void transform(const trans_affine& affine);
// Set Gamma
//--------------------------------------------------------------------
template<class GammaF> void gamma(const GammaF& f)
{
m_rasterizer.gamma(f);
}
// Accessors
//--------------------------------------------------------------------
int last_error() const { return m_last_error; }
unsigned resolution() const { return m_resolution; }
const char* name() const { return m_name; }
unsigned num_faces() const;
FT_Encoding char_map() const { return m_char_map; }
double height() const { return double(m_height) / 64.0; }
double width() const { return double(m_width) / 64.0; }
double ascender() const;
double descender() const;
int face_height() const;
int face_units_em()const;
bool hinting() const { return m_hinting; }
bool flip_y() const { return m_flip_y; }
// Interface mandatory to implement for font_cache_manager
//--------------------------------------------------------------------
const char* font_signature() const { return m_signature; }
int change_stamp() const { return m_change_stamp; }
bool prepare_glyph(unsigned glyph_code);
unsigned glyph_index() const { return m_glyph_index; }
unsigned data_size() const { return m_data_size; }
glyph_data_type data_type() const { return m_data_type; }
const rect_i& bounds() const { return m_bounds; }
double advance_x() const { return m_advance_x; }
double advance_y() const { return m_advance_y; }
void write_glyph_to(int8u* data) const;
bool add_kerning(unsigned first, unsigned second,
double* x, double* y);
private:
font_engine_freetype_base(const font_engine_freetype_base&);
const font_engine_freetype_base& operator = (const font_engine_freetype_base&);
void update_char_size();
void update_signature();
int find_face(const char* face_name) const;
bool m_flag32;
int m_change_stamp;
int m_last_error;
char* m_name;
unsigned m_name_len;
unsigned m_face_index;
FT_Encoding m_char_map;
char* m_signature;
unsigned m_height;
unsigned m_width;
bool m_hinting;
bool m_flip_y;
bool m_library_initialized;
FT_Library m_library; // handle to library
FT_Face* m_faces; // A pool of font faces
char** m_face_names;
unsigned m_num_faces;
unsigned m_max_faces;
FT_Face m_cur_face; // handle to the current face object
int m_resolution;
glyph_rendering m_glyph_rendering;
unsigned m_glyph_index;
unsigned m_data_size;
glyph_data_type m_data_type;
rect_i m_bounds;
double m_advance_x;
double m_advance_y;
trans_affine m_affine;
path_storage_integer<int16, 6> m_path16;
path_storage_integer<int32, 6> m_path32;
conv_curve<path_storage_integer<int16, 6> > m_curves16;
conv_curve<path_storage_integer<int32, 6> > m_curves32;
scanline_u8 m_scanline_aa;
scanline_bin m_scanline_bin;
scanlines_aa_type m_scanlines_aa;
scanlines_bin_type m_scanlines_bin;
rasterizer_scanline_aa<> m_rasterizer;
};
//------------------------------------------------font_engine_freetype_int16
// This class uses values of type int16 (10.6 format) for the vector cache.
// The vector cache is compact, but when rendering glyphs of height
// more that 200 there integer overflow can occur.
//
class font_engine_freetype_int16 : public font_engine_freetype_base
{
public:
typedef serialized_integer_path_adaptor<int16, 6> path_adaptor_type;
typedef font_engine_freetype_base::gray8_adaptor_type gray8_adaptor_type;
typedef font_engine_freetype_base::mono_adaptor_type mono_adaptor_type;
typedef font_engine_freetype_base::scanlines_aa_type scanlines_aa_type;
typedef font_engine_freetype_base::scanlines_bin_type scanlines_bin_type;
font_engine_freetype_int16(unsigned max_faces = 32) :
font_engine_freetype_base(false, max_faces) {}
};
//------------------------------------------------font_engine_freetype_int32
// This class uses values of type int32 (26.6 format) for the vector cache.
// The vector cache is twice larger than in font_engine_freetype_int16,
// but it allows you to render glyphs of very large sizes.
//
class font_engine_freetype_int32 : public font_engine_freetype_base
{
public:
typedef serialized_integer_path_adaptor<int32, 6> path_adaptor_type;
typedef font_engine_freetype_base::gray8_adaptor_type gray8_adaptor_type;
typedef font_engine_freetype_base::mono_adaptor_type mono_adaptor_type;
typedef font_engine_freetype_base::scanlines_aa_type scanlines_aa_type;
typedef font_engine_freetype_base::scanlines_bin_type scanlines_bin_type;
font_engine_freetype_int32(unsigned max_faces = 32) :
font_engine_freetype_base(true, max_faces) {}
};
}
#endif

View File

@ -0,0 +1,73 @@
// Adapted by Francesco Abbate for GSL Shell
// Original code's copyright below.
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef AGG_LCD_DISTRIBUTION_LUT_INCLUDED
#define AGG_LCD_DISTRIBUTION_LUT_INCLUDED
#include <cstdlib>
#include "agg_basics.h"
namespace agg
{
//=====================================================lcd_distribution_lut
class lcd_distribution_lut
{
public:
lcd_distribution_lut(double prim, double second, double tert)
{
double norm = 1.0 / (prim + second*2 + tert*2);
prim *= norm;
second *= norm;
tert *= norm;
for(unsigned i = 0; i < 256; i++)
{
unsigned b = (i << 8);
unsigned s = round(second * b);
unsigned t = round(tert * b);
unsigned p = b - (2*s + 2*t);
m_data[3*i + 1] = s; /* secondary */
m_data[3*i + 2] = t; /* tertiary */
m_data[3*i ] = p; /* primary */
}
}
unsigned convolution(const int8u* covers, int i0, int i_min, int i_max) const
{
unsigned sum = 0;
int k_min = (i0 >= i_min + 2 ? -2 : i_min - i0);
int k_max = (i0 <= i_max - 2 ? 2 : i_max - i0);
for (int k = k_min; k <= k_max; k++)
{
/* select the primary, secondary or tertiary channel */
int channel = std::abs(k) % 3;
int8u c = covers[i0 + k];
sum += m_data[3*c + channel];
}
return (sum + 128) >> 8;
}
private:
unsigned short m_data[256*3];
};
}
#endif

View File

@ -0,0 +1,93 @@
#pragma once
#include <string.h>
#include "agg_basics.h"
#include "agg_rendering_buffer.h"
namespace agg
{
// This is a special purpose color type that only has the alpha channel.
// It can be thought as a gray color but with the intensity always on.
// It is actually used to store coverage information.
struct alpha8
{
typedef int8u value_type;
typedef int32u calc_type;
typedef int32 long_type;
enum base_scale_e
{
base_shift = 8,
base_scale = 1 << base_shift,
base_mask = base_scale - 1
};
value_type a;
//--------------------------------------------------------------------
alpha8(unsigned a_=base_mask) :
a(int8u(a_)) {}
};
// Pixer format to store coverage information.
class pixfmt_alpha8
{
public:
typedef alpha8 color_type;
typedef int8u value_type;
typedef int32u calc_type;
typedef agg::rendering_buffer::row_data row_data;
//--------------------------------------------------------------------
pixfmt_alpha8(rendering_buffer& rb): m_rbuf(&rb)
{
}
//--------------------------------------------------------------------
unsigned width() const {
return m_rbuf->width();
}
unsigned height() const {
return m_rbuf->height();
}
// This method should never be called when using the scanline_u8.
// The use of scanline_p8 should be avoided because if does not works
// properly for rendering fonts because single hspan are split in many
// hline/hspan elements and pixel whitening happens.
void blend_hline(int x, int y, unsigned len,
const color_type& c, int8u cover)
{ }
void copy_hline(int x, int y, unsigned len, const color_type& c)
{
value_type* p = (value_type*) m_rbuf->row_ptr(y) + x;
do
{
*p = c.a;
p++;
}
while(--len);
}
//--------------------------------------------------------------------
void blend_solid_hspan(int x, int y,
unsigned len,
const color_type& c,
const int8u* covers)
{
value_type* p = (value_type*) m_rbuf->row_ptr(y) + x;
do
{
calc_type alpha = (calc_type(c.a) * (calc_type(*covers) + 1)) >> 8;
*p = alpha;
p++;
++covers;
}
while(--len);
}
private:
rendering_buffer* m_rbuf;
};
}

View File

@ -0,0 +1,445 @@
#include "font_renderer.h"
#include "agg_lcd_distribution_lut.h"
#include "agg_pixfmt_rgb.h"
#include "agg_pixfmt_rgba.h"
#include "font_renderer_alpha.h"
// Important: when a subpixel scale is used the width below will be the width in logical pixel.
// As each logical pixel contains 3 subpixels it means that the 'pixels' pointer
// will hold enough space for '3 * width' uint8_t values.
struct FR_Bitmap {
agg::int8u *pixels;
int width, height;
};
class FR_Renderer {
public:
// Conventional LUT values: (1./3., 2./9., 1./9.)
// The values below are fine tuned as in the Elementary Plot library.
FR_Renderer(bool hinting, bool kerning, bool subpixel, bool prescale_x) :
m_renderer(hinting, kerning, subpixel, prescale_x),
m_lcd_lut(0.448, 0.184, 0.092),
m_subpixel(subpixel)
{ }
font_renderer_alpha& renderer_alpha() { return m_renderer; }
agg::lcd_distribution_lut& lcd_distribution_lut() { return m_lcd_lut; }
int subpixel_scale() const { return (m_subpixel ? 3 : 1); }
private:
font_renderer_alpha m_renderer;
agg::lcd_distribution_lut m_lcd_lut;
int m_subpixel;
};
FR_Renderer *FR_Renderer_New(unsigned int flags) {
bool hinting = ((flags & FR_HINTING) != 0);
bool kerning = ((flags & FR_KERNING) != 0);
bool subpixel = ((flags & FR_SUBPIXEL) != 0);
bool prescale_x = ((flags & FR_PRESCALE_X) != 0);
return new FR_Renderer(hinting, kerning, subpixel, prescale_x);
}
FR_Bitmap* FR_Bitmap_New(FR_Renderer *font_renderer, int width, int height) {
const int subpixel_scale = font_renderer->subpixel_scale();
FR_Bitmap *image = (FR_Bitmap *) malloc(sizeof(FR_Bitmap) + width * height * subpixel_scale);
if (!image) { return NULL; }
image->pixels = (agg::int8u *) (image + 1);
image->width = width;
image->height = height;
return image;
}
void FR_Bitmap_Free(FR_Bitmap *image) {
free(image);
}
void FR_Renderer_Free(FR_Renderer *font_renderer) {
delete font_renderer;
}
int FR_Subpixel_Scale(FR_Renderer *font_renderer) {
return font_renderer->subpixel_scale();
}
int FR_Load_Font(FR_Renderer *font_renderer, const char *filename) {
bool success = font_renderer->renderer_alpha().load_font(filename);
return (success ? 0 : 1);
}
int FR_Get_Font_Height(FR_Renderer *font_renderer, float size) {
font_renderer_alpha& renderer_alpha = font_renderer->renderer_alpha();
double ascender, descender;
renderer_alpha.get_font_vmetrics(ascender, descender);
int face_height = renderer_alpha.get_face_height();
float scale = renderer_alpha.scale_for_em_to_pixels(size);
return int((ascender - descender) * face_height * scale + 0.5);
}
static void glyph_trim_rect(agg::rendering_buffer& ren_buf, FR_Bitmap_Glyph_Metrics& gli, int subpixel_scale) {
const int height = ren_buf.height();
int x0 = gli.x0 * subpixel_scale, x1 = gli.x1 * subpixel_scale;
int y0 = gli.y0, y1 = gli.y1;
for (int y = gli.y0; y < gli.y1; y++) {
const uint8_t *row = ren_buf.row_ptr(height - 1 - y);
unsigned int row_bitsum = 0;
for (int x = x0; x < x1; x++) {
row_bitsum |= row[x];
}
if (row_bitsum == 0) {
y0++;
} else {
break;
}
}
for (int y = gli.y1 - 1; y >= y0; y--) {
const uint8_t *row = ren_buf.row_ptr(height - 1 - y);
unsigned int row_bitsum = 0;
for (int x = x0; x < x1; x++) {
row_bitsum |= row[x];
}
if (row_bitsum == 0) {
y1--;
} else {
break;
}
}
for (int x = gli.x0 * subpixel_scale; x < gli.x1 * subpixel_scale; x += subpixel_scale) {
unsigned int xaccu = 0;
for (int y = y0; y < y1; y++) {
const uint8_t *row = ren_buf.row_ptr(height - 1 - y);
for (int i = 0; i < subpixel_scale; i++) {
xaccu |= row[x + i];
}
}
if (xaccu == 0) {
x0 += subpixel_scale;
} else {
break;
}
}
for (int x = (gli.x1 - 1) * subpixel_scale; x >= x0; x -= subpixel_scale) {
unsigned int xaccu = 0;
for (int y = y0; y < y1; y++) {
const uint8_t *row = ren_buf.row_ptr(height - 1 - y);
for (int i = 0; i < subpixel_scale; i++) {
xaccu |= row[x + i];
}
}
if (xaccu == 0) {
x1 -= subpixel_scale;
} else {
break;
}
}
gli.xoff += (x0 / subpixel_scale) - gli.x0;
gli.yoff += (y0 - gli.y0);
gli.x0 = x0 / subpixel_scale;
gli.y0 = y0;
gli.x1 = x1 / subpixel_scale;
gli.y1 = y1;
}
static void glyph_lut_convolution(agg::rendering_buffer ren_buf, agg::lcd_distribution_lut& lcd_lut, agg::int8u *covers_buf, FR_Bitmap_Glyph_Metrics& gli) {
const int subpixel = 3;
const int x0 = gli.x0, y0 = gli.y0, x1 = gli.x1, y1 = gli.y1;
const int len = (x1 - x0) * subpixel;
const int height = ren_buf.height();
for (int y = y0; y < y1; y++) {
agg::int8u *covers = ren_buf.row_ptr(height - 1 - y) + x0 * subpixel;
memcpy(covers_buf, covers, len);
for (int x = x0 - 1; x < x1 + 1; x++) {
for (int i = 0; i < subpixel; i++) {
const int cx = (x - x0) * subpixel + i;
covers[cx] = lcd_lut.convolution(covers_buf, cx, 0, len - 1);
}
}
}
gli.x0 -= 1;
gli.x1 += 1;
gli.xoff -= 1;
}
// The two functions below are needed because in C and C++ integer division
// is rounded toward zero.
// euclidean division rounded toward positive infinite
static int div_pos(int n, int p) {
return n >= 0 ? (n + p - 1) / p : (n / p);
}
// euclidean division rounded toward negative infinite
static int div_neg(int n, int p) {
return n >= 0 ? (n / p) : ((n - p + 1) / p);
}
FR_Bitmap *FR_Bake_Font_Bitmap(FR_Renderer *font_renderer, int font_height,
int first_char, int num_chars, FR_Bitmap_Glyph_Metrics *glyphs)
{
font_renderer_alpha& renderer_alpha = font_renderer->renderer_alpha();
agg::lcd_distribution_lut& lcd_lut = font_renderer->lcd_distribution_lut();
const int subpixel_scale = font_renderer->subpixel_scale();
double ascender, descender;
renderer_alpha.get_font_vmetrics(ascender, descender);
const int ascender_px = int(ascender * font_height);
const int pad_y = 1;
// When using subpixel font rendering it is needed to leave a padding pixel on the left and on the right.
// Since each pixel is composed by n subpixel we set below x_start to subpixel_scale instead than zero.
// In addition we need one more pixel on the left because of subpixel positioning so
// it adds up to 2 * subpixel_scale.
// Note about the coordinates: they are AGG-like so x is positive toward the right and
// y is positive in the upper direction.
const int x_start = 2 * subpixel_scale;
const agg::alpha8 text_color(0xff);
#ifdef FONT_RENDERER_HEIGHT_HACK
const int font_height_reduced = (font_height * 86) / 100;
#else
const int font_height_reduced = font_height;
#endif
renderer_alpha.set_font_height(font_height_reduced);
int *index = (int *) malloc(num_chars * sizeof(int));
agg::rect_i *bounds = (agg::rect_i *) malloc(num_chars * sizeof(agg::rect_i));
if (!index || !bounds) {
free(index);
free(bounds);
return NULL;
}
int x_size_sum = 0, glyph_count = 0;
for (int i = 0; i < num_chars; i++) {
int codepoint = first_char + i;
index[i] = i;
if (renderer_alpha.codepoint_bounds(codepoint, subpixel_scale, bounds[i])) {
// Invalid glyph
bounds[i].x1 = 0;
bounds[i].y1 = 0;
bounds[i].x2 = -1;
bounds[i].y2 = -1;
} else {
if (bounds[i].x2 > bounds[i].x1) {
x_size_sum += bounds[i].x2 - bounds[i].x1;
glyph_count++;
}
bounds[i].x1 = subpixel_scale * div_neg(bounds[i].x1, subpixel_scale);
bounds[i].x2 = subpixel_scale * div_pos(bounds[i].x2, subpixel_scale);
}
}
// Simple insertion sort algorithm: https://en.wikipedia.org/wiki/Insertion_sort
int i = 1;
while (i < num_chars) {
int j = i;
while (j > 0 && bounds[index[j-1]].y2 - bounds[index[j-1]].y1 > bounds[index[j]].y2 - bounds[index[j]].y1) {
int tmp = index[j];
index[j] = index[j-1];
index[j-1] = tmp;
j = j - 1;
}
i = i + 1;
}
const int glyph_avg_width = glyph_count > 0 ? x_size_sum / (glyph_count * subpixel_scale) : font_height;
const int pixels_width = glyph_avg_width * 28;
// dry run simulating pixel position to estimate required image's height
int x = x_start, y = 0, y_bottom = y;
for (int i = 0; i < num_chars; i++) {
const agg::rect_i& gbounds = bounds[index[i]];
if (gbounds.x2 < gbounds.x1) continue;
// 1. It is very important to ensure that the x's increment below (1) and in
// (2), (3) and (4) are perfectly the same.
// Note that x_step below is always an integer multiple of subpixel_scale.
const int x_step = gbounds.x2 + 3 * subpixel_scale;
if (x + x_step >= pixels_width * subpixel_scale) {
x = x_start;
y = y_bottom;
}
// 5. Ensure that y's increment below is exactly the same to the one used in (6)
const int glyph_y_bottom = y - 2 * pad_y - (gbounds.y2 - gbounds.y1);
y_bottom = (y_bottom > glyph_y_bottom ? glyph_y_bottom : y_bottom);
// 2. Ensure x's increment is aligned with (1)
x = x + x_step;
}
agg::int8u *cover_swap_buffer = (agg::int8u *) malloc(sizeof(agg::int8u) * (pixels_width * subpixel_scale));
if (!cover_swap_buffer) {
free(index);
free(bounds);
return NULL;
}
const int pixels_height = -y_bottom + 1;
const int pixel_size = 1;
FR_Bitmap *image = FR_Bitmap_New(font_renderer, pixels_width, pixels_height);
if (!image) {
free(index);
free(bounds);
free(cover_swap_buffer);
return NULL;
}
agg::int8u *pixels = image->pixels;
memset(pixels, 0x00, pixels_width * pixels_height * subpixel_scale * pixel_size);
agg::rendering_buffer ren_buf(pixels, pixels_width * subpixel_scale, pixels_height, -pixels_width * subpixel_scale * pixel_size);
// The variable y_bottom will be used to go down to the next row by taking into
// account the space occupied by each glyph of the current row along the y direction.
x = x_start;
// Set y to the image's height minus one to begin writing glyphs in the upper part of the image.
y = pixels_height - 1;
y_bottom = y;
for (int i = 0; i < num_chars; i++) {
// Important: the variable x in this loop should always be an integer multiple
// of subpixel_scale.
int codepoint = first_char + index[i];
const agg::rect_i& gbounds = bounds[index[i]];
if (gbounds.x2 < gbounds.x1) continue;
// 3. Ensure x's increment is aligned with (1)
// Note that x_step below is always an integer multiple of subpixel_scale.
// We need 3 * subpixel_scale because:
// . +1 pixel on the left, because of RGB color filter
// . +1 pixel on the right, because of RGB color filter
// . +1 pixel on the right, because of subpixel positioning
// and each pixel requires "subpixel_scale" sub-pixels.
const int x_step = gbounds.x2 + 3 * subpixel_scale;
if (x + x_step >= pixels_width * subpixel_scale) {
// No more space along x, begin writing the row below.
x = x_start;
y = y_bottom;
}
const int y_baseline = y - pad_y - gbounds.y2;
// 6. Ensure the y's increment below is aligned with the increment used in (5)
const int glyph_y_bottom = y - 2 * pad_y - (gbounds.y2 - gbounds.y1);
y_bottom = (y_bottom > glyph_y_bottom ? glyph_y_bottom : y_bottom);
double x_next = x, y_next = y_baseline;
renderer_alpha.render_codepoint(ren_buf, text_color, x_next, y_next, codepoint, subpixel_scale);
// The y coordinate for the glyph below is positive in the bottom direction,
// like is used by Lite's drawing system.
FR_Bitmap_Glyph_Metrics& glyph_info = glyphs[index[i]];
glyph_info.x0 = x / subpixel_scale;
glyph_info.y0 = pixels_height - 1 - (y_baseline + gbounds.y2 + pad_y);
glyph_info.x1 = div_pos(x_next + 0.5, subpixel_scale);
glyph_info.y1 = pixels_height - 1 - (y_baseline + gbounds.y1 - pad_y);
glyph_info.xoff = 0;
glyph_info.yoff = -pad_y - gbounds.y2 + ascender_px;
// Note that below the xadvance is in pixels times the subpixel_scale.
// This is meant for subpixel positioning.
glyph_info.xadvance = roundf(x_next - x);
if (subpixel_scale != 1 && glyph_info.x1 > glyph_info.x0) {
glyph_lut_convolution(ren_buf, lcd_lut, cover_swap_buffer, glyph_info);
}
glyph_trim_rect(ren_buf, glyph_info, subpixel_scale);
// When subpixel is activated we need one padding pixel on the left and on the right
// and one more because of subpixel positioning.
// 4. Ensure x's increment is aligned with (1)
x = x + x_step;
}
free(index);
free(bounds);
free(cover_swap_buffer);
return image;
}
template <typename Order>
void blend_solid_hspan(agg::rendering_buffer& rbuf, int x, int y, unsigned len,
const agg::rgba8& c, const agg::int8u* covers)
{
const int pixel_size = 4;
agg::int8u* p = rbuf.row_ptr(y) + x * pixel_size;
do
{
const unsigned alpha = *covers;
const unsigned r = p[Order::R], g = p[Order::G], b = p[Order::B];
p[Order::R] = (((unsigned(c.r) - r) * alpha) >> 8) + r;
p[Order::G] = (((unsigned(c.g) - g) * alpha) >> 8) + g;
p[Order::B] = (((unsigned(c.b) - b) * alpha) >> 8) + b;
// Leave p[3], the alpha channel value unmodified.
p += 4;
++covers;
}
while(--len);
}
template <typename Order>
void blend_solid_hspan_subpixel(agg::rendering_buffer& rbuf, agg::lcd_distribution_lut& lcd_lut,
const int x, const int y, unsigned len,
const agg::rgba8& c,
const agg::int8u* covers)
{
const int pixel_size = 4;
const unsigned rgb[3] = { c.r, c.g, c.b };
agg::int8u* p = rbuf.row_ptr(y) + x * pixel_size;
// Indexes to adress RGB colors in a BGRA32 format.
const int pixel_index[3] = {Order::R, Order::G, Order::B};
for (unsigned cx = 0; cx < len; cx += 3)
{
for (int i = 0; i < 3; i++) {
const unsigned cover_value = covers[cx + i];
const unsigned alpha = (cover_value + 1) * (c.a + 1);
const unsigned src_col = *(p + pixel_index[i]);
*(p + pixel_index[i]) = (((rgb[i] - src_col) * alpha) + (src_col << 16)) >> 16;
}
// Leave p[3], the alpha channel value unmodified.
p += 4;
}
}
// destination implicitly BGRA32. Source implictly single-byte renderer_alpha coverage with subpixel scale = 3.
// FIXME: consider using something like RenColor* instead of uint8_t * for dst.
void FR_Blend_Glyph(FR_Renderer *font_renderer, FR_Clip_Area *clip, int x_mult, int y, uint8_t *dst, int dst_width, const FR_Bitmap *glyphs_bitmap, const FR_Bitmap_Glyph_Metrics *glyph, FR_Color color) {
agg::lcd_distribution_lut& lcd_lut = font_renderer->lcd_distribution_lut();
const int subpixel_scale = font_renderer->subpixel_scale();
const int pixel_size = 4; // Pixel size for BGRA32 format.
int x = x_mult / subpixel_scale;
x += glyph->xoff;
y += glyph->yoff;
int glyph_x = glyph->x0, glyph_y = glyph->y0;
int glyph_x_subpixel = -(x_mult % subpixel_scale);
int glyph_width = glyph->x1 - glyph->x0;
int glyph_height = glyph->y1 - glyph->y0;
int n;
if ((n = clip->left - x) > 0) { glyph_width -= n; glyph_x += n; x += n; }
if ((n = clip->top - y) > 0) { glyph_height -= n; glyph_y += n; y += n; }
if ((n = x + glyph_width - clip->right ) > 0) { glyph_width -= n; }
if ((n = y + glyph_height - clip->bottom) > 0) { glyph_height -= n; }
if (glyph_width <= 0 || glyph_height <= 0) {
return;
}
dst += (x + y * dst_width) * pixel_size;
agg::rendering_buffer dst_ren_buf(dst, glyph_width, glyph_height, dst_width * pixel_size);
uint8_t *src = glyphs_bitmap->pixels + (glyph_x + glyph_y * glyphs_bitmap->width) * subpixel_scale + glyph_x_subpixel;
int src_stride = glyphs_bitmap->width * subpixel_scale;
const agg::rgba8 color_a(color.r, color.g, color.b);
for (int x = 0, y = 0; y < glyph_height; y++) {
agg::int8u *covers = src + y * src_stride;
if (subpixel_scale == 1) {
blend_solid_hspan<agg::order_bgra>(dst_ren_buf, x, y, glyph_width, color_a, covers);
} else {
blend_solid_hspan_subpixel<agg::order_bgra>(dst_ren_buf, lcd_lut, x, y, glyph_width * subpixel_scale, color_a, covers);
}
}
}

View File

@ -0,0 +1,58 @@
#ifndef FONT_RENDERER_H
#define FONT_RENDERER_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
unsigned short x0, y0, x1, y1;
float xoff, yoff, xadvance;
} FR_Bitmap_Glyph_Metrics;
typedef struct FR_Bitmap FR_Bitmap;
#ifdef __cplusplus
class FR_Renderer;
#else
struct FR_Renderer;
typedef struct FR_Renderer FR_Renderer;
#endif
enum {
FR_HINTING = 1 << 0,
FR_KERNING = 1 << 1,
FR_SUBPIXEL = 1 << 2,
FR_PRESCALE_X = 1 << 3,
};
typedef struct {
uint8_t r, g, b;
} FR_Color;
typedef struct {
int left, top, right, bottom;
} FR_Clip_Area;
FR_Renderer * FR_Renderer_New(unsigned int flags);
void FR_Renderer_Free(FR_Renderer *);
int FR_Load_Font(FR_Renderer *, const char *filename);
FR_Bitmap* FR_Bitmap_New(FR_Renderer *, int width, int height);
void FR_Bitmap_Free(FR_Bitmap *image);
int FR_Get_Font_Height(FR_Renderer *, float size);
FR_Bitmap * FR_Bake_Font_Bitmap(FR_Renderer *, int font_height,
int first_char, int num_chars, FR_Bitmap_Glyph_Metrics *glyph_info);
void FR_Blend_Glyph(FR_Renderer *font_renderer,
FR_Clip_Area *clip, int x, int y,
uint8_t *dst, int dst_width,
const FR_Bitmap *glyphs_bitmap,
const FR_Bitmap_Glyph_Metrics *glyph, FR_Color color);
int FR_Subpixel_Scale(FR_Renderer *);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,164 @@
#pragma once
#include "agg_basics.h"
#include "agg_conv_curve.h"
#include "agg_conv_transform.h"
#include "agg_gamma_lut.h"
#include "agg_font_freetype.h"
#include "agg_pixfmt_alpha8.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_renderer_primitives.h"
#include "agg_renderer_scanline.h"
#include "agg_rendering_buffer.h"
#include "agg_scanline_u.h"
class font_renderer_alpha
{
typedef agg::pixfmt_alpha8 pixfmt_type;
typedef agg::renderer_base<pixfmt_type> base_ren_type;
typedef agg::renderer_scanline_aa_solid<base_ren_type> renderer_solid;
typedef agg::font_engine_freetype_int32 font_engine_type;
typedef agg::font_cache_manager<font_engine_type> font_manager_type;
font_engine_type m_feng;
font_manager_type m_fman;
// Font rendering options.
bool m_hinting;
bool m_kerning;
bool m_subpixel;
bool m_prescale_x;
bool m_font_loaded;
// Pipeline to process the vectors glyph paths (curves + contour)
agg::trans_affine m_mtx;
agg::conv_curve<font_manager_type::path_adaptor_type> m_curves;
agg::conv_transform<agg::conv_curve<font_manager_type::path_adaptor_type> > m_trans;
public:
typedef agg::pixfmt_alpha8::color_type color_type;
font_renderer_alpha(bool hinting, bool kerning, bool subpixel, bool prescale_x):
m_feng(),
m_fman(m_feng),
m_hinting(hinting),
m_kerning(kerning),
m_subpixel(subpixel),
m_prescale_x(prescale_x),
m_font_loaded(false),
m_curves(m_fman.path_adaptor()),
m_trans(m_curves, m_mtx)
{ }
int get_face_height() const {
return m_feng.face_height();
}
void get_font_vmetrics(double& ascender, double& descender) {
double current_height = m_feng.height();
m_feng.height(1.0);
ascender = m_feng.ascender();
descender = m_feng.descender();
m_feng.height(current_height);
}
float scale_for_em_to_pixels(float size) {
int units_per_em = m_feng.face_units_em();
if (units_per_em > 0) {
return size / units_per_em;
}
return 0.0;
}
bool load_font(const char *font_filename) {
if(m_feng.load_font(font_filename, 0, agg::glyph_ren_outline)) {
m_font_loaded = true;
m_feng.hinting(m_hinting);
}
return m_font_loaded;
}
void set_font_height(double height) {
const double scale_x = (m_prescale_x ? 100.0 : 1.0);
m_feng.height(height);
m_feng.width(height * scale_x);
}
template<class Rasterizer, class Scanline, class RenSolid>
void draw_codepoint(Rasterizer& ras, Scanline& sl,
RenSolid& ren_solid, const color_type color,
int codepoint, double& x, double& y, const int subpixel_scale)
{
const double scale_x = (m_prescale_x ? 100.0 : 1.0);
// Coefficient to scale back the glyph to the final scale.
const double cx_inv_scale = subpixel_scale / scale_x;
// Represent the delta in x scaled by scale_x.
double x_delta = 0;
double start_x = x;
const agg::glyph_cache* glyph = m_fman.glyph(codepoint);
if(glyph)
{
if(m_kerning)
{
m_fman.add_kerning(&x_delta, &y);
}
m_fman.init_embedded_adaptors(glyph, 0, 0);
if(glyph->data_type == agg::glyph_data_outline)
{
double ty = m_hinting ? floor(y + 0.5) : y;
ras.reset();
m_mtx.reset();
m_mtx *= agg::trans_affine_scaling(cx_inv_scale, 1);
m_mtx *= agg::trans_affine_translation(start_x + cx_inv_scale * x_delta, ty);
ras.add_path(m_trans);
ren_solid.color(color);
agg::render_scanlines(ras, sl, ren_solid);
}
y += glyph->advance_y;
x += cx_inv_scale * (x_delta + glyph->advance_x);
}
}
void clear(agg::rendering_buffer& ren_buf, const color_type color) {
pixfmt_type pf(ren_buf);
base_ren_type ren_base(pf);
ren_base.clear(color);
}
void render_codepoint(agg::rendering_buffer& ren_buf,
const color_type text_color,
double& x, double& y,
int codepoint, const int subpixel_scale)
{
if (!m_font_loaded) {
return;
}
agg::scanline_u8 sl;
agg::rasterizer_scanline_aa<> ras;
ras.clip_box(0, 0, ren_buf.width(), ren_buf.height());
agg::pixfmt_alpha8 pf(ren_buf);
base_ren_type ren_base(pf);
renderer_solid ren_solid(ren_base);
draw_codepoint(ras, sl, ren_solid, text_color, codepoint, x, y, subpixel_scale);
}
int codepoint_bounds(int codepoint, const int subpixel_scale, agg::rect_i& bounds)
{
if (!m_font_loaded) return 1;
const double scale_x = (m_prescale_x ? 100.0 : 1.0);
const double cx_inv_scale = subpixel_scale / scale_x;
const agg::glyph_cache* glyph = m_fman.glyph(codepoint);
if (glyph) {
bounds = glyph->bounds;
bounds.x1 *= cx_inv_scale;
bounds.x2 *= cx_inv_scale;
return 0;
}
return 1;
}
};

View File

@ -0,0 +1,23 @@
freetype_dep = dependency('freetype2')
libagg_dep = dependency('libagg', required: false)
if not libagg_dep.found()
libagg_subproject = subproject('libagg')
libagg_dep = libagg_subproject.get_variable('libagg_dep')
endif
font_renderer_sources = [
'agg_font_freetype.cpp',
'font_renderer.cpp',
]
font_renderer_cdefs = ['-DFONT_RENDERER_HEIGHT_HACK']
font_renderer_include = include_directories('.')
libfontrenderer = static_library('fontrenderer',
font_renderer_sources,
dependencies: [libagg_dep, freetype_dep],
cpp_args: font_renderer_cdefs,
)

View File

@ -0,0 +1,125 @@
```c
stbtt_InitFont
stbtt_ScaleForMappingEmToPixels x 3
stbtt_ScaleForPixelHeight
stbtt_BakeFontBitmap
stbtt_GetFontVMetrics x 2
typedef struct {
unsigned short x0, y0, x1, y1; // coordinates of bbox in bitmap
float xoff, yoff, xadvance;
} stbtt_bakedchar;
struct RenImage {
RenColor *pixels;
int width, height;
};
typedef struct {
RenImage *image;
stbtt_bakedchar glyphs[256];
} GlyphSet;
struct RenFont {
void *data;
stbtt_fontinfo stbfont;
GlyphSet *sets[MAX_GLYPHSET];
float size;
int height;
};
```
The function stbtt_BakeFontBitmap is used to write bitmap data into set->image->pixels (where set is a GlyphSet).
Note that set->image->pixels need data in RGB format. After stbtt_BakeFontBitmap call the bitmap data are converted into RGB.
With a single call many glyphs corresponding to a range of codepoints, all in a
single image.
## STB truetype font metrics
stbtt_ScaleForPixelHeight takes a float 'height' and returns height / (ascent - descent).
stbtt_ScaleForMappingEmToPixels take a float 'pixels' and returns pixels / unitsPerEm.
### Computing RenFont
When loading a font, in renderer.c, the font->height is determined as:
```c
int ascent, descent, linegap;
stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap);
float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, font->size);
font->height = (ascent - descent + linegap) * scale + 0.5;
```
so, mathematically
```c
font->height = (ascent - descent + linegap) * font->size / unitsPerEm + 0.5;
```
**TO DO**: find out for what font->height is actually used.
### Call to BakeFontBitmap
In the same file, renderer.c, to create the glyphset image it computes:
```c
// Using stbtt functions
float s = ScaleForMappingEmToPixels(1) / ScaleForPixelHeight(1);
```
so 's' is actually equal to (ascent - descent) / unitsPerEm.
Then BakeFontBitmap is called and `font->size * s` is used for the pixel_height argument.
So BakeFontBitmap gets
```c
pixel_height = (ascent - descent) * font->size / unitsPerEm;
```
In turns BakeFontBitmap passes pixel_height to ScaleForPixelHeight() and uses the
resulting 'scale' to scale the glyphs by passing it to MakeGlyphBitmap().
This is equal almost equal to font->height except the 0.5, the missing linegap calculation
and the fact that this latter is an integer instead of a float.
## AGG Font Engine
Calls
`FT_Init_FreeType` (initialize the library)
In `load_font()` method:
`FT_New_Face` or `FT_New_Memory_Face` (use `FT_Done_Face` when done)
`FT_Attach_File`
`FT_Select_Charmap`
In `update_char_size()` method:
`FT_Set_Char_Size` or `FT_Set_Pixel_Sizes`
In `prepare_glyph()` method:
`FT_Get_Char_Index`
`FT_Load_Glyph`
`FT_Render_Glyph` (if glyph_render_native_mono or native_gray8)
in `add_kerning()` method
`FT_Get_Kerning`
`FT_Done_FreeType` (end with library)
## Freetype2's metrics related function and structs
`FT_New_Face` return a `FT_Face` (a pointer) with font face information.
## AGG font engine's font size
The variable `m_height` is the size of the font muliplied by 64.
It will be used to set font size with:
- `FT_Set_Char_Size` if m_resolution is set (> 0)
- `FT_Set_Pixel_Sizes`, divided by 64, if m_resolution is not set (= 0)
The method height() returns m_height / 64;

View File

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

View File

@ -1,114 +1,74 @@
project('lite-xl', project('lite-xl', 'c', 'cpp', default_options : ['c_std=gnu11', 'cpp_std=c++03'])
['c'],
version : '2.0.3',
license : 'MIT',
meson_version : '>= 0.54',
default_options : ['c_std=gnu11']
)
#=============================================================================== version = get_option('version')
# Configuration
#===============================================================================
conf_data = configuration_data() conf_data = configuration_data()
conf_data.set('PROJECT_BUILD_DIR', meson.current_build_dir()) conf_data.set('PROJECT_VERSION', version)
conf_data.set('PROJECT_SOURCE_DIR', meson.current_source_dir())
conf_data.set('PROJECT_VERSION', meson.project_version())
#===============================================================================
# Compiler Settings
#===============================================================================
if host_machine.system() == 'darwin' if host_machine.system() == 'darwin'
add_languages('objc') add_languages('objc')
endif endif
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
libm = cc.find_library('m', required : false)
libdl = cc.find_library('dl', required : false)
libx11 = dependency('x11', required : false)
lua_dep = dependency('lua5.2', required : false)
pcre2_dep = dependency('libpcre2-8')
sdl_dep = dependency('sdl2', method: 'config-tool')
if not lua_dep.found()
lua_subproject = subproject('lua', default_options: ['shared=false', 'use_readline=false', 'app=false'])
lua_dep = lua_subproject.get_variable('lua_dep')
endif
reproc_subproject = subproject('reproc', default_options: ['default_library=static', 'multithreaded=false', 'reproc-cpp=false', 'examples=false'])
reproc_dep = reproc_subproject.get_variable('reproc_dep')
lite_deps = [lua_dep, sdl_dep, reproc_dep, pcre2_dep, libm, libdl, libx11]
if host_machine.system() == 'windows'
# Note that we need to explicitly add the windows socket DLL because
# the pkg-config file from reproc does not include it.
lite_deps += meson.get_compiler('cpp').find_library('ws2_32', required: true)
endif
lite_includes = []
lite_cargs = [] lite_cargs = []
if get_option('portable')
lite_docdir = 'doc'
lite_datadir = 'data'
else
lite_docdir = 'share/doc/lite-xl'
lite_datadir = 'share/lite-xl'
endif
lite_include = include_directories('src')
foreach data_module : ['core', 'fonts', 'plugins', 'colors']
install_subdir('data' / data_module , install_dir : lite_datadir)
endforeach
install_data('licenses/licenses.md', install_dir : lite_docdir)
lite_link_args = []
if cc.get_id() == 'gcc' and get_option('buildtype') == 'release'
lite_link_args += ['-static-libgcc', '-static-libstdc++']
endif
if host_machine.system() == 'darwin'
lite_link_args += ['-framework', 'CoreServices', '-framework', 'Foundation']
endif
lite_rc = []
if host_machine.system() == 'windows'
windows = import('windows')
lite_rc += windows.compile_resources('resources/icons/icon.rc')
iss = configure_file(input : 'scripts/innosetup/innosetup.iss.in',
output : 'innosetup.iss',
configuration : conf_data)
endif
# On macos we need to use the SDL renderer to support retina displays # On macos we need to use the SDL renderer to support retina displays
if get_option('renderer') or host_machine.system() == 'darwin' if get_option('renderer') or host_machine.system() == 'darwin'
lite_cargs += '-DLITE_USE_SDL_RENDERER' lite_cargs += '-DLITE_USE_SDL_RENDERER'
endif endif
#===============================================================================
# Linker Settings
#===============================================================================
lite_link_args = []
if cc.get_id() == 'gcc' and get_option('buildtype') == 'release'
lite_link_args += ['-static-libgcc']
endif
if host_machine.system() == 'darwin' subdir('lib/font_renderer')
lite_link_args += ['-framework', 'CoreServices', '-framework', 'Foundation'] subdir('src')
endif
#===============================================================================
# Dependencies
#===============================================================================
if not get_option('source-only')
libm = cc.find_library('m', required : false)
libdl = cc.find_library('dl', required : false)
threads_dep = dependency('threads')
lua_dep = dependency('lua5.2', fallback: ['lua', 'lua_dep'],
default_options: ['shared=false', 'use_readline=false', 'app=false']
)
pcre2_dep = dependency('libpcre2-8')
freetype_dep = dependency('freetype2')
sdl_dep = dependency('sdl2', method: 'config-tool')
lite_deps = [lua_dep, sdl_dep, freetype_dep, pcre2_dep, libm, libdl, threads_dep]
endif
#===============================================================================
# Install Configuration
#===============================================================================
if get_option('portable') or host_machine.system() == 'windows'
lite_bindir = '/'
lite_docdir = '/doc'
lite_datadir = '/data'
elif get_option('bundle') and host_machine.system() == 'darwin'
lite_cargs += '-DMACOS_USE_BUNDLE'
lite_bindir = 'Contents/MacOS'
lite_docdir = 'Contents/Resources'
lite_datadir = 'Contents/Resources'
install_data('resources/icons/icon.icns', install_dir : 'Contents/Resources')
configure_file(
input : 'resources/macos/Info.plist.in',
output : 'Info.plist',
configuration : conf_data,
install : true,
install_dir : 'Contents'
)
else
lite_bindir = 'bin'
lite_docdir = 'share/doc/lite-xl'
lite_datadir = 'share/lite-xl'
if host_machine.system() == 'linux'
install_data('resources/icons/lite-xl.svg',
install_dir : 'share/icons/hicolor/scalable/apps'
)
install_data('resources/linux/org.lite_xl.lite_xl.desktop',
install_dir : 'share/applications'
)
install_data('resources/linux/org.lite_xl.lite_xl.appdata.xml',
install_dir : 'share/metainfo'
)
endif
endif
install_data('licenses/licenses.md', install_dir : lite_docdir)
install_subdir('data' / 'core' , install_dir : lite_datadir, exclude_files : 'start.lua')
foreach data_module : ['fonts', 'plugins', 'colors']
install_subdir('data' / data_module , install_dir : lite_datadir)
endforeach
configure_file(
input : 'data/core/start.lua',
output : 'start.lua',
configuration : conf_data,
install : true,
install_dir : lite_datadir / 'core',
)
if not get_option('source-only')
subdir('lib/dmon')
subdir('src')
subdir('scripts')
endif

View File

@ -1,4 +1,4 @@
option('bundle', type : 'boolean', value : false, description: 'Build a macOS bundle') option('innosetup', type : 'boolean', value : false, description: 'Build Windows setup package')
option('source-only', type : 'boolean', value : false, description: 'Configure source files only, doesn\'t checks for dependencies')
option('portable', type : 'boolean', value : false, description: 'Portable install') option('portable', type : 'boolean', value : false, description: 'Portable install')
option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer') option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer')
option('version', type : 'string', value : '0.0.0', description: 'Project version')

Binary file not shown.

View File

@ -5,6 +5,6 @@ Comment=A lightweight text editor written in Lua
Exec=lite-xl %F Exec=lite-xl %F
Icon=lite-xl Icon=lite-xl
Terminal=false Terminal=false
StartupWMClass=lite-xl StartupNotify=false
Categories=Development;IDE; Categories=Utility;TextEditor;Development;
MimeType=text/plain; MimeType=text/plain;

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<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" />
<description>
<p>
Lite XL is a text editor and development tool written mainly in Lua,
on top of a minimalistic C core using the SDL2 graphics library.
</p>
</description>
<screenshots>
<screenshot type="default">
<caption>The editor window</caption>
<image>https://lite-xl.github.io/assets/img/screenshots/editor.png</image>
</screenshot>
</screenshots>
<url type="homepage">https://lite-xl.github.io</url>
<provides>
<binary>lite-xl</binary>
</provides>
<releases>
<release version="2.0.1" date="2021-08-28" />
</releases>
</component>

View File

@ -1,768 +0,0 @@
#ifndef LITE_XL_PLUGIN_API
#define LITE_XL_PLUGIN_API
/**
The lite_xl plugin API is quite simple. Any shared library can be a plugin file, so long
as it has an entrypoint that looks like the following, where xxxxx is the plugin name:
#include "lite_xl_plugin_api.h"
int lua_open_lite_xl_xxxxx(lua_State* L, void* XL) {
lite_xl_plugin_init(XL);
...
return 1;
}
In linux, to compile this file, you'd do: 'gcc -o xxxxx.so -shared xxxxx.c'. Simple!
Due to the way the API is structured, you *should not* link or include lua libraries.
This file was automatically generated. DO NOT MODIFY DIRECTLY.
**/
#include <stdarg.h>
#include <stdio.h> // for BUFSIZ? this is kinda weird
/** luaconf.h **/
#ifndef lconfig_h
#define lconfig_h
#include <limits.h>
#include <stddef.h>
#if !defined(LUA_ANSI) && defined(__STRICT_ANSI__)
#define LUA_ANSI
#endif
#if !defined(LUA_ANSI) && defined(_WIN32) && !defined(_WIN32_WCE)
#define LUA_WIN
#endif
#if defined(LUA_WIN)
#define LUA_DL_DLL
#define LUA_USE_AFORMAT
#endif
#if defined(LUA_USE_LINUX)
#define LUA_USE_POSIX
#define LUA_USE_DLOPEN
#define LUA_USE_READLINE
#define LUA_USE_STRTODHEX
#define LUA_USE_AFORMAT
#define LUA_USE_LONGLONG
#endif
#if defined(LUA_USE_MACOSX)
#define LUA_USE_POSIX
#define LUA_USE_DLOPEN
#define LUA_USE_READLINE
#define LUA_USE_STRTODHEX
#define LUA_USE_AFORMAT
#define LUA_USE_LONGLONG
#endif
#if defined(LUA_USE_POSIX)
#define LUA_USE_MKSTEMP
#define LUA_USE_ISATTY
#define LUA_USE_POPEN
#define LUA_USE_ULONGJMP
#define LUA_USE_GMTIME_R
#endif
#if defined(_WIN32)
#define LUA_LDIR "!\\lua\\"
#define LUA_CDIR "!\\"
#define LUA_PATH_DEFAULT LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" ".\\?.lua"
#define LUA_CPATH_DEFAULT LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll;" ".\\?.dll"
#else
#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR "/"
#define LUA_ROOT "/usr/local/"
#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR
#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR
#define LUA_PATH_DEFAULT LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" "./?.lua"
#define LUA_CPATH_DEFAULT LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so"
#endif
#if defined(_WIN32)
#define LUA_DIRSEP "\\"
#else
#define LUA_DIRSEP "/"
#endif
#define LUA_ENV "_ENV"
#if defined(LUA_BUILD_AS_DLL)
#if defined(LUA_CORE) || defined(LUA_LIB)
#define LUA_API __declspec(dllexport)
#else
#define LUA_API __declspec(dllimport)
#endif
#else
#define LUA_API extern
#endif
#define LUALIB_API LUA_API
#define LUAMOD_API LUALIB_API
#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && defined(__ELF__)
#define LUAI_FUNC __attribute__((visibility("hidden"))) extern
#define LUAI_DDEC LUAI_FUNC
#define LUAI_DDEF
#else
#define LUAI_FUNC extern
#define LUAI_DDEC extern
#define LUAI_DDEF
#endif
#define LUA_QL(x) "'" x "'"
#define LUA_QS LUA_QL("%s")
#define LUA_IDSIZE 60
#if defined(LUA_LIB) || defined(lua_c)
#include <stdio.h>
#define luai_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
#define luai_writeline() (luai_writestring("\n", 1), fflush(stdout))
#endif
#define luai_writestringerror(s,p) (fprintf(stderr, (s), (p)), fflush(stderr))
#define LUAI_MAXSHORTLEN 40
#if defined(LUA_COMPAT_ALL)
#define LUA_COMPAT_UNPACK
#define LUA_COMPAT_LOADERS
#define lua_cpcall(L,f,u) (lua_pushcfunction(L, (f)), lua_pushlightuserdata(L,(u)), lua_pcall(L,1,0,0))
#define LUA_COMPAT_LOG10
#define LUA_COMPAT_LOADSTRING
#define LUA_COMPAT_MAXN
#define lua_strlen(L,i) lua_rawlen(L, (i))
#define lua_objlen(L,i) lua_rawlen(L, (i))
#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT)
#define LUA_COMPAT_MODULE
#endif
#if INT_MAX-20 < 32760
#define LUAI_BITSINT 16
#elif INT_MAX > 2147483640L
#define LUAI_BITSINT 32
#else
#error "you must define LUA_BITSINT with number of bits in an integer"
#endif
#if LUAI_BITSINT >= 32
#define LUA_INT32 int
#define LUAI_UMEM size_t
#define LUAI_MEM ptrdiff_t
#else
#define LUA_INT32 long
#define LUAI_UMEM unsigned long
#define LUAI_MEM long
#endif
#if LUAI_BITSINT >= 32
#define LUAI_MAXSTACK 1000000
#else
#define LUAI_MAXSTACK 15000
#endif
#define LUAI_FIRSTPSEUDOIDX (-LUAI_MAXSTACK - 1000)
#define LUAL_BUFFERSIZE BUFSIZ
#define LUA_NUMBER_DOUBLE
#define LUA_NUMBER double
#define LUAI_UACNUMBER double
#define LUA_NUMBER_SCAN "%lf"
#define LUA_NUMBER_FMT "%.14g"
#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n))
#define LUAI_MAXNUMBER2STR 32
#define l_mathop(x) (x)
#define lua_str2number(s,p) strtod((s), (p))
#if defined(LUA_USE_STRTODHEX)
#define lua_strx2number(s,p) strtod((s), (p))
#endif
#if defined(lobject_c) || defined(lvm_c)
#include <math.h>
#define luai_nummod(L,a,b) ((a) - l_mathop(floor)((a)/(b))*(b))
#define luai_numpow(L,a,b) (l_mathop(pow)(a,b))
#endif
#if defined(LUA_CORE)
#define luai_numadd(L,a,b) ((a)+(b))
#define luai_numsub(L,a,b) ((a)-(b))
#define luai_nummul(L,a,b) ((a)*(b))
#define luai_numdiv(L,a,b) ((a)/(b))
#define luai_numunm(L,a) (-(a))
#define luai_numeq(a,b) ((a)==(b))
#define luai_numlt(L,a,b) ((a)<(b))
#define luai_numle(L,a,b) ((a)<=(b))
#define luai_numisnan(L,a) (!luai_numeq((a), (a)))
#endif
#define LUA_INTEGER ptrdiff_t
#define LUA_UNSIGNED unsigned LUA_INT32
#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI)
#if defined(LUA_WIN) && defined(_MSC_VER) && defined(_M_IX86)
#define LUA_MSASMTRICK
#define LUA_IEEEENDIAN 0
#define LUA_NANTRICK
#elif defined(__i386__) || defined(__i386) || defined(__X86__)
#define LUA_IEEE754TRICK
#define LUA_IEEELL
#define LUA_IEEEENDIAN 0
#define LUA_NANTRICK
#elif defined(__x86_64)
#define LUA_IEEE754TRICK
#define LUA_IEEEENDIAN 0
#elif defined(__POWERPC__) || defined(__ppc__)
#define LUA_IEEE754TRICK
#define LUA_IEEEENDIAN 1
#else
#define LUA_IEEE754TRICK
#endif
#endif
#endif
/** lua.h **/
typedef struct lua_State lua_State;
typedef int (*lua_CFunction) (lua_State *L);
typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
typedef LUA_NUMBER lua_Number;
typedef LUA_INTEGER lua_Integer;
typedef LUA_UNSIGNED lua_Unsigned;
typedef struct lua_Debug lua_Debug;
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
struct lua_Debug {
int event;
const char *name;
const char *namewhat;
const char *what;
const char *source;
int currentline;
int linedefined;
int lastlinedefined;
unsigned char nups;
unsigned char nparams;
char isvararg;
char istailcall;
char short_src[LUA_IDSIZE];
struct CallInfo *i_ci;
};
static lua_State *(*lua_newstate) (lua_Alloc f, void *ud);
static void (*lua_close) (lua_State *L);
static lua_State *(*lua_newthread) (lua_State *L);
static lua_CFunction (*lua_atpanic) (lua_State *L, lua_CFunction panicf);
static const lua_Number *(*lua_version) (lua_State *L);
static int (*lua_absindex) (lua_State *L, int idx);
static int (*lua_gettop) (lua_State *L);
static void (*lua_settop) (lua_State *L, int idx);
static void (*lua_pushvalue) (lua_State *L, int idx);
static void (*lua_remove) (lua_State *L, int idx);
static void (*lua_insert) (lua_State *L, int idx);
static void (*lua_replace) (lua_State *L, int idx);
static void (*lua_copy) (lua_State *L, int fromidx, int toidx);
static int (*lua_checkstack) (lua_State *L, int sz);
static void (*lua_xmove) (lua_State *from, lua_State *to, int n);
static int (*lua_isnumber) (lua_State *L, int idx);
static int (*lua_isstring) (lua_State *L, int idx);
static int (*lua_iscfunction) (lua_State *L, int idx);
static int (*lua_isuserdata) (lua_State *L, int idx);
static int (*lua_type) (lua_State *L, int idx);
static const char *(*lua_typename) (lua_State *L, int tp);
static lua_Number (*lua_tonumberx) (lua_State *L, int idx, int *isnum);
static lua_Integer (*lua_tointegerx) (lua_State *L, int idx, int *isnum);
static lua_Unsigned (*lua_tounsignedx) (lua_State *L, int idx, int *isnum);
static int (*lua_toboolean) (lua_State *L, int idx);
static const char *(*lua_tolstring) (lua_State *L, int idx, size_t *len);
static size_t (*lua_rawlen) (lua_State *L, int idx);
static lua_CFunction (*lua_tocfunction) (lua_State *L, int idx);
static void *(*lua_touserdata) (lua_State *L, int idx);
static lua_State *(*lua_tothread) (lua_State *L, int idx);
static const void *(*lua_topointer) (lua_State *L, int idx);
static void (*lua_arith) (lua_State *L, int op);
static int (*lua_rawequal) (lua_State *L, int idx1, int idx2);
static int (*lua_compare) (lua_State *L, int idx1, int idx2, int op);
static void (*lua_pushnil) (lua_State *L);
static void (*lua_pushnumber) (lua_State *L, lua_Number n);
static void (*lua_pushinteger) (lua_State *L, lua_Integer n);
static void (*lua_pushunsigned) (lua_State *L, lua_Unsigned n);
static const char *(*lua_pushlstring) (lua_State *L, const char *s, size_t l);
static const char *(*lua_pushstring) (lua_State *L, const char *s);
static const char *(*lua_pushvfstring) (lua_State *L, const char *fmt, va_list argp);
static const char *(*lua_pushfstring) (lua_State *L, const char *fmt, ...);
static void (*lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
static void (*lua_pushboolean) (lua_State *L, int b);
static void (*lua_pushlightuserdata) (lua_State *L, void *p);
static int (*lua_pushthread) (lua_State *L);
static void (*lua_getglobal) (lua_State *L, const char *var);
static void (*lua_gettable) (lua_State *L, int idx);
static void (*lua_getfield) (lua_State *L, int idx, const char *k);
static void (*lua_rawget) (lua_State *L, int idx);
static void (*lua_rawgeti) (lua_State *L, int idx, int n);
static void (*lua_rawgetp) (lua_State *L, int idx, const void *p);
static void (*lua_createtable) (lua_State *L, int narr, int nrec);
static void *(*lua_newuserdata) (lua_State *L, size_t sz);
static int (*lua_getmetatable) (lua_State *L, int objindex);
static void (*lua_getuservalue) (lua_State *L, int idx);
static void (*lua_setglobal) (lua_State *L, const char *var);
static void (*lua_settable) (lua_State *L, int idx);
static void (*lua_setfield) (lua_State *L, int idx, const char *k);
static void (*lua_rawset) (lua_State *L, int idx);
static void (*lua_rawseti) (lua_State *L, int idx, int n);
static void (*lua_rawsetp) (lua_State *L, int idx, const void *p);
static int (*lua_setmetatable) (lua_State *L, int objindex);
static void (*lua_setuservalue) (lua_State *L, int idx);
static void (*lua_callk) (lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k);
static int (*lua_getctx) (lua_State *L, int *ctx);
static int (*lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k);
static int (*lua_load) (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode);
static int (*lua_dump) (lua_State *L, lua_Writer writer, void *data);
static int (*lua_yieldk) (lua_State *L, int nresults, int ctx, lua_CFunction k);
static int (*lua_resume) (lua_State *L, lua_State *from, int narg);
static int (*lua_status) (lua_State *L);
static int (*lua_gc) (lua_State *L, int what, int data);
static int (*lua_error) (lua_State *L);
static int (*lua_next) (lua_State *L, int idx);
static void (*lua_concat) (lua_State *L, int n);
static void (*lua_len) (lua_State *L, int idx);
static lua_Alloc (*lua_getallocf) (lua_State *L, void **ud);
static void (*lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
static int (*lua_getstack) (lua_State *L, int level, lua_Debug *ar);
static int (*lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
static const char *(*lua_getlocal) (lua_State *L, const lua_Debug *ar, int n);
static const char *(*lua_setlocal) (lua_State *L, const lua_Debug *ar, int n);
static const char *(*lua_getupvalue) (lua_State *L, int funcindex, int n);
static const char *(*lua_setupvalue) (lua_State *L, int funcindex, int n);
static void *(*lua_upvalueid) (lua_State *L, int fidx, int n);
static void (*lua_upvaluejoin) (lua_State *L, int fidx1, int n1, int fidx2, int n2);
static int (*lua_sethook) (lua_State *L, lua_Hook func, int mask, int count);
static lua_Hook (*lua_gethook) (lua_State *L);
static int (*lua_gethookmask) (lua_State *L);
static int (*lua_gethookcount) (lua_State *L);
#define lua_h
#define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "2"
#define LUA_VERSION_NUM 502
#define LUA_VERSION_RELEASE "4"
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2015 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
#define LUA_SIGNATURE "\033Lua"
#define LUA_MULTRET (-1)
#define LUA_REGISTRYINDEX LUAI_FIRSTPSEUDOIDX
#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))
#define LUA_OK 0
#define LUA_YIELD 1
#define LUA_ERRRUN 2
#define LUA_ERRSYNTAX 3
#define LUA_ERRMEM 4
#define LUA_ERRGCMM 5
#define LUA_ERRERR 6
#define LUA_TNONE (-1)
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
#define LUA_NUMTAGS 9
#define LUA_MINSTACK 20
#define LUA_RIDX_MAINTHREAD 1
#define LUA_RIDX_GLOBALS 2
#define LUA_RIDX_LAST LUA_RIDX_GLOBALS
#define LUA_OPADD 0
#define LUA_OPSUB 1
#define LUA_OPMUL 2
#define LUA_OPDIV 3
#define LUA_OPMOD 4
#define LUA_OPPOW 5
#define LUA_OPUNM 6
#define LUA_OPEQ 0
#define LUA_OPLT 1
#define LUA_OPLE 2
#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL)
#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL)
#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL)
#define LUA_GCSTOP 0
#define LUA_GCRESTART 1
#define LUA_GCCOLLECT 2
#define LUA_GCCOUNT 3
#define LUA_GCCOUNTB 4
#define LUA_GCSTEP 5
#define LUA_GCSETPAUSE 6
#define LUA_GCSETSTEPMUL 7
#define LUA_GCSETMAJORINC 8
#define LUA_GCISRUNNING 9
#define LUA_GCGEN 10
#define LUA_GCINC 11
#define lua_tonumber(L,i) lua_tonumberx(L,i,NULL)
#define lua_tointeger(L,i) lua_tointegerx(L,i,NULL)
#define lua_tounsigned(L,i) lua_tounsignedx(L,i,NULL)
#define lua_pop(L,n) lua_settop(L, -(n)-1)
#define lua_newtable(L) lua_createtable(L, 0, 0)
#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
#define lua_pushliteral(L, s) lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)
#define lua_pushglobaltable(L) lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
#define LUA_HOOKCALL 0
#define LUA_HOOKRET 1
#define LUA_HOOKLINE 2
#define LUA_HOOKCOUNT 3
#define LUA_HOOKTAILCALL 4
#define LUA_MASKCALL (1 << LUA_HOOKCALL)
#define LUA_MASKRET (1 << LUA_HOOKRET)
#define LUA_MASKLINE (1 << LUA_HOOKLINE)
#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
static lua_State * __lite_xl_fallback_lua_newstate (lua_Alloc f, void *ud) { fputs("warning: lua_newstate is a stub", stderr); }
static void __lite_xl_fallback_lua_close (lua_State *L) { fputs("warning: lua_close is a stub", stderr); }
static lua_State * __lite_xl_fallback_lua_newthread (lua_State *L) { fputs("warning: lua_newthread is a stub", stderr); }
static lua_CFunction __lite_xl_fallback_lua_atpanic (lua_State *L, lua_CFunction panicf) { fputs("warning: lua_atpanic is a stub", stderr); }
static const lua_Number * __lite_xl_fallback_lua_version (lua_State *L) { fputs("warning: lua_version is a stub", stderr); }
static int __lite_xl_fallback_lua_absindex (lua_State *L, int idx) { fputs("warning: lua_absindex is a stub", stderr); }
static int __lite_xl_fallback_lua_gettop (lua_State *L) { fputs("warning: lua_gettop is a stub", stderr); }
static void __lite_xl_fallback_lua_settop (lua_State *L, int idx) { fputs("warning: lua_settop is a stub", stderr); }
static void __lite_xl_fallback_lua_pushvalue (lua_State *L, int idx) { fputs("warning: lua_pushvalue is a stub", stderr); }
static void __lite_xl_fallback_lua_remove (lua_State *L, int idx) { fputs("warning: lua_remove is a stub", stderr); }
static void __lite_xl_fallback_lua_insert (lua_State *L, int idx) { fputs("warning: lua_insert is a stub", stderr); }
static void __lite_xl_fallback_lua_replace (lua_State *L, int idx) { fputs("warning: lua_replace is a stub", stderr); }
static void __lite_xl_fallback_lua_copy (lua_State *L, int fromidx, int toidx) { fputs("warning: lua_copy is a stub", stderr); }
static int __lite_xl_fallback_lua_checkstack (lua_State *L, int sz) { fputs("warning: lua_checkstack is a stub", stderr); }
static void __lite_xl_fallback_lua_xmove (lua_State *from, lua_State *to, int n) { fputs("warning: lua_xmove is a stub", stderr); }
static int __lite_xl_fallback_lua_isnumber (lua_State *L, int idx) { fputs("warning: lua_isnumber is a stub", stderr); }
static int __lite_xl_fallback_lua_isstring (lua_State *L, int idx) { fputs("warning: lua_isstring is a stub", stderr); }
static int __lite_xl_fallback_lua_iscfunction (lua_State *L, int idx) { fputs("warning: lua_iscfunction is a stub", stderr); }
static int __lite_xl_fallback_lua_isuserdata (lua_State *L, int idx) { fputs("warning: lua_isuserdata is a stub", stderr); }
static int __lite_xl_fallback_lua_type (lua_State *L, int idx) { fputs("warning: lua_type is a stub", stderr); }
static const char * __lite_xl_fallback_lua_typename (lua_State *L, int tp) { fputs("warning: lua_typename is a stub", stderr); }
static lua_Number __lite_xl_fallback_lua_tonumberx (lua_State *L, int idx, int *isnum) { fputs("warning: lua_tonumberx is a stub", stderr); }
static lua_Integer __lite_xl_fallback_lua_tointegerx (lua_State *L, int idx, int *isnum) { fputs("warning: lua_tointegerx is a stub", stderr); }
static lua_Unsigned __lite_xl_fallback_lua_tounsignedx (lua_State *L, int idx, int *isnum) { fputs("warning: lua_tounsignedx is a stub", stderr); }
static int __lite_xl_fallback_lua_toboolean (lua_State *L, int idx) { fputs("warning: lua_toboolean is a stub", stderr); }
static const char * __lite_xl_fallback_lua_tolstring (lua_State *L, int idx, size_t *len) { fputs("warning: lua_tolstring is a stub", stderr); }
static size_t __lite_xl_fallback_lua_rawlen (lua_State *L, int idx) { fputs("warning: lua_rawlen is a stub", stderr); }
static lua_CFunction __lite_xl_fallback_lua_tocfunction (lua_State *L, int idx) { fputs("warning: lua_tocfunction is a stub", stderr); }
static void * __lite_xl_fallback_lua_touserdata (lua_State *L, int idx) { fputs("warning: lua_touserdata is a stub", stderr); }
static lua_State * __lite_xl_fallback_lua_tothread (lua_State *L, int idx) { fputs("warning: lua_tothread is a stub", stderr); }
static const void * __lite_xl_fallback_lua_topointer (lua_State *L, int idx) { fputs("warning: lua_topointer is a stub", stderr); }
static void __lite_xl_fallback_lua_arith (lua_State *L, int op) { fputs("warning: lua_arith is a stub", stderr); }
static int __lite_xl_fallback_lua_rawequal (lua_State *L, int idx1, int idx2) { fputs("warning: lua_rawequal is a stub", stderr); }
static int __lite_xl_fallback_lua_compare (lua_State *L, int idx1, int idx2, int op) { fputs("warning: lua_compare is a stub", stderr); }
static void __lite_xl_fallback_lua_pushnil (lua_State *L) { fputs("warning: lua_pushnil is a stub", stderr); }
static void __lite_xl_fallback_lua_pushnumber (lua_State *L, lua_Number n) { fputs("warning: lua_pushnumber is a stub", stderr); }
static void __lite_xl_fallback_lua_pushinteger (lua_State *L, lua_Integer n) { fputs("warning: lua_pushinteger is a stub", stderr); }
static void __lite_xl_fallback_lua_pushunsigned (lua_State *L, lua_Unsigned n) { fputs("warning: lua_pushunsigned is a stub", stderr); }
static const char * __lite_xl_fallback_lua_pushlstring (lua_State *L, const char *s, size_t l) { fputs("warning: lua_pushlstring is a stub", stderr); }
static const char * __lite_xl_fallback_lua_pushstring (lua_State *L, const char *s) { fputs("warning: lua_pushstring is a stub", stderr); }
static const char * __lite_xl_fallback_lua_pushvfstring (lua_State *L, const char *fmt, va_list argp) { fputs("warning: lua_pushvfstring is a stub", stderr); }
static const char * __lite_xl_fallback_lua_pushfstring (lua_State *L, const char *fmt, ...) { fputs("warning: lua_pushfstring is a stub", stderr); }
static void __lite_xl_fallback_lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { fputs("warning: lua_pushcclosure is a stub", stderr); }
static void __lite_xl_fallback_lua_pushboolean (lua_State *L, int b) { fputs("warning: lua_pushboolean is a stub", stderr); }
static void __lite_xl_fallback_lua_pushlightuserdata (lua_State *L, void *p) { fputs("warning: lua_pushlightuserdata is a stub", stderr); }
static int __lite_xl_fallback_lua_pushthread (lua_State *L) { fputs("warning: lua_pushthread is a stub", stderr); }
static void __lite_xl_fallback_lua_getglobal (lua_State *L, const char *var) { fputs("warning: lua_getglobal is a stub", stderr); }
static void __lite_xl_fallback_lua_gettable (lua_State *L, int idx) { fputs("warning: lua_gettable is a stub", stderr); }
static void __lite_xl_fallback_lua_getfield (lua_State *L, int idx, const char *k) { fputs("warning: lua_getfield is a stub", stderr); }
static void __lite_xl_fallback_lua_rawget (lua_State *L, int idx) { fputs("warning: lua_rawget is a stub", stderr); }
static void __lite_xl_fallback_lua_rawgeti (lua_State *L, int idx, int n) { fputs("warning: lua_rawgeti is a stub", stderr); }
static void __lite_xl_fallback_lua_rawgetp (lua_State *L, int idx, const void *p) { fputs("warning: lua_rawgetp is a stub", stderr); }
static void __lite_xl_fallback_lua_createtable (lua_State *L, int narr, int nrec) { fputs("warning: lua_createtable is a stub", stderr); }
static void * __lite_xl_fallback_lua_newuserdata (lua_State *L, size_t sz) { fputs("warning: lua_newuserdata is a stub", stderr); }
static int __lite_xl_fallback_lua_getmetatable (lua_State *L, int objindex) { fputs("warning: lua_getmetatable is a stub", stderr); }
static void __lite_xl_fallback_lua_getuservalue (lua_State *L, int idx) { fputs("warning: lua_getuservalue is a stub", stderr); }
static void __lite_xl_fallback_lua_setglobal (lua_State *L, const char *var) { fputs("warning: lua_setglobal is a stub", stderr); }
static void __lite_xl_fallback_lua_settable (lua_State *L, int idx) { fputs("warning: lua_settable is a stub", stderr); }
static void __lite_xl_fallback_lua_setfield (lua_State *L, int idx, const char *k) { fputs("warning: lua_setfield is a stub", stderr); }
static void __lite_xl_fallback_lua_rawset (lua_State *L, int idx) { fputs("warning: lua_rawset is a stub", stderr); }
static void __lite_xl_fallback_lua_rawseti (lua_State *L, int idx, int n) { fputs("warning: lua_rawseti is a stub", stderr); }
static void __lite_xl_fallback_lua_rawsetp (lua_State *L, int idx, const void *p) { fputs("warning: lua_rawsetp is a stub", stderr); }
static int __lite_xl_fallback_lua_setmetatable (lua_State *L, int objindex) { fputs("warning: lua_setmetatable is a stub", stderr); }
static void __lite_xl_fallback_lua_setuservalue (lua_State *L, int idx) { fputs("warning: lua_setuservalue is a stub", stderr); }
static void __lite_xl_fallback_lua_callk (lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k) { fputs("warning: lua_callk is a stub", stderr); }
static int __lite_xl_fallback_lua_getctx (lua_State *L, int *ctx) { fputs("warning: lua_getctx is a stub", stderr); }
static int __lite_xl_fallback_lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k) { fputs("warning: lua_pcallk is a stub", stderr); }
static int __lite_xl_fallback_lua_load (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode) { fputs("warning: lua_load is a stub", stderr); }
static int __lite_xl_fallback_lua_dump (lua_State *L, lua_Writer writer, void *data) { fputs("warning: lua_dump is a stub", stderr); }
static int __lite_xl_fallback_lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) { fputs("warning: lua_yieldk is a stub", stderr); }
static int __lite_xl_fallback_lua_resume (lua_State *L, lua_State *from, int narg) { fputs("warning: lua_resume is a stub", stderr); }
static int __lite_xl_fallback_lua_status (lua_State *L) { fputs("warning: lua_status is a stub", stderr); }
static int __lite_xl_fallback_lua_gc (lua_State *L, int what, int data) { fputs("warning: lua_gc is a stub", stderr); }
static int __lite_xl_fallback_lua_error (lua_State *L) { fputs("warning: lua_error is a stub", stderr); }
static int __lite_xl_fallback_lua_next (lua_State *L, int idx) { fputs("warning: lua_next is a stub", stderr); }
static void __lite_xl_fallback_lua_concat (lua_State *L, int n) { fputs("warning: lua_concat is a stub", stderr); }
static void __lite_xl_fallback_lua_len (lua_State *L, int idx) { fputs("warning: lua_len is a stub", stderr); }
static lua_Alloc __lite_xl_fallback_lua_getallocf (lua_State *L, void **ud) { fputs("warning: lua_getallocf is a stub", stderr); }
static void __lite_xl_fallback_lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { fputs("warning: lua_setallocf is a stub", stderr); }
static int __lite_xl_fallback_lua_getstack (lua_State *L, int level, lua_Debug *ar) { fputs("warning: lua_getstack is a stub", stderr); }
static int __lite_xl_fallback_lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { fputs("warning: lua_getinfo is a stub", stderr); }
static const char * __lite_xl_fallback_lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { fputs("warning: lua_getlocal is a stub", stderr); }
static const char * __lite_xl_fallback_lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { fputs("warning: lua_setlocal is a stub", stderr); }
static const char * __lite_xl_fallback_lua_getupvalue (lua_State *L, int funcindex, int n) { fputs("warning: lua_getupvalue is a stub", stderr); }
static const char * __lite_xl_fallback_lua_setupvalue (lua_State *L, int funcindex, int n) { fputs("warning: lua_setupvalue is a stub", stderr); }
static void * __lite_xl_fallback_lua_upvalueid (lua_State *L, int fidx, int n) { fputs("warning: lua_upvalueid is a stub", stderr); }
static void __lite_xl_fallback_lua_upvaluejoin (lua_State *L, int fidx1, int n1, int fidx2, int n2) { fputs("warning: lua_upvaluejoin is a stub", stderr); }
static int __lite_xl_fallback_lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { fputs("warning: lua_sethook is a stub", stderr); }
static lua_Hook __lite_xl_fallback_lua_gethook (lua_State *L) { fputs("warning: lua_gethook is a stub", stderr); }
static int __lite_xl_fallback_lua_gethookmask (lua_State *L) { fputs("warning: lua_gethookmask is a stub", stderr); }
static int __lite_xl_fallback_lua_gethookcount (lua_State *L) { fputs("warning: lua_gethookcount is a stub", stderr); }
/** lauxlib.h **/
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
typedef struct luaL_Buffer {
char *b;
size_t size;
size_t n;
lua_State *L;
char initb[LUAL_BUFFERSIZE];
} luaL_Buffer;
typedef struct luaL_Stream {
FILE *f;
lua_CFunction closef;
} luaL_Stream;
static void (*luaL_checkversion_) (lua_State *L, lua_Number ver);
static int (*luaL_getmetafield) (lua_State *L, int obj, const char *e);
static int (*luaL_callmeta) (lua_State *L, int obj, const char *e);
static const char *(*luaL_tolstring) (lua_State *L, int idx, size_t *len);
static int (*luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
static const char *(*luaL_checklstring) (lua_State *L, int numArg, size_t *l);
static const char *(*luaL_optlstring) (lua_State *L, int numArg, const char *def, size_t *l);
static lua_Number (*luaL_checknumber) (lua_State *L, int numArg);
static lua_Number (*luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
static lua_Integer (*luaL_checkinteger) (lua_State *L, int numArg);
static lua_Integer (*luaL_optinteger) (lua_State *L, int nArg, lua_Integer def);
static lua_Unsigned (*luaL_checkunsigned) (lua_State *L, int numArg);
static lua_Unsigned (*luaL_optunsigned) (lua_State *L, int numArg, lua_Unsigned def);
static void (*luaL_checkstack) (lua_State *L, int sz, const char *msg);
static void (*luaL_checktype) (lua_State *L, int narg, int t);
static void (*luaL_checkany) (lua_State *L, int narg);
static int (*luaL_newmetatable) (lua_State *L, const char *tname);
static void (*luaL_setmetatable) (lua_State *L, const char *tname);
static void *(*luaL_testudata) (lua_State *L, int ud, const char *tname);
static void *(*luaL_checkudata) (lua_State *L, int ud, const char *tname);
static void (*luaL_where) (lua_State *L, int lvl);
static int (*luaL_error) (lua_State *L, const char *fmt, ...);
static int (*luaL_checkoption) (lua_State *L, int narg, const char *def, const char *const lst[]);
static int (*luaL_fileresult) (lua_State *L, int stat, const char *fname);
static int (*luaL_execresult) (lua_State *L, int stat);
static int (*luaL_ref) (lua_State *L, int t);
static void (*luaL_unref) (lua_State *L, int t, int ref);
static int (*luaL_loadfilex) (lua_State *L, const char *filename, const char *mode);
static int (*luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode);
static int (*luaL_loadstring) (lua_State *L, const char *s);
static lua_State *(*luaL_newstate) (void);
static int (*luaL_len) (lua_State *L, int idx);
static const char *(*luaL_gsub) (lua_State *L, const char *s, const char *p, const char *r);
static void (*luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
static int (*luaL_getsubtable) (lua_State *L, int idx, const char *fname);
static void (*luaL_traceback) (lua_State *L, lua_State *L1, const char *msg, int level);
static void (*luaL_requiref) (lua_State *L, const char *modname, lua_CFunction openf, int glb);
static void (*luaL_buffinit) (lua_State *L, luaL_Buffer *B);
static char *(*luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
static void (*luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
static void (*luaL_addstring) (luaL_Buffer *B, const char *s);
static void (*luaL_addvalue) (luaL_Buffer *B);
static void (*luaL_pushresult) (luaL_Buffer *B);
static void (*luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
static char *(*luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
#define lauxlib_h
#define LUA_ERRFILE (LUA_ERRERR+1)
#define luaL_checkversion(L) luaL_checkversion_(L, LUA_VERSION_NUM)
#define LUA_NOREF (-2)
#define LUA_REFNIL (-1)
#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
#define luaL_newlibtable(L,l) lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
#define luaL_newlib(L,l) (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
#define luaL_argcheck(L, cond,numarg,extramsg) ((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
#define luaL_dofile(L, fn) (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_dostring(L, s) (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
#define luaL_addchar(B,c) ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), ((B)->b[(B)->n++] = (c)))
#define luaL_addsize(B,s) ((B)->n += (s))
#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
#define LUA_FILEHANDLE "FILE*"
static void __lite_xl_fallback_luaL_checkversion_ (lua_State *L, lua_Number ver) { fputs("warning: luaL_checkversion_ is a stub", stderr); }
static int __lite_xl_fallback_luaL_getmetafield (lua_State *L, int obj, const char *e) { fputs("warning: luaL_getmetafield is a stub", stderr); }
static int __lite_xl_fallback_luaL_callmeta (lua_State *L, int obj, const char *e) { fputs("warning: luaL_callmeta is a stub", stderr); }
static const char * __lite_xl_fallback_luaL_tolstring (lua_State *L, int idx, size_t *len) { fputs("warning: luaL_tolstring is a stub", stderr); }
static int __lite_xl_fallback_luaL_argerror (lua_State *L, int numarg, const char *extramsg) { fputs("warning: luaL_argerror is a stub", stderr); }
static const char * __lite_xl_fallback_luaL_checklstring (lua_State *L, int numArg, size_t *l) { fputs("warning: luaL_checklstring is a stub", stderr); }
static const char * __lite_xl_fallback_luaL_optlstring (lua_State *L, int numArg, const char *def, size_t *l) { fputs("warning: luaL_optlstring is a stub", stderr); }
static lua_Number __lite_xl_fallback_luaL_checknumber (lua_State *L, int numArg) { fputs("warning: luaL_checknumber is a stub", stderr); }
static lua_Number __lite_xl_fallback_luaL_optnumber (lua_State *L, int nArg, lua_Number def) { fputs("warning: luaL_optnumber is a stub", stderr); }
static lua_Integer __lite_xl_fallback_luaL_checkinteger (lua_State *L, int numArg) { fputs("warning: luaL_checkinteger is a stub", stderr); }
static lua_Integer __lite_xl_fallback_luaL_optinteger (lua_State *L, int nArg, lua_Integer def) { fputs("warning: luaL_optinteger is a stub", stderr); }
static lua_Unsigned __lite_xl_fallback_luaL_checkunsigned (lua_State *L, int numArg) { fputs("warning: luaL_checkunsigned is a stub", stderr); }
static lua_Unsigned __lite_xl_fallback_luaL_optunsigned (lua_State *L, int numArg, lua_Unsigned def) { fputs("warning: luaL_optunsigned is a stub", stderr); }
static void __lite_xl_fallback_luaL_checkstack (lua_State *L, int sz, const char *msg) { fputs("warning: luaL_checkstack is a stub", stderr); }
static void __lite_xl_fallback_luaL_checktype (lua_State *L, int narg, int t) { fputs("warning: luaL_checktype is a stub", stderr); }
static void __lite_xl_fallback_luaL_checkany (lua_State *L, int narg) { fputs("warning: luaL_checkany is a stub", stderr); }
static int __lite_xl_fallback_luaL_newmetatable (lua_State *L, const char *tname) { fputs("warning: luaL_newmetatable is a stub", stderr); }
static void __lite_xl_fallback_luaL_setmetatable (lua_State *L, const char *tname) { fputs("warning: luaL_setmetatable is a stub", stderr); }
static void * __lite_xl_fallback_luaL_testudata (lua_State *L, int ud, const char *tname) { fputs("warning: luaL_testudata is a stub", stderr); }
static void * __lite_xl_fallback_luaL_checkudata (lua_State *L, int ud, const char *tname) { fputs("warning: luaL_checkudata is a stub", stderr); }
static void __lite_xl_fallback_luaL_where (lua_State *L, int lvl) { fputs("warning: luaL_where is a stub", stderr); }
static int __lite_xl_fallback_luaL_error (lua_State *L, const char *fmt, ...) { fputs("warning: luaL_error is a stub", stderr); }
static int __lite_xl_fallback_luaL_checkoption (lua_State *L, int narg, const char *def, const char *const lst[]) { fputs("warning: luaL_checkoption is a stub", stderr); }
static int __lite_xl_fallback_luaL_fileresult (lua_State *L, int stat, const char *fname) { fputs("warning: luaL_fileresult is a stub", stderr); }
static int __lite_xl_fallback_luaL_execresult (lua_State *L, int stat) { fputs("warning: luaL_execresult is a stub", stderr); }
static int __lite_xl_fallback_luaL_ref (lua_State *L, int t) { fputs("warning: luaL_ref is a stub", stderr); }
static void __lite_xl_fallback_luaL_unref (lua_State *L, int t, int ref) { fputs("warning: luaL_unref is a stub", stderr); }
static int __lite_xl_fallback_luaL_loadfilex (lua_State *L, const char *filename, const char *mode) { fputs("warning: luaL_loadfilex is a stub", stderr); }
static int __lite_xl_fallback_luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode) { fputs("warning: luaL_loadbufferx is a stub", stderr); }
static int __lite_xl_fallback_luaL_loadstring (lua_State *L, const char *s) { fputs("warning: luaL_loadstring is a stub", stderr); }
static lua_State * __lite_xl_fallback_luaL_newstate (void) { fputs("warning: luaL_newstate is a stub", stderr); }
static int __lite_xl_fallback_luaL_len (lua_State *L, int idx) { fputs("warning: luaL_len is a stub", stderr); }
static const char * __lite_xl_fallback_luaL_gsub (lua_State *L, const char *s, const char *p, const char *r) { fputs("warning: luaL_gsub is a stub", stderr); }
static void __lite_xl_fallback_luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { fputs("warning: luaL_setfuncs is a stub", stderr); }
static int __lite_xl_fallback_luaL_getsubtable (lua_State *L, int idx, const char *fname) { fputs("warning: luaL_getsubtable is a stub", stderr); }
static void __lite_xl_fallback_luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level) { fputs("warning: luaL_traceback is a stub", stderr); }
static void __lite_xl_fallback_luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb) { fputs("warning: luaL_requiref is a stub", stderr); }
static void __lite_xl_fallback_luaL_buffinit (lua_State *L, luaL_Buffer *B) { fputs("warning: luaL_buffinit is a stub", stderr); }
static char * __lite_xl_fallback_luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { fputs("warning: luaL_prepbuffsize is a stub", stderr); }
static void __lite_xl_fallback_luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { fputs("warning: luaL_addlstring is a stub", stderr); }
static void __lite_xl_fallback_luaL_addstring (luaL_Buffer *B, const char *s) { fputs("warning: luaL_addstring is a stub", stderr); }
static void __lite_xl_fallback_luaL_addvalue (luaL_Buffer *B) { fputs("warning: luaL_addvalue is a stub", stderr); }
static void __lite_xl_fallback_luaL_pushresult (luaL_Buffer *B) { fputs("warning: luaL_pushresult is a stub", stderr); }
static void __lite_xl_fallback_luaL_pushresultsize (luaL_Buffer *B, size_t sz) { fputs("warning: luaL_pushresultsize is a stub", stderr); }
static char * __lite_xl_fallback_luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { fputs("warning: luaL_buffinitsize is a stub", stderr); }
#define IMPORT_SYMBOL(name, ret, ...) name = (name = (ret (*) (__VA_ARGS__)) symbol(#name), name == NULL ? &__lite_xl_fallback_##name : name)
static void lite_xl_plugin_init(void *XL) {
void* (*symbol)(const char *) = (void* (*) (const char *)) 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_atpanic, lua_CFunction , lua_State *L, lua_CFunction panicf);
IMPORT_SYMBOL(lua_version, const lua_Number *, lua_State *L);
IMPORT_SYMBOL(lua_absindex, int , lua_State *L, int idx);
IMPORT_SYMBOL(lua_gettop, int , lua_State *L);
IMPORT_SYMBOL(lua_settop, void , lua_State *L, int idx);
IMPORT_SYMBOL(lua_pushvalue, void , lua_State *L, int idx);
IMPORT_SYMBOL(lua_remove, void , lua_State *L, int idx);
IMPORT_SYMBOL(lua_insert, void , lua_State *L, int idx);
IMPORT_SYMBOL(lua_replace, void , lua_State *L, int idx);
IMPORT_SYMBOL(lua_copy, void , lua_State *L, int fromidx, int toidx);
IMPORT_SYMBOL(lua_checkstack, int , lua_State *L, int sz);
IMPORT_SYMBOL(lua_xmove, void , lua_State *from, lua_State *to, int n);
IMPORT_SYMBOL(lua_isnumber, int , lua_State *L, int idx);
IMPORT_SYMBOL(lua_isstring, int , lua_State *L, int idx);
IMPORT_SYMBOL(lua_iscfunction, int , lua_State *L, int idx);
IMPORT_SYMBOL(lua_isuserdata, int , lua_State *L, int idx);
IMPORT_SYMBOL(lua_type, int , lua_State *L, int idx);
IMPORT_SYMBOL(lua_typename, const char *, lua_State *L, int tp);
IMPORT_SYMBOL(lua_tonumberx, lua_Number , lua_State *L, int idx, int *isnum);
IMPORT_SYMBOL(lua_tointegerx, lua_Integer , lua_State *L, int idx, int *isnum);
IMPORT_SYMBOL(lua_tounsignedx, lua_Unsigned , lua_State *L, int idx, int *isnum);
IMPORT_SYMBOL(lua_toboolean, int , lua_State *L, int idx);
IMPORT_SYMBOL(lua_tolstring, const char *, lua_State *L, int idx, size_t *len);
IMPORT_SYMBOL(lua_rawlen, size_t , lua_State *L, int idx);
IMPORT_SYMBOL(lua_tocfunction, lua_CFunction , lua_State *L, int idx);
IMPORT_SYMBOL(lua_touserdata, void *, lua_State *L, int idx);
IMPORT_SYMBOL(lua_tothread, lua_State *, lua_State *L, int idx);
IMPORT_SYMBOL(lua_topointer, const void *, lua_State *L, int idx);
IMPORT_SYMBOL(lua_arith, void , lua_State *L, int op);
IMPORT_SYMBOL(lua_rawequal, int , lua_State *L, int idx1, int idx2);
IMPORT_SYMBOL(lua_compare, int , lua_State *L, int idx1, int idx2, int op);
IMPORT_SYMBOL(lua_pushnil, void , lua_State *L);
IMPORT_SYMBOL(lua_pushnumber, void , lua_State *L, lua_Number n);
IMPORT_SYMBOL(lua_pushinteger, void , lua_State *L, lua_Integer n);
IMPORT_SYMBOL(lua_pushunsigned, void , lua_State *L, lua_Unsigned n);
IMPORT_SYMBOL(lua_pushlstring, const char *, lua_State *L, const char *s, size_t l);
IMPORT_SYMBOL(lua_pushstring, const char *, lua_State *L, const char *s);
IMPORT_SYMBOL(lua_pushvfstring, const char *, lua_State *L, const char *fmt, va_list argp);
IMPORT_SYMBOL(lua_pushfstring, const char *, lua_State *L, const char *fmt, ...);
IMPORT_SYMBOL(lua_pushcclosure, void , lua_State *L, lua_CFunction fn, int n);
IMPORT_SYMBOL(lua_pushboolean, void , lua_State *L, int b);
IMPORT_SYMBOL(lua_pushlightuserdata, void , lua_State *L, void *p);
IMPORT_SYMBOL(lua_pushthread, int , lua_State *L);
IMPORT_SYMBOL(lua_getglobal, void , lua_State *L, const char *var);
IMPORT_SYMBOL(lua_gettable, void , lua_State *L, int idx);
IMPORT_SYMBOL(lua_getfield, void , lua_State *L, int idx, const char *k);
IMPORT_SYMBOL(lua_rawget, void , lua_State *L, int idx);
IMPORT_SYMBOL(lua_rawgeti, void , lua_State *L, int idx, int n);
IMPORT_SYMBOL(lua_rawgetp, void , lua_State *L, int idx, const void *p);
IMPORT_SYMBOL(lua_createtable, void , lua_State *L, int narr, int nrec);
IMPORT_SYMBOL(lua_newuserdata, void *, lua_State *L, size_t sz);
IMPORT_SYMBOL(lua_getmetatable, int , lua_State *L, int objindex);
IMPORT_SYMBOL(lua_getuservalue, void , lua_State *L, int idx);
IMPORT_SYMBOL(lua_setglobal, void , lua_State *L, const char *var);
IMPORT_SYMBOL(lua_settable, void , lua_State *L, int idx);
IMPORT_SYMBOL(lua_setfield, void , lua_State *L, int idx, const char *k);
IMPORT_SYMBOL(lua_rawset, void , lua_State *L, int idx);
IMPORT_SYMBOL(lua_rawseti, void , lua_State *L, int idx, int n);
IMPORT_SYMBOL(lua_rawsetp, void , lua_State *L, int idx, const void *p);
IMPORT_SYMBOL(lua_setmetatable, int , lua_State *L, int objindex);
IMPORT_SYMBOL(lua_setuservalue, void , lua_State *L, int idx);
IMPORT_SYMBOL(lua_callk, void , lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k);
IMPORT_SYMBOL(lua_getctx, int , lua_State *L, int *ctx);
IMPORT_SYMBOL(lua_pcallk, int , lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k);
IMPORT_SYMBOL(lua_load, int , lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode);
IMPORT_SYMBOL(lua_dump, int , lua_State *L, lua_Writer writer, void *data);
IMPORT_SYMBOL(lua_yieldk, int , lua_State *L, int nresults, int ctx, lua_CFunction k);
IMPORT_SYMBOL(lua_resume, int , lua_State *L, lua_State *from, int narg);
IMPORT_SYMBOL(lua_status, int , lua_State *L);
IMPORT_SYMBOL(lua_gc, int , lua_State *L, int what, int data);
IMPORT_SYMBOL(lua_error, int , lua_State *L);
IMPORT_SYMBOL(lua_next, int , lua_State *L, int idx);
IMPORT_SYMBOL(lua_concat, void , lua_State *L, int n);
IMPORT_SYMBOL(lua_len, void , lua_State *L, int idx);
IMPORT_SYMBOL(lua_getallocf, lua_Alloc , lua_State *L, void **ud);
IMPORT_SYMBOL(lua_setallocf, void , lua_State *L, lua_Alloc f, void *ud);
IMPORT_SYMBOL(lua_getstack, int , lua_State *L, int level, lua_Debug *ar);
IMPORT_SYMBOL(lua_getinfo, int , lua_State *L, const char *what, lua_Debug *ar);
IMPORT_SYMBOL(lua_getlocal, const char *, lua_State *L, const lua_Debug *ar, int n);
IMPORT_SYMBOL(lua_setlocal, const char *, lua_State *L, const lua_Debug *ar, int n);
IMPORT_SYMBOL(lua_getupvalue, const char *, lua_State *L, int funcindex, int n);
IMPORT_SYMBOL(lua_setupvalue, const char *, lua_State *L, int funcindex, int n);
IMPORT_SYMBOL(lua_upvalueid, void *, lua_State *L, int fidx, int n);
IMPORT_SYMBOL(lua_upvaluejoin, void , lua_State *L, int fidx1, int n1, int fidx2, int n2);
IMPORT_SYMBOL(lua_sethook, int , lua_State *L, lua_Hook func, int mask, int count);
IMPORT_SYMBOL(lua_gethook, lua_Hook , lua_State *L);
IMPORT_SYMBOL(lua_gethookmask, int , lua_State *L);
IMPORT_SYMBOL(lua_gethookcount, int , lua_State *L);
IMPORT_SYMBOL(luaL_checkversion_, void , lua_State *L, lua_Number ver);
IMPORT_SYMBOL(luaL_getmetafield, int , lua_State *L, int obj, const char *e);
IMPORT_SYMBOL(luaL_callmeta, int , lua_State *L, int obj, const char *e);
IMPORT_SYMBOL(luaL_tolstring, const char *, lua_State *L, int idx, size_t *len);
IMPORT_SYMBOL(luaL_argerror, int , lua_State *L, int numarg, const char *extramsg);
IMPORT_SYMBOL(luaL_checklstring, const char *, lua_State *L, int numArg, size_t *l);
IMPORT_SYMBOL(luaL_optlstring, const char *, lua_State *L, int numArg, const char *def, size_t *l);
IMPORT_SYMBOL(luaL_checknumber, lua_Number , lua_State *L, int numArg);
IMPORT_SYMBOL(luaL_optnumber, lua_Number , lua_State *L, int nArg, lua_Number def);
IMPORT_SYMBOL(luaL_checkinteger, lua_Integer , lua_State *L, int numArg);
IMPORT_SYMBOL(luaL_optinteger, lua_Integer , lua_State *L, int nArg, lua_Integer def);
IMPORT_SYMBOL(luaL_checkunsigned, lua_Unsigned , lua_State *L, int numArg);
IMPORT_SYMBOL(luaL_optunsigned, lua_Unsigned , lua_State *L, int numArg, lua_Unsigned def);
IMPORT_SYMBOL(luaL_checkstack, void , lua_State *L, int sz, const char *msg);
IMPORT_SYMBOL(luaL_checktype, void , lua_State *L, int narg, int t);
IMPORT_SYMBOL(luaL_checkany, void , lua_State *L, int narg);
IMPORT_SYMBOL(luaL_newmetatable, int , lua_State *L, const char *tname);
IMPORT_SYMBOL(luaL_setmetatable, void , lua_State *L, const char *tname);
IMPORT_SYMBOL(luaL_testudata, void *, lua_State *L, int ud, const char *tname);
IMPORT_SYMBOL(luaL_checkudata, void *, lua_State *L, int ud, const char *tname);
IMPORT_SYMBOL(luaL_where, void , lua_State *L, int lvl);
IMPORT_SYMBOL(luaL_error, int , lua_State *L, const char *fmt, ...);
IMPORT_SYMBOL(luaL_checkoption, int , lua_State *L, int narg, const char *def, const char *const lst[]);
IMPORT_SYMBOL(luaL_fileresult, int , lua_State *L, int stat, const char *fname);
IMPORT_SYMBOL(luaL_execresult, int , lua_State *L, int stat);
IMPORT_SYMBOL(luaL_ref, int , lua_State *L, int t);
IMPORT_SYMBOL(luaL_unref, void , lua_State *L, int t, int ref);
IMPORT_SYMBOL(luaL_loadfilex, int , lua_State *L, const char *filename, const char *mode);
IMPORT_SYMBOL(luaL_loadbufferx, int , lua_State *L, const char *buff, size_t sz, const char *name, const char *mode);
IMPORT_SYMBOL(luaL_loadstring, int , lua_State *L, const char *s);
IMPORT_SYMBOL(luaL_newstate, lua_State *, void);
IMPORT_SYMBOL(luaL_len, int , lua_State *L, int idx);
IMPORT_SYMBOL(luaL_gsub, const char *, lua_State *L, const char *s, const char *p, const char *r);
IMPORT_SYMBOL(luaL_setfuncs, void , lua_State *L, const luaL_Reg *l, int nup);
IMPORT_SYMBOL(luaL_getsubtable, int , lua_State *L, int idx, const char *fname);
IMPORT_SYMBOL(luaL_traceback, void , lua_State *L, lua_State *L1, const char *msg, int level);
IMPORT_SYMBOL(luaL_requiref, void , lua_State *L, const char *modname, lua_CFunction openf, int glb);
IMPORT_SYMBOL(luaL_buffinit, void , lua_State *L, luaL_Buffer *B);
IMPORT_SYMBOL(luaL_prepbuffsize, char *, luaL_Buffer *B, size_t sz);
IMPORT_SYMBOL(luaL_addlstring, void , luaL_Buffer *B, const char *s, size_t l);
IMPORT_SYMBOL(luaL_addstring, void , luaL_Buffer *B, const char *s);
IMPORT_SYMBOL(luaL_addvalue, void , luaL_Buffer *B);
IMPORT_SYMBOL(luaL_pushresult, void , luaL_Buffer *B);
IMPORT_SYMBOL(luaL_pushresultsize, void , luaL_Buffer *B, size_t sz);
IMPORT_SYMBOL(luaL_buffinitsize, char *, lua_State *L, luaL_Buffer *B, size_t sz);
}
#endif

View File

@ -2,30 +2,25 @@
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>lite-xl</string> <string>lite-xl</string>
<key>CFBundleGetInfoString</key> <key>CFBundleGetInfoString</key>
<string>lite-xl</string> <string>lite-xl</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>icon.icns</string> <string>icon</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>Lite XL</string> <string>lite-xl</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>
<true/> <true/>
<key>LSMinimumSystemVersion</key> <key>MinimumOSVersion</key><string>10.13</string>
<string>10.11</string> <key>NSDocumentsFolderUsageDescription</key><string>To access, edit and index your projects.</string>
<key>NSDocumentsFolderUsageDescription</key> <key>NSDesktopFolderUsageDescription</key><string>To access, edit and index your projects.</string>
<string>To access, edit and index your projects.</string> <key>NSDownloadsFolderUsageDescription</key><string>To access, edit and index your projects.</string>
<key>NSDesktopFolderUsageDescription</key>
<string>To access, edit and index your projects.</string>
<key>NSDownloadsFolderUsageDescription</key>
<string>To access, edit and index your projects.</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>@PROJECT_VERSION@</string> <string>1.16.10</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>© 2019-2021 Francesco Abbate</string> <string>© 2019-2021 Francesco Abbate</string>
</dict> </dict>
</plist> </plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

View File

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

View File

@ -1,30 +0,0 @@
# Scripts
Various scripts and configurations used to configure, build, and package Lite XL.
### Build
- **build.sh**
- **build-packages.sh**: In root directory, as all in one script; relies to the
ones in this directory.
### Package
- **appdmg.sh**: Create a macOS DMG image using [AppDMG][1].
- **appimage.sh**: [AppImage][2] builder.
- **innosetup.sh**: Creates a 32/64 bit [InnoSetup][3] installer package.
- **package.sh**: Creates all binary / DMG image / installer / source packages.
### Utility
- **common.sh**: Common functions used by other scripts.
- **install-dependencies.sh**: Installs required applications to build, package
and run Lite XL, mainly useful for CI and documentation purpose.
Preferably not to be used in user systems.
- **fontello-config.json**: Used by the icons generator.
- **generate_header.sh**: Generates a header file for native plugin API
- **keymap-generator**: Generates a JSON file containing the keymap
[1]: https://github.com/LinusU/node-appdmg
[2]: https://docs.appimage.org/
[3]: https://jrsoftware.org/isinfo.php

View File

@ -1,30 +0,0 @@
#!/bin/bash
set -ex
if [ ! -e "src/api/api.h" ]; then
echo "Please run this script from the root directory of Lite XL."
exit 1
fi
cat > lite-xl-dmg.json << EOF
{
"title": "Lite XL",
"icon": "$(pwd)/resources/icons/icon.icns",
"background": "$(pwd)/resources/macos/appdmg.png",
"window": {
"position": {
"x": 360,
"y": 360
},
"size": {
"width": 480,
"height": 360
}
},
"contents": [
{ "x": 144, "y": 248, "type": "file", "path": "$(pwd)/Lite XL.app" },
{ "x": 336, "y": 248, "type": "link", "path": "/Applications" }
]
}
EOF
~/node_modules/appdmg/bin/appdmg.js lite-xl-dmg.json "$(pwd)/$1.dmg"

View File

@ -1,162 +0,0 @@
#!/bin/env bash
set -ex
if [ ! -e "src/api/api.h" ]; then
echo "Please run this script from the root directory of Lite XL."
exit 1
fi
source scripts/common.sh
show_help(){
echo
echo "Usage: $0 <OPTIONS>"
echo
echo "Available options:"
echo
echo "-h --help Show this help and exits."
echo "-b --builddir DIRNAME Sets the name of the build dir (no path)."
echo " Default: 'build'."
echo "-n --nobuild Skips the build step, use existing files."
echo "-s --static Specify if building using static libraries"
echo " by using lhelper tool."
echo "-v --version VERSION Specify a version, non whitespace separated string."
echo
}
ARCH="$(uname -m)"
BUILD_DIR="$(get_default_build_dir)"
RUN_BUILD=true
STATIC_BUILD=false
for i in "$@"; do
case $i in
-h|--belp)
show_help
exit 0
;;
-b|--builddir)
BUILD_DIR="$2"
shift
shift
;;
-n|--nobuild)
RUN_BUILD=false
shift
;;
-s|--static)
STATIC_BUILD=true
shift
;;
-v|--version)
VERSION="$2"
shift
shift
;;
*)
# unknown option
;;
esac
done
# TODO: Versioning using git
#if [[ -z $VERSION && -d .git ]]; then
# VERSION=$(git describe --tags --long | sed 's/^v//; s/\([^-]*-g\)/r\1/; s/-/./g')
#fi
if [[ -n $1 ]]; then
show_help
exit 1
fi
setup_appimagetool() {
if ! which appimagetool > /dev/null ; then
if [ ! -e appimagetool ]; then
if ! wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${ARCH}.AppImage" ; then
echo "Could not download the appimagetool for the arch '${ARCH}'."
exit 1
else
chmod 0755 appimagetool
fi
fi
fi
}
download_appimage_apprun() {
if [ ! -e AppRun ]; then
if ! wget -O AppRun "https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-${ARCH}" ; then
echo "Could not download AppRun for the arch '${ARCH}'."
exit 1
else
chmod 0755 AppRun
fi
fi
}
build_litexl() {
if [ -e build ]; then
rm -rf build
fi
if [ -e ${BUILD_DIR} ]; then
rm -rf ${BUILD_DIR}
fi
echo "Build lite-xl..."
sleep 1
meson setup --buildtype=release --prefix /usr ${BUILD_DIR}
meson compile -C ${BUILD_DIR}
}
generate_appimage() {
if [ -e LiteXL.AppDir ]; then
rm -rf LiteXL.AppDir
fi
echo "Creating LiteXL.AppDir..."
DESTDIR="$(realpath LiteXL.AppDir)" meson install --skip-subprojects -C ${BUILD_DIR}
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/org.lite_xl.lite_xl.desktop LiteXL.AppDir/
if [[ $STATIC_BUILD == false ]]; then
echo "Copying libraries..."
mkdir -p LiteXL.AppDir/usr/lib/
local allowed_libs=(
libfreetype
libpcre2
libSDL2
libsndio
liblua
)
while read line; do
local libname="$(echo $line | cut -d' ' -f1)"
local libpath="$(echo $line | cut -d' ' -f2)"
for lib in "${allowed_libs[@]}" ; do
if echo "$libname" | grep "$lib" > /dev/null ; then
cp "$libpath" LiteXL.AppDir/usr/lib/
continue 2
fi
done
echo " Ignoring: $libname"
done < <(ldd build/src/lite-xl | awk '{print $1 " " $3}')
fi
echo "Generating AppImage..."
local version=""
if [ -n "$VERSION" ]; then
version="-$VERSION"
fi
./appimagetool LiteXL.AppDir LiteXL${version}-${ARCH}.AppImage
}
setup_appimagetool
download_appimage_apprun
if [[ $RUN_BUILD == true ]]; then build_litexl; fi
generate_appimage $1

View File

@ -1,117 +0,0 @@
#!/bin/bash
set -e
if [ ! -e "src/api/api.h" ]; then
echo "Please run this script from the root directory of Lite XL."; exit 1
fi
source scripts/common.sh
show_help() {
echo
echo "Usage: $0 <OPTIONS>"
echo
echo "Available options:"
echo
echo "-b --builddir DIRNAME Sets the name of the build directory (not path)."
echo " Default: '$(get_default_build_dir)'."
echo " --debug Debug this script."
echo "-f --forcefallback Force to build dependencies statically."
echo "-h --help Show this help and exit."
echo "-p --prefix PREFIX Install directory prefix. Default: '/'."
echo "-B --bundle Create an App bundle (macOS only)"
echo "-P --portable Create a portable binary package."
echo "-O --pgo Use profile guided optimizations (pgo)."
echo " macOS: disabled when used with --bundle,"
echo " Windows: Implicit being the only option."
echo
}
main() {
local platform="$(get_platform_name)"
local build_dir="$(get_default_build_dir)"
local prefix=/
local force_fallback
local bundle
local portable
local pgo
for i in "$@"; do
case $i in
-h|--help)
show_help
exit 0
;;
-b|--builddir)
build_dir="$2"
shift
shift
;;
--debug)
set -x
shift
;;
-f|--forcefallback)
force_fallback="--wrap-mode=forcefallback"
shift
;;
-p|--prefix)
prefix="$2"
shift
shift
;;
-B|--bundle)
if [[ "$platform" != "macos" ]]; then
echo "Warning: ignoring --bundle option, works only under macOS."
else
bundle="-Dbundle=true"
fi
shift
;;
-P|--portable)
portable="-Dportable=true"
shift
;;
-O|--pgo)
pgo="-Db_pgo=generate"
shift
;;
*)
# unknown option
;;
esac
done
if [[ -n $1 ]]; then
show_help
exit 1
fi
if [[ $platform == "macos" && -n $bundle && -n $portable ]]; then
echo "Warning: \"bundle\" and \"portable\" specified; excluding portable package."
portable=""
fi
rm -rf "${build_dir}"
CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS meson setup \
--buildtype=release \
--prefix "$prefix" \
$force_fallback \
$bundle \
$portable \
$pgo \
"${build_dir}"
meson compile -C "${build_dir}"
if [ ! -z ${pgo+x} ]; then
cp -r data "${build_dir}/src"
"${build_dir}/src/lite-xl"
meson configure -Db_pgo=use "${build_dir}"
meson compile -C "${build_dir}"
rm -fr "${build_dir}/data"
fi
}
main "$@"

View File

@ -1,25 +0,0 @@
#!/bin/bash
set -e
get_platform_name() {
if [[ "$OSTYPE" == "msys" ]]; then
echo "windows"
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo "macos"
elif [[ "$OSTYPE" == "linux"* || "$OSTYPE" == "freebsd"* ]]; then
echo "linux"
else
echo "UNSUPPORTED-OS"
fi
}
get_default_build_dir() {
platform=$(get_platform_name)
echo "build-$platform-$(uname -m)"
}
if [[ $(get_platform_name) == "UNSUPPORTED-OS" ]]; then
echo "Error: unknown OS type: \"$OSTYPE\""
exit 1
fi

View File

@ -1,144 +0,0 @@
#!/bin/bash
##### CONFIG
# symbols to ignore
IGNORE_SYM='luaL_pushmodule\|luaL_openlib'
##### CONFIG
# https://stackoverflow.com/a/13062682
uncomment() {
[ $# -eq 2 ] && arg="$1" || arg=""
eval file="\$$#"
sed 's/a/aA/g; s/__/aB/g; s/#/aC/g' "$file" | \
gcc -P -E $arg - | \
sed 's/aC/#/g; s/aB/__/g; s/aA/a/g'
}
# this is the magic that turns multiline statements into
# single line statements
# LITERALLY DOES NOT WORK WITH PREPROCESSOR
onelineize() {
grep -v '^#' | sed -e ':r;$!{N;br};s/\([^{;]\)\n\s*/\1 /g'
}
discard_preprocessors() {
grep -v '#\(include\|if\|endif\)'
}
# sed regex for extracting data from function signature
# if this isn't regex, idk what is
# LUA_API (return type as \2) (function name as \3) (args as \4)
sym_regex='^LUA\(LIB\)\?_API\s\+\([^(]\+\)\s*(\([^)]\+\))\s\+(\([^)]\+\));'
# get funcptr declarations
ptrize() {
grep '^LUA' | grep -v "$IGNORE_SYM" | sed -e "s/$sym_regex/static\t\2(*\3)\t(\4);/"
}
# create a stub function that warns user when calling it
makestub() {
grep '^LUA' | grep -v "$IGNORE_SYM" | sed -e "s/$sym_regex/static\t\2\t__lite_xl_fallback_\3\t(\4) { fputs(\"warning: \3 is a stub\", stderr); }/"
}
import_sym() {
grep '^LUA' | grep -v "$IGNORE_SYM" | sed -e "s/$sym_regex/\tIMPORT_SYMBOL(\3, \2, \4);/"
}
decl() {
echo "/** $(basename "$1") **/"
echo
header="$(uncomment $1 | discard_preprocessors)"
header1="$(onelineize <<< "$header")"
# typedef
grep -v '^\(LUA\|#\|extern\)' <<< "$header1"
# funcptrs
ptrize <<< "$header1"
# defines
(grep '^#' | grep -v "$IGNORE_SYM") <<< "$header"
# stubs
makestub <<< "$header1"
}
decl_import() {
uncomment $1 | onelineize | import_sym
}
generate_header() {
local LUA_PATH="$1"
echo "#ifndef LITE_XL_PLUGIN_API"
echo "#define LITE_XL_PLUGIN_API"
echo "/**"
echo "The lite_xl plugin API is quite simple. Any shared library can be a plugin file, so long"
echo "as it has an entrypoint that looks like the following, where xxxxx is the plugin name:"
echo '#include "lite_xl_plugin_api.h"'
echo "int lua_open_lite_xl_xxxxx(lua_State* L, void* XL) {"
echo " lite_xl_plugin_init(XL);"
echo " ..."
echo " return 1;"
echo "}"
echo "In linux, to compile this file, you'd do: 'gcc -o xxxxx.so -shared xxxxx.c'. Simple!"
echo "Due to the way the API is structured, you *should not* link or include lua libraries."
echo "This file was automatically generated. DO NOT MODIFY DIRECTLY."
echo "**/"
echo
echo
echo "#include <stdarg.h>"
echo "#include <stdio.h> // for BUFSIZ? this is kinda weird"
echo
echo "/** luaconf.h **/"
echo
uncomment "$LUA_PATH/luaconf.h"
echo
decl "$LUA_PATH/lua.h"
echo
decl "$LUA_PATH/lauxlib.h"
echo
echo "#define IMPORT_SYMBOL(name, ret, ...) name = (name = (ret (*) (__VA_ARGS__)) symbol(#name), name == NULL ? &__lite_xl_fallback_##name : name)"
echo "static void lite_xl_plugin_init(void *XL) {"
echo -e "\tvoid* (*symbol)(const char *) = (void* (*) (const char *)) XL;"
decl_import "$LUA_PATH/lua.h"
decl_import "$LUA_PATH/lauxlib.h"
echo "}"
echo "#endif"
}
show_help() {
echo -e "Usage: $0 <OPTIONS> prefix"
echo
echo -e "Available options:"
echo -e "-p\t--prefix\tSet prefix (where to find lua.h and lauxlib.h)"
}
main() {
local prefix=""
for i in "$@"; do
case $i in
-h|--help)
show_help
exit 0
;;
-p|--prefix)
prefix="$2"
shift
shift
;;
*)
;;
esac
done
generate_header "$prefix"
}
main "$@"
# create a stub function that warns user when calling it

View File

@ -1,88 +1,83 @@
#define MyAppName "Lite XL" #define MyAppName "Lite XL"
#define MyAppVersion "@PROJECT_VERSION@" #define MyAppVersion "@PROJECT_VERSION@"
#define MyAppPublisher "Lite XL Team" #define MyAppPublisher "Lite XL Team"
#define MyAppURL "https://lite-xl.github.io" #define MyAppURL "https://lite-xl.github.io"
#define MyAppExeName "lite-xl.exe" #define MyAppExeName "lite-xl.exe"
#define BuildDir "@PROJECT_BUILD_DIR@" #define BuildDir "@PROJECT_BUILD_DIR@"
#define SourceDir "@PROJECT_SOURCE_DIR@" #define SourceDir "."
; Use /dArch option to create a setup for a different architecture, e.g.: ; Use /dArch option to create a setup for a different architecture, e.g.:
; iscc /dArch=x86 innosetup.iss ; iscc /dArch=x86 innosetup.iss
#ifndef Arch #ifndef Arch
#define Arch "x64" #define Arch "x64"
#endif #endif
[Setup] [Setup]
; NOTE: The value of AppId uniquely identifies this application. ; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications. ; Do not use the same AppId value in installers for other applications.
; To generate a new GUID, click Tools | Generate GUID inside the InnoSetup IDE. ; To generate a new GUID, click Tools | Generate GUID inside the InnoSetup IDE.
AppId={{06761240-D97C-43DE-B9ED-C15F765A2D65} AppId={{06761240-D97C-43DE-B9ED-C15F765A2D65}
AppName={#MyAppName} AppName={#MyAppName}
AppVersion={#MyAppVersion} AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion} ;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher} AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL} AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL} AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL} AppUpdatesURL={#MyAppURL}
#if Arch=="x64" #if Arch=="x64"
ArchitecturesAllowed=x64 ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64 ArchitecturesInstallIn64BitMode={#Arch}
#define ArchInternal "x86_64" #endif
#else
#define ArchInternal "i686" AllowNoIcons=yes
#endif Compression=lzma
SolidCompression=yes
AllowNoIcons=yes DefaultDirName={autopf}\{#MyAppName}
Compression=lzma DefaultGroupName={#MyAppPublisher}
SolidCompression=yes UninstallFilesDir={app}
DefaultDirName={autopf}/{#MyAppName}
DefaultGroupName={#MyAppPublisher} ; Uncomment the following line to run in non administrative install mode
UninstallFilesDir={app} ; (install for current user only.)
;PrivilegesRequired=lowest
; Uncomment the following line to run in non administrative install mode PrivilegesRequiredOverridesAllowed=dialog
; (install for current user only.)
;PrivilegesRequired=lowest ; The [Icons] "quicklaunchicon" entry uses {userappdata}
PrivilegesRequiredOverridesAllowed=dialog ; but its [Tasks] entry has a proper IsAdminInstallMode Check.
UsedUserAreasWarning=no
; The [Icons] "quicklaunchicon" entry uses {userappdata}
; but its [Tasks] entry has a proper IsAdminInstallMode Check. OutputDir=.
UsedUserAreasWarning=no OutputBaseFilename=LiteXL-{#MyAppVersion}-{#Arch}-setup
;DisableDirPage=yes
OutputDir=. ;DisableProgramGroupPage=yes
OutputBaseFilename=LiteXL-{#MyAppVersion}-{#ArchInternal}-setup
;DisableDirPage=yes LicenseFile={#SourceDir}\LICENSE
;DisableProgramGroupPage=yes SetupIconFile={#SourceDir}\icon.ico
WizardImageFile="wizard-modern-image.bmp"
LicenseFile={#SourceDir}/LICENSE WizardSmallImageFile="litexl-55px.bmp"
SetupIconFile={#SourceDir}/resources/icons/icon.ico
WizardImageFile="{#SourceDir}/scripts/innosetup/wizard-modern-image.bmp" [Languages]
WizardSmallImageFile="{#SourceDir}/scripts/innosetup/litexl-55px.bmp" Name: "english"; MessagesFile: "compiler:Default.isl"
[Languages] [Tasks]
Name: "english"; MessagesFile: "compiler:Default.isl" Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 6.1; Check: not IsAdminInstallMode
[Tasks] Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 6.1; Check: not IsAdminInstallMode [Files]
Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked Source: "{#BuildDir}\lite-xl.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#BuildDir}\data\*"; DestDir: "{app}\data"; Flags: ignoreversion recursesubdirs
[Files] ; NOTE: Don't use "Flags: ignoreversion" on any shared system 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}')) [Icons]
Source: "{#SourceDir}/data/*"; DestDir: "{app}/data"; Flags: ignoreversion recursesubdirs Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: not IsTaskSelected('portablemode')
; NOTE: Don't use "Flags: ignoreversion" on any shared system files Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"; Check: not IsTaskSelected('portablemode')
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Icons] Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon; Check: not IsTaskSelected('portablemode')
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: not WizardIsTaskSelected('portablemode') [Run]
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"; Check: not WizardIsTaskSelected('portablemode') Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon; Check: not WizardIsTaskSelected('portablemode')
; Name: "{usersendto}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" [Setup]
Uninstallable=not IsTaskSelected('portablemode')
[Run]
Filename: "{app}/{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
[Setup]
Uninstallable=not WizardIsTaskSelected('portablemode')

View File

@ -1,65 +0,0 @@
#!/bin/bash
set -e
if [ ! -e "src/api/api.h" ]; then
echo "Please run this script from the root directory of Lite XL."; exit 1
fi
source scripts/common.sh
show_help() {
echo
echo "Usage: $0 <OPTIONS>"
echo
echo "Available options:"
echo
echo "-b --builddir DIRNAME Sets the name of the build directory (not path)."
echo " Default: '$(get_default_build_dir)'."
echo " --debug Debug this script."
echo
}
main() {
local build_dir=$(get_default_build_dir)
local arch
if [[ $MSYSTEM == "MINGW64" ]]; then arch=x64; else arch=Win32; fi
for i in "$@"; do
case $i in
-h|--help)
show_help
exit 0
;;
-b|--builddir)
build_dir="$2"
shift
shift
;;
--debug)
set -x
shift
;;
*)
# unknown option
;;
esac
done
if [[ -n $1 ]]; then
show_help
exit 1
fi
# Copy MinGW libraries dependencies.
# MSYS2 ldd command seems to be only 64bit, so use ntldd
# see https://github.com/msys2/MINGW-packages/issues/4164
local mingwLibsDir="${build_dir}/mingwLibs$arch"
mkdir -p "$mingwLibsDir"
ntldd -R "${build_dir}/src/lite-xl.exe" | grep mingw | awk '{print $3}' | sed 's#\\#/#g' | xargs -I '{}' cp -v '{}' $mingwLibsDir
"/c/Program Files (x86)/Inno Setup 6/ISCC.exe" -dARCH=$arch "${build_dir}/scripts/innosetup.iss"
pushd "${build_dir}/scripts"; mv LiteXL*.exe "./../../"; popd
}
main "$@"

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