Compare commits
2 Commits
os4
...
context-me
Author | SHA1 | Date |
---|---|---|
takase1121 | e3c42d865e | |
takase1121 | 084012ed60 |
|
@ -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
|
|
||||||
|
|
|
@ -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/**/*
|
|
|
@ -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
|
|
|
@ -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
|
|
||||||
if: ${{ matrix.config.cc == 'gcc' }}
|
|
||||||
run: |
|
|
||||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
||||||
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
|
|
||||||
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-linux-$(uname -m)" >> "$GITHUB_ENV"
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Python Setup
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: 3.6
|
python-version: 3.6
|
||||||
- name: Update Packages
|
- name: Install dependencies
|
||||||
run: sudo apt-get update
|
|
||||||
- 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: |
|
run: |
|
||||||
bash scripts/install-dependencies.sh --debug --lhelper
|
sudo apt-get install -qq libsdl2-dev libfreetype6 ninja-build
|
||||||
bash scripts/lhelper.sh --debug
|
pip3 install meson
|
||||||
- name: Build
|
- name: Build package
|
||||||
run: |
|
run: bash build-packages.sh x86-64
|
||||||
bash --version
|
- name: upload packages
|
||||||
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
|
uses: actions/upload-artifact@v2
|
||||||
if: ${{ matrix.config.cc == 'gcc' }}
|
|
||||||
with:
|
with:
|
||||||
name: Linux Artifacts
|
name: Ubuntu Package
|
||||||
path: |
|
path: lite-xl-linux-*.tar.gz
|
||||||
${{ 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
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
config:
|
||||||
|
# - { name: "GCC", cc: gcc-10, cxx: g++-10 }
|
||||||
|
- { name: "clang", cc: clang, cxx: clang++ }
|
||||||
env:
|
env:
|
||||||
CC: clang
|
CC: ${{ matrix.config.cc }}
|
||||||
CXX: clang++
|
CXX: ${{ matrix.config.cxx }}
|
||||||
steps:
|
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
|
- uses: actions/checkout@v2
|
||||||
- name: Python Setup
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: 3.9
|
python-version: 3.9
|
||||||
- name: Install Dependencies
|
- 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: |
|
run: |
|
||||||
bash scripts/install-dependencies.sh --debug --lhelper
|
pip3 install meson
|
||||||
bash scripts/lhelper.sh --debug
|
brew install ninja sdl2
|
||||||
- name: Build
|
- name: Build package
|
||||||
run: |
|
run: bash build-packages.sh x86-64
|
||||||
bash --version
|
- name: upload packages
|
||||||
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
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: macOS DMG Image
|
name: Mac OS X Package
|
||||||
path: ${{ env.INSTALL_NAME }}.dmg
|
path: lite-xl-macosx-*.zip
|
||||||
- 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:
|
|
||||||
matrix:
|
|
||||||
msystem: [MINGW32, MINGW64]
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: msys2 {0}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: msys2/setup-msys2@v2
|
|
||||||
with:
|
|
||||||
#msystem: MINGW64
|
|
||||||
msystem: ${{ matrix.msystem }}
|
|
||||||
update: true
|
|
||||||
install: >-
|
|
||||||
base-devel
|
|
||||||
git
|
|
||||||
zip
|
|
||||||
- name: Set Environment Variables
|
|
||||||
run: |
|
|
||||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
||||||
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-windows-$(uname -m)" >> "$GITHUB_ENV"
|
|
||||||
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
|
|
||||||
- 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
|
|
||||||
- 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
|
|
||||||
|
|
|
@ -1,26 +1,13 @@
|
||||||
build*/
|
build*
|
||||||
.build*/
|
.build*
|
||||||
lhelper/
|
|
||||||
submodules/
|
|
||||||
subprojects/lua/
|
|
||||||
subprojects/reproc/
|
|
||||||
/appimage*
|
|
||||||
.ccls-cache
|
|
||||||
.lite-debug.log
|
|
||||||
.run*
|
.run*
|
||||||
*.diff
|
|
||||||
*.exe
|
|
||||||
*.tar.gz
|
|
||||||
*.zip
|
*.zip
|
||||||
*.DS_Store
|
*.tar.gz
|
||||||
*App*
|
.lite-debug.log
|
||||||
compile_commands.json
|
subprojects/lua
|
||||||
|
subprojects/libagg
|
||||||
|
subprojects/reproc
|
||||||
|
lite-xl
|
||||||
error.txt
|
error.txt
|
||||||
lite-xl*
|
.ccls-cache
|
||||||
LiteXL*
|
compile_commands.json
|
||||||
lite
|
|
||||||
.config/
|
|
||||||
*.lha
|
|
||||||
release_files
|
|
||||||
*.o
|
|
||||||
|
|
||||||
|
|
82
Makefile.os4
82
Makefile.os4
|
@ -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
117
README.md
|
@ -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
|
||||||
|
|
204
README_OS4.md
204
README_OS4.md
|
@ -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.
|
@ -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"
|
}
|
||||||
|
|
||||||
|
# Build using Profile Guided Optimizations (PGO)
|
||||||
|
lite_build_pgo () {
|
||||||
|
local build="$1"
|
||||||
|
build_dir_is_usable "$build" || exit 1
|
||||||
|
rm -fr "$build"
|
||||||
|
meson setup --buildtype=release -Db_pgo=generate "$build" || exit 1
|
||||||
|
ninja -C "$build" || exit 1
|
||||||
|
copy_directory_from_repo data "$build/src"
|
||||||
|
"$build/src/lite-xl"
|
||||||
|
meson configure -Db_pgo=use "$build"
|
||||||
|
ninja -C "$build" || exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
lite_build_package_windows () {
|
||||||
|
local portable="-msys"
|
||||||
|
if [ "$1" == "-portable" ]; then
|
||||||
|
portable=""
|
||||||
shift
|
shift
|
||||||
|
fi
|
||||||
|
local build="$1"
|
||||||
|
local arch="$2"
|
||||||
|
local os="win"
|
||||||
|
local pdir=".package-build/lite-xl"
|
||||||
|
if [ -z "$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.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"
|
||||||
|
}
|
||||||
|
|
||||||
|
lite_build_package_macos () {
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
shift
|
||||||
;;
|
;;
|
||||||
-d|--destdir)
|
-branch=*)
|
||||||
dest_dir="$2"
|
use_branch="${1#-branch=}"
|
||||||
shift
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-f|--forcefallback)
|
|
||||||
force_fallback="--forcefallback"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-p|--prefix)
|
|
||||||
prefix="$2"
|
|
||||||
shift
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-v|--version)
|
|
||||||
version="$2"
|
|
||||||
shift
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-A|--appimage)
|
|
||||||
appimage="--appimage"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-B|--bundle)
|
|
||||||
bundle="--bundle"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-D|--dmg)
|
|
||||||
dmg="--dmg"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-I|--innosetup)
|
|
||||||
innosetup="--innosetup"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-P|--portable)
|
|
||||||
portable="--portable"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-S|--source)
|
|
||||||
source="--source"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-O|--pgo)
|
|
||||||
pgo="--pgo"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--debug)
|
|
||||||
debug="--debug"
|
|
||||||
set -x
|
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
# unknown option
|
arch="$1"
|
||||||
;;
|
break
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ -n $1 ]]; then
|
if [ -z ${arch+set} ]; then
|
||||||
show_help
|
echo "usage: $0 [options] <arch>"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n $build_dir ]]; then build_dir_option=("--builddir" "${build_dir}"); fi
|
if [ -z ${use_branch+set} ]; then
|
||||||
if [[ -n $dest_dir ]]; then dest_dir_option=("--destdir" "${dest_dir}"); fi
|
use_branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
if [[ -n $prefix ]]; then prefix_option=("--prefix" "${prefix}"); fi
|
fi
|
||||||
if [[ -n $version ]]; then version_option=("--version" "${version}"); fi
|
|
||||||
|
|
||||||
source scripts/build.sh \
|
build_dir=".build-$arch"
|
||||||
${build_dir_option[@]} \
|
|
||||||
${prefix_option[@]} \
|
|
||||||
$debug \
|
|
||||||
$force_fallback \
|
|
||||||
$bundle \
|
|
||||||
$portable \
|
|
||||||
$pgo
|
|
||||||
|
|
||||||
source scripts/package.sh \
|
if [ -z ${pgo+set} ]; then
|
||||||
${build_dir_option[@]} \
|
lite_build "$build_dir"
|
||||||
${dest_dir_option[@]} \
|
else
|
||||||
${prefix_option[@]} \
|
lite_build_pgo "$build_dir"
|
||||||
${version_option[@]} \
|
fi
|
||||||
--binary \
|
lite_copy_third_party_modules "$build_dir"
|
||||||
--addons \
|
lite_build_package "$build_dir" "$arch"
|
||||||
$debug \
|
if [[ ! ( "$OSTYPE" == "linux"* || "$OSTYPE" == "freebsd"* || "$OSTYPE" == "darwin"* ) ]]; then
|
||||||
$appimage \
|
lite_build_package -portable "$build_dir" "$arch"
|
||||||
$dmg \
|
fi
|
||||||
$innosetup \
|
|
||||||
$source
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@"
|
|
||||||
|
|
106
changelog.md
106
changelog.md
|
@ -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.
|
||||||
|
|
|
@ -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) }
|
|
|
@ -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
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
local core = require "core"
|
||||||
|
local command = require "core.command"
|
||||||
|
|
||||||
|
|
||||||
|
command.add(nil, {
|
||||||
|
["context-menu:show"] = function()
|
||||||
|
core.context_menu:show(core.active_view.position.x, core.active_view.position.y)
|
||||||
|
end
|
||||||
|
})
|
|
@ -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,7 +66,7 @@ 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 = {}
|
||||||
|
@ -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,
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
@ -394,30 +393,6 @@ local commands = {
|
||||||
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)
|
||||||
doc():merge_cursors()
|
doc():merge_cursors()
|
||||||
|
@ -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)
|
|
||||||
|
|
|
@ -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
|
|
||||||
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)
|
|
||||||
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 l1, c1, l2, c2 = doc():get_selection(true)
|
||||||
local text = doc():get_text(l1, c1, l2, c2)
|
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 })
|
||||||
end
|
|
||||||
if l2 then doc():set_selection(l2, c2, l1, c1) end
|
if l2 then doc():set_selection(l2, c2, l1, c1) end
|
||||||
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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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,13 +21,7 @@ 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,
|
|
||||||
|
|
||||||
["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,
|
end,
|
||||||
|
|
||||||
["root:switch-to-previous-tab"] = function()
|
["root:switch-to-previous-tab"] = function()
|
||||||
|
@ -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
|
|
||||||
})
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 = item.info or 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,16 +66,12 @@ 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
|
|
||||||
local lw, lh = get_item_size(subitems)
|
|
||||||
items_list.height = items_list.height + lh
|
|
||||||
table.insert(items_list, subitems)
|
table.insert(items_list, subitems)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if #items_list > 0 then
|
if #items_list > 0 then
|
||||||
self.items = items_list
|
self.items = items_list
|
||||||
|
@ -133,6 +129,7 @@ function ContextMenu:on_mouse_moved(px, py)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ContextMenu:on_selected(item)
|
function ContextMenu:on_selected(item)
|
||||||
|
if item.disabled then return end
|
||||||
if type(item.command) == "string" then
|
if type(item.command) == "string" then
|
||||||
command.perform(item.command)
|
command.perform(item.command)
|
||||||
else
|
else
|
||||||
|
@ -207,11 +204,12 @@ function ContextMenu:draw_context_menu()
|
||||||
if item == DIVIDER then
|
if item == DIVIDER then
|
||||||
renderer.draw_rect(x, y, w, h, style.caret)
|
renderer.draw_rect(x, y, w, h, style.caret)
|
||||||
else
|
else
|
||||||
if i == self.selected then
|
if i == self.selected and not self.disabled then
|
||||||
renderer.draw_rect(x, y, w, h, style.selection)
|
renderer.draw_rect(x, y, w, h, style.selection)
|
||||||
end
|
end
|
||||||
|
|
||||||
common.draw_text(style.font, style.text, item.text, "left", x + style.padding.x, y, w, h)
|
local text_color = item.disabled and style.dim or style.text
|
||||||
|
common.draw_text(style.font, text_color, item.text, "left", x + style.padding.x, y, w, h)
|
||||||
if item.info then
|
if item.info then
|
||||||
common.draw_text(style.font, style.dim, item.info, "right", x, y, w - style.padding.x, h)
|
common.draw_text(style.font, style.dim, item.info, "right", x, y, w - style.padding.x, h)
|
||||||
end
|
end
|
||||||
|
|
|
@ -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 = {}
|
||||||
|
|
|
@ -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
|
||||||
|
@ -357,21 +318,13 @@ 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
|
||||||
|
@ -389,16 +342,8 @@ 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)
|
||||||
|
|
|
@ -22,63 +22,38 @@ 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
|
||||||
|
local s, e = re:cmatch(line_text, col)
|
||||||
|
if s then
|
||||||
|
return line, s, line, e
|
||||||
|
end
|
||||||
|
col = 1
|
||||||
|
else
|
||||||
|
if opt.no_case then
|
||||||
line_text = line_text:lower()
|
line_text = line_text:lower()
|
||||||
end
|
end
|
||||||
local s, e
|
local s, e = line_text:find(text, col, true)
|
||||||
if opt.reverse then
|
|
||||||
s, e = rfind(search_func, line_text, pattern, col - 1, plain)
|
|
||||||
else
|
|
||||||
s, e = search_func(line_text, pattern, col, plain)
|
|
||||||
end
|
|
||||||
if s then
|
if s then
|
||||||
return line, s, line, e + 1
|
return line, s, line, e + 1
|
||||||
end
|
end
|
||||||
col = opt.reverse and -1 or 1
|
col = 1
|
||||||
|
end
|
||||||
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, #doc.lines, #doc.lines[#doc.lines], text, opt)
|
|
||||||
else
|
|
||||||
return search.find(doc, 1, 1, text, opt)
|
return search.find(doc, 1, 1, text, opt)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,10 +366,15 @@ 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
|
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
|
||||||
|
|
||||||
-- draw line's text
|
-- draw line's text
|
||||||
|
@ -382,21 +403,19 @@ 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
|
|
||||||
or (core.blink_timer - core.blink_start) % T < T / 2 then
|
|
||||||
local x, y = self:get_line_screen_position(line)
|
local x, y = self:get_line_screen_position(line)
|
||||||
self:draw_caret(x + self:get_col_x_offset(line, col), y)
|
self:draw_caret(x + self:get_col_x_offset(line, col), y)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
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
|
||||||
|
|
|
@ -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
|
|
|
@ -9,6 +9,7 @@ local RootView
|
||||||
local StatusView
|
local StatusView
|
||||||
local TitleView
|
local TitleView
|
||||||
local CommandView
|
local CommandView
|
||||||
|
local ContextMenu
|
||||||
local NagView
|
local NagView
|
||||||
local DocView
|
local DocView
|
||||||
local Doc
|
local Doc
|
||||||
|
@ -17,7 +18,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 +31,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 +38,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 +54,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 +81,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 +101,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 +109,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
|
||||||
|
local info = system.get_file_info(root .. file)
|
||||||
|
if info and info.size < size_limit then
|
||||||
|
info.filename = strip_leading_path(file)
|
||||||
table.insert(info.type == "dir" and dirs or files, info)
|
table.insert(info.type == "dir" and dirs or files, info)
|
||||||
entries_count = entries_count + 1
|
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 +145,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
|
||||||
end
|
|
||||||
|
|
||||||
|
while true do
|
||||||
function core.project_subdir_is_shown(dir, filename)
|
-- get project files and replace previous table if the new table is
|
||||||
return not dir.files_limit or dir.shown_subdir[filename]
|
-- different
|
||||||
end
|
local i = 1
|
||||||
|
while not core.project_files_limit and i <= #core.project_directories do
|
||||||
|
local dir = core.project_directories[i]
|
||||||
local function show_max_files_warning(dir)
|
local t, entries_count = get_directory_files(dir.name, "", {}, true)
|
||||||
local message = dir.slow_filesystem and
|
if diff_files(dir.files, t) then
|
||||||
"Filesystem is too slow: project files will not be indexed." or
|
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 "..
|
"Too many files in project directory: stopped reading at "..
|
||||||
config.max_project_files.." files. For more information see "..
|
config.max_project_files.." files. For more information see "..
|
||||||
"usage.md at github.com/lite-xl/lite-xl."
|
"usage.md at github.com/franko/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
|
||||||
|
dir.files = t
|
||||||
|
core.redraw = true
|
||||||
end
|
end
|
||||||
while inf <= sup and not system.path_compare(filename, type, files[inf].filename, files[inf].type) do
|
if dir.name == core.project_dir then
|
||||||
if files[inf].filename == filename then
|
core.project_files = dir.files
|
||||||
return inf, true
|
|
||||||
end
|
end
|
||||||
inf = inf + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
return inf, false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
-- wait for next scan
|
||||||
local function project_scan_add_entry(dir, fileinfo)
|
coroutine.yield(config.project_scan_rate)
|
||||||
local index, match = file_search(dir.files, fileinfo)
|
|
||||||
if not match then
|
|
||||||
table.insert(dir.files, index, fileinfo)
|
|
||||||
dir.is_dirty = true
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function files_info_equal(a, b)
|
function core.is_project_folder(dirname)
|
||||||
return a.filename == b.filename and a.type == b.type
|
for _, dir in ipairs(core.project_directories) do
|
||||||
end
|
if dir.name == dirname then
|
||||||
|
|
||||||
-- 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
|
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
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local function project_subdir_bounds(dir, filename)
|
|
||||||
local index, n = 0, #dir.files
|
function core.scan_project_folder(dirname, filename)
|
||||||
|
for _, dir in ipairs(core.project_directories) do
|
||||||
|
if dir.name == dirname then
|
||||||
for i, file in ipairs(dir.files) do
|
for i, file in ipairs(dir.files) do
|
||||||
local file = dir.files[i]
|
local file = dir.files[i]
|
||||||
if file.filename == filename then
|
if file.filename == filename then
|
||||||
index, n = i, #dir.files - i
|
if file.scanned then return end
|
||||||
for j = 1, #dir.files - i do
|
local new_files = get_directory_files(dirname, PATHSEP .. filename, {})
|
||||||
if not common.path_belongs_to(dir.files[i + j].filename, filename) then
|
for j, new_file in ipairs(new_files) do
|
||||||
n = j - 1
|
table.insert(dir.files, i + j, new_file)
|
||||||
break
|
end
|
||||||
|
file.scanned = true
|
||||||
|
return
|
||||||
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
|
||||||
|
if not common.match_pattern(file, config.ignore_files) then
|
||||||
local file = path .. PATHSEP .. file
|
local file = path .. PATHSEP .. file
|
||||||
local info = system.get_file_info(root .. file)
|
local info = system.get_file_info(root .. file)
|
||||||
if info then
|
if info and info.size < size_limit then
|
||||||
info.filename = strip_leading_path(file)
|
info.filename = strip_leading_path(file)
|
||||||
if info.type == "file" then
|
if info.type == "file" then
|
||||||
coroutine.yield(root, info)
|
coroutine.yield(root, info)
|
||||||
else
|
else
|
||||||
find_files_rec(root, PATHSEP .. info.filename)
|
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
|
|
||||||
-- We have a coroutine to fetch for files, use the coroutine.
|
|
||||||
-- Used for directories that exceeds the files nuumber limit.
|
|
||||||
local ok, name, file = coroutine.resume(state.co, dir.name, "")
|
|
||||||
if ok and name then
|
|
||||||
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
|
state.file_index = state.file_index + 1
|
||||||
while dir and state.file_index > #dir.files do
|
while dir and state.file_index > #dir.files do
|
||||||
state.dir_index = state.dir_index + 1
|
state.dir_index = state.dir_index + 1
|
||||||
state.file_index = 1
|
state.file_index = 1
|
||||||
dir = core.project_directories[state.dir_index]
|
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()
|
||||||
|
if core.project_files_limit then
|
||||||
|
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 }
|
local state = { dir_index = 1, file_index = 0 }
|
||||||
return project_files_iter, state
|
return project_files_iter, state
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function core.project_files_number()
|
function core.project_files_number()
|
||||||
|
if not core.project_files_limit then
|
||||||
local n = 0
|
local n = 0
|
||||||
for i = 1, #core.project_directories do
|
for i = 1, #core.project_directories do
|
||||||
if core.project_directories[i].files_limit then return end
|
|
||||||
n = n + #core.project_directories[i].files
|
n = n + #core.project_directories[i].files
|
||||||
end
|
end
|
||||||
return n
|
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
|
|
||||||
|
|
||||||
|
|
||||||
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 +321,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 +370,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 +395,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
|
||||||
|
@ -591,6 +418,40 @@ local function reload_on_user_module_save()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function find_selected_occurence()
|
||||||
|
local doc = core.active_view.doc
|
||||||
|
if not doc or not doc:has_selection() then return end
|
||||||
|
|
||||||
|
-- get selection. If #358 is merged we won't need this
|
||||||
|
local text = {}
|
||||||
|
for idx, line1, col1, line2, col2 in doc:get_selections() do
|
||||||
|
if line1 ~= line2 or col1 ~= col2 then
|
||||||
|
local t = doc:get_text(line1, col1, line2, col2)
|
||||||
|
if t ~= "" then text[#text + 1] = t end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
text = table.concat(text, "\n")
|
||||||
|
|
||||||
|
command.perform "find-replace:find"
|
||||||
|
core.command_view:set_text(text)
|
||||||
|
core.command_view:submit()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function setup_context_menu()
|
||||||
|
local info = keymap.reverse_map["find-replace:find"]
|
||||||
|
core.context_menu:register("core.docview", {
|
||||||
|
{ text = "Find Occurence...", command = find_selected_occurence, info = info },
|
||||||
|
ContextMenu.DIVIDER,
|
||||||
|
{ text = "Cut", command = "doc:cut" },
|
||||||
|
{ text = "Copy", command = "doc:copy" },
|
||||||
|
{ text = "Paste", command = "doc:paste" },
|
||||||
|
ContextMenu.DIVIDER,
|
||||||
|
{ text = "Command Palette...", command = "core:find-command" }
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function core.init()
|
function core.init()
|
||||||
command = require "core.command"
|
command = require "core.command"
|
||||||
keymap = require "core.keymap"
|
keymap = require "core.keymap"
|
||||||
|
@ -598,26 +459,25 @@ function core.init()
|
||||||
StatusView = require "core.statusview"
|
StatusView = require "core.statusview"
|
||||||
TitleView = require "core.titleview"
|
TitleView = require "core.titleview"
|
||||||
CommandView = require "core.commandview"
|
CommandView = require "core.commandview"
|
||||||
|
ContextMenu = require "core.contextmenu"
|
||||||
NagView = require "core.nagview"
|
NagView = require "core.nagview"
|
||||||
DocView = require "core.docview"
|
DocView = require "core.docview"
|
||||||
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,12 +497,9 @@ 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.
|
|
||||||
if not ARGS[i]:match("^-psn") then
|
|
||||||
delayed_error = string.format("error: invalid file or directory %q", ARGS[i])
|
delayed_error = string.format("error: invalid file or directory %q", ARGS[i])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
core.frame_start = 0
|
core.frame_start = 0
|
||||||
core.clip_rect_stack = {{ 0,0,0,0 }}
|
core.clip_rect_stack = {{ 0,0,0,0 }}
|
||||||
|
@ -673,10 +530,11 @@ 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()
|
||||||
|
core.context_menu = ContextMenu()
|
||||||
core.status_view = StatusView()
|
core.status_view = StatusView()
|
||||||
core.nag_view = NagView()
|
core.nag_view = NagView()
|
||||||
core.title_view = TitleView()
|
core.title_view = TitleView()
|
||||||
|
@ -690,22 +548,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,21 +590,22 @@ 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
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
setup_context_menu()
|
||||||
reload_on_user_module_save()
|
reload_on_user_module_save()
|
||||||
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 +665,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 +699,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,23 +715,20 @@ 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, true) == 1 and 'userdir' or 'datadir'].plugins
|
local list = refused_list[plugin_dir:find(USERDIR) == 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
|
||||||
|
@ -888,7 +739,6 @@ function core.load_plugins()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
return no_errors, refused_list
|
return no_errors, refused_list
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -931,12 +781,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 +840,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 +912,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 +925,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 +937,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 +961,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 +1067,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 +1119,3 @@ end
|
||||||
|
|
||||||
|
|
||||||
return core
|
return core
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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,15 +199,16 @@ 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",
|
||||||
["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",
|
||||||
["ctrl+shift+up"] = "doc:create-cursor-previous-line",
|
["ctrl+shift+up"] = "doc:create-cursor-previous-line",
|
||||||
["ctrl+shift+down"] = "doc:create-cursor-next-line"
|
["ctrl+shift+down"] = "doc:create-cursor-next-line",
|
||||||
|
|
||||||
|
["menu"] = "context-menu:show"
|
||||||
}
|
}
|
||||||
|
|
||||||
return keymap
|
return keymap
|
||||||
|
|
||||||
|
|
|
@ -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 is_expanded(item) then
|
|
||||||
y = y + common.round(style.padding.y / 2)
|
|
||||||
_, 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
|
if item.info then
|
||||||
_, y = draw_text_multiline(style.font, item.info, x, y, style.dim)
|
subx, y = draw_text_multiline(style.font, item.info, subx, y, style.dim)
|
||||||
end
|
y = y + th
|
||||||
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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
@ -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
|
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
@ -12,11 +12,11 @@ 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
|
||||||
max_suggestions = 100,
|
max_suggestions = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
local autocomplete = {}
|
local autocomplete = {}
|
||||||
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
-- check all doc modified times
|
||||||
for _, doc in ipairs(core.docs) do
|
for _, doc in ipairs(core.docs) do
|
||||||
local info = system.get_file_info(doc.filename or "")
|
local info = system.get_file_info(doc.filename or "")
|
||||||
if doc.abs_filename == abs_filename and info and times[doc] ~= info.modified then
|
if info and times[doc] ~= info.modified then
|
||||||
reload_doc(doc)
|
reload_doc(doc)
|
||||||
break
|
|
||||||
end
|
end
|
||||||
|
coroutine.yield()
|
||||||
end
|
end
|
||||||
on_modify(dir, filepath)
|
|
||||||
end
|
-- wait for next scan
|
||||||
|
coroutine.yield(config.project_scan_rate)
|
||||||
|
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
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
-- mod-version:2 -- lite-xl 2.0
|
|
||||||
local core = require "core"
|
|
||||||
local command = require "core.command"
|
|
||||||
local keymap = require "core.keymap"
|
|
||||||
local ContextMenu = require "core.contextmenu"
|
|
||||||
local RootView = require "core.rootview"
|
|
||||||
|
|
||||||
local menu = ContextMenu()
|
|
||||||
local on_view_mouse_pressed = RootView.on_view_mouse_pressed
|
|
||||||
local on_mouse_moved = RootView.on_mouse_moved
|
|
||||||
local root_view_update = RootView.update
|
|
||||||
local root_view_draw = RootView.draw
|
|
||||||
|
|
||||||
function RootView:on_mouse_moved(...)
|
|
||||||
if menu:on_mouse_moved(...) then return end
|
|
||||||
on_mouse_moved(self, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
function RootView.on_view_mouse_pressed(button, x, y, clicks)
|
|
||||||
-- We give the priority to the menu to process mouse pressed events.
|
|
||||||
local handled = menu:on_mouse_pressed(button, x, y, clicks)
|
|
||||||
return handled or on_view_mouse_pressed(button, x, y, clicks)
|
|
||||||
end
|
|
||||||
|
|
||||||
function RootView:update(...)
|
|
||||||
root_view_update(self, ...)
|
|
||||||
menu:update()
|
|
||||||
end
|
|
||||||
|
|
||||||
function RootView:draw(...)
|
|
||||||
root_view_draw(self, ...)
|
|
||||||
menu:draw()
|
|
||||||
end
|
|
||||||
|
|
||||||
command.add(nil, {
|
|
||||||
["context:show"] = function()
|
|
||||||
menu:show(core.active_view.position.x, core.active_view.position.y)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
keymap.add {
|
|
||||||
["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
|
|
||||||
menu:register("core.docview", {
|
|
||||||
{ text = "Cut", command = "doc:cut" },
|
|
||||||
{ text = "Copy", command = "doc:copy" },
|
|
||||||
{ text = "Paste", command = "doc:paste" },
|
|
||||||
{ text = "Font +", command = "scale:increase" },
|
|
||||||
{ text = "Font -", command = "scale:decrease" },
|
|
||||||
{ text = "Font Reset", command = "scale:reset" },
|
|
||||||
ContextMenu.DIVIDER,
|
|
||||||
{ text = "Find", command = "find-replace:find" },
|
|
||||||
{ text = "Replace", command = "find-replace:replace" }
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
return menu
|
|
|
@ -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()
|
|
||||||
if not confirmed then
|
|
||||||
update_cache(self)
|
update_cache(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
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
|
|
||||||
})
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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" },
|
||||||
|
|
|
@ -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 = {
|
||||||
{
|
{
|
||||||
|
|
|
@ -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" },
|
||||||
|
|
|
@ -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 = "--",
|
||||||
|
|
|
@ -1,45 +1,11 @@
|
||||||
-- 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 = { "```python", "```" }, type = "string", syntax = ".py" },
|
|
||||||
{ pattern = { "```ruby", "```" }, type = "string", syntax = ".rb" },
|
|
||||||
{ pattern = { "```perl", "```" }, type = "string", syntax = ".pl" },
|
|
||||||
{ pattern = { "```php", "```" }, type = "string", syntax = ".php" },
|
|
||||||
{ pattern = { "```javascript", "```" }, type = "string", syntax = ".js" },
|
|
||||||
{ pattern = { "```html", "```" }, type = "string", syntax = ".html" },
|
|
||||||
{ pattern = { "```xml", "```" }, type = "string", syntax = ".xml" },
|
|
||||||
{ pattern = { "```css", "```" }, type = "string", syntax = ".css" },
|
|
||||||
{ pattern = { "```lua", "```" }, type = "string", syntax = ".lua" },
|
|
||||||
{ pattern = { "```bash", "```" }, type = "string", syntax = ".sh" },
|
|
||||||
{ 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 = "string" },
|
||||||
{ pattern = { "`", "`", "\\" }, type = "string" },
|
{ pattern = { "`", "`", "\\" }, type = "string" },
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
-- 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" },
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
-- 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 offset = self:get_font():get_width_subpixel(ns) / ss
|
||||||
local x = self:get_line_screen_position(1) + offset
|
local x = self:get_line_screen_position(1) + offset
|
||||||
local y = self.position.y
|
local y = self.position.y
|
||||||
local w = math.ceil(SCALE * 1)
|
local w = math.ceil(SCALE * 1)
|
||||||
|
@ -16,6 +16,6 @@ function DocView:draw_overlay(...)
|
||||||
|
|
||||||
local color = style.guide or style.selection
|
local color = style.guide or style.selection
|
||||||
renderer.draw_rect(x, y, w, h, color)
|
renderer.draw_rect(x, y, w, h, color)
|
||||||
end
|
|
||||||
draw_overlay(self, ...)
|
draw_overlay(self, ...)
|
||||||
end
|
end
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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()
|
||||||
|
scale_level = 0
|
||||||
set_scale(default_scale)
|
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 {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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.last[dir.name] = dir.files
|
||||||
|
else
|
||||||
|
if dir.files ~= last_files then
|
||||||
self:invalidate_cache(dir.name)
|
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
|
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
|
||||||
|
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)
|
|
||||||
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)
|
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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
---
|
---
|
||||||
|
|
1595
lib/dmon/dmon.h
1595
lib/dmon/dmon.h
File diff suppressed because it is too large
Load Diff
|
@ -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__
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
lite_includes += include_directories('.')
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
};
|
|
@ -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,
|
||||||
|
)
|
||||||
|
|
|
@ -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;
|
|
@ -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.
|
||||||
|
|
158
meson.build
158
meson.build
|
@ -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
|
|
||||||
|
|
|
@ -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.
|
@ -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;
|
|
@ -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>
|
|
|
@ -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
|
|
|
@ -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 |
|
@ -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.
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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"
|
|
|
@ -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
|
|
117
scripts/build.sh
117
scripts/build.sh
|
@ -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 "$@"
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -4,12 +4,12 @@
|
||||||
#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]
|
||||||
|
@ -27,17 +27,14 @@ AppSupportURL={#MyAppURL}
|
||||||
AppUpdatesURL={#MyAppURL}
|
AppUpdatesURL={#MyAppURL}
|
||||||
|
|
||||||
#if Arch=="x64"
|
#if Arch=="x64"
|
||||||
ArchitecturesAllowed=x64
|
ArchitecturesAllowed=x64
|
||||||
ArchitecturesInstallIn64BitMode=x64
|
ArchitecturesInstallIn64BitMode={#Arch}
|
||||||
#define ArchInternal "x86_64"
|
|
||||||
#else
|
|
||||||
#define ArchInternal "i686"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AllowNoIcons=yes
|
AllowNoIcons=yes
|
||||||
Compression=lzma
|
Compression=lzma
|
||||||
SolidCompression=yes
|
SolidCompression=yes
|
||||||
DefaultDirName={autopf}/{#MyAppName}
|
DefaultDirName={autopf}\{#MyAppName}
|
||||||
DefaultGroupName={#MyAppPublisher}
|
DefaultGroupName={#MyAppPublisher}
|
||||||
UninstallFilesDir={app}
|
UninstallFilesDir={app}
|
||||||
|
|
||||||
|
@ -51,14 +48,14 @@ PrivilegesRequiredOverridesAllowed=dialog
|
||||||
UsedUserAreasWarning=no
|
UsedUserAreasWarning=no
|
||||||
|
|
||||||
OutputDir=.
|
OutputDir=.
|
||||||
OutputBaseFilename=LiteXL-{#MyAppVersion}-{#ArchInternal}-setup
|
OutputBaseFilename=LiteXL-{#MyAppVersion}-{#Arch}-setup
|
||||||
;DisableDirPage=yes
|
;DisableDirPage=yes
|
||||||
;DisableProgramGroupPage=yes
|
;DisableProgramGroupPage=yes
|
||||||
|
|
||||||
LicenseFile={#SourceDir}/LICENSE
|
LicenseFile={#SourceDir}\LICENSE
|
||||||
SetupIconFile={#SourceDir}/resources/icons/icon.ico
|
SetupIconFile={#SourceDir}\icon.ico
|
||||||
WizardImageFile="{#SourceDir}/scripts/innosetup/wizard-modern-image.bmp"
|
WizardImageFile="wizard-modern-image.bmp"
|
||||||
WizardSmallImageFile="{#SourceDir}/scripts/innosetup/litexl-55px.bmp"
|
WizardSmallImageFile="litexl-55px.bmp"
|
||||||
|
|
||||||
[Languages]
|
[Languages]
|
||||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||||
|
@ -69,20 +66,18 @@ Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescrip
|
||||||
Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked
|
Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked
|
||||||
|
|
||||||
[Files]
|
[Files]
|
||||||
Source: "{#BuildDir}/src/lite-xl.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "{#BuildDir}\lite-xl.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "{#BuildDir}/mingwLibs{#Arch}/*"; DestDir: "{app}"; Flags: ignoreversion ; Check: DirExists(ExpandConstant('{#BuildDir}/mingwLibs{#Arch}'))
|
Source: "{#BuildDir}\data\*"; DestDir: "{app}\data"; Flags: ignoreversion recursesubdirs
|
||||||
Source: "{#SourceDir}/data/*"; DestDir: "{app}/data"; Flags: ignoreversion recursesubdirs
|
|
||||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||||
|
|
||||||
[Icons]
|
[Icons]
|
||||||
|
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: not IsTaskSelected('portablemode')
|
||||||
|
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"; Check: not IsTaskSelected('portablemode')
|
||||||
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: not WizardIsTaskSelected('portablemode')
|
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon; Check: not IsTaskSelected('portablemode')
|
||||||
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"; Check: not WizardIsTaskSelected('portablemode')
|
|
||||||
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon; Check: not WizardIsTaskSelected('portablemode')
|
|
||||||
; Name: "{usersendto}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
|
||||||
|
|
||||||
[Run]
|
[Run]
|
||||||
Filename: "{app}/{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
Uninstallable=not WizardIsTaskSelected('portablemode')
|
Uninstallable=not IsTaskSelected('portablemode')
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue