Compare commits

..

2 Commits

Author SHA1 Message Date
George Sokianos 0bc333f57d Meson changes to build LiteXL 2023-01-29 17:57:21 +00:00
George Sokianos abfd7f5876 Build the Haiku version with a makefile 2023-01-29 17:55:21 +00:00
201 changed files with 2020 additions and 15120 deletions

View File

@ -30,9 +30,9 @@ jobs:
echo "$HOME/.local/bin" >> "$GITHUB_PATH" echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV" echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-linux-$(uname -m)-portable" >> "$GITHUB_ENV" echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-linux-$(uname -m)-portable" >> "$GITHUB_ENV"
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Python Setup - name: Python Setup
uses: actions/setup-python@v4 uses: actions/setup-python@v2
with: with:
python-version: 3.9 python-version: 3.9
- name: Update Packages - name: Update Packages
@ -47,21 +47,18 @@ jobs:
if: ${{ matrix.config.cc == 'gcc' }} if: ${{ matrix.config.cc == 'gcc' }}
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --binary run: bash scripts/package.sh --version ${INSTALL_REF} --debug --binary
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v2
if: ${{ matrix.config.cc == 'gcc' }} if: ${{ matrix.config.cc == 'gcc' }}
with: with:
name: Linux Artifacts name: Linux Artifacts
path: ${{ env.INSTALL_NAME }}.tar.gz path: ${{ env.INSTALL_NAME }}.tar.gz
build_macos: build_macos:
name: macOS name: macOS (x86_64)
runs-on: macos-11 runs-on: macos-11
env: env:
CC: clang CC: clang
CXX: clang++ CXX: clang++
strategy:
matrix:
arch: ['x86_64', 'arm64']
steps: steps:
- name: System Information - name: System Information
run: | run: |
@ -73,64 +70,24 @@ jobs:
run: | run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH" echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV" echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-macos-${{ matrix.arch }}" >> "$GITHUB_ENV" echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-macos-$(uname -m)" >> "$GITHUB_ENV"
if [[ $(uname -m) != ${{ matrix.arch }} ]]; then echo "ARCH=--cross-arch ${{ matrix.arch }}" >> "$GITHUB_ENV"; fi - uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Python Setup - name: Python Setup
uses: actions/setup-python@v4 uses: actions/setup-python@v2
with: with:
python-version: 3.9 python-version: 3.9
- name: Install Dependencies - name: Install Dependencies
# --lhelper will eliminate a warning with arm64 and libusb run: bash scripts/install-dependencies.sh --debug
run: bash scripts/install-dependencies.sh --debug --lhelper
- name: Build - name: Build
run: | run: |
bash --version bash --version
bash scripts/build.sh --bundle --debug --forcefallback $ARCH bash scripts/build.sh --bundle --debug --forcefallback
- name: Create DMG Image - name: Create DMG Image
run: bash scripts/package.sh --version ${INSTALL_REF} $ARCH --debug --dmg run: bash scripts/package.sh --version ${INSTALL_REF} --debug --dmg
- name: Upload DMG Image - name: Upload DMG Image
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v2
with: with:
name: macOS DMG Images name: macOS DMG Image
path: ${{ env.INSTALL_NAME }}.dmg
build_macos_universal:
name: macOS (Universal)
runs-on: macos-11
needs: build_macos
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_NAME=lite-xl-${GITHUB_REF##*/}-macos-universal" >> "$GITHUB_ENV"
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dmgbuild
run: pip install dmgbuild
- uses: actions/checkout@v3
- name: Download artifacts
uses: actions/download-artifact@v3
id: download
with:
name: macOS DMG Images
path: dmgs-original
- name: Make universal bundles
run: |
bash --version
bash scripts/make-universal-binaries.sh ${{ steps.download.outputs.download-path }} "${INSTALL_NAME}"
- name: Upload DMG Image
uses: actions/upload-artifact@v3
with:
name: macOS Universal DMG Images
path: ${{ env.INSTALL_NAME }}.dmg path: ${{ env.INSTALL_NAME }}.dmg
build_windows_msys2: build_windows_msys2:
@ -138,26 +95,20 @@ jobs:
runs-on: windows-2019 runs-on: windows-2019
strategy: strategy:
matrix: matrix:
config: msystem: [MINGW32, MINGW64]
- {msystem: MINGW32, arch: i686}
- {msystem: MINGW64, arch: x86_64}
defaults: defaults:
run: run:
shell: msys2 {0} shell: msys2 {0}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- uses: msys2/setup-msys2@v2 - uses: msys2/setup-msys2@v2
with: with:
msystem: ${{ matrix.config.msystem }} msystem: ${{ matrix.msystem }}
update: true
install: >- install: >-
base-devel base-devel
git git
zip zip
mingw-w64-${{ matrix.config.arch }}-gcc
mingw-w64-${{ matrix.config.arch }}-meson
mingw-w64-${{ matrix.config.arch }}-ninja
mingw-w64-${{ matrix.config.arch }}-ca-certificates
mingw-w64-${{ matrix.config.arch }}-ntldd
- name: Set Environment Variables - name: Set Environment Variables
run: | run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH" echo "$HOME/.local/bin" >> "$GITHUB_PATH"
@ -168,7 +119,6 @@ jobs:
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-windows-i686" >> "$GITHUB_ENV" echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-windows-i686" >> "$GITHUB_ENV"
fi fi
- name: Install Dependencies - name: Install Dependencies
if: false
run: bash scripts/install-dependencies.sh --debug run: bash scripts/install-dependencies.sh --debug
- name: Build - name: Build
run: | run: |
@ -177,7 +127,7 @@ jobs:
- name: Package - name: Package
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --binary run: bash scripts/package.sh --version ${INSTALL_REF} --debug --binary
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v2
with: with:
name: Windows Artifacts name: Windows Artifacts
path: ${{ env.INSTALL_NAME }}.zip path: ${{ env.INSTALL_NAME }}.zip
@ -187,41 +137,34 @@ jobs:
runs-on: windows-2019 runs-on: windows-2019
strategy: strategy:
matrix: matrix:
arch: arch: [amd64, amd64_x86]
- { target: x86, name: i686 }
- { target: x64, name: x86_64 }
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- uses: ilammy/msvc-dev-cmd@v1 - uses: ilammy/msvc-dev-cmd@v1
with: with:
arch: ${{ matrix.arch.target }} arch: ${{ matrix.arch }}
- uses: actions/setup-python@v4 - uses: actions/setup-python@v1
with: with:
python-version: '3.x' python-version: '3.x'
- name: Install meson and ninja - name: Install meson and ninja
run: pip install meson ninja run: pip install meson ninja
- name: Set up environment variables - name: Set up environment variables
run: | run: |
"INSTALL_NAME=lite-xl-$($env:GITHUB_REF -replace ".*/")-windows-msvc-${{ matrix.arch.name }}" >> $env:GITHUB_ENV "INSTALL_NAME=lite-xl-$($env:GITHUB_REF -replace ".*/")-windows-msvc-${{ matrix.arch }}" >> $env:GITHUB_ENV
"INSTALL_REF=$($env:GITHUB_REF -replace ".*/")" >> $env:GITHUB_ENV "INSTALL_REF=$($env:GITHUB_REF -replace ".*/")" >> $env:GITHUB_ENV
"LUA_SUBPROJECT_PATH=subprojects/$(awk -F ' *= *' '/directory/ { printf $2 }' subprojects/lua.wrap)" >> $env:GITHUB_ENV "LUA_SUBPROJECT_PATH=subprojects/lua-5.4.4" >> $env:GITHUB_ENV
- name: Download and patch subprojects
shell: bash
run: |
meson subprojects download
cat resources/windows/001-lua-unicode.diff | patch -Np1 -d "$LUA_SUBPROJECT_PATH"
- name: Configure - name: Configure
run: | run: |
meson setup --wrap-mode=forcefallback build meson setup --wrap-mode=forcefallback build
Get-Content -Path resources/windows/001-lua-unicode.diff -Raw | patch -d $env:LUA_SUBPROJECT_PATH -p1 --forward
- name: Build - name: Build
run: | run: meson install -C build --destdir="../lite-xl"
meson install -C build --destdir="../lite-xl"
- name: Package - name: Package
run: | run: |
Remove-Item -Recurse -Force -Path "lite-xl/lib","lite-xl/include" Remove-Item -Recurse -Force -Path "lite-xl/lib","lite-xl/include"
Compress-Archive -Path lite-xl -DestinationPath "$env:INSTALL_NAME.zip" Compress-Archive -Path lite-xl -DestinationPath "$env:INSTALL_NAME.zip"
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v2
with: with:
name: Windows Artifacts (MSVC) name: Windows Artifacts (MSVC)
path: ${{ env.INSTALL_NAME }}.zip path: ${{ env.INSTALL_NAME }}.zip

View File

@ -9,21 +9,19 @@ on:
inputs: inputs:
version: version:
description: Release Version description: Release Version
default: v2.1.4 default: v2.1.1
required: true required: true
jobs: jobs:
release: release:
name: Create Release name: Create Release
runs-on: ubuntu-latest runs-on: ubuntu-18.04
permissions:
contents: write
outputs: outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
version: ${{ steps.tag.outputs.version }} version: ${{ steps.tag.outputs.version }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Fetch Version - name: Fetch Version
id: tag id: tag
run: | run: |
@ -51,8 +49,7 @@ jobs:
build_linux: build_linux:
name: Linux name: Linux
needs: release needs: release
runs-on: ubuntu-latest runs-on: ubuntu-18.04
container: ghcr.io/lite-xl/lite-xl-build-box:latest
env: env:
CC: gcc CC: gcc
CXX: g++ CXX: g++
@ -61,27 +58,17 @@ jobs:
run: | run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH" echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_REF=${{ needs.release.outputs.version }}" >> "$GITHUB_ENV" echo "INSTALL_REF=${{ needs.release.outputs.version }}" >> "$GITHUB_ENV"
- uses: actions/checkout@v3 - uses: actions/checkout@v2
# disabled because this will break our own Python install
- name: Python Setup - name: Python Setup
if: false uses: actions/setup-python@v2
uses: actions/setup-python@v4
with: with:
python-version: 3.9 python-version: 3.9
# disabled because the container has up-to-date packages
- name: Update Packages - name: Update Packages
if: false
run: sudo apt-get update run: sudo apt-get update
# disabled as the dependencies are already installed
- name: Install Dependencies - name: Install Dependencies
if: false
run: | run: |
bash scripts/install-dependencies.sh --debug bash scripts/install-dependencies.sh --debug
sudo apt-get install -y ccache sudo apt-get install -y ccache
- name: Build Portable - name: Build Portable
run: | run: |
bash --version bash --version
@ -106,7 +93,7 @@ jobs:
LiteXL-${{ env.INSTALL_REF }}-addons-x86_64.AppImage LiteXL-${{ env.INSTALL_REF }}-addons-x86_64.AppImage
build_macos: build_macos:
name: macOS name: macOS (x86_64)
needs: release needs: release
runs-on: macos-11 runs-on: macos-11
strategy: strategy:
@ -128,10 +115,9 @@ jobs:
echo "INSTALL_REF=${{ needs.release.outputs.version }}" >> "$GITHUB_ENV" echo "INSTALL_REF=${{ needs.release.outputs.version }}" >> "$GITHUB_ENV"
echo "INSTALL_NAME=lite-xl-${{ needs.release.outputs.version }}-macos-${{ matrix.arch }}" >> "$GITHUB_ENV" echo "INSTALL_NAME=lite-xl-${{ needs.release.outputs.version }}-macos-${{ matrix.arch }}" >> "$GITHUB_ENV"
echo "INSTALL_NAME_ADDONS=lite-xl-${{ needs.release.outputs.version }}-addons-macos-${{ matrix.arch }}" >> "$GITHUB_ENV" echo "INSTALL_NAME_ADDONS=lite-xl-${{ needs.release.outputs.version }}-addons-macos-${{ matrix.arch }}" >> "$GITHUB_ENV"
if [[ $(uname -m) != ${{ matrix.arch }} ]]; then echo "ARCH=--cross-arch ${{ matrix.arch }}" >> "$GITHUB_ENV"; fi - uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Python Setup - name: Python Setup
uses: actions/setup-python@v4 uses: actions/setup-python@v2
with: with:
python-version: 3.9 python-version: 3.9
- name: Install Dependencies - name: Install Dependencies
@ -139,18 +125,11 @@ jobs:
- name: Build - name: Build
run: | run: |
bash --version bash --version
bash scripts/build.sh --bundle --debug --forcefallback --release $ARCH CROSS_ARCH=${{ matrix.arch }} bash scripts/build.sh --bundle --debug --forcefallback --release
- name: Create DMG Image - name: Create DMG Image
run: | run: |
bash scripts/package.sh --version ${INSTALL_REF} $ARCH --debug --dmg --release CROSS_ARCH=${{ matrix.arch }} bash scripts/package.sh --version ${INSTALL_REF} --debug --dmg --release
bash scripts/package.sh --version ${INSTALL_REF} $ARCH --debug --addons --dmg --release CROSS_ARCH=${{ matrix.arch }} bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --dmg --release
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: macOS DMG Images
path: |
${{ env.INSTALL_NAME }}.dmg
${{ env.INSTALL_NAME_ADDONS }}.dmg
- name: Upload Files - name: Upload Files
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
@ -160,54 +139,6 @@ jobs:
${{ env.INSTALL_NAME }}.dmg ${{ env.INSTALL_NAME }}.dmg
${{ env.INSTALL_NAME_ADDONS }}.dmg ${{ env.INSTALL_NAME_ADDONS }}.dmg
build_macos_universal:
name: macOS (Universal)
needs: [release, build_macos]
runs-on: macos-11
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_BASE=lite-xl-${{ needs.release.outputs.version }}-macos" >> "$GITHUB_ENV"
echo "INSTALL_BASE_ADDONS=lite-xl-${{ needs.release.outputs.version }}-addons-macos" >> "$GITHUB_ENV"
- uses: actions/checkout@v2
- name: Download Artifacts
uses: actions/download-artifact@v3
id: download
with:
name: macOS DMG Images
path: dmgs-original
- name: Python Setup
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install dmgbuild
run: pip install dmgbuild
- name: Prepare DMG Images
run: |
mkdir -p dmgs-addons dmgs-normal
mv -v "${{ steps.download.outputs.download-path }}/$INSTALL_BASE-"{x86_64,arm64}.dmg dmgs-normal
mv -v "${{ steps.download.outputs.download-path }}/$INSTALL_BASE_ADDONS-"{x86_64,arm64}.dmg dmgs-addons
- name: Create Universal DMGs
run: |
bash --version
bash scripts/make-universal-binaries.sh dmgs-normal "$INSTALL_BASE-universal"
bash scripts/make-universal-binaries.sh dmgs-addons "$INSTALL_BASE_ADDONS-universal"
- name: Upload Files
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.release.outputs.version }}
draft: true
files: |
${{ env.INSTALL_BASE }}-universal.dmg
${{ env.INSTALL_BASE_ADDONS }}-universal.dmg
build_windows_msys2: build_windows_msys2:
name: Windows name: Windows
needs: release needs: release
@ -219,7 +150,7 @@ jobs:
run: run:
shell: msys2 {0} shell: msys2 {0}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- uses: msys2/setup-msys2@v2 - uses: msys2/setup-msys2@v2
with: with:
msystem: ${{ matrix.msystem }} msystem: ${{ matrix.msystem }}

8
.gitignore vendored
View File

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

View File

@ -1,6 +1,4 @@
Copyright (c) 2020 rxi Copyright (c) 2020-2021 Lite XL Team
Copyright (c) 2020-2022 Francesco Abbate
Copyright (c) 2022-present Lite XL Team
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in

View File

@ -1,30 +1,32 @@
# #
# Project: Lite XL # Project: Lite XL
# #
# Created on: 26-12-2021 # Created on: 26-12-2021
# #
LiteXL_OBJ := \ LiteXL_OBJ := \
src/main.o src/rencache.o src/renderer.o src/renwindow.o \ src/main.o src/rencache.o src/renderer.o src/renwindow.o \
src/api/api.o src/api/dirmonitor.o \ src/api/api.o src/api/dirmonitor.o \
src/api/regex.o src/api/renderer.o src/api/system.o \ src/api/regex.o src/api/renderer.o src/api/system.o \
src/api/utf8.o src/platform/amigaos4.o \ src/api/utf8.o src/api/dirmonitor/dummy.o src/api/process.o
src/api/dirmonitor/os4.o src/platform/codesets.o
outfile := lite-xl outfile := lite-xl
compiler := gcc compiler := gcc
cxxcompiler := g++ cxxcompiler := g++
INCPATH := -Isrc -I/sdk/local/newlib/include/SDL2 \ INCPATH := -Isrc -Ilib/dmon -I/boot/system/develop/headers/SDL2 \
-I/sdk/local/common/include/lua54 -I/sdk/local/common/include/freetype2 -I/boot/system/develop/headers/lua54 -I/boot/home/development/freetype-2.12.1/include
DFLAGS += -D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR
DFLAGS += -D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR CFLAGS += -Werror -Wwrite-strings -O2 -g -std=gnu11 -fno-strict-aliasing
CFLAGS ?= -Werror -Wwrite-strings -O3 -std=gnu11 -fno-strict-aliasing
LFLAGS ?= -mcrt=newlib -lpcre2-8 -lSDL2 -llua54 -lfreetype -lpng -lz \ LFLAGS += -L/boot/home/development/freetype-2.12.1/objs/.libs \
-lpthread -athread=native -lpcre2-8 -lSDL2 -llua -lfreetype -lz -lm -lpthread
ifeq ($(DEBUG),1) ifeq ($(DEBUG),1)
CFLAGS += -g -gstabs CFLAGS += -gstabs
LFLAGS += -gstabs LFLAGS += -gstabs
ifeq ($(PROFYLER),1) ifeq ($(PROFYLER),1)
@ -45,7 +47,7 @@ clean:
LiteXL: $(LiteXL_OBJ) LiteXL: $(LiteXL_OBJ)
@echo "Linking LiteXL" @echo "Linking LiteXL"
@$(compiler) -o $(outfile) $(LiteXL_OBJ) $(LFLAGS) @$(compiler) -o $(outfile) $(LiteXL_OBJ) $(LFLAGS)
.c.o: .c.o:
@ -54,7 +56,7 @@ LiteXL: $(LiteXL_OBJ)
src/main.o: src/main.c src/api/api.h src/rencache.h \ src/main.o: src/main.c src/api/api.h src/rencache.h \
src/renderer.h src/platform/amigaos4.h src/platform/codesets.h src/renderer.h
src/rencache.o: src/rencache.c src/rencache.o: src/rencache.c
@ -68,26 +70,21 @@ src/api/regex.o: src/api/regex.c
src/api/renderer.o: src/api/renderer.c src/api/renderer.o: src/api/renderer.c
src/api/system.o: src/api/system.c src/platform/amigaos4.h src/api/system.o: src/api/system.c
src/platform/amigaos4.o: src/platform/amigaos4.c src/api/dirmonitor.o: src/api/dirmonitor.c src/api/dirmonitor/dummy.c
src/platform/codesets.o: src/platform/codesets.c src/api/utf8.o: src/api/utf8.c
src/api/dirmonitor.o: src/api/dirmonitor.c src/api/dirmonitor/os4.c src/api/dirmonitor/dummy.o: src/api/dirmonitor/dummy.c
src/api/utf8.o: src/api/utf8.c src/platform/amigaos4.h
src/api/dirmonitor/os4.o: src/api/dirmonitor/os4.c
src/api/process.o: src/api/process.c src/api/process.o: src/api/process.c
release: clean LiteXL release:
@echo "Creating release files..." @echo "Creating release files..."
@mkdir -p release/LiteXL2 @mkdir -p release/LiteXL2
@cp -r resources/amiga/* release/LiteXL2/ @cp -r release_files/* release/LiteXL2/
@mv release/LiteXL2/LiteXL2.info release/ @mv release/LiteXL2/LiteXL2.info release/
@mv release/LiteXL2/AutoInstall release/
@cp -r data release/LiteXL2/ @cp -r data release/LiteXL2/
@cp changelog.md release/LiteXL2/ @cp changelog.md release/LiteXL2/
@cp $(outfile) release/LiteXL2/ @cp $(outfile) release/LiteXL2/
@ -95,8 +92,8 @@ release: clean LiteXL
@cp README.md release/LiteXL2/ @cp README.md release/LiteXL2/
@cp README_Amiga.md release/LiteXL2/ @cp README_Amiga.md release/LiteXL2/
@cp LICENSE release/LiteXL2/ @cp LICENSE release/LiteXL2/
@cp -r licenses release/LiteXL2/
@echo "Creating release archive..." @echo "Creating release archive..."
@lha -aeqr3 a LiteXL2_OS4.lha release/ @lha -aeqr3 a LiteXL2_OS4.lha release/
@echo "Clean release files..." @echo "Clean release files..."
@delete release ALL QUIET FORCE @delete release ALL QUIET FORCE

View File

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

View File

@ -81,39 +81,6 @@ affects only the place where the application is actually installed.
Head over to [releases](https://github.com/lite-xl/lite-xl/releases) and download the version for your operating system. Head over to [releases](https://github.com/lite-xl/lite-xl/releases) and download the version for your operating system.
### Windows
Lite XL comes with installers on Windows for typical installations.
Alternatively, we provide ZIP archives that you can download and extract anywhere and run directly.
To make Lite XL portable (e.g. running Lite XL from a thumb drive),
simply create a `user` folder where `lite-xl.exe` is located.
Lite XL will load and store all your configurations and plugins in the folder.
### macOS
We provide DMG files for macOS. Simply drag the program into your Applications folder.
> **Important**
> Newer versions of Lite XL are signed with a self-signed certificate,
> so you'll have to follow these steps when running Lite XL for the first time.
>
> 1. Find Lite XL in Finder (do not open it in Launchpad).
> 2. Control-click Lite XL, then choose `Open` from the shortcut menu.
> 3. Click `Open` in the popup menu.
>
> The correct steps may vary between macOS versions, so you should refer to
> the [macOS User Guide](https://support.apple.com/en-my/guide/mac-help/mh40616/mac).
>
> On an older version of Lite XL, you will need to run these commands instead:
>
> ```sh
> # clears attributes from the directory
> xattr -cr /Applications/Lite\ XL.app
> ```
>
> Otherwise, macOS will display a **very misleading error** saying that the application is damaged.
### Linux ### Linux
Unzip the file and `cd` into the `lite-xl` directory: Unzip the file and `cd` into the `lite-xl` directory:
@ -125,15 +92,15 @@ cd lite-xl
To run lite-xl without installing: To run lite-xl without installing:
```sh ```sh
cd bin
./lite-xl ./lite-xl
``` ```
To install lite-xl copy files over into appropriate directories: To install lite-xl copy files over into appropriate directories:
```sh ```sh
rm -rf $HOME/.local/share/lite-xl $HOME/.local/bin/lite-xl mkdir -p $HOME/.local/bin && cp bin/lite-xl $HOME/.local/bin
mkdir -p $HOME/.local/bin && cp lite-xl $HOME/.local/bin/ cp -r share $HOME/.local
mkdir -p $HOME/.local/share/lite-xl && cp -r data/* $HOME/.local/share/lite-xl/
``` ```
If `$HOME/.local/bin` is not in PATH: If `$HOME/.local/bin` is not in PATH:

View File

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

Binary file not shown.

View File

@ -13,36 +13,33 @@ show_help() {
echo echo
echo "Common options:" echo "Common options:"
echo echo
echo "-h --help Show this help and exit." echo "-h --help Show this help and exit."
echo "-b --builddir DIRNAME Set the name of the build directory (not path)." echo "-b --builddir DIRNAME Set the name of the build directory (not path)."
echo " Default: '$(get_default_build_dir)'." echo " Default: '$(get_default_build_dir)'."
echo "-p --prefix PREFIX Install directory prefix." echo "-p --prefix PREFIX Install directory prefix."
echo " Default: '/'." echo " Default: '/'."
echo " --cross-platform PLATFORM The platform to cross compile for." echo " --debug Debug this script."
echo " --cross-arch ARCH The architecture to cross compile for."
echo " --debug Debug this script."
echo echo
echo "Build options:" echo "Build options:"
echo echo
echo "-f --forcefallback Force to build subprojects dependencies statically." echo "-f --forcefallback Force to build subprojects dependencies statically."
echo "-B --bundle Create an App bundle (macOS only)" echo "-B --bundle Create an App bundle (macOS only)"
echo "-P --portable Create a portable package." echo "-P --portable Create a portable package."
echo "-O --pgo Use profile guided optimizations (pgo)." echo "-O --pgo Use profile guided optimizations (pgo)."
echo " Requires running the application iteractively." echo " Requires running the application iteractively."
echo " --cross-file CROSS_FILE The cross file used for compiling."
echo echo
echo "Package options:" echo "Package options:"
echo echo
echo "-d --destdir DIRNAME Set the name of the package directory (not path)." echo "-d --destdir DIRNAME Set the name of the package directory (not path)."
echo " Default: 'lite-xl'." echo " Default: 'lite-xl'."
echo "-v --version VERSION Sets the version on the package name." echo "-v --version VERSION Sets the version on the package name."
echo "-A --appimage Create an AppImage (Linux only)." echo "-A --appimage Create an AppImage (Linux only)."
echo "-D --dmg Create a DMG disk image (macOS only)." echo "-D --dmg Create a DMG disk image (macOS only)."
echo " Requires dmgbuild." echo " Requires NPM and AppDMG."
echo "-I --innosetup Create an InnoSetup installer (Windows only)." echo "-I --innosetup Create an InnoSetup installer (Windows only)."
echo "-r --release Compile in release mode." echo "-r --release Compile in release mode."
echo "-S --source Create a source code package," echo "-S --source Create a source code package,"
echo " including subprojects dependencies." echo " including subprojects dependencies."
echo echo
} }
@ -63,12 +60,6 @@ main() {
local portable local portable
local pgo local pgo
local release local release
local cross_platform
local cross_platform_option=()
local cross_arch
local cross_arch_option=()
local cross_file
local cross_file_option=()
for i in "$@"; do for i in "$@"; do
case $i in case $i in
@ -132,21 +123,6 @@ main() {
pgo="--pgo" pgo="--pgo"
shift shift
;; ;;
--cross-platform)
cross_platform="$2"
shift
shift
;;
--cross-arch)
cross_arch="$2"
shift
shift
;;
--cross-file)
cross_file="$2"
shift
shift
;;
--debug) --debug)
debug="--debug" debug="--debug"
set -x set -x
@ -167,18 +143,10 @@ main() {
if [[ -n $dest_dir ]]; then dest_dir_option=("--destdir" "${dest_dir}"); fi if [[ -n $dest_dir ]]; then dest_dir_option=("--destdir" "${dest_dir}"); fi
if [[ -n $prefix ]]; then prefix_option=("--prefix" "${prefix}"); fi if [[ -n $prefix ]]; then prefix_option=("--prefix" "${prefix}"); fi
if [[ -n $version ]]; then version_option=("--version" "${version}"); fi if [[ -n $version ]]; then version_option=("--version" "${version}"); fi
if [[ -n $cross_platform ]]; then cross_platform_option=("--cross-platform" "${cross_platform}"); fi
if [[ -n $cross_arch ]]; then cross_arch_option=("--cross-arch" "${cross_arch}"); fi
if [[ -n $cross_file ]]; then cross_file_option=("--cross-file" "${cross_file}"); fi
source scripts/build.sh \ source scripts/build.sh \
${build_dir_option[@]} \ ${build_dir_option[@]} \
${prefix_option[@]} \ ${prefix_option[@]} \
${cross_platform_option[@]} \
${cross_arch_option[@]} \
${cross_file_option[@]} \
$debug \ $debug \
$force_fallback \ $force_fallback \
$bundle \ $bundle \
@ -191,8 +159,6 @@ main() {
${dest_dir_option[@]} \ ${dest_dir_option[@]} \
${prefix_option[@]} \ ${prefix_option[@]} \
${version_option[@]} \ ${version_option[@]} \
${cross_platform_option[@]} \
${cross_arch_option[@]} \
--binary \ --binary \
--addons \ --addons \
$debug \ $debug \

View File

@ -1,505 +1,5 @@
# Changes Log # Changes Log
## [2.1.4] - 2024-04-16
This release addresses severe bugs not found in previous releases,
and improves the usability of the program.
### Features
* Add `.pyi` extension to `language_python`.
([#1728](https://github.com/lite-xl/lite-xl/pull/1728))
* Improve autocomplete suggestions box behavior with long text
([#1734](https://github.com/lite-xl/lite-xl/pull/1734))
* Improve `CommandView` and autocomplete scroll behavior
([#1732](https://github.com/lite-xl/lite-xl/pull/1732))
* Add `from` symbol to support ESM
([#1754](https://github.com/lite-xl/lite-xl/pull/1754))
* Add Arduino syntax highlighting support in `language_cpp`
([#1767](https://github.com/lite-xl/lite-xl/pull/1767))
* Skip patterns matching nothing in tokenizer
([#1743](https://github.com/lite-xl/lite-xl/pull/1743))
### Fixes
* Fix uninitialized variables in `src/api/process.c`
([#1719](https://github.com/lite-xl/lite-xl/pull/1719))
* Fix `language_js` regex/comment distinction
([#1731](https://github.com/lite-xl/lite-xl/pull/1731))
* Fix compilation on non-MINGW64 platforms
([#1739](https://github.com/lite-xl/lite-xl/pull/1739))
* Limit `language_js` regex avoidance to numbers, and fix starting `/*` comments
([#1744](https://github.com/lite-xl/lite-xl/pull/1744))
* Fix `buffer_size` in `g_read` for Windows
([#1722](https://github.com/lite-xl/lite-xl/pull/1722))
* Fix missing permission for creating releases
([#1770](https://github.com/lite-xl/lite-xl/pull/1770))
### Other Changes
* Rectify LICENSE dates and owners
([#1748](https://github.com/lite-xl/lite-xl/pull/1748))
* Fix some typos in `core.init`
([#1755](https://github.com/lite-xl/lite-xl/pull/1755))
## [2.1.3] - 2024-01-29
This release addresses severe bugs not found in previous releases.
### Fixes
* Fix `doc:create-cursor-{previous,next}-line` with tabs
([#1697](https://github.com/lite-xl/lite-xl/pull/1697))
* Fix heap buffer overflow and memory leaks in process and renderer API
([#1705](https://github.com/lite-xl/lite-xl/pull/1705))
* Improve Python number syntax highlighting
([#1704](https://github.com/lite-xl/lite-xl/pull/1704))
* Fix inconsistent NagView options on `doc:save`
([#1696](https://github.com/lite-xl/lite-xl/pull/1696))
* Fix crashes with autoreload when files are deleted externally and replaced with a directory.
([#1698](https://github.com/lite-xl/lite-xl/pull/1698))
* Improve JavaScript number syntax highlighting
([#1710](https://github.com/lite-xl/lite-xl/pull/1710))
### Other Changes
* Process API style changes
([#1709](https://github.com/lite-xl/lite-xl/pull/1709))
## [2.1.2] - 2023-12-29
This release addresses some issues present in the previous release,
and improves the performance and stability of Lite XL.
### New Features
* The context menu in TreeView is now navigable with a keyboard.
([#1338](https://github.com/lite-xl/lite-xl/pull/1338))
* A universal build of Lite XL is now available for macOS.
This build runs natively on both Intel and Apple Silicon macs.
([#1458](https://github.com/lite-xl/lite-xl/pull/1458))
* Most Unicode characters should be displayed properly
if your fonts support them.
([#1524](https://github.com/lite-xl/lite-xl/pull/1524))
* LogView will no longer scroll automatically if the user had scrolled.
The LogView will only scroll automatically when the user scrolls up
to the last entry.
([#1546](https://github.com/lite-xl/lite-xl/pull/1546))
* When using different fonts (especially fonts that render different scripts),
the letters will be aligned vertically.
([#1560](https://github.com/lite-xl/lite-xl/pull/1560))
* Unsaved named files are now saved in the workspace.
([#1597](https://github.com/lite-xl/lite-xl/pull/1597))
* macOS builds are now signed with a developer certificate.
This allows the user to right click the application in Finder and execute
it directly.
([#1656](https://github.com/lite-xl/lite-xl/pull/1656))
### Performance Improvements
* Allow command buffer to be expanded.
([#1297](https://github.com/lite-xl/lite-xl/pull/1297))
* Use table.move to implement `common.splice`.
([#1324](https://github.com/lite-xl/lite-xl/pull/1324))
* Create renderer only when it doesn't exist.
([#1315](https://github.com/lite-xl/lite-xl/pull/1315))
* Avoid drawing hidden text in `DocView:draw_line_text`.
([#1298](https://github.com/lite-xl/lite-xl/pull/1298))
* Don't calculate widths per-uft8-char when not needed.
([#1409](https://github.com/lite-xl/lite-xl/pull/1409))
* Allow tokenizer to pause and resume in the middle of a line.
([#1444](https://github.com/lite-xl/lite-xl/pull/1444))
* Optimize CI build times on MSYS2.
([#1435](https://github.com/lite-xl/lite-xl/pull/1435))
* Significant memory usage improvements when using huge fonts on Windows.
([#1555](https://github.com/lite-xl/lite-xl/pull/1555))
* Optimize background tasks response time.
([#1601](https://github.com/lite-xl/lite-xl/pull/1601))
### Backward Incompatible Changes
* The native plugin API is now usable on multiple source files,
without causing any duplicated symbol errors during compilation.
Plugins using the new plugin API header must define `LITE_XL_PLUGIN_ENTRYPOINT`
before importing the header, in one of their source files.
([#1335](https://github.com/lite-xl/lite-xl/pull/1335))
* The native plugin API header now follows the Lua 5.4 API.
Previously, the plugin API header followed the Lua 5.2 API.
([#1436](https://github.com/lite-xl/lite-xl/pull/1436))
* On Linux, `process.start()` will now throw an error if `execv()` fails.
([#1363](https://github.com/lite-xl/lite-xl/pull/1363))
* Lite XL will use the default `SCALE` of 1 due to unreliable display
scale detection. This may be fixed in a later version of Lite XL.
Set the `LITE_SCALE` environment variable to override this value.
### Fixes
* Fix minor typos in user module
([#1289](https://github.com/lite-xl/lite-xl/pull/1289))
* Do not allow users to create an empty font group
([#1303](https://github.com/lite-xl/lite-xl/pull/1303))
* Fix a memory leak
([#1305](https://github.com/lite-xl/lite-xl/pull/1305))
* Make dirwatch sorting compatible with what file_bisect expects
([#1300](https://github.com/lite-xl/lite-xl/pull/1300))
* Handle readlink errors
([#1292](https://github.com/lite-xl/lite-xl/pull/1292))
* Disable horizontal scrolling when linewrapping is enabled
([#1309](https://github.com/lite-xl/lite-xl/pull/1309))
* Update widgets install location
* Add missing luaL_typeerror symbol to plugin API
([#1313](https://github.com/lite-xl/lite-xl/pull/1313))
* Defer lua error until after cleanup
([#1310](https://github.com/lite-xl/lite-xl/pull/1310))
* Make empty groups in regex.gmatch return their offset
([#1325](https://github.com/lite-xl/lite-xl/pull/1325))
* Add missing header declaration
* Fix msys build now requiring ca-certificates
([#1348](https://github.com/lite-xl/lite-xl/pull/1348))
* Fix path to macOS arm64 cross file in GitHub workflows
* Fix Doc contextmenu not registering commands if scale plugin is not found
([#1338](https://github.com/lite-xl/lite-xl/pull/1338))
* Fix TreeView contextmenu commands not working if the mouse hovers DocView
([#1338](https://github.com/lite-xl/lite-xl/pull/1338))
* Fix incorrect contextmenu predicate
([#1338](https://github.com/lite-xl/lite-xl/pull/1338))
* Properly rescale NagView on scale change
([#1379](https://github.com/lite-xl/lite-xl/pull/1379))
* Scale plugin also rescales `style.expanded_scrollbar_size`
([#1380](https://github.com/lite-xl/lite-xl/pull/1380))
* Improve DocView:get_visible_line_range precision
([#1382](https://github.com/lite-xl/lite-xl/pull/1382))
* Fix up some post 5.1/JIT Symbols
([#1385](https://github.com/lite-xl/lite-xl/pull/1385))
* Fix incorrect x_offset if opened docs have different tab sizes
([#1383](https://github.com/lite-xl/lite-xl/pull/1383))
* Use correct view for scrolling to find-replace:repeat-find results
([#1400](https://github.com/lite-xl/lite-xl/pull/1400))
* Improve text width calculation precision
([#1408](https://github.com/lite-xl/lite-xl/pull/1408))
* Add asynchronous process reaping
([#1412](https://github.com/lite-xl/lite-xl/pull/1412))
* Fix cursors positions when deleting multiple selections
([#1393](https://github.com/lite-xl/lite-xl/pull/1393),
[#1463](https://github.com/lite-xl/lite-xl/pull/1463))
* Fix invalid EXEFILE and EXEDIR on Windows
([#1396](https://github.com/lite-xl/lite-xl/pull/1396))
* Fix `os.getenv()` not supporting UTF-8 output
([#1397](https://github.com/lite-xl/lite-xl/pull/1397))
* Fix differing stacktrace on stdout and file
([#1404](https://github.com/lite-xl/lite-xl/pull/1404))
* Update api_require to expose more symbols
([#1437](https://github.com/lite-xl/lite-xl/pull/1437))
* Make system.path_compare more case-aware
([#1457](https://github.com/lite-xl/lite-xl/pull/1457))
* Fix for api_require wrong macro && conditions
([#1465](https://github.com/lite-xl/lite-xl/pull/1465))
* Merge carets after doc:move-to-{previous,next}-char
([#1462](https://github.com/lite-xl/lite-xl/pull/1462))
* Process API improvements (again)
([#1370](https://github.com/lite-xl/lite-xl/pull/1370))
* Make system.path_compare more digit-aware
([#1474](https://github.com/lite-xl/lite-xl/pull/1474))
* Check for HANDLE_INVALID in Process API
([#1475](https://github.com/lite-xl/lite-xl/pull/1475))
* Fix linewrapping bug to do with wordwrapping
* Fix compiler warning for printing size_t in rencache.c
* Return error string from C searcher
* Restore horizontal scroll position after scale change
([#494](https://github.com/lite-xl/lite-xl/pull/494))
* Fix memory leak in renderer.c when freeing glyphsets
* Move lineguide below blinking cursor
([#1511](https://github.com/lite-xl/lite-xl/pull/1511))
* Close lua state when exiting on a runtime error
([#1487](https://github.com/lite-xl/lite-xl/pull/1487))
* Mark linewrapping open_files table as weak
* Don't use core.status_view if not yet initialized when logging
* Revert "core syntax: strip the path from filename on syntax.get ([#1168](https://github.com/lite-xl/lite-xl/pull/1168))"
([#1322](https://github.com/lite-xl/lite-xl/pull/1322))
* Make Doc:sanitize_position return a more appropriate col
([#1469](https://github.com/lite-xl/lite-xl/pull/1469))
* Skip checking files if no filename was provided to syntax.get
* Normalize stroke before adding keybind
([#1334](https://github.com/lite-xl/lite-xl/pull/1334))
* Make DocView aware of scrollbars sizes
([#1177](https://github.com/lite-xl/lite-xl/pull/1177))
* Normalize strokes in fixed order
([#1572](https://github.com/lite-xl/lite-xl/pull/1572))
* Defer core:open-log until everything is loaded
([#1585](https://github.com/lite-xl/lite-xl/pull/1585))
* Fix returned percent when clicking the Scrollbar track
* Fix C++14 digit separators
([#1593](https://github.com/lite-xl/lite-xl/pull/1593))
* Make linewrapping consider the expanded Scrollbar size
* Fix dimmed text when antialiasing is turned off
([#1641](https://github.com/lite-xl/lite-xl/pull/1641))
* Mark unsaved named files as dirty
([#1598](https://github.com/lite-xl/lite-xl/pull/1598))
* Make `common.serialize()` locale-independent and nan/inf compatible
([#1640](https://github.com/lite-xl/lite-xl/pull/1640))
* Ignore keypresses during IME composition
([#1573](https://github.com/lite-xl/lite-xl/pull/1573))
* Fix deadlock if error handler jumps somewhere else
([#1647](https://github.com/lite-xl/lite-xl/pull/1647))
* Avoid considering single spaces in detectindent
([#1595](https://github.com/lite-xl/lite-xl/pull/1595))
* Fix deleting indentation with multiple cursors
([#1670](https://github.com/lite-xl/lite-xl/pull/1670))
* Fix `set_target_size` passing the wrong value to plugins
([#1657](https://github.com/lite-xl/lite-xl/pull/1657))
* Limit `system.{sleep,wait_event}` to `timeouts >= 0`
([#1666](https://github.com/lite-xl/lite-xl/pull/1666))
* Fix running core.step when receiving an event while not waiting
([#1667](https://github.com/lite-xl/lite-xl/pull/1667))
* Fix dirmonitor sorting issues
([#1599](https://github.com/lite-xl/lite-xl/pull/1599))
* Scale mouse coordinates by window scale
([#1630](https://github.com/lite-xl/lite-xl/pull/1630))
* Made coroutines make more sense, and fixed a bug
([#1381](https://github.com/lite-xl/lite-xl/pull/1381))
* Fix selecting newlines with `find-replace:select-add-{next,all}`
([#1608](https://github.com/lite-xl/lite-xl/pull/1608))
* Fix editing after undo not clearing the change id
([#1574](https://github.com/lite-xl/lite-xl/pull/1574))
* Fix language_js regex constant detection
([#1581](https://github.com/lite-xl/lite-xl/pull/1581))
* Fix patterns starting with `^` in tokenizer
([#1645](https://github.com/lite-xl/lite-xl/pull/1645))
* Use x offset to define render command rect in rencache_draw_text
([#1618](https://github.com/lite-xl/lite-xl/pull/1618))
* Improve font/color change detection in `language_md`
([#1614](https://github.com/lite-xl/lite-xl/pull/1614))
* Allow long commands and envs on process_start
([#1477](https://github.com/lite-xl/lite-xl/pull/1477))
* Fix typo in `drawwhitespace.lua`
* Fix NagBar save failed message
([#1678](https://github.com/lite-xl/lite-xl/pull/1678))
* Fix typo in `drawwhitespace.lua`
* Add autocompletion to multicursor
([#1394](https://github.com/lite-xl/lite-xl/pull/1394))
### Other Changes
* Make api_require's nodes const
([#1296](https://github.com/lite-xl/lite-xl/pull/1296))
* Don't set a value twice
([#1306](https://github.com/lite-xl/lite-xl/pull/1306))
* Center title and version in emptyview
([#1311](https://github.com/lite-xl/lite-xl/pull/1311))
* Use master branch for packaging plugins for addons release
* Reorganize resources folder and add wasm target
([#1244](https://github.com/lite-xl/lite-xl/pull/1244))
* Replace uses of SDL_Window with RenWindow
([#1319](https://github.com/lite-xl/lite-xl/pull/1319))
* Update dummy dirmonitor method signature to match prototypes
* Remove static libgcc from meson
([#1290](https://github.com/lite-xl/lite-xl/pull/1290))
* Pass RenWindow by argument
([#1321](https://github.com/lite-xl/lite-xl/pull/1321))
* Get rid of annoying forward slash on windows
([#1345](https://github.com/lite-xl/lite-xl/pull/1345))
* Improve plugins config table handling
([#1356](https://github.com/lite-xl/lite-xl/pull/1356))
* Add manifest on Windows
([#1405](https://github.com/lite-xl/lite-xl/pull/1405))
* Split Command struct into different structs for each command type
([#1407](https://github.com/lite-xl/lite-xl/pull/1407))
* Move SetProcessDPIAware to manifests
([#1413](https://github.com/lite-xl/lite-xl/pull/1413))
* Use clipping functions provided by SDL
([#1426](https://github.com/lite-xl/lite-xl/pull/1426))
* Aggregate SDL_Surfaces and their scale in RenSurface
([#1429](https://github.com/lite-xl/lite-xl/pull/1429))
* Disable trimwhitespace and drawwhitespace via their configs
([#1446](https://github.com/lite-xl/lite-xl/pull/1446))
* Bump dependency versions
([#1434](https://github.com/lite-xl/lite-xl/pull/1434))
* Improvements to cross-compilation
([#1458](https://github.com/lite-xl/lite-xl/pull/1458))
* Move native plugin API header into include/
([#1440](https://github.com/lite-xl/lite-xl/pull/1440))
* Build releases with Ubuntu 18.04 container
([#1460](https://github.com/lite-xl/lite-xl/pull/1460))
* Update GitHub Actions dependencies
* Make all parameters for set_window_hit_test optional in documentation
* Attach command buffer to Renderer Window
([#1472](https://github.com/lite-xl/lite-xl/pull/1472))
* Fix comment typo in object.lua
([#1541](https://github.com/lite-xl/lite-xl/pull/1541))
* Allow setting custom glyphset size
([#1542](https://github.com/lite-xl/lite-xl/pull/1542))
* Use FreeType header names in renderer.c
([#1554](https://github.com/lite-xl/lite-xl/pull/1554))
* Add documentation for core.common
([#1510](https://github.com/lite-xl/lite-xl/pull/1510))
* Document missing parameter for system.path_compare
([#1566](https://github.com/lite-xl/lite-xl/pull/1566))
* Add documentation for core.command
([#1564](https://github.com/lite-xl/lite-xl/pull/1564))
* Update the *Installing prebuild* section in README.md
([#1548](https://github.com/lite-xl/lite-xl/pull/1548))
* Update README.md to remove previously installed files
prior to installing a new version
* Use lite-xl Build Box to build releases
([#1571](https://github.com/lite-xl/lite-xl/pull/1571))
* Use Lua wrap by default
([#1481](https://github.com/lite-xl/lite-xl/pull/1481))
* Add documentation for contextmenu
([#1567](https://github.com/lite-xl/lite-xl/pull/1567))
* Use dmgbuild to create DMGs
([#1664](https://github.com/lite-xl/lite-xl/pull/1664))
* Un-hardcode lua subproject detection and update dependencies
([#1676](https://github.com/lite-xl/lite-xl/pull/1676))
* Make license time-independent
([#1655](https://github.com/lite-xl/lite-xl/pull/1655))
## [2.1.1] - 2022-12-29 ## [2.1.1] - 2022-12-29
### New Features ### New Features
@ -1504,9 +1004,6 @@ A new global variable `USERDIR` is exposed to point to the user's directory.
- subpixel font rendering with gamma correction - subpixel font rendering with gamma correction
[2.1.4]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.4
[2.1.3]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.3
[2.1.2]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.2
[2.1.1]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.1 [2.1.1]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.1
[2.1.0]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.0 [2.1.0]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.0
[2.0.5]: https://github.com/lite-xl/lite-xl/releases/tag/v2.0.5 [2.0.5]: https://github.com/lite-xl/lite-xl/releases/tag/v2.0.5

View File

@ -1,55 +1,25 @@
local core = require "core" local core = require "core"
local command = {} local command = {}
---A predicate function accepts arguments from `command.perform()` and evaluates to a boolean. </br>
---If the function returns true, then the function associated with the command is executed.
---
---The predicate function can also return other values after the boolean, which will
---be passed into the function associated with the command.
---@alias core.command.predicate_function fun(...: any): boolean, ...
---A predicate is a string, an Object or a function, that is used to determine
---whether a command should be executed.
---
---If the predicate is a string, it is resolved into an `Object` via `require()`
---and checked against the active view with `Object:extends()`. </br>
---For example, `"core.docview"` will match any view that inherits from `DocView`. </br>
---A `!` can be appended to the predicate to strictly match the current view via `Object:is()`,
---instead of matching any view that inherits the predicate.
---
---If the predicate is a table, it is checked against the active view with `Object:extends()`.
---Strict matching via `Object:is()` is not available.
---
---If the predicate is a function, it must behave like a predicate function.
---@see core.command.predicate_function
---@alias core.command.predicate string|core.object|core.command.predicate_function
---A command is identified by a command name.
---The command name contains a category and the name itself, separated by a colon (':').
---
---All commands should be in lowercase and should not contain whitespaces; instead
---they should be replaced by a dash ('-').
---@alias core.command.command_name string
---The predicate and its associated function.
---@class core.command.command
---@field predicate core.command.predicate_function
---@field perform fun(...: any)
---@type { [string]: core.command.command }
command.map = {} command.map = {}
---@type core.command.predicate_function
local always_true = function() return true end local always_true = function() return true end
---This function takes in a predicate and produces a predicate function ---Used iternally by command.add, statusview, and contextmenu to generate a
---that is internally used to dispatch and execute commands. ---function with a condition to evaluate returning the boolean result of this
---evaluation.
--- ---
---This function should not be called manually. ---If a string predicate is given it is treated as a require import that should
---@see core.command.predicate ---return a valid object which is checked against the current active view,
---@param predicate core.command.predicate|nil If nil, the predicate always evaluates to true. ---eg: "core.docview" will match any view that inherits from DocView. Appending
---@return core.command.predicate_function ---a `!` at the end of the string means we want to match the given object
---from the import strcitly eg: "core.docview!" only DocView is matched.
---A function that returns a boolean can be used instead to perform a custom
---evaluation, setting to nil means always evaluates to true.
---
---@param predicate string | table | function
---@return function
function command.generate_predicate(predicate) function command.generate_predicate(predicate)
predicate = predicate or always_true predicate = predicate or always_true
local strict = false local strict = false
@ -68,20 +38,10 @@ function command.generate_predicate(predicate)
predicate = function(...) return core.active_view:is(class), core.active_view, ... end predicate = function(...) return core.active_view:is(class), core.active_view, ... end
end end
end end
---@cast predicate core.command.predicate_function
return predicate return predicate
end end
---Adds commands to the map.
---
---The function accepts a table containing a list of commands
---and their functions. </br>
---If a command already exists, it will be replaced.
---@see core.command.predicate
---@see core.command.command_name
---@param predicate core.command.predicate
---@param map { [core.command.command_name]: fun(...: any) }
function command.add(predicate, map) function command.add(predicate, map)
predicate = command.generate_predicate(predicate) predicate = command.generate_predicate(predicate)
for name, fn in pairs(map) do for name, fn in pairs(map) do
@ -97,21 +57,11 @@ local function capitalize_first(str)
return str:sub(1, 1):upper() .. str:sub(2) return str:sub(1, 1):upper() .. str:sub(2)
end end
---Prettifies the command name.
---
---This function adds a space between the colon and the command name,
---replaces dashes with spaces and capitalizes the command appropriately.
---@see core.command.command_name
---@param name core.command.command_name
---@return string
function command.prettify_name(name) function command.prettify_name(name)
---@diagnostic disable-next-line: redundant-return-value
return name:gsub(":", ": "):gsub("-", " "):gsub("%S+", capitalize_first) return name:gsub(":", ": "):gsub("-", " "):gsub("%S+", capitalize_first)
end end
---Returns all the commands that can be executed (their predicates evaluate to true).
---@return core.command.command_name[]
function command.get_all_valid() function command.get_all_valid()
local res = {} local res = {}
local memoized_predicates = {} local memoized_predicates = {}
@ -126,10 +76,6 @@ function command.get_all_valid()
return res return res
end end
---Checks whether a command can be executed (its predicate evaluates to true).
---@param name core.command.command_name
---@param ... any
---@return boolean
function command.is_valid(name, ...) function command.is_valid(name, ...)
return command.map[name] and command.map[name].predicate(...) return command.map[name] and command.map[name].predicate(...)
end end
@ -152,30 +98,16 @@ local function perform(name, ...)
end end
---Performs a command. function command.perform(...)
--- local ok, res = core.try(perform, ...)
---The arguments passed into this function are forwarded to the predicate function. </br>
---If the predicate function returns more than 1 value, the other values are passed
---to the command.
---
---Otherwise, the arguments passed into this function are passed directly
---to the command.
---@see core.command.predicate
---@see core.command.predicate_function
---@param name core.command.command_name
---@param ... any
---@return boolean # true if the command is performed successfully.
function command.perform(name, ...)
local ok, res = core.try(perform, name, ...)
return not ok or res return not ok or res
end end
---Inserts the default commands for Lite XL into the map.
function command.add_defaults() function command.add_defaults()
local reg = { local reg = {
"core", "root", "command", "doc", "findreplace", "core", "root", "command", "doc", "findreplace",
"files", "dialog", "log", "statusbar" "files", "drawwhitespace", "dialog", "log", "statusbar"
} }
for _, name in ipairs(reg) do for _, name in ipairs(reg) do
require("core.commands." .. name) require("core.commands." .. name)

View File

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

View File

@ -43,9 +43,9 @@ local function save(filename)
core.log("Saved \"%s\"", saved_filename) core.log("Saved \"%s\"", saved_filename)
else else
core.error(err) core.error(err)
core.nag_view:show("Saving failed", string.format("Couldn't save file \"%s\". Do you want to save to another location?", doc().filename), { core.nag_view:show("Saving failed", string.format("Could not save \"%s\" do you want to save to another location?", doc().filename), {
{ text = "Yes", default_yes = true }, { font = style.font, text = "No", default_no = true },
{ text = "No", default_no = true } { font = style.font, text = "Yes" , default_yes = true }
}, function(item) }, function(item)
if item.text == "Yes" then if item.text == "Yes" then
core.add_thread(function() core.add_thread(function()
@ -62,10 +62,10 @@ local function cut_or_copy(delete)
local text = "" local text = ""
core.cursor_clipboard = {} core.cursor_clipboard = {}
core.cursor_clipboard_whole_line = {} core.cursor_clipboard_whole_line = {}
for idx, line1, col1, line2, col2 in doc():get_selections(true, true) do for idx, line1, col1, line2, col2 in doc():get_selections() do
if line1 ~= line2 or col1 ~= col2 then if line1 ~= line2 or col1 ~= col2 then
text = doc():get_text(line1, col1, line2, col2) text = doc():get_text(line1, col1, line2, col2)
full_text = full_text == "" and text or (text .. " " .. full_text) full_text = full_text == "" and text or (full_text .. " " .. text)
core.cursor_clipboard_whole_line[idx] = false core.cursor_clipboard_whole_line[idx] = false
if delete then if delete then
doc():delete_to_cursor(idx, 0) doc():delete_to_cursor(idx, 0)
@ -73,7 +73,7 @@ local function cut_or_copy(delete)
else -- Cut/copy whole line else -- Cut/copy whole line
-- Remove newline from the text. It will be added as needed on paste. -- Remove newline from the text. It will be added as needed on paste.
text = string.sub(doc().lines[line1], 1, -2) text = string.sub(doc().lines[line1], 1, -2)
full_text = full_text == "" and text .. "\n" or (text .. "\n" .. full_text) full_text = full_text == "" and text or (full_text .. text .. "\n")
core.cursor_clipboard_whole_line[idx] = true core.cursor_clipboard_whole_line[idx] = true
if delete then if delete then
if line1 < #doc().lines then if line1 < #doc().lines then
@ -83,24 +83,19 @@ local function cut_or_copy(delete)
else else
doc():remove(line1 - 1, math.huge, line1, math.huge) doc():remove(line1 - 1, math.huge, line1, math.huge)
end end
doc():set_selections(idx, line1, col1, line2, col2)
end end
end end
core.cursor_clipboard[idx] = text core.cursor_clipboard[idx] = text
end end
if delete then doc():merge_cursors() end
core.cursor_clipboard["full"] = full_text core.cursor_clipboard["full"] = full_text
system.set_clipboard(full_text) system.set_clipboard(full_text)
end end
local function split_cursor(dv, direction) local function split_cursor(direction)
local new_cursors = {} local new_cursors = {}
local dv_translate = direction < 0 for _, line1, col1 in doc():get_selections() do
and DocView.translate.previous_line if line1 + direction >= 1 and line1 + direction <= #doc().lines then
or DocView.translate.next_line table.insert(new_cursors, { line1 + direction, col1 })
for _, line1, col1 in dv.doc:get_selections() do
if line1 + direction >= 1 and line1 + direction <= #dv.doc.lines then
table.insert(new_cursors, { dv_translate(dv.doc, line1, col1, dv) })
end end
end end
-- add selections in the order that will leave the "last" added one as doc.last_selection -- add selections in the order that will leave the "last" added one as doc.last_selection
@ -110,7 +105,7 @@ local function split_cursor(dv, direction)
end end
for i = start, stop, direction do for i = start, stop, direction do
local v = new_cursors[i] local v = new_cursors[i]
dv.doc:add_selection(v[1], v[2]) doc():add_selection(v[1], v[2])
end end
core.blink_reset() core.blink_reset()
end end
@ -328,7 +323,7 @@ local commands = {
end, end,
["doc:delete"] = function(dv) ["doc:delete"] = function(dv)
for idx, line1, col1, line2, col2 in dv.doc:get_selections(true, true) do for idx, line1, col1, line2, col2 in dv.doc:get_selections() do
if line1 == line2 and col1 == col2 and dv.doc.lines[line1]:find("^%s*$", col1) then if line1 == line2 and col1 == col2 and dv.doc.lines[line1]:find("^%s*$", col1) then
dv.doc:remove(line1, col1, line1, math.huge) dv.doc:remove(line1, col1, line1, math.huge)
end end
@ -338,16 +333,15 @@ local commands = {
["doc:backspace"] = function(dv) ["doc:backspace"] = function(dv)
local _, indent_size = dv.doc:get_indent_info() local _, indent_size = dv.doc:get_indent_info()
for idx, line1, col1, line2, col2 in dv.doc:get_selections(true, true) do for idx, line1, col1, line2, col2 in dv.doc:get_selections() do
if line1 == line2 and col1 == col2 then if line1 == line2 and col1 == col2 then
local text = dv.doc:get_text(line1, 1, line1, col1) local text = dv.doc:get_text(line1, 1, line1, col1)
if #text >= indent_size and text:find("^ *$") then if #text >= indent_size and text:find("^ *$") then
dv.doc:delete_to_cursor(idx, 0, -indent_size) dv.doc:delete_to_cursor(idx, 0, -indent_size)
goto continue return
end end
end end
dv.doc:delete_to_cursor(idx, translate.previous_char) dv.doc:delete_to_cursor(idx, translate.previous_char)
::continue::
end end
end, end,
@ -624,12 +618,12 @@ local commands = {
end, end,
["doc:create-cursor-previous-line"] = function(dv) ["doc:create-cursor-previous-line"] = function(dv)
split_cursor(dv, -1) split_cursor(-1)
dv.doc:merge_cursors() dv.doc:merge_cursors()
end, end,
["doc:create-cursor-next-line"] = function(dv) ["doc:create-cursor-next-line"] = function(dv)
split_cursor(dv, 1) split_cursor(1)
dv.doc:merge_cursors() dv.doc:merge_cursors()
end end
@ -704,7 +698,6 @@ commands["doc:move-to-previous-char"] = function(dv)
dv.doc:move_to_cursor(idx, translate.previous_char) dv.doc:move_to_cursor(idx, translate.previous_char)
end end
end end
dv.doc:merge_cursors()
end end
commands["doc:move-to-next-char"] = function(dv) commands["doc:move-to-next-char"] = function(dv)
@ -715,7 +708,6 @@ commands["doc:move-to-next-char"] = function(dv)
dv.doc:move_to_cursor(idx, translate.next_char) dv.doc:move_to_cursor(idx, translate.next_char)
end end
end end
dv.doc:merge_cursors()
end end
command.add("core.docview", commands) command.add("core.docview", commands)

View File

@ -0,0 +1,16 @@
local command = require "core.command"
local config = require "core.config"
command.add(nil, {
["draw-whitespace:toggle"] = function()
config.draw_whitespace = not config.draw_whitespace
end,
["draw-whitespace:disable"] = function()
config.draw_whitespace = false
end,
["draw-whitespace:enable"] = function()
config.draw_whitespace = true
end,
})

View File

@ -164,16 +164,13 @@ local function is_in_any_selection(line, col)
end end
local function select_add_next(all) local function select_add_next(all)
local il1, ic1 local il1, ic1 = doc():get_selection(true)
for _, l1, c1, l2, c2 in doc():get_selections(true, true) do for idx, l1, c1, l2, c2 in doc():get_selections(true, true) do
if not il1 then
il1, ic1 = l1, c1
end
local text = doc():get_text(l1, c1, l2, c2) local text = doc():get_text(l1, c1, l2, c2)
repeat repeat
l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true }) l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true })
if l1 == il1 and c1 == ic1 then break end if l1 == il1 and c1 == ic1 then break end
if l2 and not is_in_any_selection(l2, c2) then if l2 and (all or not is_in_any_selection(l2, c2)) then
doc():add_selection(l2, c2, l1, c1) doc():add_selection(l2, c2, l1, c1)
if not all then if not all then
core.active_view:scroll_to_make_visible(l2, c2) core.active_view:scroll_to_make_visible(l2, c2)
@ -243,38 +240,34 @@ command.add("core.docview!", {
}) })
local function valid_for_finding() local function valid_for_finding()
-- Allow using this while in the CommandView return core.active_view:is(DocView) or core.active_view:is(CommandView)
if core.active_view:is(CommandView) and last_view then
return true, last_view
end
return core.active_view:is(DocView), core.active_view
end end
command.add(valid_for_finding, { command.add(valid_for_finding, {
["find-replace:repeat-find"] = function(dv) ["find-replace:repeat-find"] = function()
if not last_fn then if not last_fn then
core.error("No find to continue from") core.error("No find to continue from")
else else
local sl1, sc1, sl2, sc2 = dv.doc:get_selection(true) local sl1, sc1, sl2, sc2 = doc():get_selection(true)
local line1, col1, line2, col2 = last_fn(dv.doc, sl2, sc2, last_text, case_sensitive, find_regex, false) local line1, col1, line2, col2 = last_fn(doc(), sl1, sc2, last_text, case_sensitive, find_regex, false)
if line1 then if line1 then
dv.doc:set_selection(line2, col2, line1, col1) doc():set_selection(line2, col2, line1, col1)
dv:scroll_to_line(line2, true) last_view:scroll_to_line(line2, true)
else else
core.error("Couldn't find %q", last_text) core.error("Couldn't find %q", last_text)
end end
end end
end, end,
["find-replace:previous-find"] = function(dv) ["find-replace:previous-find"] = function()
if not last_fn then if not last_fn then
core.error("No find to continue from") core.error("No find to continue from")
else else
local sl1, sc1, sl2, sc2 = dv.doc:get_selection(true) local sl1, sc1, sl2, sc2 = doc():get_selection(true)
local line1, col1, line2, col2 = last_fn(dv.doc, sl1, sc1, last_text, case_sensitive, find_regex, true) local line1, col1, line2, col2 = last_fn(doc(), sl1, sc1, last_text, case_sensitive, find_regex, true)
if line1 then if line1 then
dv.doc:set_selection(line2, col2, line1, col1) doc():set_selection(line2, col2, line1, col1)
dv:scroll_to_line(line2, true) last_view:scroll_to_line(line2, true)
else else
core.error("Couldn't find %q", last_text) core.error("Couldn't find %q", last_text)
end end

View File

@ -50,7 +50,6 @@ local default_state = {
function CommandView:new() function CommandView:new()
CommandView.super.new(self, SingleLineDoc()) CommandView.super.new(self, SingleLineDoc())
self.suggestion_idx = 1 self.suggestion_idx = 1
self.suggestions_offset = 1
self.suggestions = {} self.suggestions = {}
self.suggestions_height = 0 self.suggestions_height = 0
self.last_change_id = 0 self.last_change_id = 0
@ -124,24 +123,6 @@ function CommandView:move_suggestion_idx(dir)
end end
end end
local function get_suggestions_offset()
local max_visible = math.min(max_suggestions, #self.suggestions)
if dir > 0 then
if self.suggestions_offset + max_visible < self.suggestion_idx + 1 then
return self.suggestion_idx - max_visible + 1
elseif self.suggestions_offset > self.suggestion_idx then
return self.suggestion_idx
end
else
if self.suggestions_offset > self.suggestion_idx then
return self.suggestion_idx
elseif self.suggestions_offset + max_visible < self.suggestion_idx + 1 then
return self.suggestion_idx - max_visible + 1
end
end
return self.suggestions_offset
end
if self.state.show_suggestions then if self.state.show_suggestions then
local n = self.suggestion_idx + dir local n = self.suggestion_idx + dir
self.suggestion_idx = overflow_suggestion_idx(n, #self.suggestions) self.suggestion_idx = overflow_suggestion_idx(n, #self.suggestions)
@ -165,8 +146,6 @@ function CommandView:move_suggestion_idx(dir)
self.last_change_id = self.doc:get_change_id() self.last_change_id = self.doc:get_change_id()
self.state.suggest(self:get_text()) self.state.suggest(self:get_text())
end end
self.suggestions_offset = get_suggestions_offset()
end end
@ -277,7 +256,6 @@ function CommandView:update_suggestions()
end end
self.suggestions = res self.suggestions = res
self.suggestion_idx = 1 self.suggestion_idx = 1
self.suggestions_offset = 1
end end
@ -321,7 +299,7 @@ function CommandView:update()
self:move_towards("suggestions_height", dest, nil, "commandview") self:move_towards("suggestions_height", dest, nil, "commandview")
-- update suggestion cursor offset -- update suggestion cursor offset
local dest = (self.suggestion_idx - self.suggestions_offset + 1) * self:get_suggestion_line_height() local dest = math.min(self.suggestion_idx, max_suggestions) * self:get_suggestion_line_height()
self:move_towards("selection_offset", dest, nil, "commandview") self:move_towards("selection_offset", dest, nil, "commandview")
-- update size based on whether this is the active_view -- update size based on whether this is the active_view
@ -357,7 +335,6 @@ local function draw_suggestions_box(self)
local h = math.ceil(self.suggestions_height) local h = math.ceil(self.suggestions_height)
local rx, ry, rw, rh = self.position.x, self.position.y - h - dh, self.size.x, h local rx, ry, rw, rh = self.position.x, self.position.y - h - dh, self.size.x, h
core.push_clip_rect(rx, ry, rw, rh)
-- draw suggestions background -- draw suggestions background
if #self.suggestions > 0 then if #self.suggestions > 0 then
renderer.draw_rect(rx, ry, rw, rh, style.background3) renderer.draw_rect(rx, ry, rw, rh, style.background3)
@ -367,12 +344,14 @@ local function draw_suggestions_box(self)
end end
-- draw suggestion text -- draw suggestion text
local first = math.max(self.suggestions_offset, 1) local offset = math.max(self.suggestion_idx - max_suggestions, 0)
local last = math.min(self.suggestions_offset + max_suggestions, #self.suggestions) local last = math.min(offset + max_suggestions, #self.suggestions)
core.push_clip_rect(rx, ry, rw, rh)
local first = 1 + offset
for i=first, last do for i=first, last do
local item = self.suggestions[i] local item = self.suggestions[i]
local color = (i == self.suggestion_idx) and style.accent or style.text local color = (i == self.suggestion_idx) and style.accent or style.text
local y = self.position.y - (i - first + 1) * lh - dh local y = self.position.y - (i - offset) * lh - dh
common.draw_text(self:get_font(), color, item.text, nil, x, y, 0, lh) common.draw_text(self:get_font(), color, item.text, nil, x, y, 0, lh)
if item.info then if item.info then

View File

@ -1,41 +1,22 @@
local common = {} local common = {}
---Checks if the byte at offset is a UTF-8 continuation byte.
---
---UTF-8 encodes code points in 1 to 4 bytes.
---For a multi-byte sequence, each byte following the start byte is a continuation byte.
---@param s string
---@param offset? integer The offset of the string to start searching. Defaults to 1.
---@return boolean
function common.is_utf8_cont(s, offset) function common.is_utf8_cont(s, offset)
local byte = s:byte(offset or 1) local byte = s:byte(offset or 1)
return byte >= 0x80 and byte < 0xc0 return byte >= 0x80 and byte < 0xc0
end end
---Returns an iterator that yields a UTF-8 character on each iteration.
---@param text string
---@return fun(): string
function common.utf8_chars(text) function common.utf8_chars(text)
return text:gmatch("[\0-\x7f\xc2-\xf4][\x80-\xbf]*") return text:gmatch("[\0-\x7f\xc2-\xf4][\x80-\xbf]*")
end end
---Clamps the number n between lo and hi.
---@param n number
---@param lo number
---@param hi number
---@return number
function common.clamp(n, lo, hi) function common.clamp(n, lo, hi)
return math.max(math.min(n, hi), lo) return math.max(math.min(n, hi), lo)
end end
---Returns a new table containing the contents of b merged into a.
---@param a table|nil
---@param b table?
---@return table
function common.merge(a, b) function common.merge(a, b)
a = type(a) == "table" and a or {} a = type(a) == "table" and a or {}
local t = {} local t = {}
@ -51,19 +32,11 @@ function common.merge(a, b)
end end
---Returns the value of a number rounded to the nearest integer.
---@param n number
---@return number
function common.round(n) function common.round(n)
return n >= 0 and math.floor(n + 0.5) or math.ceil(n - 0.5) return n >= 0 and math.floor(n + 0.5) or math.ceil(n - 0.5)
end end
---Returns the first index where a subtable in tbl has prop set.
---If none is found, nil is returned.
---@param tbl table
---@param prop any
---@return number|nil
function common.find_index(tbl, prop) function common.find_index(tbl, prop)
for i, o in ipairs(tbl) do for i, o in ipairs(tbl) do
if o[prop] then return i end if o[prop] then return i end
@ -71,16 +44,6 @@ function common.find_index(tbl, prop)
end end
---Returns a value between a and b on a linear scale, based on the
---interpolation point t.
---
---If a and b are tables, a table containing the result for all the
---elements in a and b is returned.
---@param a number
---@param b number
---@param t number
---@return number
---@overload fun(a: table, b: table, t: number): table
function common.lerp(a, b, t) function common.lerp(a, b, t)
if type(a) ~= "table" then if type(a) ~= "table" then
return a + (b - a) * t return a + (b - a) * t
@ -93,29 +56,11 @@ function common.lerp(a, b, t)
end end
---Returns the euclidean distance between two points.
---@param x1 number
---@param y1 number
---@param x2 number
---@param y2 number
---@return number
function common.distance(x1, y1, x2, y2) function common.distance(x1, y1, x2, y2)
return math.sqrt(((x2-x1) ^ 2)+((y2-y1) ^ 2)) return math.sqrt(((x2-x1) ^ 2)+((y2-y1) ^ 2))
end end
---Parses a CSS color string.
---
---Only these formats are supported:
---* `rgb(r, g, b)`
---* `rgba(r, g, b, a)`
---* `#rrggbbaa`
---* `#rrggbb`
---@param str string
---@return number r
---@return number g
---@return number b
---@return number a
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)(%x?%x?)$")
if r then if r then
@ -136,21 +81,26 @@ function common.color(str)
end end
---Splices a numerically indexed table.
---This function mutates the original table.
---@param t any[]
---@param at number Index at which to start splicing.
---@param remove number Number of elements to remove.
---@param insert? any[] A table containing elements to insert after splicing.
function common.splice(t, at, remove, insert) function common.splice(t, at, remove, insert)
assert(remove >= 0, "bad argument #3 to 'splice' (non-negative value expected)")
insert = insert or {} insert = insert or {}
local len = #insert local offset = #insert - remove
if remove ~= len then table.move(t, at + remove, #t + remove, at + len) end local old_len = #t
table.move(insert, 1, len, at, t) if offset < 0 then
for i = at - offset, old_len - offset do
t[i + offset] = t[i]
end
elseif offset > 0 then
for i = old_len, at, -1 do
t[i + offset] = t[i]
end
end
for i, item in ipairs(insert) do
t[at + i - 1] = item
end
end end
local function compare_score(a, b) local function compare_score(a, b)
return a.score > b.score return a.score > b.score
end end
@ -171,16 +121,6 @@ local function fuzzy_match_items(items, needle, files)
end end
---Performs fuzzy matching.
---
---If the haystack is a string, a score ranging from 0 to 1 is returned. </br>
---If the haystack is a table, a table containing the haystack sorted in ascending
---order of similarity is returned.
---@param haystack string
---@param needle string
---@param files boolean If true, the matching process will be performed in reverse to better match paths.
---@return number
---@overload fun(haystack: string[], needle: string, files: boolean): string[]
function common.fuzzy_match(haystack, needle, files) function common.fuzzy_match(haystack, needle, files)
if type(haystack) == "table" then if type(haystack) == "table" then
return fuzzy_match_items(haystack, needle, files) return fuzzy_match_items(haystack, needle, files)
@ -189,14 +129,6 @@ function common.fuzzy_match(haystack, needle, files)
end end
---Performs fuzzy matching and returns recently used strings if needed.
---
---If the needle is empty, then a list of recently used strings
---are added to the result, followed by strings from the haystack.
---@param haystack string[]
---@param recents string[]
---@param needle string
---@return string[]
function common.fuzzy_match_with_recents(haystack, recents, needle) function common.fuzzy_match_with_recents(haystack, recents, needle)
if needle == "" then if needle == "" then
local recents_ext = {} local recents_ext = {}
@ -215,24 +147,11 @@ function common.fuzzy_match_with_recents(haystack, recents, needle)
end end
---Returns a list of paths that are relative to the input path.
---
---If a root directory is specified, the function returns paths
---that are relative to the root directory.
---@param text string The input path.
---@param root? string The root directory.
---@return string[]
function common.path_suggest(text, root) function common.path_suggest(text, root)
if root and root:sub(-1) ~= PATHSEP then if root and root:sub(-1) ~= PATHSEP then
root = root .. PATHSEP root = root .. PATHSEP
end end
local path, name local path, name = text:match("^(.-)([^/\\]*)$")
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
path, name = text:match("^(.-)([^:"..PATHSEP.."]*)$")
else
path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
end
local clean_dotslash = false local clean_dotslash = false
-- ignore root if path is absolute -- ignore root if path is absolute
local is_absolute = common.is_absolute_path(text) local is_absolute = common.is_absolute_path(text)
@ -281,16 +200,8 @@ function common.path_suggest(text, root)
end end
---Returns a list of directories that are related to a path.
---@param text string The input path.
---@return string[]
function common.dir_path_suggest(text) function common.dir_path_suggest(text)
local path, name local path, name = text:match("^(.-)([^/\\]*)$")
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
path, name = text:match("^(.-)([^:"..PATHSEP.."]*)$")
else
path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
end
local files = system.list_dir(path == "" and "." or path) or {} local files = system.list_dir(path == "" and "." or path) or {}
local res = {} local res = {}
for _, file in ipairs(files) do for _, file in ipairs(files) do
@ -304,18 +215,8 @@ function common.dir_path_suggest(text)
end end
---Filters a list of paths to find those that are related to the input path.
---@param text string The input path.
---@param dir_list string[] A list of paths to filter.
---@return string[]
function common.dir_list_suggest(text, dir_list) function common.dir_list_suggest(text, dir_list)
local path, name local path, name = text:match("^(.-)([^/\\]*)$")
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
path, name = text:match("^(.-)([^:"..PATHSEP.."]*)$")
else
path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
end
local res = {} local res = {}
for _, dir_path in ipairs(dir_list) do for _, dir_path in ipairs(dir_list) do
if dir_path:lower():find(text:lower(), nil, true) == 1 then if dir_path:lower():find(text:lower(), nil, true) == 1 then
@ -326,15 +227,6 @@ function common.dir_list_suggest(text, dir_list)
end end
---Matches a string against a list of patterns.
---
---If a match was found, its start and end index is returned.
---Otherwise, false is returned.
---@param text string
---@param pattern string|string[]
---@param ... any Other options for string.find().
---@return number|boolean start_index
---@return number|nil end_index
function common.match_pattern(text, pattern, ...) function common.match_pattern(text, pattern, ...)
if type(pattern) == "string" then if type(pattern) == "string" then
return text:find(pattern, ...) return text:find(pattern, ...)
@ -347,24 +239,8 @@ function common.match_pattern(text, pattern, ...)
end end
---Draws text onto the window.
---The function returns the X and Y coordinates of the bottom-right
---corner of the text.
---@param font renderer.font
---@param color renderer.color
---@param text string
---@param align string
---| '"left"' # Align text to the left of the bounding box
---| '"right"' # Align text to the right of the bounding box
---| '"center"' # Center text in the bounding box
---@param x number
---@param y number
---@param w number
---@param h number
---@return number x_advance
---@return number y_advance
function common.draw_text(font, color, text, align, x,y,w,h) function common.draw_text(font, color, text, align, x,y,w,h)
local tw, th = font:get_width(text), font:get_height() local tw, th = font:get_width(text), font:get_height(text)
if align == "center" then if align == "center" then
x = x + (w - tw) / 2 x = x + (w - tw) / 2
elseif align == "right" then elseif align == "right" then
@ -375,16 +251,6 @@ function common.draw_text(font, color, text, align, x,y,w,h)
end end
---Prints the execution time of a function.
---
---The execution time and percentage of frame time
---for the function is printed to standard output. </br>
---The frame rate is always assumed to be 60 FPS, thus
---a value of 100% would mean that the benchmark took
---1/60 of a second to execute.
---@param name string
---@param fn fun(...: any): any
---@return any # The result returned by the function
function common.bench(name, fn, ...) function common.bench(name, fn, ...)
local start = system.get_time() local start = system.get_time()
local res = fn(...) local res = fn(...)
@ -395,15 +261,12 @@ function common.bench(name, fn, ...)
return res return res
end end
-- From gvx/Ser
local oddvals = {[tostring(1/0)] = "1/0", [tostring(-1/0)] = "-1/0", [tostring(-(0/0))] = "-(0/0)", [tostring(0/0)] = "0/0"}
local function serialize(val, pretty, indent_str, escape, sort, limit, level) local function serialize(val, pretty, indent_str, escape, sort, limit, level)
local space = pretty and " " or "" local space = pretty and " " or ""
local indent = pretty and string.rep(indent_str, level) or "" local indent = pretty and string.rep(indent_str, level) or ""
local newline = pretty and "\n" or "" local newline = pretty and "\n" or ""
local ty = type(val) if type(val) == "string" then
if ty == "string" then
local out = string.format("%q", val) local out = string.format("%q", val)
if escape then if escape then
out = string.gsub(out, "\\\n", "\\n") out = string.gsub(out, "\\\n", "\\n")
@ -415,7 +278,7 @@ local function serialize(val, pretty, indent_str, escape, sort, limit, level)
out = string.gsub(out, "\\13", "\\r") out = string.gsub(out, "\\13", "\\r")
end end
return out return out
elseif ty == "table" then elseif type(val) == "table" then
-- early exit -- early exit
if level >= limit then return tostring(val) end if level >= limit then return tostring(val) end
local next_indent = pretty and (indent .. indent_str) or "" local next_indent = pretty and (indent .. indent_str) or ""
@ -430,37 +293,17 @@ local function serialize(val, pretty, indent_str, escape, sort, limit, level)
if sort then table.sort(t) end if sort then table.sort(t) end
return "{" .. newline .. table.concat(t, "," .. newline) .. newline .. indent .. "}" return "{" .. newline .. table.concat(t, "," .. newline) .. newline .. indent .. "}"
end end
if ty == "number" then
-- tostring is locale-dependent, so we need to replace an eventual `,` with `.`
local res, _ = tostring(val):gsub(",", ".")
-- handle inf/nan
return oddvals[res] or res
end
return tostring(val) return tostring(val)
end end
-- Serialize `val` into a parsable string.
---@class common.serializeoptions -- Available options
---@field pretty boolean Enables pretty printing. -- * pretty: enable pretty printing
---@field indent_str string The indentation character to use. Defaults to `" "`. -- * indent_str: indent to use (" " by default)
---@field escape boolean Uses normal escape characters ("\n") instead of decimal escape sequences ("\10"). -- * escape: use normal escape characters instead of the ones used by string.format("%q", ...)
---@field limit number Limits the depth when serializing nested tables. Defaults to `math.huge`. -- * sort: sort the keys inside tables
---@field sort boolean Sorts the output if it is a sortable table. -- * limit: limit how deep to serialize
---@field initial_indent number The initial indentation level. Defaults to 0. -- * initial_indent: the initial indentation level
---Serializes a value into a Lua string that is loadable with load().
---
---Only these basic types are supported:
---* nil
---* boolean
---* number (except very large numbers and special constants, e.g. `math.huge`, `inf` and `nan`)
---* integer
---* string
---* table
---
---@param val any
---@param opts? common.serializeoptions
---@return string
function common.serialize(val, opts) function common.serialize(val, opts)
opts = opts or {} opts = opts or {}
local indent_str = opts.indent_str or " " local indent_str = opts.indent_str or " "
@ -472,52 +315,19 @@ function common.serialize(val, opts)
end end
---Returns the last portion of a path.
---@param path string
---@return string
function common.basename(path) function common.basename(path)
-- a path should never end by / or \ except if it is '/' (unix root) or -- a path should never end by / or \ except if it is '/' (unix root) or
-- 'X:\' (windows drive) -- 'X:\' (windows drive)
return path:match("[^"..PATHSEP.."]+$") or path return path:match("[^\\/]+$") or path
end end
---Returns the base path with the pathsep, if needed. -- can return nil if there is no directory part in the path
---@param path string
---@return string
function common.basepath(path)
-- Check for AmigaOS 4 and MorphOS if the last character is semicolon
-- In these systems the volume name doesn't have a / or \ after the name
-- but it is like VOLUME:
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") and (string.sub(path, -1) == ":") then
return path
end
return path .. PATHSEP
end
---Returns the directory name of a path.
---If the path doesn't have a directory, this function may return nil.
---@param path string
---@return string|nil
function common.dirname(path) function common.dirname(path)
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then return path:match("(.+)[\\/][^\\/]+$")
local drive, relpath = path:match('^([%w%s]*:)(.+)')
if drive and relpath then
local dir = relpath:match("(.+)["..PATHSEP.."][^"..PATHSEP.."]+$")
if dir then
return drive .. dir
end
end
return path
end
return path:match("(.+)["..PATHSEP.."][^"..PATHSEP.."]+$")
end end
---Returns a path where the user's home directory is replaced by `"~"`.
---@param text string
---@return string
function common.home_encode(text) function common.home_encode(text)
if HOME and string.find(text, HOME, 1, true) == 1 then if HOME and string.find(text, HOME, 1, true) == 1 then
local dir_pos = #HOME + 1 local dir_pos = #HOME + 1
@ -531,9 +341,6 @@ function common.home_encode(text)
end end
---Returns a list of paths where the user's home directory is replaced by `"~"`.
---@param paths string[] A list of paths to encode
---@return string[]
function common.home_encode_list(paths) function common.home_encode_list(paths)
local t = {} local t = {}
for i = 1, #paths do for i = 1, #paths do
@ -543,44 +350,29 @@ function common.home_encode_list(paths)
end end
---Expands the `"~"` prefix in a path into the user's home directory.
---This function is not guaranteed to return an absolute path.
---@param text string
---@return string
function common.home_expand(text) function common.home_expand(text)
if text == nil then
return HOME
end
return HOME and text:gsub("^~", HOME) or text return HOME and text:gsub("^~", HOME) or text
end end
local function split_on_slash(s, sep_pattern) local function split_on_slash(s, sep_pattern)
local t = {} local t = {}
if s:match("^["..PATHSEP.."]") then if s:match("^[/\\]") then
t[#t + 1] = "" t[#t + 1] = ""
end end
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then for fragment in string.gmatch(s, "([^/\\]+)") do
local drive = s:match("^([%w%s]*:)")
if drive then
t[#t + 1] = ""
s = s:gsub("^" .. drive, "")
end
end
for fragment in string.gmatch(s, "([^"..PATHSEP.."]+)") do
t[#t + 1] = fragment t[#t + 1] = fragment
end end
return t return t
end end
---Normalizes the drive letter in a Windows path to uppercase. -- The filename argument given to the function is supposed to
---This function expects an absolute path, e.g. a path from `system.absolute_path`. -- come from system.absolute_path and as such should be an
--- -- absolute path without . or .. elements.
---This function is needed because the path returned by `system.absolute_path` -- This function exists because on Windows the drive letter returned
---may contain drive letters in upper or lowercase. -- by system.absolute_path is sometimes with a lower case and sometimes
---@param filename string|nil The input path. -- with an upper case so we normalize to upper case.
---@return string|nil
function common.normalize_volume(filename) function common.normalize_volume(filename)
if not filename then return end if not filename then return end
if PATHSEP == '\\' then if PATHSEP == '\\' then
@ -589,23 +381,10 @@ function common.normalize_volume(filename)
return drive:upper() .. rem return drive:upper() .. rem
end end
end end
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
local drive, rem = filename:match('^([%w%s]*:)(.-)' .. PATHSEP .. '?$')
if drive then
return drive .. rem
end
end
return filename return filename
end end
---Normalizes a path into the same format across platforms.
---
---On Windows, all drive letters are converted to uppercase.
---UNC paths with drive letters are converted back to ordinary Windows paths.
---All path separators (`"/"`, `"\\"`) are converted to platform-specific ones.
---@param filename string|nil
---@return string|nil
function common.normalize_path(filename) function common.normalize_path(filename)
if not filename then return end if not filename then return end
local volume local volume
@ -620,11 +399,6 @@ function common.normalize_path(filename)
volume, filename = drive, rem volume, filename = drive, rem
end end
end end
elseif (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
local drive, relpath = filename:match('^([%w%s]*:)(.+)')
if relpath then
volume, filename = drive, relpath
end
else else
local relpath = filename:match('^/(.+)') local relpath = filename:match('^/(.+)')
if relpath then if relpath then
@ -651,32 +425,18 @@ function common.normalize_path(filename)
end end
---Checks whether a path is absolute or relative.
---@param path string
---@return boolean
function common.is_absolute_path(path) function common.is_absolute_path(path)
return path:sub(1, 1) == PATHSEP or path:match("^(%a):\\") or path:match('^([%w%s]*):') return path:sub(1, 1) == PATHSEP or path:match("^(%a):\\")
end end
---Checks whether a path belongs to a parent directory.
---@param filename string The path to check.
---@param path string The parent path.
---@return boolean
function common.path_belongs_to(filename, path) function common.path_belongs_to(filename, path)
return string.find(filename, common.basepath(path), 1, true) == 1 return string.find(filename, path .. PATHSEP, 1, true) == 1
end end
---Checks whether a path is relative to another path.
---@param ref_dir string The path to check against.
---@param dir string The input path.
---@return boolean
function common.relative_path(ref_dir, dir) function common.relative_path(ref_dir, dir)
local drive_pattern = "^(%a):\\" local drive_pattern = "^(%a):\\"
if (PLATFORM == "AmigaOS 4" or PLATFORM == "MorphOS") then
drive_pattern = "^([%w%s]*:)"
end
local drive, ref_drive = dir:match(drive_pattern), ref_dir:match(drive_pattern) local drive, ref_drive = dir:match(drive_pattern), ref_dir:match(drive_pattern)
if drive and ref_drive and drive ~= ref_drive then if drive and ref_drive and drive ~= ref_drive then
-- Windows, different drives, system.absolute_path fails for C:\..\D:\ -- Windows, different drives, system.absolute_path fails for C:\..\D:\
@ -700,11 +460,6 @@ function common.relative_path(ref_dir, dir)
end end
---Creates a directory recursively if necessary.
---@param path string
---@return boolean success
---@return string|nil error
---@return string|nil path The path where an error occured.
function common.mkdirp(path) function common.mkdirp(path)
local stat = system.get_file_info(path) local stat = system.get_file_info(path)
if stat and stat.type then if stat and stat.type then
@ -714,12 +469,12 @@ function common.mkdirp(path)
while path and path ~= "" do while path and path ~= "" do
local success_mkdir = system.mkdir(path) local success_mkdir = system.mkdir(path)
if success_mkdir then break end if success_mkdir then break end
local updir, basedir = path:match("(.*)["..PATHSEP.."](.+)$") local updir, basedir = path:match("(.*)[/\\](.+)$")
table.insert(subdirs, 1, basedir or path) table.insert(subdirs, 1, basedir or path)
path = updir path = updir
end end
for _, dirname in ipairs(subdirs) do for _, dirname in ipairs(subdirs) do
path = path and common.basepath(path) .. dirname or dirname path = path and path .. PATHSEP .. dirname or dirname
if not system.mkdir(path) then if not system.mkdir(path) then
return false, "cannot create directory", path return false, "cannot create directory", path
end end
@ -727,13 +482,6 @@ function common.mkdirp(path)
return true return true
end end
---Removes a path.
---@param path string
---@param recursively boolean If true, the function will attempt to remove everything in the specified path.
---@return boolean success
---@return string|nil error
---@return string|nil path The path where the error occured.
function common.rm(path, recursively) function common.rm(path, recursively)
local stat = system.get_file_info(path) local stat = system.get_file_info(path)
if not stat or (stat.type ~= "file" and stat.type ~= "dir") then if not stat or (stat.type ~= "file" and stat.type ~= "dir") then

View File

@ -1,5 +1,3 @@
local common = require "core.common"
local config = {} local config = {}
config.fps = 60 config.fps = 60
@ -57,49 +55,18 @@ config.max_clicks = 3
-- set as true to be able to test non supported plugins -- set as true to be able to test non supported plugins
config.skip_plugins_version = false config.skip_plugins_version = false
-- holds the plugins real config table
local plugins_config = {}
-- virtual representation of plugins config table
config.plugins = {} config.plugins = {}
-- Allow you to set plugin configs even if we haven't seen the plugin before.
-- allows virtual access to the plugins config table
setmetatable(config.plugins, { setmetatable(config.plugins, {
__index = function(_, k) __index = function(t, k)
if not plugins_config[k] then local v = rawget(t, k)
plugins_config[k] = { enabled = true, config = {} } if v == true or v == nil then v = {} rawset(t, k, v) end
end return v
if plugins_config[k].enabled ~= false then
return plugins_config[k].config
end
return false
end,
__newindex = function(_, k, v)
if not plugins_config[k] then
plugins_config[k] = { enabled = nil, config = {} }
end
if v == false and package.loaded["plugins."..k] then
local core = require "core"
core.warn("[%s] is already enabled, restart the editor for the change to take effect", k)
return
elseif plugins_config[k].enabled == false and v ~= false then
plugins_config[k].enabled = true
end
if v == false then
plugins_config[k].enabled = false
elseif type(v) == "table" then
plugins_config[k].enabled = true
plugins_config[k].config = common.merge(plugins_config[k].config, v)
end
end,
__pairs = function()
return coroutine.wrap(function()
for name, status in pairs(plugins_config) do
coroutine.yield(name, status.config)
end
end)
end end
}) })
-- Disable these plugins by default.
config.plugins.trimwhitespace = false
config.plugins.drawwhitespace = false
return config return config

View File

@ -12,31 +12,11 @@ local divider_width = 1
local divider_padding = 5 local divider_padding = 5
local DIVIDER = {} local DIVIDER = {}
---An item in the context menu.
---@class core.contextmenu.item
---@field text string
---@field info string|nil If provided, this text is displayed on the right side of the menu.
---@field command string|fun()
---A list of items with the same predicate.
---@see core.command.predicate
---@class core.contextmenu.itemset
---@field predicate core.command.predicate
---@field items core.contextmenu.item[]
---A context menu.
---@class core.contextmenu : core.object ---@class core.contextmenu : core.object
---@field itemset core.contextmenu.itemset[]
---@field show_context_menu boolean
---@field selected number
---@field position core.view.position
---@field current_scale number
local ContextMenu = Object:extend() local ContextMenu = Object:extend()
---A unique value representing the divider in a context menu.
ContextMenu.DIVIDER = DIVIDER ContextMenu.DIVIDER = DIVIDER
---Creates a new context menu.
function ContextMenu:new() function ContextMenu:new()
self.itemset = {} self.itemset = {}
self.show_context_menu = false self.show_context_menu = false
@ -75,19 +55,12 @@ local function update_items_size(items, update_binding)
items.width, items.height = width, height items.width, items.height = width, height
end end
---Registers a list of items into the context menu with a predicate.
---@param predicate core.command.predicate
---@param items core.contextmenu.item[]
function ContextMenu:register(predicate, items) function ContextMenu:register(predicate, items)
predicate = command.generate_predicate(predicate) predicate = command.generate_predicate(predicate)
update_items_size(items, true) update_items_size(items, true)
table.insert(self.itemset, { predicate = predicate, items = items }) table.insert(self.itemset, { predicate = predicate, items = items })
end end
---Shows the context menu.
---@param x number
---@param y number
---@return boolean # If true, the context menu is shown.
function ContextMenu:show(x, y) function ContextMenu:show(x, y)
self.items = nil self.items = nil
local items_list = { width = 0, height = 0 } local items_list = { width = 0, height = 0 }
@ -121,7 +94,6 @@ function ContextMenu:show(x, y)
return false return false
end end
---Hides the context menu.
function ContextMenu:hide() function ContextMenu:hide()
self.show_context_menu = false self.show_context_menu = false
self.items = nil self.items = nil
@ -130,8 +102,6 @@ function ContextMenu:hide()
core.request_cursor(core.active_view.cursor) core.request_cursor(core.active_view.cursor)
end end
---Returns an iterator that iterates over each context menu item and their dimensions.
---@return fun(): number, core.contextmenu.item, number, number, number, number
function ContextMenu:each_item() function ContextMenu:each_item()
local x, y, w = self.position.x, self.position.y, self.items.width local x, y, w = self.position.x, self.position.y, self.items.width
local oy = y local oy = y
@ -145,12 +115,8 @@ function ContextMenu:each_item()
end) end)
end end
---Event handler for mouse movements.
---@param px any
---@param py any
---@return boolean # true if the event is caught.
function ContextMenu:on_mouse_moved(px, py) function ContextMenu:on_mouse_moved(px, py)
if not self.show_context_menu then return false end if not self.show_context_menu then return end
self.selected = -1 self.selected = -1
for i, item, x, y, w, h in self:each_item() do for i, item, x, y, w, h in self:each_item() do
@ -162,8 +128,6 @@ function ContextMenu:on_mouse_moved(px, py)
return true return true
end end
---Event handler for when the selection is confirmed.
---@param item core.contextmenu.item
function ContextMenu:on_selected(item) function ContextMenu:on_selected(item)
if type(item.command) == "string" then if type(item.command) == "string" then
command.perform(item.command) command.perform(item.command)
@ -176,7 +140,6 @@ local function change_value(value, change)
return value + change return value + change
end end
---Selects the the previous item.
function ContextMenu:focus_previous() function ContextMenu:focus_previous()
self.selected = (self.selected == -1 or self.selected == 1) and #self.items or change_value(self.selected, -1) self.selected = (self.selected == -1 or self.selected == 1) and #self.items or change_value(self.selected, -1)
if self:get_item_selected() == DIVIDER then if self:get_item_selected() == DIVIDER then
@ -184,7 +147,6 @@ function ContextMenu:focus_previous()
end end
end end
---Selects the next item.
function ContextMenu:focus_next() function ContextMenu:focus_next()
self.selected = (self.selected == -1 or self.selected == #self.items) and 1 or change_value(self.selected, 1) self.selected = (self.selected == -1 or self.selected == #self.items) and 1 or change_value(self.selected, 1)
if self:get_item_selected() == DIVIDER then if self:get_item_selected() == DIVIDER then
@ -192,13 +154,10 @@ function ContextMenu:focus_next()
end end
end end
---Gets the currently selected item.
---@return core.contextmenu.item|nil
function ContextMenu:get_item_selected() function ContextMenu:get_item_selected()
return (self.items or {})[self.selected] return (self.items or {})[self.selected]
end end
---Hides the context menu and performs the command if an item is selected.
function ContextMenu:call_selected_item() function ContextMenu:call_selected_item()
local selected = self:get_item_selected() local selected = self:get_item_selected()
self:hide() self:hide()
@ -207,12 +166,6 @@ function ContextMenu:call_selected_item()
end end
end end
---Event handler for mouse press.
---@param button core.view.mousebutton
---@param px number
---@param py number
---@param clicks number
---@return boolean # true if the event is caught.
function ContextMenu:on_mouse_pressed(button, px, py, clicks) function ContextMenu:on_mouse_pressed(button, px, py, clicks)
local caught = false local caught = false
@ -233,20 +186,14 @@ function ContextMenu:on_mouse_pressed(button, px, py, clicks)
return caught return caught
end end
---@type fun(self: table, k: string, dest: number, rate?: number, name?: string)
ContextMenu.move_towards = View.move_towards ContextMenu.move_towards = View.move_towards
---Event handler for content update.
function ContextMenu:update() function ContextMenu:update()
if self.show_context_menu then if self.show_context_menu then
self:move_towards("height", self.items.height, nil, "contextmenu") self:move_towards("height", self.items.height, nil, "contextmenu")
end end
end end
---Draws the context menu.
---
---This wraps `ContextMenu:draw_context_menu()`.
---@see core.contextmenu.draw_context_menu
function ContextMenu:draw() function ContextMenu:draw()
if not self.show_context_menu then return end if not self.show_context_menu then return end
if self.current_scale ~= SCALE then if self.current_scale ~= SCALE then
@ -259,7 +206,6 @@ function ContextMenu:draw()
core.root_view:defer_draw(self.draw_context_menu, self) core.root_view:defer_draw(self.draw_context_menu, self)
end end
---Draws the context menu.
function ContextMenu:draw_context_menu() function ContextMenu:draw_context_menu()
if not self.items then return end if not self.items then return end
local bx, by, bw, bh = self.position.x, self.position.y, self.items.width, self.height local bx, by, bw, bh = self.position.x, self.position.y, self.items.width, self.height

View File

@ -91,7 +91,6 @@ end
-- designed to be run inside a coroutine. -- designed to be run inside a coroutine.
function dirwatch:check(change_callback, scan_time, wait_time) function dirwatch:check(change_callback, scan_time, wait_time)
local had_change = false local had_change = false
local last_error
self.monitor:check(function(id) self.monitor:check(function(id)
had_change = true had_change = true
if self.monitor:mode() == "single" then if self.monitor:mode() == "single" then
@ -103,10 +102,7 @@ function dirwatch:check(change_callback, scan_time, wait_time)
elseif self.reverse_watched[id] then elseif self.reverse_watched[id] then
change_callback(self.reverse_watched[id]) change_callback(self.reverse_watched[id])
end end
end, function(err)
last_error = err
end) end)
if last_error ~= nil then error(last_error) end
local start_time = system.get_time() local start_time = system.get_time()
for directory, old_modified in pairs(self.scanned) do for directory, old_modified in pairs(self.scanned) do
if old_modified then if old_modified then
@ -177,8 +173,7 @@ end
-- compute a file's info entry completed with "filename" to be used -- compute a file's info entry completed with "filename" to be used
-- in project scan or falsy if it shouldn't appear in the list. -- in project scan or falsy if it shouldn't appear in the list.
local function get_project_file_info(root, file, ignore_compiled) local function get_project_file_info(root, file, ignore_compiled)
local info = system.get_file_info(common.basepath(root) .. file) local info = system.get_file_info(root .. PATHSEP .. file)
-- info can be not nil but info.type may be nil if is neither a file neither -- info can be not nil but info.type may be nil if is neither a file neither
-- a directory, for example for /dev/* entries on linux. -- a directory, for example for /dev/* entries on linux.
if info and info.type then if info and info.type then
@ -191,48 +186,49 @@ end
-- "root" will by an absolute path without trailing '/' -- "root" will by an absolute path without trailing '/'
-- "path" will be a path starting without '/' and without trailing '/' -- "path" will be a path starting without '/' and without trailing '/'
-- or the empty string. -- or the empty string.
-- It identifies a sub-path within "root". -- It will identifies a sub-path within "root.
-- The current path location will therefore always be: root .. path. -- The current path location will therefore always be: root .. path.
-- When recursing, "root" will always be the same, only "path" will change. -- When recursing "root" will always be the same, only "path" will change.
-- Returns a list of file "items". In each item the "filename" will be the -- Returns a list of file "items". In each item the "filename" will be the
-- complete file path relative to "root" *without* the trailing '/', and without the starting '/'. -- complete file path relative to "root" *without* the trailing '/', and without the starting '/'.
function dirwatch.get_directory_files(dir, root, path, entries_count, recurse_pred) function dirwatch.get_directory_files(dir, root, path, t, entries_count, recurse_pred)
local t = {}
local t0 = system.get_time() local t0 = system.get_time()
local t_elapsed = system.get_time() - t0
local dirs, files = {}, {}
local ignore_compiled = compile_ignore_files() local ignore_compiled = compile_ignore_files()
local all = system.list_dir(common.basepath(root) .. path)
local all = system.list_dir(root .. PATHSEP .. path)
if not all then return nil end if not all then return nil end
local entries = { }
for _, file in ipairs(all) do for _, file in ipairs(all or {}) do
local info = get_project_file_info(root, (path ~= "" and (path .. PATHSEP) or "") .. file, ignore_compiled) local info = get_project_file_info(root, (path ~= "" and (path .. PATHSEP) or "") .. file, ignore_compiled)
if info then if info then
table.insert(entries, info) table.insert(info.type == "dir" and dirs or files, info)
entries_count = entries_count + 1
end end
end end
table.sort(entries, compare_file)
local recurse_complete = true local recurse_complete = true
for _, info in ipairs(entries) do table.sort(dirs, compare_file)
table.insert(t, info) for _, f in ipairs(dirs) do
entries_count = entries_count + 1 table.insert(t, f)
if info.type == "dir" then if recurse_pred(dir, f.filename, entries_count, t_elapsed) then
if recurse_pred(dir, info.filename, entries_count, system.get_time() - t0) then local _, complete, n = dirwatch.get_directory_files(dir, root, f.filename, t, entries_count, recurse_pred)
local t_rec, complete, n = dirwatch.get_directory_files(dir, root, info.filename, entries_count, recurse_pred) recurse_complete = recurse_complete and complete
recurse_complete = recurse_complete and complete if n ~= nil then
if n ~= nil then entries_count = n
entries_count = n
for _, info_rec in ipairs(t_rec) do
table.insert(t, info_rec)
end
end
else
recurse_complete = false
end end
else
recurse_complete = false
end end
end end
table.sort(files, compare_file)
for _, f in ipairs(files) do
table.insert(t, f)
end
return t, recurse_complete, entries_count return t, recurse_complete, entries_count
end end

View File

@ -19,34 +19,25 @@ function Highlighter:start()
if self.running then return end if self.running then return end
self.running = true self.running = true
core.add_thread(function() core.add_thread(function()
while self.first_invalid_line <= self.max_wanted_line do while self.first_invalid_line < self.max_wanted_line do
local max = math.min(self.first_invalid_line + 40, self.max_wanted_line) local max = math.min(self.first_invalid_line + 40, self.max_wanted_line)
local retokenized_from local retokenized_from
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 line and line.resume and (line.init_state ~= state or line.text ~= self.doc.lines[i]) then if not (line and line.init_state == state and line.text == self.doc.lines[i]) then
-- Reset the progress if no longer valid
line.resume = nil
end
if not (line and line.init_state == state and line.text == self.doc.lines[i] and not line.resume) then
retokenized_from = retokenized_from or i retokenized_from = retokenized_from or i
self.lines[i] = self:tokenize_line(i, state, line and line.resume) self.lines[i] = self:tokenize_line(i, state)
if self.lines[i].resume then
self.first_invalid_line = i
goto yield
end
elseif retokenized_from then elseif retokenized_from then
self:update_notify(retokenized_from, i - retokenized_from - 1) self:update_notify(retokenized_from, i - retokenized_from - 1)
retokenized_from = nil retokenized_from = nil
end end
end end
self.first_invalid_line = max + 1
::yield::
if retokenized_from then if retokenized_from then
self:update_notify(retokenized_from, max - retokenized_from) self:update_notify(retokenized_from, max - retokenized_from)
end end
self.first_invalid_line = max + 1
core.redraw = true core.redraw = true
coroutine.yield() coroutine.yield()
end end
@ -57,7 +48,7 @@ end
local function set_max_wanted_lines(self, amount) local function set_max_wanted_lines(self, amount)
self.max_wanted_line = amount self.max_wanted_line = amount
if self.first_invalid_line <= self.max_wanted_line then if self.first_invalid_line < self.max_wanted_line then
self:start() self:start()
end end
end end
@ -100,11 +91,11 @@ function Highlighter:update_notify(line, n)
end end
function Highlighter:tokenize_line(idx, state, resume) function Highlighter:tokenize_line(idx, state)
local res = {} local res = {}
res.init_state = state res.init_state = state
res.text = self.doc.lines[idx] res.text = self.doc.lines[idx]
res.tokens, res.state, res.resume = tokenizer.tokenize(self.doc.syntax, res.text, state, resume) res.tokens, res.state = tokenizer.tokenize(self.doc.syntax, res.text, state)
return res return res
end end

View File

@ -44,12 +44,7 @@ end
function Doc:reset_syntax() function Doc:reset_syntax()
local header = self:get_text(1, 1, self:position_offset(1, 1, 128)) local header = self:get_text(1, 1, self:position_offset(1, 1, 128))
local path = self.abs_filename local syn = syntax.get(self.filename or "", header)
if not path and self.filename then
path = common.basepath(core.project_dir) .. self.filename
end
if path then path = common.normalize_path(path) end
local syn = syntax.get(path, header)
if self.syntax ~= syn then if self.syntax ~= syn then
self.syntax = syn self.syntax = syn
self.highlighter:soft_reset() self.highlighter:soft_reset()
@ -123,7 +118,6 @@ end
function Doc:is_dirty() function Doc:is_dirty()
if self.new_file then if self.new_file then
if self.filename then return true end
return #self.lines > 1 or #self.lines[1] > 1 return #self.lines > 1 or #self.lines[1] > 1
else else
return self.clean_change_id ~= self:get_change_id() return self.clean_change_id ~= self:get_change_id()
@ -282,13 +276,9 @@ end
-- End of cursor seciton. -- End of cursor seciton.
function Doc:sanitize_position(line, col) function Doc:sanitize_position(line, col)
local nlines = #self.lines line = common.clamp(line, 1, #self.lines)
if line > nlines then col = common.clamp(col, 1, #self.lines[line])
return nlines, #self.lines[nlines] return line, col
elseif line < 1 then
return 1, 1
end
return line, common.clamp(col, 1, #self.lines[line])
end end
@ -437,61 +427,25 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
local before = self.lines[line1]:sub(1, col1 - 1) local before = self.lines[line1]:sub(1, col1 - 1)
local after = self.lines[line2]:sub(col2) local after = self.lines[line2]:sub(col2)
local line_removal = line2 - line1
local col_removal = col2 - col1
-- splice line into line array -- splice line into line array
common.splice(self.lines, line1, line_removal + 1, { before .. after }) common.splice(self.lines, line1, line2 - line1 + 1, { before .. after })
local merge = false -- move all cursors back if they share a line with the removed text
-- keep selections in correct positions: each pair (line, col)
-- * remains unchanged if before the deleted text
-- * is set to (line1, col1) if in the deleted text
-- * is set to (line1, col - col_removal) if on line2 but out of the deleted text
-- * is set to (line - line_removal, col) if after line2
for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do
if cline2 < line1 then break end if cline1 < line2 then break end
local l1, c1, l2, c2 = cline1, ccol1, cline2, ccol2 local line_removal = line2 - line1
local column_removal = line2 == cline2 and col2 < ccol1 and (line2 == line1 and col2 - col1 or col2) or 0
if cline1 > line1 or (cline1 == line1 and ccol1 > col1) then self:set_selections(idx, cline1 - line_removal, ccol1 - column_removal, cline2 - line_removal, ccol2 - column_removal)
if cline1 > line2 then
l1 = l1 - line_removal
else
l1 = line1
c1 = (cline1 == line2 and ccol1 > col2) and c1 - col_removal or col1
end
end
if cline2 > line1 or (cline2 == line1 and ccol2 > col1) then
if cline2 > line2 then
l2 = l2 - line_removal
else
l2 = line1
c2 = (cline2 == line2 and ccol2 > col2) and c2 - col_removal or col1
end
end
if l1 == line1 and c1 == col1 then merge = true end
self:set_selections(idx, l1, c1, l2, c2)
end
if merge then
self:merge_cursors()
end end
-- update highlighter and assure selection is in bounds -- update highlighter and assure selection is in bounds
self.highlighter:remove_notify(line1, line_removal) self.highlighter:remove_notify(line1, line2 - line1)
self:sanitize_selection() self:sanitize_selection()
end end
function Doc:insert(line, col, text) function Doc:insert(line, col, text)
self.redo_stack = { idx = 1 } self.redo_stack = { idx = 1 }
-- Reset the clean id when we're pushing something new before it
if self:get_change_id() < self.clean_change_id then
self.clean_change_id = -1
end
line, col = self:sanitize_position(line, col) line, col = self:sanitize_position(line, col)
self:raw_insert(line, col, text, self.undo_stack, system.get_time()) self:raw_insert(line, col, text, self.undo_stack, system.get_time())
self:on_text_change("insert") self:on_text_change("insert")

View File

@ -66,18 +66,7 @@ function search.find(doc, line, col, text, opt)
s, e = search_func(line_text, pattern, col, plain) s, e = search_func(line_text, pattern, col, plain)
end end
if s then if s then
local line2 = line return line, s, line, e + 1
-- If we've matched the newline too,
-- return until the initial character of the next line.
if e >= #doc.lines[line] then
line2 = line + 1
e = 0
end
-- Avoid returning matches that go beyond the last line.
-- This is needed to avoid selecting the "last" newline.
if line2 <= #doc.lines then
return line, s, line2, e + 1
end
end end
col = opt.reverse and -1 or 1 col = opt.reverse and -1 or 1
end end

View File

@ -112,8 +112,7 @@ end
function DocView:get_scrollable_size() function DocView:get_scrollable_size()
if not config.scroll_past_end then if not config.scroll_past_end then
local _, _, _, h_scroll = self.h_scrollbar:get_track_rect() return self:get_line_height() * (#self.doc.lines) + style.padding.y * 2
return self:get_line_height() * (#self.doc.lines) + style.padding.y * 2 + h_scroll
end 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
@ -161,36 +160,24 @@ end
function DocView:get_visible_line_range() function DocView:get_visible_line_range()
local x, y, x2, y2 = self:get_content_bounds() local x, y, x2, y2 = self:get_content_bounds()
local lh = self:get_line_height() local lh = self:get_line_height()
local minline = math.max(1, math.floor((y - style.padding.y) / lh) + 1) local minline = math.max(1, math.floor(y / lh))
local maxline = math.min(#self.doc.lines, math.floor((y2 - style.padding.y) / lh) + 1) local maxline = math.min(#self.doc.lines, math.floor(y2 / lh) + 1)
return minline, maxline return minline, maxline
end end
function DocView:get_col_x_offset(line, col) function DocView:get_col_x_offset(line, col)
local default_font = self:get_font() local default_font = self:get_font()
local _, indent_size = self.doc:get_indent_info()
default_font:set_tab_size(indent_size)
local column = 1 local column = 1
local xoffset = 0 local xoffset = 0
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
if font ~= default_font then font:set_tab_size(indent_size) end for char in common.utf8_chars(text) do
local length = #text if column == col then
if column + length <= col then
xoffset = xoffset + font:get_width(text)
column = column + length
if column >= col then
return xoffset return xoffset
end end
else xoffset = xoffset + font:get_width(char)
for char in common.utf8_chars(text) do column = column + #char
if column >= col then
return xoffset
end
xoffset = xoffset + font:get_width(char)
column = column + #char
end
end end
end end
@ -203,27 +190,16 @@ 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 _, indent_size = self.doc:get_indent_info()
default_font:set_tab_size(indent_size)
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
if font ~= default_font then font:set_tab_size(indent_size) end for char in common.utf8_chars(text) do
local width = font:get_width(text) local w = font:get_width(char)
-- Don't take the shortcut if the width matches x, if xoffset >= x then
-- because we need last_i which should be calculated using utf-8. return (xoffset - x > w / 2) and last_i or i
if xoffset + width < x then
xoffset = xoffset + width
i = i + #text
else
for char in common.utf8_chars(text) do
local w = font:get_width(char)
if xoffset >= x then
return (xoffset - x > w / 2) and last_i or i
end
xoffset = xoffset + w
last_i = i
i = i + #char
end end
xoffset = xoffset + w
last_i = i
i = i + #char
end end
end end
@ -245,8 +221,7 @@ function DocView:scroll_to_line(line, ignore_if_visible, instant)
if not (ignore_if_visible and line > min and line < max) then if not (ignore_if_visible and line > min and line < max) then
local x, y = self:get_line_screen_position(line) local x, y = self:get_line_screen_position(line)
local ox, oy = self:get_content_offset() local ox, oy = self:get_content_offset()
local _, _, _, scroll_h = self.h_scrollbar:get_track_rect() self.scroll.to.y = math.max(0, y - oy - self.size.y / 2)
self.scroll.to.y = math.max(0, y - oy - (self.size.y - scroll_h) / 2)
if instant then if instant then
self.scroll.y = self.scroll.to.y self.scroll.y = self.scroll.to.y
end end
@ -255,20 +230,17 @@ end
function DocView:scroll_to_make_visible(line, col) function DocView:scroll_to_make_visible(line, col)
local _, oy = self:get_content_offset() local ox, oy = self:get_content_offset()
local _, ly = self:get_line_screen_position(line, col) local _, ly = self:get_line_screen_position(line, col)
local lh = self:get_line_height() local lh = self:get_line_height()
local _, _, _, scroll_h = self.h_scrollbar:get_track_rect() self.scroll.to.y = common.clamp(self.scroll.to.y, ly - oy - self.size.y + lh * 2, ly - oy - lh)
self.scroll.to.y = common.clamp(self.scroll.to.y, ly - oy - self.size.y + scroll_h + lh * 2, ly - oy - lh)
local gw = self:get_gutter_width() local gw = self:get_gutter_width()
local xoffset = self:get_col_x_offset(line, col) local xoffset = self:get_col_x_offset(line, col)
local xmargin = 3 * self:get_font():get_width(' ') local xmargin = 3 * self:get_font():get_width(' ')
local xsup = xoffset + gw + xmargin local xsup = xoffset + gw + xmargin
local xinf = xoffset - xmargin local xinf = xoffset - xmargin
local _, _, scroll_w = self.v_scrollbar:get_track_rect() if xsup > self.scroll.x + self.size.x then
local size_x = math.max(0, self.size.x - scroll_w) self.scroll.to.x = xsup - self.size.x
if xsup > self.scroll.x + size_x then
self.scroll.to.x = xsup - size_x
elseif xinf < self.scroll.x then elseif xinf < self.scroll.x then
self.scroll.to.x = math.max(0, xinf) self.scroll.to.x = math.max(0, xinf)
end end
@ -441,7 +413,7 @@ function DocView:draw_line_text(line, x, y)
local last_token = nil local last_token = nil
local tokens = self.doc.highlighter:get_line(line).tokens local tokens = self.doc.highlighter:get_line(line).tokens
local tokens_count = #tokens local tokens_count = #tokens
if tokens[tokens_count] ~= nil and string.sub(tokens[tokens_count], -1) == "\n" then if string.sub(tokens[tokens_count], -1) == "\n" then
last_token = tokens_count - 1 last_token = tokens_count - 1
end end
for tidx, type, text in self.doc.highlighter:each_token(line) do for tidx, type, text in self.doc.highlighter:each_token(line) do
@ -450,7 +422,6 @@ function DocView:draw_line_text(line, x, y)
-- do not render newline, fixes issue #1164 -- do not render newline, fixes issue #1164
if tidx == last_token then text = text:sub(1, -2) end if tidx == last_token then text = text:sub(1, -2) end
tx = renderer.draw_text(font, text, tx, ty, color) tx = renderer.draw_text(font, text, tx, ty, color)
if tx > self.position.x + self.size.x then break end
end end
return self:get_line_height() return self:get_line_height()
end end

View File

@ -18,13 +18,13 @@ local Doc
local core = {} local core = {}
local function load_session() local function load_session()
local ok, t = pcall(dofile, common.basepath(USERDIR) .. "session.lua") local ok, t = pcall(dofile, USERDIR .. "/session.lua")
return ok and t or {} return ok and t or {}
end end
local function save_session() local function save_session()
local fp = io.open(common.basepath(USERDIR) .. "session.lua", "w") local fp = io.open(USERDIR .. "/session.lua", "w")
if fp then if fp then
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())),
@ -102,7 +102,7 @@ local function strip_leading_path(filename)
end end
local function strip_trailing_slash(filename) local function strip_trailing_slash(filename)
if filename:match("[^:]["..PATHSEP.."]$") then if filename:match("[^:][/\\]$") then
return filename:sub(1, -2) return filename:sub(1, -2)
end end
return filename return filename
@ -120,7 +120,9 @@ local function show_max_files_warning(dir)
"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 https://github.com/lite-xl/lite-xl." "usage.md at https://github.com/lite-xl/lite-xl."
core.warn(message) if core.status_view then
core.status_view:show_message("!", style.accent, message)
end
end end
@ -182,13 +184,13 @@ local function refresh_directory(topdir, target)
directory_start_idx = directory_start_idx + 1 directory_start_idx = directory_start_idx + 1
end end
local files = dirwatch.get_directory_files(topdir, topdir.name, (target or ""), 0, function() return false end) local files = dirwatch.get_directory_files(topdir, topdir.name, (target or ""), {}, 0, function() return false end)
local change = false local change = false
-- If this file doesn't exist, we should be calling this on our parent directory, assume we'll do that. -- If this file doesn't exist, we should be calling this on our parent directory, assume we'll do that.
-- Unwatch just in case. -- Unwatch just in case.
if files == nil then if files == nil then
topdir.watch:unwatch(common.basepath(topdir.name) .. (target or "")) topdir.watch:unwatch(topdir.name .. PATHSEP .. (target or ""))
return true return true
end end
@ -212,7 +214,7 @@ local function refresh_directory(topdir, target)
-- If it's not there, remove the entry from the list as being out of order. -- If it's not there, remove the entry from the list as being out of order.
table.remove(topdir.files, old_idx) table.remove(topdir.files, old_idx)
if old_info.type == "dir" then if old_info.type == "dir" then
topdir.watch:unwatch(common.basepath(topdir.name) .. old_info.filename) topdir.watch:unwatch(topdir.name .. PATHSEP .. old_info.filename)
end end
directory_end_idx = directory_end_idx - 1 directory_end_idx = directory_end_idx - 1
end end
@ -223,7 +225,7 @@ local function refresh_directory(topdir, target)
end end
end end
for i, v in ipairs(new_directories) do for i, v in ipairs(new_directories) do
topdir.watch:watch(common.basepath(topdir.name) .. v.filename) topdir.watch:watch(topdir.name .. PATHSEP .. v.filename)
if not topdir.files_limit or core.project_subdir_is_shown(topdir, v.filename) then if not topdir.files_limit or core.project_subdir_is_shown(topdir, v.filename) then
refresh_directory(topdir, v.filename) refresh_directory(topdir, v.filename)
end end
@ -263,7 +265,7 @@ function core.add_project_directory(path)
local fstype = PLATFORM == "Linux" and system.get_fs_type(topdir.name) or "unknown" local fstype = PLATFORM == "Linux" and system.get_fs_type(topdir.name) or "unknown"
topdir.force_scans = (fstype == "nfs" or fstype == "fuse") topdir.force_scans = (fstype == "nfs" or fstype == "fuse")
local t, complete, entries_count = dirwatch.get_directory_files(topdir, topdir.name, "", 0, timed_max_files_pred) local t, complete, entries_count = dirwatch.get_directory_files(topdir, topdir.name, "", {}, 0, timed_max_files_pred)
topdir.files = t topdir.files = t
if not complete then if not complete then
topdir.slow_filesystem = not complete and (entries_count <= config.max_project_files) topdir.slow_filesystem = not complete and (entries_count <= config.max_project_files)
@ -272,7 +274,7 @@ function core.add_project_directory(path)
refresh_directory(topdir) refresh_directory(topdir)
else else
for i,v in ipairs(t) do for i,v in ipairs(t) do
if v.type == "dir" then topdir.watch:watch(common.basepath(path) .. v.filename) end if v.type == "dir" then topdir.watch:watch(path .. PATHSEP .. v.filename) end
end end
end end
topdir.watch:watch(topdir.name) topdir.watch:watch(topdir.name)
@ -286,7 +288,7 @@ function core.add_project_directory(path)
local changed = topdir.watch:check(function(target) local changed = topdir.watch:check(function(target)
if target == topdir.name then return refresh_directory(topdir) end if target == topdir.name then return refresh_directory(topdir) end
local dirpath = target:sub(#topdir.name + 2) local dirpath = target:sub(#topdir.name + 2)
local abs_dirpath = common.basepath(topdir.name) .. dirpath local abs_dirpath = topdir.name .. PATHSEP .. dirpath
if dirpath then if dirpath then
-- check if the directory is in the project files list, if not exit. -- check if the directory is in the project files list, if not exit.
local dir_index, dir_match = file_search(topdir.files, {filename = dirpath, type = "dir"}) local dir_index, dir_match = file_search(topdir.files, {filename = dirpath, type = "dir"})
@ -303,7 +305,7 @@ function core.add_project_directory(path)
end end
end end
if project_dir_open then if project_dir_open then
coroutine.yield(changed and 0 or 0.05) coroutine.yield(changed and 0.05 or 0)
else else
return return
end end
@ -373,9 +375,9 @@ function core.update_project_subdir(dir, filename, expanded)
assert(dir.files_limit, "function should be called only when directory is in files limit mode") assert(dir.files_limit, "function should be called only when directory is in files limit mode")
dir.shown_subdir[filename] = expanded dir.shown_subdir[filename] = expanded
if expanded then if expanded then
dir.watch:watch(common.basepath(dir.name) .. filename) dir.watch:watch(dir.name .. PATHSEP .. filename)
else else
dir.watch:unwatch(common.basepath(dir.name) .. filename) dir.watch:unwatch(dir.name .. PATHSEP .. filename)
end end
return refresh_directory(dir, filename) return refresh_directory(dir, filename)
end end
@ -387,7 +389,7 @@ end
local function find_files_rec(root, path) local function find_files_rec(root, path)
local all = system.list_dir(root .. path) or {} local all = system.list_dir(root .. path) or {}
for _, file in ipairs(all) do for _, file in ipairs(all) do
local file = common.basepath(path) .. 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 then
info.filename = strip_leading_path(file) info.filename = strip_leading_path(file)
@ -462,7 +464,7 @@ local function create_user_directory()
error("cannot create directory \"" .. USERDIR .. "\": " .. err) error("cannot create directory \"" .. USERDIR .. "\": " .. err)
end end
for _, modname in ipairs {'plugins', 'colors', 'fonts'} do for _, modname in ipairs {'plugins', 'colors', 'fonts'} do
local subdirname = common.basepath(USERDIR) .. modname local subdirname = USERDIR .. PATHSEP .. modname
if not system.mkdir(subdirname) then if not system.mkdir(subdirname) then
error("cannot create directory: \"" .. subdirname .. "\"") error("cannot create directory: \"" .. subdirname .. "\"")
end end
@ -529,9 +531,12 @@ local style = require "core.style"
------------------------------ Plugins ---------------------------------------- ------------------------------ Plugins ----------------------------------------
-- disable plugin loading setting config entries: -- enable or disable plugin loading setting config entries:
-- disable plugin detectindent, otherwise it is enabled by default: -- enable plugins.trimwhitespace, otherwise it is disabled by default:
-- config.plugins.trimwhitespace = true
--
-- disable detectindent, otherwise it is enabled by default
-- config.plugins.detectindent = false -- config.plugins.detectindent = false
---------------------------- Miscellaneous ------------------------------------- ---------------------------- Miscellaneous -------------------------------------
@ -572,22 +577,22 @@ local config = require "core.config"
-- --
-- Here some examples: -- Here some examples:
-- --
-- "^%." matches any file of directory whose basename begins with a dot. -- "^%." match any file of directory whose basename begins with a dot.
-- --
-- When there is an '/' or a '/$' at the end, the pattern will only match -- When there is an '/' or a '/$' at the end the pattern it will only match
-- directories. When using such a pattern a final '/' will be added to the name -- directories. When using such a pattern a final '/' will be added to the name
-- of any directory entry before checking if it matches. -- of any directory entry before checking if it matches.
-- --
-- "^%.git/" matches any directory named ".git" anywhere in the project. -- "^%.git/" matches any directory named ".git" anywhere in the project.
-- --
-- If a "/" appears anywhere in the pattern (except when it appears at the end or -- If a "/" appears anywhere in the pattern except if it appears at the end or
-- is immediately followed by a '$'), then the pattern will be applied to the full -- is immediately followed by a '$' then the pattern will be applied to the full
-- path of the file or directory. An initial "/" will be prepended to the file's -- path of the file or directory. An initial "/" will be prepended to the file's
-- or directory's path to indicate the project's root. -- or directory's path to indicate the project's root.
-- --
-- "^/node_modules/" will match a directory named "node_modules" at the project's root. -- "^/node_modules/" will match a directory named "node_modules" at the project's root.
-- "^/build.*/" will match any top level directory whose name begins with "build". -- "^/build.*/" match any top level directory whose name begins with "build"
-- "^/subprojects/.+/" will match any directory inside a top-level folder named "subprojects". -- "^/subprojects/.+/" match any directory inside a top-level folder named "subprojects".
-- You may activate some plugins on a per-project basis to override the user's settings. -- You may activate some plugins on a per-project basis to override the user's settings.
-- config.plugins.trimwitespace = true -- config.plugins.trimwitespace = true
@ -602,7 +607,7 @@ function core.load_user_directory()
if not stat_dir then if not stat_dir then
create_user_directory() create_user_directory()
end end
local init_filename = common.basepath(USERDIR) .. "init.lua" local init_filename = USERDIR .. "/init.lua"
local stat_file = system.get_file_info(init_filename) local stat_file = system.get_file_info(init_filename)
if not stat_file then if not stat_file then
write_user_init_file(init_filename) write_user_init_file(init_filename)
@ -635,7 +640,7 @@ end
local function add_config_files_hooks() local function add_config_files_hooks()
-- 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
local user_filename = system.absolute_path(common.basepath(USERDIR) .. "init.lua") local user_filename = system.absolute_path(USERDIR .. PATHSEP .. "init.lua")
function Doc:save(filename, abs_filename) function Doc:save(filename, abs_filename)
local module_filename = system.absolute_path(".lite_project.lua") local module_filename = system.absolute_path(".lite_project.lua")
doc_save(self, filename, abs_filename) doc_save(self, filename, abs_filename)
@ -660,9 +665,9 @@ function core.project_absolute_path(filename)
return common.normalize_path(filename) return common.normalize_path(filename)
elseif not core.project_dir then elseif not core.project_dir then
local cwd = system.absolute_path(".") local cwd = system.absolute_path(".")
return common.basepath(cwd) .. common.normalize_path(filename) return cwd .. PATHSEP .. common.normalize_path(filename)
else else
return common.basepath(core.project_dir) .. filename return core.project_dir .. PATHSEP .. filename
end end
end end
@ -713,7 +718,7 @@ function core.init()
local file_abs = core.project_absolute_path(arg_filename) local file_abs = core.project_absolute_path(arg_filename)
if file_abs then if file_abs then
table.insert(files, file_abs) table.insert(files, file_abs)
project_dir = file_abs:match("^(.+)[:/\\].+$") project_dir = file_abs:match("^(.+)[/\\].+$")
end end
end end
end end
@ -788,7 +793,7 @@ function core.init()
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
@ -806,19 +811,15 @@ function core.init()
end end
if not plugins_success or got_user_error or got_project_error then if not plugins_success or got_user_error or got_project_error then
-- defer LogView to after everything is initialized, command.perform("core:open-log")
-- so that EmptyView won't be added after LogView.
core.add_thread(function()
command.perform("core:open-log")
end)
end end
core.configure_borderless_window() core.configure_borderless_window()
if #plugins_refuse_list.userdir.plugins > 0 or #plugins_refuse_list.datadir.plugins > 0 then if #plugins_refuse_list.userdir.plugins > 0 or #plugins_refuse_list.datadir.plugins > 0 then
local opt = { local opt = {
{ text = "Exit", default_no = true }, { font = style.font, text = "Exit", default_no = true },
{ text = "Continue", default_yes = true } { font = style.font, text = "Continue" , default_yes = true }
} }
local msg = {} local msg = {}
for _, entry in pairs(plugins_refuse_list) do for _, entry in pairs(plugins_refuse_list) do
@ -859,8 +860,8 @@ function core.confirm_close_docs(docs, close_fn, ...)
end end
local args = {...} local args = {...}
local opt = { local opt = {
{ text = "Yes", default_yes = true }, { font = style.font, text = "Yes", default_yes = true },
{ text = "No", default_no = true } { font = style.font, text = "No" , default_no = true }
} }
core.nag_view:show("Unsaved Changes", text, opt, function(item) core.nag_view:show("Unsaved Changes", text, opt, function(item)
if item.text == "Yes" then close_fn(table.unpack(args)) end if item.text == "Yes" then close_fn(table.unpack(args)) end
@ -878,7 +879,7 @@ function core.delete_temp_files(dir)
dir = type(dir) == "string" and common.normalize_path(dir) or USERDIR dir = type(dir) == "string" and common.normalize_path(dir) or USERDIR
for _, filename in ipairs(system.list_dir(dir) or {}) do for _, filename in ipairs(system.list_dir(dir) or {}) do
if filename:find(temp_file_prefix, 1, true) == 1 then if filename:find(temp_file_prefix, 1, true) == 1 then
os.remove(common.basepath(dir) .. filename) os.remove(dir .. PATHSEP .. filename)
end end
end end
end end
@ -886,7 +887,7 @@ end
function core.temp_filename(ext, dir) function core.temp_filename(ext, dir)
dir = type(dir) == "string" and common.normalize_path(dir) or USERDIR dir = type(dir) == "string" and common.normalize_path(dir) or USERDIR
temp_file_counter = temp_file_counter + 1 temp_file_counter = temp_file_counter + 1
return common.basepath(dir) .. temp_file_prefix return dir .. PATHSEP .. temp_file_prefix
.. string.format("%06x", temp_file_counter) .. (ext or "") .. string.format("%06x", temp_file_counter) .. (ext or "")
end end
@ -923,7 +924,7 @@ end
local function get_plugin_details(filename) local function get_plugin_details(filename)
local info = system.get_file_info(filename) local info = system.get_file_info(filename)
if info ~= nil and info.type == "dir" then if info ~= nil and info.type == "dir" then
filename = filename .. PATHSEP .. "init.lua" filename = filename .. "/init.lua"
info = system.get_file_info(filename) info = system.get_file_info(filename)
end end
if not info or not filename:match("%.lua$") then return false end if not info or not filename:match("%.lua$") then return false end
@ -962,7 +963,7 @@ function core.load_plugins()
} }
local files, ordered = {}, {} local files, ordered = {}, {}
for _, root_dir in ipairs {DATADIR, USERDIR} do for _, root_dir in ipairs {DATADIR, USERDIR} do
local plugin_dir = common.basepath(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 if not files[filename] then
table.insert( table.insert(
@ -977,7 +978,7 @@ function core.load_plugins()
for _, plugin in ipairs(ordered) do for _, plugin in ipairs(ordered) do
local dir = files[plugin.file] local dir = files[plugin.file]
local name = plugin.file:match("(.-)%.lua$") or plugin.file local name = plugin.file:match("(.-)%.lua$") or plugin.file
local is_lua_file, details = get_plugin_details(common.basepath(dir) .. plugin.file) local is_lua_file, details = get_plugin_details(dir .. '/' .. plugin.file)
plugin.valid = is_lua_file plugin.valid = is_lua_file
plugin.name = name plugin.name = name
@ -1164,9 +1165,7 @@ function core.custom_log(level, show, backtrace, fmt, ...)
local text = string.format(fmt, ...) local text = string.format(fmt, ...)
if show then if show then
local s = style.log[level] local s = style.log[level]
if core.status_view then core.status_view:show_message(s.icon, s.color, text)
core.status_view:show_message(s.icon, s.color, text)
end
end end
local info = debug.getinfo(2, "Sl") local info = debug.getinfo(2, "Sl")
@ -1241,9 +1240,6 @@ function core.on_event(type, ...)
elseif type == "textediting" then elseif type == "textediting" then
ime.on_text_editing(...) ime.on_text_editing(...)
elseif type == "keypressed" then elseif type == "keypressed" then
-- In some cases during IME composition input is still sent to us
-- so we just ignore it.
if ime.editing then return false end
did_keymap = keymap.on_key_pressed(...) did_keymap = keymap.on_key_pressed(...)
elseif type == "keyreleased" then elseif type == "keyreleased" then
keymap.on_key_released(...) keymap.on_key_released(...)
@ -1354,7 +1350,7 @@ end
local run_threads = coroutine.wrap(function() local run_threads = coroutine.wrap(function()
while true do while true do
local max_time = 1 / config.fps - 0.004 local max_time = 1 / config.fps - 0.004
local minimal_time_to_wake = math.huge local need_more_work = false
for k, thread in pairs(core.threads) do for k, thread in pairs(core.threads) do
-- run thread -- run thread
@ -1368,72 +1364,48 @@ local run_threads = coroutine.wrap(function()
end end
elseif wait then elseif wait then
thread.wake = system.get_time() + wait thread.wake = system.get_time() + wait
minimal_time_to_wake = math.min(minimal_time_to_wake, wait)
else else
minimal_time_to_wake = 0 need_more_work = true
end end
else
minimal_time_to_wake = math.min(minimal_time_to_wake, thread.wake - system.get_time())
end end
-- stop running threads if we're about to hit the end of frame -- stop running threads if we're about to hit the end of frame
if system.get_time() - core.frame_start > max_time then if system.get_time() - core.frame_start > max_time then
coroutine.yield(0, false) coroutine.yield(true)
end end
end end
coroutine.yield(minimal_time_to_wake, true) if not need_more_work then coroutine.yield(false) end
end end
end) end)
function core.run() function core.run()
local next_step local idle_iterations = 0
local last_frame_time
local run_threads_full = 0
while true do while true do
core.frame_start = system.get_time() core.frame_start = system.get_time()
local time_to_wake, threads_done = run_threads() local need_more_work = run_threads()
if threads_done then local did_redraw = core.step()
run_threads_full = run_threads_full + 1
end
local did_redraw = false
local did_step = false
local force_draw = core.redraw and last_frame_time and core.frame_start - last_frame_time > (1 / config.fps)
if force_draw or not next_step or system.get_time() >= next_step then
if core.step() then
did_redraw = true
last_frame_time = core.frame_start
end
next_step = nil
did_step = true
end
if core.restart_request or core.quit_request then break end if core.restart_request or core.quit_request then break end
if not did_redraw and not need_more_work then
if not did_redraw then idle_iterations = idle_iterations + 1
if system.window_has_focus() or not did_step or run_threads_full < 2 then -- do not wait of events at idle_iterations = 1 to give a chance at core.step to run
local now = system.get_time() -- and set "redraw" flag.
if not next_step then -- compute the time until the next blink if idle_iterations > 1 then
local t = now - core.blink_start if system.window_has_focus() then
-- keep running even with no events to make the cursor blinks
local t = system.get_time() - core.blink_start
local h = config.blink_period / 2 local h = config.blink_period / 2
local dt = math.ceil(t / h) * h - t local dt = math.ceil(t / h) * h - t
local cursor_time_to_wake = dt + 1 / config.fps system.wait_event(dt + 1 / config.fps)
next_step = now + cursor_time_to_wake else
system.wait_event()
end end
if system.wait_event(math.min(next_step - now, time_to_wake)) then
next_step = nil -- if we've recevied an event, perform a step
end
else
system.wait_event()
next_step = nil -- perform a step when we're not in focus if get we an event
end end
else -- if we redrew, then make sure we only draw at most FPS/sec else
run_threads_full = 0 idle_iterations = 0
local now = system.get_time() local elapsed = system.get_time() - core.frame_start
local elapsed = now - core.frame_start system.sleep(math.max(0, 1 / config.fps - elapsed))
local next_frame = math.max(0, 1 / config.fps - elapsed)
next_step = next_step or (now + next_frame)
system.sleep(math.min(next_frame, time_to_wake))
end end
end end
end end
@ -1451,7 +1423,7 @@ end
function core.on_error(err) function core.on_error(err)
-- write error to file -- write error to file
local fp = io.open(common.basepath(USERDIR) .. "error.txt", "wb") local fp = io.open(USERDIR .. "/error.txt", "wb")
fp:write("Error: " .. tostring(err) .. "\n") fp:write("Error: " .. tostring(err) .. "\n")
fp:write(debug.traceback("", 4) .. "\n") fp:write(debug.traceback("", 4) .. "\n")
fp:close() fp:close()

View File

@ -24,11 +24,9 @@ keymap.map = {}
keymap.reverse_map = {} keymap.reverse_map = {}
local macos = PLATFORM == "Mac OS X" local macos = PLATFORM == "Mac OS X"
local os4 = PLATFORM == "AmigaOS 4"
local mos = PLATFORM == "MORPHOS"
-- Thanks to mathewmariani, taken from his lite-macos github repository. -- Thanks to mathewmariani, taken from his lite-macos github repository.
local modkeys_os = require("core.modkeys-" .. (macos and "macos" or os4 and "os4" or mos and "mos" or "generic")) local modkeys_os = require("core.modkeys-" .. (macos and "macos" or "generic"))
---@type table<keymap.modkey, keymap.modkey> ---@type table<keymap.modkey, keymap.modkey>
local modkey_map = modkeys_os.map local modkey_map = modkeys_os.map
@ -37,38 +35,17 @@ local modkey_map = modkeys_os.map
local modkeys = modkeys_os.keys local modkeys = modkeys_os.keys
---Normalizes a stroke sequence to follow the modkeys table
---@param stroke string
---@return string
local function normalize_stroke(stroke)
local stroke_table = {}
for key in stroke:gmatch("[^+]+") do
table.insert(stroke_table, key)
end
table.sort(stroke_table, function(a, b)
if a == b then return false end
for _, mod in ipairs(modkeys) do
if a == mod or b == mod then
return a == mod
end
end
return a < b
end)
return table.concat(stroke_table, "+")
end
---Generates a stroke sequence including currently pressed mod keys. ---Generates a stroke sequence including currently pressed mod keys.
---@param key string ---@param key string
---@return string ---@return string
local function key_to_stroke(key) local function key_to_stroke(key)
local keys = { key } local stroke = ""
for _, mk in ipairs(modkeys) do for _, mk in ipairs(modkeys) do
if keymap.modkeys[mk] then if keymap.modkeys[mk] then
table.insert(keys, mk) stroke = stroke .. mk .. "+"
end end
end end
return normalize_stroke(table.concat(keys, "+")) return stroke .. key
end end
@ -97,12 +74,11 @@ end
---@param map keymap.map ---@param map keymap.map
local function remove_duplicates(map) local function remove_duplicates(map)
for stroke, commands in pairs(map) do for stroke, commands in pairs(map) do
local normalized_stroke = normalize_stroke(stroke)
if type(commands) == "string" or type(commands) == "function" then if type(commands) == "string" or type(commands) == "function" then
commands = { commands } commands = { commands }
end end
if keymap.map[normalized_stroke] then if keymap.map[stroke] then
for _, registered_cmd in ipairs(keymap.map[normalized_stroke]) do for _, registered_cmd in ipairs(keymap.map[stroke]) do
local j = 0 local j = 0
for i=1, #commands do for i=1, #commands do
while commands[i + j] == registered_cmd do while commands[i + j] == registered_cmd do
@ -120,12 +96,11 @@ local function remove_duplicates(map)
end end
end end
---Add bindings by replacing commands that were previously assigned to a shortcut. ---Add bindings by replacing commands that were previously assigned to a shortcut.
---@param map keymap.map ---@param map keymap.map
function keymap.add_direct(map) function keymap.add_direct(map)
for stroke, commands in pairs(map) do for stroke, commands in pairs(map) do
stroke = normalize_stroke(stroke)
if type(commands) == "string" or type(commands) == "function" then if type(commands) == "string" or type(commands) == "function" then
commands = { commands } commands = { commands }
end end
@ -153,7 +128,6 @@ function keymap.add(map, overwrite)
if macos then if macos then
stroke = stroke:gsub("%f[%a]ctrl%f[%A]", "cmd") stroke = stroke:gsub("%f[%a]ctrl%f[%A]", "cmd")
end end
stroke = normalize_stroke(stroke)
if overwrite then if overwrite then
if keymap.map[stroke] then if keymap.map[stroke] then
for _, cmd in ipairs(keymap.map[stroke]) do for _, cmd in ipairs(keymap.map[stroke]) do
@ -179,7 +153,6 @@ end
---@param shortcut string ---@param shortcut string
---@param cmd string ---@param cmd string
function keymap.unbind(shortcut, cmd) function keymap.unbind(shortcut, cmd)
shortcut = normalize_stroke(shortcut)
remove_only(keymap.map, shortcut, cmd) remove_only(keymap.map, shortcut, cmd)
remove_only(keymap.reverse_map, cmd, shortcut) remove_only(keymap.reverse_map, cmd, shortcut)
end end
@ -205,6 +178,10 @@ end
-- Events listening -- Events listening
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function keymap.on_key_pressed(k, ...) function keymap.on_key_pressed(k, ...)
-- In MacOS and Windows during IME composition input is still sent to us
-- so we just ignore it
if PLATFORM ~= "Linux" and ime.editing then return false end
local mk = modkey_map[k] local mk = modkey_map[k]
if mk then if mk then
keymap.modkeys[mk] = true keymap.modkeys[mk] = true

View File

@ -125,19 +125,9 @@ 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
local lh = style.font:get_height() + style.padding.y
if 0 < self.scroll.to.y then
local index = #core.log_items
while index > 1 and self.last_item ~= core.log_items[index] do
index = index - 1
end
local diff_index = #core.log_items - index
self.scroll.to.y = self.scroll.to.y + diff_index * lh
self.scroll.y = self.scroll.to.y
else
self.yoffset = -lh
end
self.last_item = item self.last_item = item
self.scroll.to.y = 0
self.yoffset = -(style.font:get_height() + style.padding.y)
end end
local expanding = self.expanding[1] local expanding = self.expanding[1]

View File

@ -9,6 +9,6 @@ modkeys.map = {
["right alt"] = "altgr", ["right alt"] = "altgr",
} }
modkeys.keys = { "ctrl", "shift", "alt", "altgr" } modkeys.keys = { "ctrl", "alt", "altgr", "shift" }
return modkeys return modkeys

View File

@ -13,6 +13,6 @@ modkeys.map = {
["right alt"] = "altgr", ["right alt"] = "altgr",
} }
modkeys.keys = { "ctrl", "alt", "option", "altgr", "shift", "cmd" } modkeys.keys = { "cmd", "ctrl", "alt", "option", "altgr", "shift" }
return modkeys return modkeys

View File

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

View File

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

View File

@ -91,7 +91,7 @@ function NagView:each_option()
for i = #self.options, 1, -1 do for i = #self.options, 1, -1 do
opt = self.options[i] opt = self.options[i]
bw = style.font:get_width(opt.text) + 2 * BORDER_WIDTH + style.padding.x bw = opt.font:get_width(opt.text) + 2 * BORDER_WIDTH + style.padding.x
ox = ox - bw - style.padding.x ox = ox - bw - style.padding.x
coroutine.yield(i, opt, ox,oy,bw,bh) coroutine.yield(i, opt, ox,oy,bw,bh)
@ -230,7 +230,7 @@ local function draw_nagview_message(self)
renderer.draw_rect(lx,ly,uw,UNDERLINE_WIDTH, style.nagbar_text) renderer.draw_rect(lx,ly,uw,UNDERLINE_WIDTH, style.nagbar_text)
end end
common.draw_text(style.font, style.nagbar_text, opt.text, "center", fx,fy,fw,fh) common.draw_text(opt.font, style.nagbar_text, opt.text, "center", fx,fy,fw,fh)
end end
self:draw_scrollbar() self:draw_scrollbar()
@ -245,16 +245,6 @@ function NagView:draw()
core.root_view:defer_draw(draw_nagview_message, self) core.root_view:defer_draw(draw_nagview_message, self)
end end
function NagView:on_scale_change(new_scale, old_scale)
BORDER_WIDTH = common.round(1 * new_scale)
UNDERLINE_WIDTH = common.round(2 * new_scale)
UNDERLINE_MARGIN = common.round(1 * new_scale)
self.target_height = math.max(
self:get_message_height(),
self:get_buttons_height()
)
end
function NagView:get_message_height() function NagView:get_message_height()
local h = 0 local h = 0
for str in string.gmatch(self.message, "(.-)\n") do for str in string.gmatch(self.message, "(.-)\n") do

View File

@ -47,7 +47,7 @@ function Object:__tostring()
return "Object" return "Object"
end end
---Metamethod to allow using the object call as a constructor. ---Methamethod to allow using the object call as a constructor.
---@return core.object ---@return core.object
function Object:__call(...) function Object:__call(...)
local obj = setmetatable({}, self) local obj = setmetatable({}, self)

View File

@ -259,10 +259,10 @@ function RootView:on_mouse_moved(x, y, dx, dy)
if self.dragged_divider then if self.dragged_divider then
local node = self.dragged_divider local node = self.dragged_divider
if node.type == "hsplit" then if node.type == "hsplit" then
x = common.clamp(x - node.position.x, 0, self.root_node.size.x * 0.95) x = common.clamp(x, 0, self.root_node.size.x * 0.95)
resize_child_node(node, "x", x, dx) resize_child_node(node, "x", x, dx)
elseif node.type == "vsplit" then elseif node.type == "vsplit" then
y = common.clamp(y - node.position.y, 0, self.root_node.size.y * 0.95) y = common.clamp(y, 0, self.root_node.size.y * 0.95)
resize_child_node(node, "y", y, dy) resize_child_node(node, "y", y, dy)
end end
node.divider = common.clamp(node.divider, 0.01, 0.99) node.divider = common.clamp(node.divider, 0.01, 0.99)

View File

@ -189,9 +189,8 @@ function Scrollbar:_on_mouse_pressed_normal(button, x, y, clicks)
self.drag_start_offset = along - y self.drag_start_offset = along - y
return true return true
elseif overlaps == "track" then elseif overlaps == "track" then
local nr = self.normal_rect
self.drag_start_offset = - along_size / 2 self.drag_start_offset = - along_size / 2
return common.clamp((y - nr.along - along_size / 2) / (nr.along_size - along_size), 0, 1) return (y - self.normal_rect.along - along_size / 2) / self.normal_rect.along_size
end end
end end
end end

View File

@ -1,15 +1,15 @@
-- 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.1.4r1" VERSION = "@PROJECT_VERSION@"
MOD_VERSION = "3" MOD_VERSION = "3"
SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or 1 SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or SCALE
PATHSEP = package.config:sub(1, 1) PATHSEP = package.config:sub(1, 1)
EXEDIR = EXEFILE:match("^(.+)[/\\][^/\\]+$") EXEDIR = EXEFILE:match("^(.+)[/\\][^/\\]+$")
if MACOS_RESOURCES then if MACOS_RESOURCES then
DATADIR = MACOS_RESOURCES DATADIR = MACOS_RESOURCES
else else
local prefix = os.getenv('LITE_PREFIX') or EXEDIR:match("^(.+)[/\\]bin$") local prefix = EXEDIR:match("^(.+)[/\\]bin$")
DATADIR = prefix and (prefix .. PATHSEP .. 'share' .. PATHSEP .. 'lite-xl') or (EXEDIR .. PATHSEP .. 'data') DATADIR = prefix and (prefix .. PATHSEP .. 'share' .. PATHSEP .. 'lite-xl') or (EXEDIR .. PATHSEP .. 'data')
end end
USERDIR = (system.get_file_info(EXEDIR .. PATHSEP .. 'user') and (EXEDIR .. PATHSEP .. 'user')) USERDIR = (system.get_file_info(EXEDIR .. PATHSEP .. 'user') and (EXEDIR .. PATHSEP .. 'user'))
@ -35,8 +35,8 @@ package.cpath =
package.native_plugins = {} package.native_plugins = {}
package.searchers = { package.searchers[1], package.searchers[2], function(modname) package.searchers = { package.searchers[1], package.searchers[2], function(modname)
local path, err = package.searchpath(modname, package.cpath) local path = package.searchpath(modname, package.cpath)
if not path then return err end if not path then return nil end
return system.load_native_plugin, path return system.load_native_plugin, path
end } end }

View File

@ -44,7 +44,7 @@ local function find(string, field)
end end
function syntax.get(filename, header) function syntax.get(filename, header)
return (filename and find(filename, "files")) return find(common.basename(filename), "files")
or (header and find(header, "headers")) or (header and find(header, "headers"))
or plain_text_syntax or plain_text_syntax
end end

View File

@ -1,16 +1,14 @@
local core = require "core" local core = require "core"
local syntax = require "core.syntax" local syntax = require "core.syntax"
local config = require "core.config"
local tokenizer = {} local tokenizer = {}
local bad_patterns = {} local bad_patterns = {}
local function push_token(t, type, text) local function push_token(t, type, text)
if not text or #text == 0 then return end
type = type or "normal" type = type or "normal"
local prev_type = t[#t-1] local prev_type = t[#t-1]
local prev_text = t[#t] local prev_text = t[#t]
if prev_type and (prev_type == type or (prev_text:ufind("^%s*$") and type ~= "incomplete")) then if prev_type and (prev_type == type or prev_text:ufind("^%s*$")) then
t[#t-1] = type t[#t-1] = type
t[#t] = prev_text .. text t[#t] = prev_text .. text
else else
@ -123,17 +121,15 @@ local function report_bad_pattern(log_fn, syntax, pattern_idx, msg, ...)
end end
if bad_patterns[syntax][pattern_idx] then return end if bad_patterns[syntax][pattern_idx] then return end
bad_patterns[syntax][pattern_idx] = true bad_patterns[syntax][pattern_idx] = true
log_fn("Malformed pattern #%d <%s> in %s language plugin.\n" .. msg, log_fn("Malformed pattern #%d in %s language plugin. " .. msg,
pattern_idx, pattern_idx, syntax.name or "unnamed", ...)
syntax.patterns[pattern_idx].pattern or syntax.patterns[pattern_idx].regex,
syntax.name or "unnamed", ...)
end end
---@param incoming_syntax table ---@param incoming_syntax table
---@param text string ---@param text string
---@param state string ---@param state string
function tokenizer.tokenize(incoming_syntax, text, state, resume) function tokenizer.tokenize(incoming_syntax, text, state)
local res local res = {}
local i = 1 local i = 1
if #incoming_syntax.patterns == 0 then if #incoming_syntax.patterns == 0 then
@ -141,20 +137,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
end end
state = state or string.char(0) state = state or string.char(0)
if resume then
res = resume.res
-- Remove "incomplete" tokens
while res[#res-1] == "incomplete" do
table.remove(res, #res)
table.remove(res, #res)
end
i = resume.i
state = resume.state
end
res = res or {}
-- incoming_syntax : the parent syntax of the file. -- incoming_syntax : the parent syntax of the file.
-- state : a string of bytes representing syntax state (see above) -- state : a string of bytes representing syntax state (see above)
@ -213,11 +195,9 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
-- Remove '^' from the beginning of the pattern -- Remove '^' from the beginning of the pattern
if type(target) == "table" then if type(target) == "table" then
target[p_idx] = code:usub(2) target[p_idx] = code:usub(2)
code = target[p_idx]
else else
p.pattern = p.pattern and code:usub(2) p.pattern = p.pattern and code:usub(2)
p.regex = p.regex and code:usub(2) p.regex = p.regex and code:usub(2)
code = p.pattern or p.regex
end end
end end
end end
@ -265,22 +245,7 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
end end
local text_len = text:ulen() local text_len = text:ulen()
local start_time = system.get_time() while i <= text_len do
local starting_i = i
while text_len ~= nil and i <= text_len do
-- Every 200 chars, check if we're out of time
if i - starting_i > 200 then
starting_i = i
if system.get_time() - start_time > 0.5 / config.fps then
-- We're out of time
push_token(res, "incomplete", string.usub(text, i))
return res, string.char(0), {
res = res,
i = i,
state = state
}
end
end
-- continue trying to match the end pattern of a pair if we have a state set -- continue trying to match the end pattern of a pair if we have a state set
if current_pattern_idx > 0 then if current_pattern_idx > 0 then
local p = current_syntax.patterns[current_pattern_idx] local p = current_syntax.patterns[current_pattern_idx]
@ -335,14 +300,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
for n, p in ipairs(current_syntax.patterns) do for n, p in ipairs(current_syntax.patterns) do
local find_results = { find_text(text, p, i, true, false) } local find_results = { find_text(text, p, i, true, false) }
if find_results[1] then if find_results[1] then
-- Check for patterns successfully matching nothing
if find_results[1] > find_results[2] then
report_bad_pattern(core.warn, current_syntax, n,
"Pattern successfully matched, but nothing was captured.")
goto continue
end
-- Check for patterns with mismatching number of `types`
local type_is_table = type(p.type) == "table" local type_is_table = type(p.type) == "table"
local n_types = type_is_table and #p.type or 1 local n_types = type_is_table and #p.type or 1
if #find_results == 2 and type_is_table then if #find_results == 2 and type_is_table then
@ -356,7 +313,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
report_bad_pattern(core.warn, current_syntax, n, report_bad_pattern(core.warn, current_syntax, n,
"Too many token types: got %d needed %d.", n_types, #find_results - 1) "Too many token types: got %d needed %d.", n_types, #find_results - 1)
end end
-- matched pattern; make and add tokens -- matched pattern; make and add tokens
push_tokens(res, current_syntax, p, text, find_results) push_tokens(res, current_syntax, p, text, find_results)
-- update state if this was a start|end pattern pair -- update state if this was a start|end pattern pair
@ -372,7 +328,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
i = find_results[2] + 1 i = find_results[2] + 1
matched = true matched = true
break break
::continue::
end end
end end

View File

@ -212,14 +212,12 @@ end)
local partial = "" local partial = ""
local suggestions_offset = 1
local suggestions_idx = 1 local suggestions_idx = 1
local suggestions = {} local suggestions = {}
local last_line, last_col local last_line, last_col
local function reset_suggestions() local function reset_suggestions()
suggestions_offset = 1
suggestions_idx = 1 suggestions_idx = 1
suggestions = {} suggestions = {}
@ -263,7 +261,6 @@ local function update_suggestions()
end end
end end
suggestions_idx = 1 suggestions_idx = 1
suggestions_offset = 1
end end
local function get_partial_symbol() local function get_partial_symbol()
@ -279,10 +276,8 @@ local function get_active_view()
end end
end end
local last_max_width = 0
local function get_suggestions_rect(av) local function get_suggestions_rect(av)
if #suggestions == 0 then if #suggestions == 0 then
last_max_width = 0
return 0, 0, 0, 0 return 0, 0, 0, 0
end end
@ -292,47 +287,38 @@ local function get_suggestions_rect(av)
local font = av:get_font() local font = av:get_font()
local th = font:get_height() local th = font:get_height()
local ah = config.plugins.autocomplete.max_height
local max_items = math.min(ah, #suggestions)
local show_count = math.min(#suggestions, ah)
local start_index = math.max(suggestions_idx-(ah-1), 1)
local max_width = 0 local max_width = 0
for i = start_index, start_index + show_count - 1 do for _, s in ipairs(suggestions) do
local s = suggestions[i]
local w = font:get_width(s.text) local w = font:get_width(s.text)
if s.info then if s.info then
w = w + style.font:get_width(s.info) + style.padding.x w = w + style.font:get_width(s.info) + style.padding.x
end end
max_width = math.max(max_width, w) max_width = math.max(max_width, w)
end end
max_width = math.max(last_max_width, max_width)
last_max_width = max_width
max_width = max_width + style.padding.x * 2 local ah = config.plugins.autocomplete.max_height
x = x - style.padding.x
local max_items = #suggestions
if max_items > ah then
max_items = ah
end
-- additional line to display total items -- additional line to display total items
max_items = max_items + 1 max_items = max_items + 1
if max_width > core.root_view.size.x then if max_width < 150 then
max_width = core.root_view.size.x max_width = 150
end
if max_width < 150 * SCALE then
max_width = 150 * SCALE
end end
-- if portion not visiable to right, reposition to DocView right margin -- if portion not visiable to right, reposition to DocView right margin
if x + max_width > core.root_view.size.x then if (x - av.position.x) + max_width > av.size.x then
x = (av.size.x + av.position.x) - max_width x = (av.size.x + av.position.x) - max_width - (style.padding.x * 2)
end end
return return
x, x - style.padding.x,
y - style.padding.y, y - style.padding.y,
max_width, max_width + style.padding.x * 2,
max_items * (th + style.padding.y) + style.padding.y max_items * (th + style.padding.y) + style.padding.y
end end
@ -460,29 +446,16 @@ local function draw_suggestions_box(av)
local font = av:get_font() local font = av:get_font()
local lh = font:get_height() + style.padding.y local lh = font:get_height() + style.padding.y
local y = ry + style.padding.y / 2 local y = ry + style.padding.y / 2
local show_count = math.min(#suggestions, ah) local show_count = #suggestions <= ah and #suggestions or ah
local start_index = suggestions_offset local start_index = suggestions_idx > ah and (suggestions_idx-(ah-1)) or 1
for i=start_index, start_index+show_count-1, 1 do for i=start_index, start_index+show_count-1, 1 do
if not suggestions[i] then if not suggestions[i] then
break break
end end
local s = suggestions[i] local s = suggestions[i]
local info_size = s.info and (style.font:get_width(s.info) + style.padding.x) or style.padding.x
local color = (i == suggestions_idx) and style.accent or style.text local color = (i == suggestions_idx) and style.accent or style.text
-- Push clip to avoid that the suggestion text gets drawn over suggestion type/icon common.draw_text(font, color, s.text, "left", rx + style.padding.x, y, rw, lh)
core.push_clip_rect(rx + style.padding.x, y, rw - info_size - style.padding.x, lh)
local x_adv = common.draw_text(font, color, s.text, "left", rx + style.padding.x, y, rw, lh)
core.pop_clip_rect()
-- If the text wasn't fully visible, draw an ellipsis
if x_adv > rx + rw - info_size then
local ellipsis_size = font:get_width("")
local ell_x = rx + rw - info_size - ellipsis_size
renderer.draw_rect(ell_x, y, ellipsis_size, lh, style.background3)
common.draw_text(font, color, "", "left", ell_x, y, ellipsis_size, lh)
end
if s.info then if s.info then
color = (i == suggestions_idx) and style.text or style.dim color = (i == suggestions_idx) and style.text or style.dim
common.draw_text(style.font, color, s.info, "right", rx, y, rw - style.padding.x, lh) common.draw_text(style.font, color, s.info, "right", rx, y, rw - style.padding.x, lh)
@ -658,6 +631,7 @@ end
command.add(predicate, { command.add(predicate, {
["autocomplete:complete"] = function(dv) ["autocomplete:complete"] = function(dv)
local doc = dv.doc local doc = dv.doc
local line, col = doc:get_selection()
local item = suggestions[suggestions_idx] local item = suggestions[suggestions_idx]
local text = item.text local text = item.text
local inserted = false local inserted = false
@ -666,47 +640,19 @@ command.add(predicate, {
end end
if not inserted then if not inserted then
local current_partial = get_partial_symbol() local current_partial = get_partial_symbol()
local sz = #current_partial doc:insert(line, col, text)
doc:remove(line, col, line, col - #current_partial)
for _, line1, col1, line2, _ in doc:get_selections(true) do doc:set_selection(line, col + #text - #current_partial)
local n = col1 - 1
local line = doc.lines[line1]
for i = 1, sz + 1 do
local j = sz - i
local subline = line:sub(n - j, n)
local subpartial = current_partial:sub(i, -1)
if subpartial == subline then
doc:remove(line1, col1, line2, n - j)
break
end
end
end
doc:text_input(item.text)
end end
reset_suggestions() reset_suggestions()
end, end,
["autocomplete:previous"] = function() ["autocomplete:previous"] = function()
suggestions_idx = (suggestions_idx - 2) % #suggestions + 1 suggestions_idx = (suggestions_idx - 2) % #suggestions + 1
local ah = math.min(config.plugins.autocomplete.max_height, #suggestions)
if suggestions_offset > suggestions_idx then
suggestions_offset = suggestions_idx
elseif suggestions_offset + ah < suggestions_idx + 1 then
suggestions_offset = suggestions_idx - ah + 1
end
end, end,
["autocomplete:next"] = function() ["autocomplete:next"] = function()
suggestions_idx = (suggestions_idx % #suggestions) + 1 suggestions_idx = (suggestions_idx % #suggestions) + 1
local ah = math.min(config.plugins.autocomplete.max_height, #suggestions)
if suggestions_offset + ah < suggestions_idx + 1 then
suggestions_offset = suggestions_idx - ah + 1
elseif suggestions_offset > suggestions_idx then
suggestions_offset = suggestions_idx
end
end, end,
["autocomplete:cycle"] = function() ["autocomplete:cycle"] = function()

View File

@ -46,8 +46,8 @@ end
local function check_prompt_reload(doc) local function check_prompt_reload(doc)
if doc and doc.deferred_reload then if doc and doc.deferred_reload then
core.nag_view:show("File Changed", doc.filename .. " has changed. Reload this file?", { core.nag_view:show("File Changed", doc.filename .. " has changed. Reload this file?", {
{ text = "Yes", default_yes = true }, { font = style.font, text = "Yes", default_yes = true },
{ text = "No", default_no = true } { font = style.font, text = "No" , default_no = true }
}, function(item) }, function(item)
if item.text == "Yes" then reload_doc(doc) end if item.text == "Yes" then reload_doc(doc) end
doc.deferred_reload = false doc.deferred_reload = false
@ -69,7 +69,7 @@ function dirwatch:check(change_callback, ...)
for _, doc in ipairs(core.docs) do for _, doc in ipairs(core.docs) do
if doc.abs_filename and (dir == common.dirname(doc.abs_filename) or dir == doc.abs_filename) then if doc.abs_filename and (dir == common.dirname(doc.abs_filename) or dir == doc.abs_filename) then
local info = system.get_file_info(doc.filename or "") local info = system.get_file_info(doc.filename or "")
if info and info.type == "file" and times[doc] ~= info.modified then if info and times[doc] ~= info.modified then
if not doc:is_dirty() and not config.plugins.autoreload.always_show_nagview then if not doc:is_dirty() and not config.plugins.autoreload.always_show_nagview then
reload_doc(doc) reload_doc(doc)
else else

View File

@ -4,7 +4,6 @@ local command = require "core.command"
local keymap = require "core.keymap" local keymap = require "core.keymap"
local ContextMenu = require "core.contextmenu" local ContextMenu = require "core.contextmenu"
local RootView = require "core.rootview" local RootView = require "core.rootview"
local config = require "core.config"
local menu = ContextMenu() local menu = ContextMenu()
local on_view_mouse_pressed = RootView.on_view_mouse_pressed local on_view_mouse_pressed = RootView.on_view_mouse_pressed
@ -62,24 +61,18 @@ keymap.add { ["up"] = "context:focus-previous" }
keymap.add { ["down"] = "context:focus-next" } keymap.add { ["down"] = "context:focus-next" }
keymap.add { ["escape"] = "context:hide" } keymap.add { ["escape"] = "context:hide" }
if require("plugins.scale") then
local cmds = { menu:register("core.docview", {
{ text = "Cut", command = "doc:cut" }, { text = "Cut", command = "doc:cut" },
{ text = "Copy", command = "doc:copy" }, { text = "Copy", command = "doc:copy" },
{ text = "Paste", command = "doc:paste" }, { text = "Paste", command = "doc:paste" },
ContextMenu.DIVIDER, { text = "Font +", command = "scale:increase" },
{ text = "Find", command = "find-replace:find" }, { text = "Font -", command = "scale:decrease" },
{ text = "Replace", command = "find-replace:replace" } { text = "Font Reset", command = "scale:reset" },
} ContextMenu.DIVIDER,
{ text = "Find", command = "find-replace:find" },
if config.plugins.scale ~= false and require("plugins.scale") then { text = "Replace", command = "find-replace:replace" }
table.move(cmds, 4, 6, 7) })
cmds[4] = { text = "Font +", command = "scale:increase" }
cmds[5] = { text = "Font -", command = "scale:decrease" }
cmds[6] = { text = "Font Reset", command = "scale:reset" }
end end
menu:register("core.docview", cmds)
return menu return menu

View File

@ -243,7 +243,7 @@ local function detect_indent_stat(doc)
local max_lines = auto_detect_max_lines local max_lines = auto_detect_max_lines
for i, text in get_non_empty_lines(doc.syntax, doc.lines) do for i, text in get_non_empty_lines(doc.syntax, doc.lines) do
local spaces = text:match("^ +") local spaces = text:match("^ +")
if spaces and #spaces > 1 then table.insert(stat, #spaces) end if spaces then table.insert(stat, spaces:len()) end
local tabs = text:match("^\t+") local tabs = text:match("^\t+")
if tabs then tab_count = tab_count + 1 end if tabs then tab_count = tab_count + 1 end
-- if nothing found for first lines try at least 4 more times -- if nothing found for first lines try at least 4 more times

View File

@ -3,12 +3,11 @@
local style = require "core.style" local style = require "core.style"
local DocView = require "core.docview" local DocView = require "core.docview"
local common = require "core.common" local common = require "core.common"
local command = require "core.command"
local config = require "core.config" local config = require "core.config"
local Highlighter = require "core.doc.highlighter" local Highlighter = require "core.doc.highlighter"
config.plugins.drawwhitespace = common.merge({ config.plugins.drawwhitespace = common.merge({
enabled = false, enabled = true,
show_leading = true, show_leading = true,
show_trailing = true, show_trailing = true,
show_middle = true, show_middle = true,
@ -42,7 +41,7 @@ config.plugins.drawwhitespace = common.merge({
description = "Disable or enable the drawing of white spaces.", description = "Disable or enable the drawing of white spaces.",
path = "enabled", path = "enabled",
type = "toggle", type = "toggle",
default = false default = true
}, },
{ {
label = "Show Leading", label = "Show Leading",
@ -304,18 +303,3 @@ function DocView:draw_line_text(idx, x, y)
return draw_line_text(self, idx, x, y) return draw_line_text(self, idx, x, y)
end end
command.add(nil, {
["draw-whitespace:toggle"] = function()
config.plugins.drawwhitespace.enabled = not config.plugins.drawwhitespace.enabled
end,
["draw-whitespace:disable"] = function()
config.plugins.drawwhitespace.enabled = false
end,
["draw-whitespace:enable"] = function()
config.plugins.drawwhitespace.enabled = true
end,
})

View File

@ -5,8 +5,7 @@ syntax.add {
name = "C++", 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++$"
"%.ino$"
}, },
comment = "//", comment = "//",
block_comment = { "/*", "*/" }, block_comment = { "/*", "*/" },
@ -15,9 +14,9 @@ syntax.add {
{ pattern = { "/%*", "%*/" }, type = "comment" }, { pattern = { "/%*", "%*/" }, type = "comment" },
{ pattern = { '"', '"', '\\' }, type = "string" }, { pattern = { '"', '"', '\\' }, type = "string" },
{ pattern = { "'", "'", '\\' }, type = "string" }, { pattern = { "'", "'", '\\' }, type = "string" },
{ pattern = "0x%x+[%x']*", type = "number" }, { pattern = "0x%x+", type = "number" },
{ pattern = "%d+[%d%.'eE]*f?", type = "number" }, { pattern = "%d+[%d%.'eE]*f?", type = "number" },
{ pattern = "%.?%d+[%d']*f?", type = "number" }, { pattern = "%.?%d+f?", type = "number" },
{ pattern = "[%+%-=/%*%^%%<>!~|:&]", type = "operator" }, { pattern = "[%+%-=/%*%^%%<>!~|:&]", type = "operator" },
{ pattern = "##", type = "operator" }, { pattern = "##", type = "operator" },
{ pattern = "struct%s()[%a_][%w_]*", type = {"keyword", "keyword2"} }, { pattern = "struct%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },

View File

@ -1,75 +1,24 @@
-- mod-version:3 -- mod-version:3
local syntax = require "core.syntax" local syntax = require "core.syntax"
-- Regex pattern explanation:
-- This will match / and will look ahead for something that looks like a regex.
--
-- (?!/) Don't match empty regexes.
--
-- (?>...) this is using an atomic group to minimize backtracking, as that'd
-- cause "Catastrophic Backtracking" in some cases.
--
-- [^\\[\/]++ will match anything that's isn't an escape, a start of character
-- class or an end of pattern, without backtracking (the second +).
--
-- \\. will match anything that's escaped.
--
-- \[(?:[^\\\]++]|\\.)*+\] will match character classes.
--
-- /[gmiyuvsd]*\s*[\n,;\)\]\}\.]) will match the end of pattern delimiter, optionally
-- followed by pattern options, and anything that can
-- be after a pattern.
--
-- Demo with some unit tests (click on the Unit Tests entry): https://regex101.com/r/Vx5L5V/1
-- Note that it has a couple of changes to make it work on that platform.
local regex_pattern = {
[=[\/(?=(?!\/)(?:(?>[^\\[\/]++|\\.|\[(?:[^\\\]]++|\\.)*+\])*+)++\/[gmiyuvsd]*\s*(?:[\n,;\)\]\}\.]|\/[\/*]))()]=],
"/()[gmiyuvsd]*", "\\"
}
-- For the moment let's not actually differentiate the insides of the regex,
-- as this will need new token types...
local inner_regex_syntax = {
patterns = {
{ pattern = "%(()%?[:!=><]", type = { "string", "string" } },
{ pattern = "[.?+*%(%)|]", type = "string" },
{ pattern = "{%d*,?%d*}", type = "string" },
{ regex = { [=[\[()\^?]=], [=[(?:\]|(?=\n))()]=], "\\" },
type = { "string", "string" },
syntax = { -- Inside character class
patterns = {
{ pattern = "\\\\", type = "string" },
{ pattern = "\\%]", type = "string" },
{ pattern = "[^%]\n]", type = "string" }
},
symbols = {}
}
},
{ regex = "\\/", type = "string" },
{ regex = "[^/\n]", type = "string" },
},
symbols = {}
}
syntax.add { syntax.add {
name = "JavaScript", name = "JavaScript",
files = { "%.js$", "%.json$", "%.cson$", "%.mjs$", "%.cjs$" }, files = { "%.js$", "%.json$", "%.cson$", "%.mjs$", "%.cjs$" },
comment = "//", comment = "//",
block_comment = { "/*", "*/" }, block_comment = { "/*", "*/" },
patterns = { patterns = {
{ pattern = "//.*", type = "comment" }, { pattern = "//.*", type = "comment" },
{ pattern = { "/%*", "%*/" }, type = "comment" }, { pattern = { "/%*", "%*/" }, type = "comment" },
{ regex = regex_pattern, syntax = inner_regex_syntax, type = {"string", "string"} }, { pattern = { '/[^= ]', '/', '\\' },type = "string" },
{ pattern = { '"', '"', '\\' }, type = "string" }, { pattern = { '"', '"', '\\' }, type = "string" },
{ pattern = { "'", "'", '\\' }, type = "string" }, { pattern = { "'", "'", '\\' }, type = "string" },
{ pattern = { "`", "`", '\\' }, type = "string" }, { pattern = { "`", "`", '\\' }, type = "string" },
-- Use (?:\/(?!\/|\*))? to avoid that a regex can start after a number, while also allowing // and /* comments { pattern = "0x[%da-fA-F_]+n?", type = "number" },
{ regex = [[-?0[xXbBoO][\da-fA-F_]+n?()\s*()(?:\/(?!\/|\*))?]], type = {"number", "normal", "operator"} }, { pattern = "-?%d+[%d%.eE_n]*", type = "number" },
{ regex = [[-?\d+[0-9.eE_n]*()\s*()(?:\/(?!\/|\*))?]], type = {"number", "normal", "operator"} }, { pattern = "-?%.?%d+", type = "number" },
{ regex = [[-?\.?\d+()\s*()(?:\/(?!\/|\*))?]], type = {"number", "normal", "operator"} }, { pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" }, { pattern = "[%a_][%w_]*%f[(]", type = "function" },
{ pattern = "[%a_][%w_]*%f[(]", type = "function" }, { pattern = "[%a_][%w_]*", type = "symbol" },
{ pattern = "[%a_][%w_]*", type = "symbol" },
}, },
symbols = { symbols = {
["async"] = "keyword", ["async"] = "keyword",
@ -93,7 +42,6 @@ syntax.add {
["get"] = "keyword", ["get"] = "keyword",
["if"] = "keyword", ["if"] = "keyword",
["import"] = "keyword", ["import"] = "keyword",
["from"] = "keyword",
["in"] = "keyword", ["in"] = "keyword",
["of"] = "keyword", ["of"] = "keyword",
["instanceof"] = "keyword", ["instanceof"] = "keyword",

View File

@ -3,6 +3,25 @@ local syntax = require "core.syntax"
local style = require "core.style" local style = require "core.style"
local core = require "core" local core = require "core"
local initial_color = style.syntax["keyword2"]
-- Add 3 type of font styles for use on markdown files
for _, attr in pairs({"bold", "italic", "bold_italic"}) do
local attributes = {}
if attr ~= "bold_italic" then
attributes[attr] = true
else
attributes["bold"] = true
attributes["italic"] = true
end
style.syntax_fonts["markdown_"..attr] = style.code_font:copy(
style.code_font:get_size(),
attributes
)
-- also add a color for it
style.syntax["markdown_"..attr] = style.syntax["keyword2"]
end
local in_squares_match = "^%[%]" local in_squares_match = "^%[%]"
local in_parenthesis_match = "^%(%)" local in_parenthesis_match = "^%(%)"
@ -205,63 +224,12 @@ syntax.add {
-- Adjust the color on theme changes -- Adjust the color on theme changes
core.add_thread(function() core.add_thread(function()
local custom_fonts = { bold = {font = nil, color = nil}, italic = {}, bold_italic = {} }
local initial_color
local last_code_font
local function set_font(attr)
local attributes = {}
if attr ~= "bold_italic" then
attributes[attr] = true
else
attributes["bold"] = true
attributes["italic"] = true
end
local font = style.code_font:copy(
style.code_font:get_size(),
attributes
)
custom_fonts[attr].font = font
style.syntax_fonts["markdown_"..attr] = font
end
local function set_color(attr)
custom_fonts[attr].color = style.syntax["keyword2"]
style.syntax["markdown_"..attr] = style.syntax["keyword2"]
end
-- Add 3 type of font styles for use on markdown files
for attr, _ in pairs(custom_fonts) do
-- Only set it if the font wasn't manually customized
if not style.syntax_fonts["markdown_"..attr] then
set_font(attr)
end
-- Only set it if the color wasn't manually customized
if not style.syntax["markdown_"..attr] then
set_color(attr)
end
end
while true do while true do
if last_code_font ~= style.code_font then
last_code_font = style.code_font
for attr, _ in pairs(custom_fonts) do
-- Only set it if the font wasn't manually customized
if style.syntax_fonts["markdown_"..attr] == custom_fonts[attr].font then
set_font(attr)
end
end
end
if initial_color ~= style.syntax["keyword2"] then if initial_color ~= style.syntax["keyword2"] then
initial_color = style.syntax["keyword2"] for _, attr in pairs({"bold", "italic", "bold_italic"}) do
for attr, _ in pairs(custom_fonts) do style.syntax["markdown_"..attr] = style.syntax["keyword2"]
-- Only set it if the color wasn't manually customized
if style.syntax["markdown_"..attr] == custom_fonts[attr].color then
set_color(attr)
end
end end
initial_color = style.syntax["keyword2"]
end end
coroutine.yield(1) coroutine.yield(1)
end end

View File

@ -3,7 +3,7 @@ local syntax = require "core.syntax"
syntax.add { syntax.add {
name = "Python", name = "Python",
files = { "%.py$", "%.pyw$", "%.rpy$", "%.pyi$" }, files = { "%.py$", "%.pyw$", "%.rpy$" },
headers = "^#!.*[ /]python", headers = "^#!.*[ /]python",
comment = "#", comment = "#",
block_comment = { '"""', '"""' }, block_comment = { '"""', '"""' },
@ -16,8 +16,8 @@ syntax.add {
{ pattern = { "[ruU]?'''", "'''", '\\' }, type = "string" }, { pattern = { "[ruU]?'''", "'''", '\\' }, type = "string" },
{ pattern = { '[ruU]?"', '"', '\\' }, type = "string" }, { pattern = { '[ruU]?"', '"', '\\' }, type = "string" },
{ pattern = { "[ruU]?'", "'", '\\' }, type = "string" }, { pattern = { "[ruU]?'", "'", '\\' }, type = "string" },
{ pattern = "-?0[xboXBO][%da-fA-F_]+",type = "number" }, { pattern = "0x[%da-fA-F]+", type = "number" },
{ pattern = "-?%d+[%d%.eE_]*", type = "number" }, { pattern = "-?%d+[%d%.eE]*", type = "number" },
{ pattern = "-?%.?%d+", type = "number" }, { pattern = "-?%.?%d+", type = "number" },
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" }, { pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
{ pattern = "[%a_][%w_]*%f[(]", type = "function" }, { pattern = "[%a_][%w_]*%f[(]", type = "function" },

View File

@ -79,6 +79,8 @@ end
local draw_overlay = DocView.draw_overlay local draw_overlay = DocView.draw_overlay
function DocView:draw_overlay(...) function DocView:draw_overlay(...)
draw_overlay(self, ...)
if if
type(config.plugins.lineguide) == "table" type(config.plugins.lineguide) == "table"
and and
@ -104,8 +106,6 @@ function DocView:draw_overlay(...)
end end
end end
end end
-- everything else like the cursor above the line guides
draw_overlay(self, ...)
end end
command.add(nil, { command.add(nil, {

View File

@ -219,7 +219,7 @@ function LineWrapping.draw_guide(docview)
end end
function LineWrapping.update_docview_breaks(docview) function LineWrapping.update_docview_breaks(docview)
local w = docview.v_scrollbar.expanded_size or style.expanded_scrollbar_size local x,y,w,h = docview.v_scrollbar:get_thumb_rect()
local width = (type(config.plugins.linewrapping.width_override) == "function" and config.plugins.linewrapping.width_override(docview)) local width = (type(config.plugins.linewrapping.width_override) == "function" and config.plugins.linewrapping.width_override(docview))
or config.plugins.linewrapping.width_override or (docview.size.x - docview:get_gutter_width() - w) or config.plugins.linewrapping.width_override or (docview.size.x - docview:get_gutter_width() - w)
if (not docview.wrapped_settings or docview.wrapped_settings.width == nil or width ~= docview.wrapped_settings.width) then if (not docview.wrapped_settings or docview.wrapped_settings.width == nil or width ~= docview.wrapped_settings.width) then
@ -310,7 +310,7 @@ local function get_line_col_from_index_and_x(docview, idx, x)
end end
local open_files = setmetatable({ }, { __mode = "k" }) local open_files = {}
local old_doc_insert = Doc.raw_insert local old_doc_insert = Doc.raw_insert
function Doc:raw_insert(line, col, text, undo_stack, time) function Doc:raw_insert(line, col, text, undo_stack, time)
@ -485,7 +485,7 @@ local old_draw_line_body = DocView.draw_line_body
function DocView:draw_line_body(line, x, y) function DocView:draw_line_body(line, x, y)
if not self.wrapped_settings then return old_draw_line_body(self, line, x, y) end if not self.wrapped_settings then return old_draw_line_body(self, line, x, y) end
local lh = self:get_line_height() local lh = self:get_line_height()
local idx0, _, count = get_line_idx_col_count(self, line) local idx0 = get_line_idx_col_count(self, 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 line >= line1 and line <= line2 then if line >= line1 and line <= line2 then
if line1 ~= line then col1 = 1 end if line1 ~= line then col1 = 1 end
@ -493,14 +493,12 @@ function DocView:draw_line_body(line, x, y)
if col1 ~= col2 then if col1 ~= col2 then
local idx1, ncol1 = get_line_idx_col_count(self, line, col1) local idx1, ncol1 = get_line_idx_col_count(self, line, col1)
local idx2, ncol2 = get_line_idx_col_count(self, line, col2) local idx2, ncol2 = get_line_idx_col_count(self, line, col2)
local start = 0
for i = idx1, idx2 do for i = idx1, idx2 do
local x1, x2 = x + (idx1 == i and self:get_col_x_offset(line1, col1) or 0) local x1, x2 = x + (idx1 == i and self:get_col_x_offset(line1, col1) or 0)
if idx2 == i then if idx2 == i then
x2 = x + self:get_col_x_offset(line, col2) x2 = x + self:get_col_x_offset(line, col2)
else else
start = start + get_idx_line_length(self, i, line) x2 = x + self:get_col_x_offset(line, get_idx_line_length(self, i, line) + 1, true)
x2 = x + self:get_col_x_offset(line, start + 1, true)
end end
renderer.draw_rect(x1, y + (i - idx0) * lh, x2 - x1, lh, style.selection) renderer.draw_rect(x1, y + (i - idx0) * lh, x2 - x1, lh, style.selection)
end end
@ -516,6 +514,7 @@ function DocView:draw_line_body(line, x, y)
end end
end end
if draw_highlight then if draw_highlight then
local _, _, count = get_line_idx_col_count(self, line)
for i=1,count do for i=1,count do
self:draw_line_highlight(x + self.scroll.x, y + lh * (i - 1)) self:draw_line_highlight(x + self.scroll.x, y + lh * (i - 1))
end end

View File

@ -25,16 +25,11 @@ local function set_scale(scale)
scale = common.clamp(scale, 0.2, 6) scale = common.clamp(scale, 0.2, 6)
-- save scroll positions -- save scroll positions
local v_scrolls = {} local scrolls = {}
local h_scrolls = {}
for _, view in ipairs(core.root_view.root_node:get_children()) do for _, view in ipairs(core.root_view.root_node:get_children()) do
local n = view:get_scrollable_size() local n = view:get_scrollable_size()
if n ~= math.huge and n > view.size.y then if n ~= math.huge and not view:is(CommandView) and n > view.size.y then
v_scrolls[view] = view.scroll.y / (n - view.size.y) scrolls[view] = view.scroll.y / (n - view.size.y)
end
local hn = view:get_h_scrollable_size()
if hn ~= math.huge and hn > view.size.x then
h_scrolls[view] = view.scroll.x / (hn - view.size.x)
end end
end end
@ -44,13 +39,12 @@ local function set_scale(scale)
if config.plugins.scale.mode == "ui" then if config.plugins.scale.mode == "ui" then
SCALE = scale SCALE = scale
style.padding.x = style.padding.x * s style.padding.x = style.padding.x * s
style.padding.y = style.padding.y * s style.padding.y = style.padding.y * s
style.divider_size = style.divider_size * s style.divider_size = style.divider_size * s
style.scrollbar_size = style.scrollbar_size * s style.scrollbar_size = style.scrollbar_size * s
style.expanded_scrollbar_size = style.expanded_scrollbar_size * s style.caret_width = style.caret_width * s
style.caret_width = style.caret_width * s 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]:set_size(s * style[name]:get_size()) style[name]:set_size(s * style[name]:get_size())
@ -64,14 +58,10 @@ local function set_scale(scale)
end end
-- restore scroll positions -- restore scroll positions
for view, n in pairs(v_scrolls) do for view, n in pairs(scrolls) do
view.scroll.y = n * (view:get_scrollable_size() - view.size.y) view.scroll.y = n * (view:get_scrollable_size() - view.size.y)
view.scroll.to.y = view.scroll.y view.scroll.to.y = view.scroll.y
end end
for view, hn in pairs(h_scrolls) do
view.scroll.x = hn * (view:get_h_scrollable_size() - view.size.x)
view.scroll.to.x = view.scroll.x
end
core.redraw = true core.redraw = true
end end

View File

@ -24,7 +24,7 @@ local tooltip_alpha_rate = 1
local function get_depth(filename) local function get_depth(filename)
local n = 1 local n = 1
for _ in filename:gmatch(PATHSEP) do for sep in filename:gmatch("[\\/]") do
n = n + 1 n = n + 1
end end
return n return n
@ -83,7 +83,7 @@ function TreeView:get_cached(dir, item, dirname)
else else
t.filename = item.filename t.filename = item.filename
t.depth = get_depth(item.filename) t.depth = get_depth(item.filename)
t.abs_filename = common.basepath(dirname) .. item.filename t.abs_filename = dirname .. PATHSEP .. item.filename
end end
t.name = basename t.name = basename
t.type = item.type t.type = item.type
@ -518,19 +518,15 @@ local function is_primary_project_folder(path)
return core.project_dir == path return core.project_dir == path
end end
menu:register(function() return view.hovered_item end, {
local function treeitem() return view.hovered_item or view.selected_item end
menu:register(function() return core.active_view:is(TreeView) and treeitem() end, {
{ text = "Open in System", command = "treeview:open-in-system" }, { text = "Open in System", command = "treeview:open-in-system" },
ContextMenu.DIVIDER ContextMenu.DIVIDER
}) })
menu:register( menu:register(
function() function()
local item = treeitem() return view.hovered_item
return core.active_view:is(TreeView) and item and not is_project_folder(item.abs_filename) and not is_project_folder(view.hovered_item.abs_filename)
end, end,
{ {
{ text = "Rename", command = "treeview:rename" }, { text = "Rename", command = "treeview:rename" },
@ -540,8 +536,7 @@ menu:register(
menu:register( menu:register(
function() function()
local item = treeitem() return view.hovered_item and view.hovered_item.type == "dir"
return core.active_view:is(TreeView) and item and item.type == "dir"
end, end,
{ {
{ text = "New File", command = "treeview:new-file" }, { text = "New File", command = "treeview:new-file" },
@ -551,10 +546,9 @@ menu:register(
menu:register( menu:register(
function() function()
local item = treeitem() return view.hovered_item
return core.active_view:is(TreeView) and item and not is_primary_project_folder(view.hovered_item.abs_filename)
and not is_primary_project_folder(item.abs_filename) and is_project_folder(view.hovered_item.abs_filename)
and is_project_folder(item.abs_filename)
end, end,
{ {
{ text = "Remove directory", command = "treeview:remove-project-directory" }, { text = "Remove directory", command = "treeview:remove-project-directory" },
@ -595,10 +589,7 @@ command.add(nil, {
end end
}) })
command.add( command.add(TreeView, {
function()
return not menu.show_context_menu and core.active_view:extends(TreeView), TreeView
end, {
["treeview:next"] = function() ["treeview:next"] = function()
local item, _, item_y = view:get_next(view.selected_item) local item, _, item_y = view:get_next(view.selected_item)
view:set_selection(item, item_y) view:set_selection(item, item_y)
@ -669,24 +660,33 @@ command.add(
}) })
local function treeitem() return view.hovered_item or view.selected_item end
command.add( command.add(
function() function()
local item = treeitem() local item = treeitem()
return item ~= nil and (core.active_view == view or menu.show_context_menu), item return item ~= nil
and (
core.active_view == view or core.active_view == menu
or (view.toolbar and core.active_view == view.toolbar)
-- sometimes the context menu is shown on top of statusbar
or core.active_view == core.status_view
), item
end, { end, {
["treeview:delete"] = function(item) ["treeview:delete"] = function(item)
local filename = item.abs_filename local filename = item.abs_filename
local relfilename = item.filename local relfilename = item.filename
if item.dir_name ~= core.project_dir then if item.dir_name ~= core.project_dir then
-- add secondary project dirs names to the file path to show -- add secondary project dirs names to the file path to show
relfilename = common.basepath(common.basename(item.dir_name)) .. PATHSEP .. relfilename relfilename = common.basename(item.dir_name) .. PATHSEP .. relfilename
end end
local file_info = system.get_file_info(filename) local file_info = system.get_file_info(filename)
local file_type = file_info.type == "dir" and "Directory" or "File" local file_type = file_info.type == "dir" and "Directory" or "File"
-- Ask before deleting -- Ask before deleting
local opt = { local opt = {
{ text = "Yes", default_yes = true }, { font = style.font, text = "Yes", default_yes = true },
{ text = "No", default_no = true } { font = style.font, text = "No" , default_no = true }
} }
core.nag_view:show( core.nag_view:show(
string.format("Delete %s", file_type), string.format("Delete %s", file_type),
@ -724,7 +724,7 @@ command.add(
submit = function(filename) submit = function(filename)
local abs_filename = filename local abs_filename = filename
if not common.is_absolute_path(filename) then if not common.is_absolute_path(filename) then
abs_filename = common.basepath(item.dir_name) .. filename abs_filename = item.dir_name .. PATHSEP .. filename
end end
local res, err = os.rename(old_abs_filename, abs_filename) local res, err = os.rename(old_abs_filename, abs_filename)
if res then -- successfully renamed if res then -- successfully renamed
@ -754,7 +754,7 @@ command.add(
core.command_view:enter("Filename", { core.command_view:enter("Filename", {
text = text, text = text,
submit = function(filename) submit = function(filename)
local doc_filename = common.basepath(item.dir_name) .. filename local doc_filename = item.dir_name .. PATHSEP .. filename
core.log(doc_filename) core.log(doc_filename)
local file = io.open(doc_filename, "a+") local file = io.open(doc_filename, "a+")
file:write("") file:write("")
@ -776,7 +776,7 @@ command.add(
core.command_view:enter("Folder Name", { core.command_view:enter("Folder Name", {
text = text, text = text,
submit = function(filename) submit = function(filename)
local dir_path = common.basepath(item.dir_name) .. filename local dir_path = item.dir_name .. PATHSEP .. filename
common.mkdirp(dir_path) common.mkdirp(dir_path)
core.log("Created %s", dir_path) core.log("Created %s", dir_path)
end, end,
@ -789,12 +789,10 @@ command.add(
["treeview:open-in-system"] = function(item) ["treeview:open-in-system"] = function(item)
if PLATFORM == "Windows" then if PLATFORM == "Windows" then
system.exec(string.format("start \"\" %q", item.abs_filename)) system.exec(string.format("start \"\" %q", item.abs_filename))
elseif string.find(PLATFORM, "Mac") or PLATFORM == "MorphOS" then elseif string.find(PLATFORM, "Mac") then
system.exec(string.format("open %q", item.abs_filename)) system.exec(string.format("open %q", item.abs_filename))
elseif PLATFORM == "Linux" or string.find(PLATFORM, "BSD") then elseif PLATFORM == "Linux" or string.find(PLATFORM, "BSD") then
system.exec(string.format("xdg-open %q", item.abs_filename)) system.exec(string.format("xdg-open %q", item.abs_filename))
elseif PLATFORM == "AmigaOS 4" then
system.exec(string.format("WBRUN %q SHOW=all VIEWBY=name", item.abs_filename))
end end
end end
}) })
@ -802,10 +800,9 @@ command.add(
local projectsearch = pcall(require, "plugins.projectsearch") local projectsearch = pcall(require, "plugins.projectsearch")
if projectsearch then if projectsearch then
menu:register(function() menu:register(function()
local item = treeitem() return view.hovered_item and view.hovered_item.type == "dir"
return item and item.type == "dir" end, {
end, { { text = "Find in directory", command = "treeview:search-in-directory" }
{ text = "Find in directory", command = "treeview:search-in-directory" }
}) })
command.add(function() command.add(function()
return view.hovered_item and view.hovered_item.type == "dir" return view.hovered_item and view.hovered_item.type == "dir"

View File

@ -8,7 +8,7 @@ local Doc = require "core.doc"
---@field enabled boolean ---@field enabled boolean
---@field trim_empty_end_lines boolean ---@field trim_empty_end_lines boolean
config.plugins.trimwhitespace = common.merge({ config.plugins.trimwhitespace = common.merge({
enabled = false, enabled = true,
trim_empty_end_lines = false, trim_empty_end_lines = false,
config_spec = { config_spec = {
name = "Trim Whitespace", name = "Trim Whitespace",
@ -17,7 +17,7 @@ config.plugins.trimwhitespace = common.merge({
description = "Disable or enable the trimming of white spaces by default.", description = "Disable or enable the trimming of white spaces by default.",
path = "enabled", path = "enabled",
type = "toggle", type = "toggle",
default = false default = true
}, },
{ {
label = "Trim Empty End Lines", label = "Trim Empty End Lines",

View File

@ -7,7 +7,7 @@ local LogView = require "core.logview"
local function workspace_files_for(project_dir) local function workspace_files_for(project_dir)
local basename = common.basename(project_dir) local basename = common.basename(project_dir)
local workspace_dir = common.basepath(USERDIR) .. "ws" local workspace_dir = USERDIR .. PATHSEP .. "ws"
local info_wsdir = system.get_file_info(workspace_dir) local info_wsdir = system.get_file_info(workspace_dir)
if not info_wsdir then if not info_wsdir then
local ok, err = system.mkdir(workspace_dir) local ok, err = system.mkdir(workspace_dir)
@ -22,7 +22,7 @@ local function workspace_files_for(project_dir)
if file:sub(1, n) == basename then if file:sub(1, n) == basename then
local id = tonumber(file:sub(n + 1):match("^-(%d+)$")) local id = tonumber(file:sub(n + 1):match("^-(%d+)$"))
if id then if id then
coroutine.yield(common.basepath(workspace_dir) .. file, id) coroutine.yield(workspace_dir .. PATHSEP .. file, id)
end end
end end
end end
@ -52,7 +52,7 @@ local function get_workspace_filename(project_dir)
id = id + 1 id = id + 1
end end
local basename = common.basename(project_dir) local basename = common.basename(project_dir)
return common.basepath(USERDIR) .. "ws" .. PATHSEP .. basename .. "-" .. tostring(id) return USERDIR .. PATHSEP .. "ws" .. PATHSEP .. basename .. "-" .. tostring(id)
end end
@ -83,8 +83,7 @@ local function save_view(view)
filename = view.doc.filename, filename = view.doc.filename,
selection = { view.doc:get_selection() }, selection = { view.doc:get_selection() },
scroll = { x = view.scroll.to.x, y = view.scroll.to.y }, scroll = { x = view.scroll.to.x, y = view.scroll.to.y },
crlf = view.doc.crlf, text = not view.doc.filename and view.doc:get_text(1, 1, math.huge, math.huge)
text = view.doc.new_file and view.doc:get_text(1, 1, math.huge, math.huge)
} }
end end
if mt == LogView then return end if mt == LogView then return end
@ -107,6 +106,7 @@ local function load_view(t)
if not t.filename then if not t.filename then
-- document not associated to a file -- document not associated to a file
dv = DocView(core.open_doc()) dv = DocView(core.open_doc())
if t.text then dv.doc:insert(1, 1, t.text) end
else else
-- we have a filename, try to read the file -- we have a filename, try to read the file
local ok, doc = pcall(core.open_doc, t.filename) local ok, doc = pcall(core.open_doc, t.filename)
@ -114,11 +114,9 @@ local function load_view(t)
dv = DocView(doc) dv = DocView(doc)
end end
end end
-- doc view "dv" can be nil here if the filename associated to the document
-- cannot be read.
if dv and dv.doc then if dv and dv.doc then
if dv.doc.new_file and t.text then
dv.doc:insert(1, 1, t.text)
dv.doc.crlf = t.crlf
end
dv.doc:set_selection(table.unpack(t.selection)) dv.doc:set_selection(table.unpack(t.selection))
dv.last_line1, dv.last_col1, dv.last_line2, dv.last_col2 = dv.doc:get_selection() dv.last_line1, dv.last_col1, dv.last_line2, dv.last_col2 = dv.doc:get_selection()
dv.scroll.x, dv.scroll.to.x = t.scroll.x, t.scroll.x dv.scroll.x, dv.scroll.to.x = t.scroll.x, t.scroll.x
@ -166,7 +164,7 @@ local function load_node(node, t)
active_view = view active_view = view
end end
if not view:is(DocView) then if not view:is(DocView) then
view.scroll = v.scroll view.scroll = v.scroll
end end
end end
end end

View File

@ -45,14 +45,10 @@ function dirmonitor:unwatch(fd_or_path) end
---edited, removed or added. A file descriptor will be passed to the ---edited, removed or added. A file descriptor will be passed to the
---callback in "multiple" mode or a path in "single" mode. ---callback in "multiple" mode or a path in "single" mode.
--- ---
---If an error occurred during the callback execution, the error callback will be called with the error object.
---This callback should not manipulate coroutines to avoid deadlocks.
---
---@param callback dirmonitor.callback ---@param callback dirmonitor.callback
---@param error_callback fun(error: any): nil
--- ---
---@return boolean? changes True when changes were detected. ---@return boolean? changes True when changes were detected.
function dirmonitor:check(callback, error_callback) end function dirmonitor:check(callback) end
--- ---
---Get the working mode for the current file system monitoring backend. ---Get the working mode for the current file system monitoring backend.

View File

@ -61,10 +61,10 @@ function system.poll_event() end
--- ---
---Wait until an event is triggered. ---Wait until an event is triggered.
--- ---
---@param timeout? number Amount of seconds, also supports fractions ---@param timeout number Amount of seconds, also supports fractions
---of a second, eg: 0.01. If not provided, waits forever. ---of a second, eg: 0.01
--- ---
---@return boolean status True on success or false if there was an error or if no event was received. ---@return boolean status True on success or false if there was an error.
function system.wait_event(timeout) end function system.wait_event(timeout) end
--- ---
@ -107,12 +107,10 @@ function system.set_window_bordered(bordered) end
---When then window is run borderless (without system decorations), this ---When then window is run borderless (without system decorations), this
---function allows to set the size of the different regions that allow ---function allows to set the size of the different regions that allow
---for custom window management. ---for custom window management.
---To disable custom window management, call this function without any
---arguments
--- ---
---@param title_height? number Height of the window decoration ---@param title_height number
---@param controls_width? number Width of window controls (maximize,minimize and close buttons, etc). ---@param controls_width number Width of window controls (maximize,minimize and close buttons, etc).
---@param resize_border? number The amount of pixels reserved for resizing ---@param resize_border number The amount of pixels reserved for resizing
function system.set_window_hit_test(title_height, controls_width, resize_border) end function system.set_window_hit_test(title_height, controls_width, resize_border) end
--- ---
@ -318,11 +316,9 @@ function system.load_native_plugin(name, path) end
---Compares two paths in the order used by TreeView. ---Compares two paths in the order used by TreeView.
--- ---
---@param path1 string ---@param path1 string
---@param type1 system.fileinfotype
---@param path2 string ---@param path2 string
---@param type2 system.fileinfotype
---@return boolean compare_result True if path1 < path2 ---@return boolean compare_result True if path1 < path2
function system.path_compare(path1, type1, path2, type2) end function system.path_compare(path1, path2) end
return system return system

View File

@ -1,10 +1,11 @@
project('lite-xl', project('lite-xl',
['c'], ['c'],
version : '2.1.4', version : '2.1.1',
license : 'MIT', license : 'MIT',
meson_version : '>= 0.56', meson_version : '>= 0.56',
default_options : [ default_options : [
'c_std=gnu11' 'c_std=gnu11',
'wrap_mode=nofallback'
] ]
) )
@ -35,7 +36,6 @@ conf_data = configuration_data()
conf_data.set('PROJECT_BUILD_DIR', meson.current_build_dir()) conf_data.set('PROJECT_BUILD_DIR', meson.current_build_dir())
conf_data.set('PROJECT_SOURCE_DIR', meson.current_source_dir()) conf_data.set('PROJECT_SOURCE_DIR', meson.current_source_dir())
conf_data.set('PROJECT_VERSION', version) conf_data.set('PROJECT_VERSION', version)
conf_data.set('PROJECT_ASSEMBLY_VERSION', meson.project_version() + '.0')
#=============================================================================== #===============================================================================
# Compiler Settings # Compiler Settings
@ -63,6 +63,10 @@ lite_cargs += '-DLITE_ARCH_TUPLE="@0@"'.format(arch_tuple)
# Linker Settings # Linker Settings
#=============================================================================== #===============================================================================
lite_link_args = [] lite_link_args = []
if cc.get_id() == 'gcc' and get_option('buildtype') == 'release'
lite_link_args += ['-static-libgcc']
endif
if host_machine.system() == 'darwin' if host_machine.system() == 'darwin'
lite_link_args += ['-framework', 'CoreServices', '-framework', 'Foundation'] lite_link_args += ['-framework', 'CoreServices', '-framework', 'Foundation']
endif endif
@ -83,33 +87,29 @@ if not get_option('source-only')
'lua', # Fedora 'lua', # Fedora
] ]
if get_option('use_system_lua') foreach lua : lua_names
foreach lua : lua_names last_lua = (lua == lua_names[-1] or get_option('wrap_mode') == 'forcefallback')
last_lua = (lua == lua_names[-1] or get_option('wrap_mode') == 'forcefallback') lua_dep = dependency(lua, fallback: last_lua ? ['lua', 'lua_dep'] : [], required : false,
lua_dep = dependency(lua, required : false, version: '>= 5.4',
)
if lua_dep.found()
break
endif
if last_lua
# If we could not find lua on the system and fallbacks are disabled
# try the compiler as a last ditch effort, since Lua has no official
# pkg-config support.
lua_dep = cc.find_library('lua', required : true)
endif
endforeach
else
lua_dep = dependency('', fallback: ['lua', 'lua_dep'], required : true,
default_options: default_fallback_options + ['default_library=static', 'line_editing=false', 'interpreter=false'] default_options: default_fallback_options + ['default_library=static', 'line_editing=false', 'interpreter=false']
) )
endif if lua_dep.found()
break
endif
if last_lua
# If we could not find lua on the system and fallbacks are disabled
# try the compiler as a last ditch effort, since Lua has no official
# pkg-config support.
lua_dep = cc.find_library('lua', required : true)
endif
endforeach
pcre2_dep = dependency('libpcre2-8', fallback: ['pcre2', 'libpcre2_8'], pcre2_dep = dependency('libpcre2-8', fallback: ['pcre2', 'libpcre2_8'],
default_options: default_fallback_options + ['default_library=static', 'grep=false', 'test=false'] default_options: default_fallback_options + ['default_library=static', 'grep=false', 'test=false']
) )
freetype_dep = dependency('freetype2', fallback: ['freetype2', 'freetype_dep'], freetype_dep = dependency('freetype2', fallback: ['freetype_dep', 'freetype'],
default_options: default_fallback_options + ['default_library=static', 'zlib=disabled', 'bzip2=disabled', 'png=disabled', 'harfbuzz=disabled', 'brotli=disabled'] default_options: default_fallback_options + ['default_library=static', 'zlib=disabled', 'bzip2=disabled', 'png=disabled', 'harfbuzz=disabled', 'brotli=disabled']
) )
@ -123,7 +123,6 @@ if not get_option('source-only')
sdl_options += 'use_atomic=enabled' sdl_options += 'use_atomic=enabled'
sdl_options += 'use_threads=enabled' sdl_options += 'use_threads=enabled'
sdl_options += 'use_timers=enabled' sdl_options += 'use_timers=enabled'
sdl_options += 'with_main=true'
# investigate if this is truly needed # investigate if this is truly needed
# Do not remove before https://github.com/libsdl-org/SDL/issues/5413 is released # Do not remove before https://github.com/libsdl-org/SDL/issues/5413 is released
sdl_options += 'use_events=enabled' sdl_options += 'use_events=enabled'
@ -156,24 +155,12 @@ if not get_option('source-only')
sdl_options += 'use_video_vulkan=disabled' sdl_options += 'use_video_vulkan=disabled'
sdl_options += 'use_video_offscreen=disabled' sdl_options += 'use_video_offscreen=disabled'
sdl_options += 'use_power=disabled' sdl_options += 'use_power=disabled'
sdl_options += 'system_iconv=disabled'
sdl_dep = dependency('sdl2', fallback: ['sdl2', 'sdl2_dep'], sdl_dep = dependency('sdl2', fallback: ['sdl2', 'sdl2_dep'],
default_options: default_fallback_options + sdl_options default_options: default_fallback_options + sdl_options
) )
if host_machine.system() == 'windows' lite_deps = [lua_dep, sdl_dep, freetype_dep, pcre2_dep, libm, libdl]
if sdl_dep.type_name() == 'internal'
sdlmain_dep = dependency('sdl2main', fallback: ['sdl2main_dep'])
else
sdlmain_dep = cc.find_library('SDL2main')
endif
else
sdlmain_dep = dependency('', required: false)
assert(not sdlmain_dep.found(), 'checking if fake dependency has been found')
endif
lite_deps = [lua_dep, sdl_dep, sdlmain_dep, freetype_dep, pcre2_dep, libm, libdl]
endif endif
#=============================================================================== #===============================================================================
# Install Configuration # Install Configuration
@ -182,11 +169,6 @@ if get_option('portable') or host_machine.system() == 'windows'
lite_bindir = '/' lite_bindir = '/'
lite_docdir = '/doc' lite_docdir = '/doc'
lite_datadir = '/data' lite_datadir = '/data'
configure_file(
input: 'resources/windows/lite-xl.exe.manifest.in',
output: 'lite-xl.exe.manifest',
configuration: conf_data
)
elif get_option('bundle') and host_machine.system() == 'darwin' elif get_option('bundle') and host_machine.system() == 'darwin'
lite_cargs += '-DMACOS_USE_BUNDLE' lite_cargs += '-DMACOS_USE_BUNDLE'
lite_bindir = 'Contents/MacOS' lite_bindir = 'Contents/MacOS'

View File

@ -3,5 +3,4 @@ option('source-only', type : 'boolean', value : false, description: 'Configure s
option('portable', type : 'boolean', value : false, description: 'Portable install') option('portable', type : 'boolean', value : false, description: 'Portable install')
option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer') option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer')
option('dirmonitor_backend', type : 'combo', value : '', choices : ['', 'inotify', 'fsevents', 'kqueue', 'win32', 'dummy'], description: 'define what dirmonitor backend to use') option('dirmonitor_backend', type : 'combo', value : '', choices : ['', 'inotify', 'fsevents', 'kqueue', 'win32', 'dummy'], description: 'define what dirmonitor backend to use')
option('arch_tuple', type : 'string', value : '', description: 'Specify a custom architecture tuple') option('arch_tuple', type : 'string', value : '', description: 'Specify a custom architecture tuple')
option('use_system_lua', type : 'boolean', value : false, description: 'Prefer System Lua over a the meson wrap')

View File

@ -1,24 +0,0 @@
# Resources
This folder contains resources that is used for building or packaging the project.
### Build
- `cross/*.txt`: Meson [cross files][1] for cross-compiling lite-xl on other platforms.
### Packaging
- `icons/icon.{icns,ico,inl,rc,svg}`: lite-xl icon in various formats.
- `linux/com.lite_xl.LiteXL.appdata.xml`: AppStream metadata.
- `linux/com.lite_xl.LiteXL.desktop`: Desktop file for Linux desktops.
- `macos/dmg-cover.png`: Background image for packaging macOS DMGs.
- `macos/Info.plist.in`: Template for generating `info.plist` on macOS. See `macos/macos-retina-display.md` for details.
- `macos/lite-xl-dmg.py`: Configuration options for dmgbuild for packaging macOS DMGs.
- `windows/001-lua-unicode.diff`: Patch for allowing Lua to load files with UTF-8 filenames on Windows.
### Development
- `include/lite_xl_plugin_api.h`: Native plugin API header. See the contents of `lite_xl_plugin_api.h` for more details.
[1]: https://mesonbuild.com/Cross-compilation.html

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

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