Merge branch 'master' into amiga2.1
This commit is contained in:
commit
65d95c7f40
|
@ -131,3 +131,40 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: Windows Artifacts
|
name: Windows Artifacts
|
||||||
path: ${{ env.INSTALL_NAME }}.zip
|
path: ${{ env.INSTALL_NAME }}.zip
|
||||||
|
|
||||||
|
build_windows_msvc:
|
||||||
|
name: Windows (MSVC)
|
||||||
|
runs-on: windows-2019
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: [amd64, amd64_x86]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
with:
|
||||||
|
arch: ${{ matrix.arch }}
|
||||||
|
- uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- name: Install meson and ninja
|
||||||
|
run: pip install meson ninja
|
||||||
|
- name: Set up environment variables
|
||||||
|
run: |
|
||||||
|
"INSTALL_NAME=lite-xl-$($env:GITHUB_REF -replace ".*/")-windows-msvc-${{ matrix.arch }}" >> $env:GITHUB_ENV
|
||||||
|
"INSTALL_REF=$($env:GITHUB_REF -replace ".*/")" >> $env:GITHUB_ENV
|
||||||
|
"LUA_SUBPROJECT_PATH=subprojects/lua-5.4.4" >> $env:GITHUB_ENV
|
||||||
|
- name: Configure
|
||||||
|
run: |
|
||||||
|
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
|
||||||
|
run: meson install -C build --destdir="../lite-xl"
|
||||||
|
- name: Package
|
||||||
|
run: |
|
||||||
|
Remove-Item -Recurse -Force -Path "lite-xl/lib","lite-xl/include"
|
||||||
|
Compress-Archive -Path lite-xl -DestinationPath "$env:INSTALL_NAME.zip"
|
||||||
|
- name: Upload Artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: Windows Artifacts (MSVC)
|
||||||
|
path: ${{ env.INSTALL_NAME }}.zip
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v[0-9]+.*
|
||||||
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
version:
|
||||||
description: Release Version
|
description: Release Version
|
||||||
default: v2.1.0
|
default: v2.1.1
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Create Release
|
name: Create Release
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-18.04
|
||||||
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 }}
|
||||||
|
@ -26,6 +30,12 @@ jobs:
|
||||||
else
|
else
|
||||||
echo ::set-output name=version::${GITHUB_REF/refs\/tags\//}
|
echo ::set-output name=version::${GITHUB_REF/refs\/tags\//}
|
||||||
fi
|
fi
|
||||||
|
- name: Update Tag
|
||||||
|
uses: richardsimko/update-tag@v1
|
||||||
|
with:
|
||||||
|
tag_name: ${{ steps.tag.outputs.version }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
|
@ -33,14 +43,13 @@ jobs:
|
||||||
tag_name: ${{ steps.tag.outputs.version }}
|
tag_name: ${{ steps.tag.outputs.version }}
|
||||||
name: Lite XL ${{ steps.tag.outputs.version }}
|
name: Lite XL ${{ steps.tag.outputs.version }}
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
|
||||||
body_path: changelog.md
|
body_path: changelog.md
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
|
|
||||||
build_linux:
|
build_linux:
|
||||||
name: Linux
|
name: Linux
|
||||||
needs: release
|
needs: release
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-18.04
|
||||||
env:
|
env:
|
||||||
CC: gcc
|
CC: gcc
|
||||||
CXX: g++
|
CXX: g++
|
||||||
|
@ -76,6 +85,7 @@ jobs:
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ needs.release.outputs.version }}
|
tag_name: ${{ needs.release.outputs.version }}
|
||||||
|
draft: true
|
||||||
files: |
|
files: |
|
||||||
lite-xl-${{ env.INSTALL_REF }}-linux-x86_64-portable.tar.gz
|
lite-xl-${{ env.INSTALL_REF }}-linux-x86_64-portable.tar.gz
|
||||||
lite-xl-${{ env.INSTALL_REF }}-addons-linux-x86_64-portable.tar.gz
|
lite-xl-${{ env.INSTALL_REF }}-addons-linux-x86_64-portable.tar.gz
|
||||||
|
@ -86,6 +96,9 @@ jobs:
|
||||||
name: macOS (x86_64)
|
name: macOS (x86_64)
|
||||||
needs: release
|
needs: release
|
||||||
runs-on: macos-11
|
runs-on: macos-11
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: [x86_64, arm64]
|
||||||
env:
|
env:
|
||||||
CC: clang
|
CC: clang
|
||||||
CXX: clang++
|
CXX: clang++
|
||||||
|
@ -100,8 +113,8 @@ 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"
|
||||||
echo "INSTALL_NAME=lite-xl-${{ needs.release.outputs.version }}-macos-$(uname -m)" >> "$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-$(uname -m)" >> "$GITHUB_ENV"
|
echo "INSTALL_NAME_ADDONS=lite-xl-${{ needs.release.outputs.version }}-addons-macos-${{ matrix.arch }}" >> "$GITHUB_ENV"
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Python Setup
|
- name: Python Setup
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
|
@ -112,15 +125,16 @@ jobs:
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
bash --version
|
bash --version
|
||||||
bash scripts/build.sh --bundle --debug --forcefallback --release
|
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} --debug --dmg --release
|
CROSS_ARCH=${{ matrix.arch }} bash scripts/package.sh --version ${INSTALL_REF} --debug --dmg --release
|
||||||
bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --dmg --release
|
CROSS_ARCH=${{ matrix.arch }} bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --dmg --release
|
||||||
- name: Upload Files
|
- name: Upload Files
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ needs.release.outputs.version }}
|
tag_name: ${{ needs.release.outputs.version }}
|
||||||
|
draft: true
|
||||||
files: |
|
files: |
|
||||||
${{ env.INSTALL_NAME }}.dmg
|
${{ env.INSTALL_NAME }}.dmg
|
||||||
${{ env.INSTALL_NAME_ADDONS }}.dmg
|
${{ env.INSTALL_NAME_ADDONS }}.dmg
|
||||||
|
@ -176,6 +190,7 @@ jobs:
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ needs.release.outputs.version }}
|
tag_name: ${{ needs.release.outputs.version }}
|
||||||
|
draft: true
|
||||||
files: |
|
files: |
|
||||||
${{ env.INSTALL_NAME }}.zip
|
${{ env.INSTALL_NAME }}.zip
|
||||||
${{ env.INSTALL_NAME_ADDONS }}.zip
|
${{ env.INSTALL_NAME_ADDONS }}.zip
|
||||||
|
|
181
changelog.md
181
changelog.md
|
@ -1,6 +1,166 @@
|
||||||
# Changes Log
|
# Changes Log
|
||||||
|
|
||||||
## [2.1.0] - 2022-09-25
|
## [2.1.1] - 2022-12-29
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
* Add config.keep_newline_whitespace option
|
||||||
|
([#1184](https://github.com/lite-xl/lite-xl/pull/1184))
|
||||||
|
|
||||||
|
* Add regex.find_offsets, regex.find, improve regex.match
|
||||||
|
([#1232](https://github.com/lite-xl/lite-xl/pull/1232))
|
||||||
|
|
||||||
|
* Added regex.gmatch ([#1233](https://github.com/lite-xl/lite-xl/pull/1233))
|
||||||
|
|
||||||
|
* add touch events ([#1245](https://github.com/lite-xl/lite-xl/pull/1245))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* highlighter: autostop co-routine when not needed
|
||||||
|
([#881](https://github.com/lite-xl/lite-xl/pull/881))
|
||||||
|
|
||||||
|
* core: ported regex.gsub to faster native version
|
||||||
|
([#1233](https://github.com/lite-xl/lite-xl/pull/1233))
|
||||||
|
|
||||||
|
### Backward Incompatible Changes
|
||||||
|
|
||||||
|
* For correctness, the behaviour of `regex.match` was changed to more closely
|
||||||
|
behave like `string.match`.
|
||||||
|
|
||||||
|
* `regex.find_offsets` now provides the previous functionality of `regex.match`
|
||||||
|
with a more appropriate function name.
|
||||||
|
|
||||||
|
* `regex.gsub` doesn't provides the indexes of matches and replacements anymore,
|
||||||
|
now it behaves more similar to `string.gsub` (the only known affected plugin
|
||||||
|
was `regexreplacepreview` which has already been adapted)
|
||||||
|
|
||||||
|
### UI Enhancements
|
||||||
|
|
||||||
|
* statusview: respect right padding of item tooltip
|
||||||
|
([0373d29f](https://github.com/lite-xl/lite-xl/commit/0373d29f99f286b2fbdda5a6837ef3797c988b88))
|
||||||
|
|
||||||
|
* feat: encode home in statusview file path
|
||||||
|
([#1224](https://github.com/lite-xl/lite-xl/pull/1224))
|
||||||
|
|
||||||
|
* autocomplete: wrap the autocomplete results around
|
||||||
|
([#1223](https://github.com/lite-xl/lite-xl/pull/1223))
|
||||||
|
|
||||||
|
* feat: alert user via nagview if file cannot be saved
|
||||||
|
([#1230](https://github.com/lite-xl/lite-xl/pull/1230))
|
||||||
|
|
||||||
|
* contextmenu: make divider less aggressive
|
||||||
|
([#1228](https://github.com/lite-xl/lite-xl/pull/1228))
|
||||||
|
|
||||||
|
* Improve IME location updates
|
||||||
|
([#1170](https://github.com/lite-xl/lite-xl/pull/1170))
|
||||||
|
|
||||||
|
* fix: move tab scroll buttons to remove spacing before 1st tab
|
||||||
|
([#1231](https://github.com/lite-xl/lite-xl/pull/1231))
|
||||||
|
|
||||||
|
* Allow TreeView file operation commands when focused
|
||||||
|
([#1256](https://github.com/lite-xl/lite-xl/pull/1256))
|
||||||
|
|
||||||
|
* contextmenu: adjust y positioning if less than zero
|
||||||
|
([#1268](https://github.com/lite-xl/lite-xl/pull/1268))
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Don't sort in Doc:get_selection_idx with an invalid index
|
||||||
|
([b029f599](https://github.com/lite-xl/lite-xl/commit/b029f5993edb7dee5ccd2ba55faac1ec22e24609))
|
||||||
|
|
||||||
|
* tokenizer: remove the limit of 3 subsyntaxes depth
|
||||||
|
([#1186](https://github.com/lite-xl/lite-xl/pull/1186))
|
||||||
|
|
||||||
|
* dirmonitor: give kevent a timeout so it doesn't lock forever
|
||||||
|
([#1180](https://github.com/lite-xl/lite-xl/pull/1180))
|
||||||
|
|
||||||
|
* dirmonitor: fix win32 implementation name length to prevent ub
|
||||||
|
([5ab8dc0](https://github.com/lite-xl/lite-xl/commit/5ab8dc027502146dd947b3d2c7544ba096a3881b))
|
||||||
|
|
||||||
|
* Make linewrapping plugin recompute breaks before scrolling
|
||||||
|
([#1190](https://github.com/lite-xl/lite-xl/pull/1190))
|
||||||
|
|
||||||
|
* Add missing get_exe_filename() implementation for FreeBSD
|
||||||
|
([#1198](https://github.com/lite-xl/lite-xl/pull/1198))
|
||||||
|
|
||||||
|
* (Windows) Load fonts with UTF-8 filenames
|
||||||
|
([#1201](https://github.com/lite-xl/lite-xl/pull/1201))
|
||||||
|
|
||||||
|
* Use subsyntax info to toggle comments
|
||||||
|
([#1202](https://github.com/lite-xl/lite-xl/pull/1202))
|
||||||
|
|
||||||
|
* Pass the currently selected item to CommandView validation
|
||||||
|
([#1203](https://github.com/lite-xl/lite-xl/pull/1203))
|
||||||
|
|
||||||
|
* Windows font loading hotfix
|
||||||
|
([#1205](https://github.com/lite-xl/lite-xl/pull/1205))
|
||||||
|
|
||||||
|
* better error messages for checkcolor
|
||||||
|
([#1211](https://github.com/lite-xl/lite-xl/pull/1211))
|
||||||
|
|
||||||
|
* Fix native plugins not reloading upon core:restart
|
||||||
|
([#1219](https://github.com/lite-xl/lite-xl/pull/1219))
|
||||||
|
|
||||||
|
* Converted from bytes to characters, as this is what windows is expecting
|
||||||
|
([5ab8dc02](https://github.com/lite-xl/lite-xl/commit/5ab8dc027502146dd947b3d2c7544ba096a3881b))
|
||||||
|
|
||||||
|
* Fix some syntax errors ([#1243](https://github.com/lite-xl/lite-xl/pull/1243))
|
||||||
|
|
||||||
|
* toolbarview: Remove tooltip when hidden
|
||||||
|
([#1251](https://github.com/lite-xl/lite-xl/pull/1251))
|
||||||
|
|
||||||
|
* detectindent: Limit subsyntax depth
|
||||||
|
([#1253](https://github.com/lite-xl/lite-xl/pull/1253))
|
||||||
|
|
||||||
|
* Use Lua string length instead of relying on strlen (#1262)
|
||||||
|
([#1262](https://github.com/lite-xl/lite-xl/pull/1262))
|
||||||
|
|
||||||
|
* dirmonitor: fix high cpu usage
|
||||||
|
([#1271](https://github.com/lite-xl/lite-xl/pull/1271)),
|
||||||
|
([#1274](https://github.com/lite-xl/lite-xl/pull/1274))
|
||||||
|
|
||||||
|
* Fix popping subsyntaxes that end consecutively
|
||||||
|
([#1246](https://github.com/lite-xl/lite-xl/pull/1246))
|
||||||
|
|
||||||
|
* Fix userdata APIs for Lua 5.4 in native plugin interface
|
||||||
|
([#1188](https://github.com/lite-xl/lite-xl/pull/1188))
|
||||||
|
|
||||||
|
* Fix horizontal scroll with touchpad on MacOS
|
||||||
|
([74349f8e](https://github.com/lite-xl/lite-xl/commit/74349f8e566ec31acd9a831a060b677d706ae4e8))
|
||||||
|
|
||||||
|
### Other Changes
|
||||||
|
|
||||||
|
* (Windows) MSVC Support ([#1199](https://github.com/lite-xl/lite-xl/pull/1199))
|
||||||
|
|
||||||
|
* meson: updated all subproject wraps
|
||||||
|
([#1214](https://github.com/lite-xl/lite-xl/pull/1214))
|
||||||
|
|
||||||
|
* set arch tuple in meson ([#1254](https://github.com/lite-xl/lite-xl/pull/1254))
|
||||||
|
|
||||||
|
* update documentation for system
|
||||||
|
([#1210](https://github.com/lite-xl/lite-xl/pull/1210))
|
||||||
|
|
||||||
|
* docs api: added dirmonitor
|
||||||
|
([7bb86e16](https://github.com/lite-xl/lite-xl/commit/7bb86e16f291256a99d2e87beb77de890cfaf0fe))
|
||||||
|
|
||||||
|
* trimwhitespace: expose functionality and extra features
|
||||||
|
([#1238](https://github.com/lite-xl/lite-xl/pull/1238))
|
||||||
|
|
||||||
|
* plugins projectsearch: expose its functionality
|
||||||
|
([#1235](https://github.com/lite-xl/lite-xl/pull/1235))
|
||||||
|
|
||||||
|
* Simplify SDL message boxes
|
||||||
|
([#1249](https://github.com/lite-xl/lite-xl/pull/1249))
|
||||||
|
|
||||||
|
* Add example settings to _overwrite_ an existing key binding
|
||||||
|
([#1270](https://github.com/lite-xl/lite-xl/pull/1270))
|
||||||
|
|
||||||
|
* Fix two typos in data/init.lua
|
||||||
|
([#1272](https://github.com/lite-xl/lite-xl/pull/1272))
|
||||||
|
|
||||||
|
* Updated meson wraps to latest (SDL v2.26, PCRE2 v10.42)
|
||||||
|
|
||||||
|
## [2.1.0] - 2022-11-01
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
* Make distinction between
|
* Make distinction between
|
||||||
|
@ -124,6 +284,12 @@
|
||||||
* Added in ability to have init.so as a require for cpath.
|
* Added in ability to have init.so as a require for cpath.
|
||||||
([#1126](https://github.com/lite-xl/lite-xl/pull/1126))
|
([#1126](https://github.com/lite-xl/lite-xl/pull/1126))
|
||||||
|
|
||||||
|
* Added system.raise_window() ([#1131](https://github.com/lite-xl/lite-xl/pull/1131))
|
||||||
|
|
||||||
|
* Initial horizontal scrollbar support ([#1124](https://github.com/lite-xl/lite-xl/pull/1124))
|
||||||
|
|
||||||
|
* IME support ([#991](https://github.com/lite-xl/lite-xl/pull/991))
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|
||||||
* [Load space metrics only when creating font](https://github.com/lite-xl/lite-xl/pull/1032)
|
* [Load space metrics only when creating font](https://github.com/lite-xl/lite-xl/pull/1032)
|
||||||
|
@ -327,11 +493,19 @@
|
||||||
* [Fix memory leak](https://github.com/lite-xl/lite-xl/pull/1039) and wrong
|
* [Fix memory leak](https://github.com/lite-xl/lite-xl/pull/1039) and wrong
|
||||||
check in font_retrieve
|
check in font_retrieve
|
||||||
|
|
||||||
* Many, many, many more changes that are too numerous to list.
|
|
||||||
|
|
||||||
* CommandView: do not change caret size with config.line_height
|
* CommandView: do not change caret size with config.line_height
|
||||||
([#1080](https://github.com/lite-xl/lite-xl/pull/1080))
|
([#1080](https://github.com/lite-xl/lite-xl/pull/1080))
|
||||||
|
|
||||||
|
* Fixed process layer argument quoting; allows for strings with spaces
|
||||||
|
([#1132](https://github.com/lite-xl/lite-xl/pull/1132))
|
||||||
|
|
||||||
|
* Draw lite-xl icon in TitleView ([#1143](https://github.com/lite-xl/lite-xl/pull/1143))
|
||||||
|
|
||||||
|
* Add parameter validation to checkcolor and f_font_group
|
||||||
|
([#1145](https://github.com/lite-xl/lite-xl/pull/1145))
|
||||||
|
|
||||||
|
* Many, many, many more changes that are too numerous to list.
|
||||||
|
|
||||||
## [2.0.5] - 2022-01-29
|
## [2.0.5] - 2022-01-29
|
||||||
|
|
||||||
Revamp the project's user module so that modifications are immediately applied.
|
Revamp the project's user module so that modifications are immediately applied.
|
||||||
|
@ -830,6 +1004,7 @@ 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.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
|
||||||
[2.0.4]: https://github.com/lite-xl/lite-xl/releases/tag/v2.0.4
|
[2.0.4]: https://github.com/lite-xl/lite-xl/releases/tag/v2.0.4
|
||||||
|
|
|
@ -3,12 +3,9 @@ local command = require "core.command"
|
||||||
local common = require "core.common"
|
local common = require "core.common"
|
||||||
local config = require "core.config"
|
local config = require "core.config"
|
||||||
local translate = require "core.doc.translate"
|
local translate = require "core.doc.translate"
|
||||||
|
local style = require "core.style"
|
||||||
local DocView = require "core.docview"
|
local DocView = require "core.docview"
|
||||||
|
local tokenizer = require "core.tokenizer"
|
||||||
|
|
||||||
local function dv()
|
|
||||||
return core.active_view
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function doc()
|
local function doc()
|
||||||
|
@ -40,9 +37,24 @@ local function save(filename)
|
||||||
filename = core.normalize_to_project_dir(filename)
|
filename = core.normalize_to_project_dir(filename)
|
||||||
abs_filename = core.project_absolute_path(filename)
|
abs_filename = core.project_absolute_path(filename)
|
||||||
end
|
end
|
||||||
doc():save(filename, abs_filename)
|
local ok, err = pcall(doc().save, doc(), filename, abs_filename)
|
||||||
|
if ok then
|
||||||
local saved_filename = doc().filename
|
local saved_filename = doc().filename
|
||||||
core.log("Saved \"%s\"", saved_filename)
|
core.log("Saved \"%s\"", saved_filename)
|
||||||
|
else
|
||||||
|
core.error(err)
|
||||||
|
core.nag_view:show("Saving failed", string.format("Could not save \"%s\" do you want to save to another location?", doc().filename), {
|
||||||
|
{ font = style.font, text = "No", default_no = true },
|
||||||
|
{ font = style.font, text = "Yes" , default_yes = true }
|
||||||
|
}, function(item)
|
||||||
|
if item.text == "Yes" then
|
||||||
|
core.add_thread(function()
|
||||||
|
-- we need to run this in a thread because of the odd way the nagview is.
|
||||||
|
command.perform("doc:save-as")
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function cut_or_copy(delete)
|
local function cut_or_copy(delete)
|
||||||
|
@ -59,8 +71,9 @@ local function cut_or_copy(delete)
|
||||||
doc():delete_to_cursor(idx, 0)
|
doc():delete_to_cursor(idx, 0)
|
||||||
end
|
end
|
||||||
else -- Cut/copy whole line
|
else -- Cut/copy whole line
|
||||||
text = doc().lines[line1]
|
-- Remove newline from the text. It will be added as needed on paste.
|
||||||
full_text = full_text == "" and text or (full_text .. text)
|
text = string.sub(doc().lines[line1], 1, -2)
|
||||||
|
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
|
||||||
|
@ -85,7 +98,15 @@ local function split_cursor(direction)
|
||||||
table.insert(new_cursors, { line1 + direction, col1 })
|
table.insert(new_cursors, { line1 + direction, col1 })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
for i,v in ipairs(new_cursors) do doc():add_selection(v[1], v[2]) end
|
-- add selections in the order that will leave the "last" added one as doc.last_selection
|
||||||
|
local start, stop = 1, #new_cursors
|
||||||
|
if direction < 0 then
|
||||||
|
start, stop = #new_cursors, 1
|
||||||
|
end
|
||||||
|
for i = start, stop, direction do
|
||||||
|
local v = new_cursors[i]
|
||||||
|
doc():add_selection(v[1], v[2])
|
||||||
|
end
|
||||||
core.blink_reset()
|
core.blink_reset()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -177,10 +198,30 @@ local function block_comment(comment, line1, col1, line2, col2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function insert_paste(doc, value, whole_line, idx)
|
||||||
|
if whole_line then
|
||||||
|
local line1, col1 = doc:get_selection_idx(idx)
|
||||||
|
doc:insert(line1, 1, value:gsub("\r", "").."\n")
|
||||||
|
-- Because we're inserting at the start of the line,
|
||||||
|
-- if the cursor is in the middle of the line
|
||||||
|
-- it gets carried to the next line along with the old text.
|
||||||
|
-- If it's at the start of the line it doesn't get carried,
|
||||||
|
-- so we move it of as many characters as we're adding.
|
||||||
|
if col1 == 1 then
|
||||||
|
doc:move_to_cursor(idx, #value+1)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
doc:text_input(value:gsub("\r", ""), idx)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local commands = {
|
local commands = {
|
||||||
["doc:select-none"] = function(dv)
|
["doc:select-none"] = function(dv)
|
||||||
local line, col = dv.doc:get_selection()
|
local l1, c1 = dv.doc:get_selection_idx(dv.doc.last_selection)
|
||||||
dv.doc:set_selection(line, col)
|
if not l1 then
|
||||||
|
l1, c1 = dv.doc:get_selection_idx(1)
|
||||||
|
end
|
||||||
|
dv.doc:set_selection(l1, c1)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["doc:cut"] = function()
|
["doc:cut"] = function()
|
||||||
|
@ -202,27 +243,51 @@ local commands = {
|
||||||
["doc:paste"] = function(dv)
|
["doc:paste"] = function(dv)
|
||||||
local clipboard = system.get_clipboard()
|
local clipboard = system.get_clipboard()
|
||||||
-- If the clipboard has changed since our last look, use that instead
|
-- If the clipboard has changed since our last look, use that instead
|
||||||
local external_paste = core.cursor_clipboard["full"] ~= clipboard
|
if core.cursor_clipboard["full"] ~= clipboard then
|
||||||
if external_paste then
|
|
||||||
core.cursor_clipboard = {}
|
core.cursor_clipboard = {}
|
||||||
core.cursor_clipboard_whole_line = {}
|
core.cursor_clipboard_whole_line = {}
|
||||||
|
for idx in dv.doc:get_selections() do
|
||||||
|
insert_paste(dv.doc, clipboard, false, idx)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- Use internal clipboard(s)
|
||||||
|
-- If there are mixed whole lines and normal lines, consider them all as normal
|
||||||
|
local only_whole_lines = true
|
||||||
|
for _,whole_line in pairs(core.cursor_clipboard_whole_line) do
|
||||||
|
if not whole_line then
|
||||||
|
only_whole_lines = false
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
local value, whole_line
|
|
||||||
for idx, line1, col1, line2, col2 in dv.doc:get_selections() do
|
|
||||||
if #core.cursor_clipboard_whole_line == (#dv.doc.selections/4) then
|
if #core.cursor_clipboard_whole_line == (#dv.doc.selections/4) then
|
||||||
value = core.cursor_clipboard[idx]
|
-- If we have the same number of clipboards and selections,
|
||||||
whole_line = core.cursor_clipboard_whole_line[idx] == true
|
-- paste each clipboard into its corresponding selection
|
||||||
else
|
for idx in dv.doc:get_selections() do
|
||||||
value = clipboard
|
insert_paste(dv.doc, core.cursor_clipboard[idx], only_whole_lines, idx)
|
||||||
whole_line = not external_paste and clipboard:find("\n") ~= nil
|
|
||||||
end
|
|
||||||
if whole_line then
|
|
||||||
dv.doc:insert(line1, 1, value:gsub("\r", ""))
|
|
||||||
if col1 == 1 then
|
|
||||||
dv.doc:move_to_cursor(idx, #value)
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
dv.doc:text_input(value:gsub("\r", ""), idx)
|
-- Paste every clipboard and add a selection at the end of each one
|
||||||
|
local new_selections = {}
|
||||||
|
for idx in dv.doc:get_selections() do
|
||||||
|
for cb_idx in ipairs(core.cursor_clipboard_whole_line) do
|
||||||
|
insert_paste(dv.doc, core.cursor_clipboard[cb_idx], only_whole_lines, idx)
|
||||||
|
if not only_whole_lines then
|
||||||
|
table.insert(new_selections, {dv.doc:get_selection_idx(idx)})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if only_whole_lines then
|
||||||
|
table.insert(new_selections, {dv.doc:get_selection_idx(idx)})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local first = true
|
||||||
|
for _,selection in pairs(new_selections) do
|
||||||
|
if first then
|
||||||
|
dv.doc:set_selection(table.unpack(selection))
|
||||||
|
first = false
|
||||||
|
else
|
||||||
|
dv.doc:add_selection(table.unpack(selection))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -234,7 +299,7 @@ local commands = {
|
||||||
indent = indent:sub(#indent + 2 - col)
|
indent = indent:sub(#indent + 2 - col)
|
||||||
end
|
end
|
||||||
-- Remove current line if it contains only whitespace
|
-- Remove current line if it contains only whitespace
|
||||||
if dv.doc.lines[line]:match("^%s+$") then
|
if not config.keep_newline_whitespace and dv.doc.lines[line]:match("^%s+$") then
|
||||||
dv.doc:remove(line, 1, line, math.huge)
|
dv.doc:remove(line, 1, line, math.huge)
|
||||||
end
|
end
|
||||||
dv.doc:text_input("\n" .. indent, idx)
|
dv.doc:text_input("\n" .. indent, idx)
|
||||||
|
@ -380,15 +445,28 @@ local commands = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["doc:toggle-block-comments"] = function(dv)
|
["doc:toggle-block-comments"] = function(dv)
|
||||||
local comment = dv.doc.syntax.block_comment
|
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
|
||||||
|
local current_syntax = dv.doc.syntax
|
||||||
|
if line1 > 1 then
|
||||||
|
-- Use the previous line state, as it will be the state
|
||||||
|
-- of the beginning of the current line
|
||||||
|
local state = dv.doc.highlighter:get_line(line1 - 1).state
|
||||||
|
local syntaxes = tokenizer.extract_subsyntaxes(dv.doc.syntax, state)
|
||||||
|
-- Go through all the syntaxes until the first with `block_comment` defined
|
||||||
|
for _, s in pairs(syntaxes) do
|
||||||
|
if s.block_comment then
|
||||||
|
current_syntax = s
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local comment = current_syntax.block_comment
|
||||||
if not comment then
|
if not comment then
|
||||||
if dv.doc.syntax.comment then
|
if dv.doc.syntax.comment then
|
||||||
command.perform "doc:toggle-line-comments"
|
command.perform "doc:toggle-line-comments"
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
|
|
||||||
-- if nothing is selected, toggle the whole line
|
-- if nothing is selected, toggle the whole line
|
||||||
if line1 == line2 and col1 == col2 then
|
if line1 == line2 and col1 == col2 then
|
||||||
col1 = 1
|
col1 = 1
|
||||||
|
@ -399,9 +477,23 @@ local commands = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["doc:toggle-line-comments"] = function(dv)
|
["doc:toggle-line-comments"] = function(dv)
|
||||||
local comment = dv.doc.syntax.comment or dv.doc.syntax.block_comment
|
|
||||||
if comment then
|
|
||||||
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
|
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
|
||||||
|
local current_syntax = dv.doc.syntax
|
||||||
|
if line1 > 1 then
|
||||||
|
-- Use the previous line state, as it will be the state
|
||||||
|
-- of the beginning of the current line
|
||||||
|
local state = dv.doc.highlighter:get_line(line1 - 1).state
|
||||||
|
local syntaxes = tokenizer.extract_subsyntaxes(dv.doc.syntax, state)
|
||||||
|
-- Go through all the syntaxes until the first with comments defined
|
||||||
|
for _, s in pairs(syntaxes) do
|
||||||
|
if s.comment or s.block_comment then
|
||||||
|
current_syntax = s
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local comment = current_syntax.comment or current_syntax.block_comment
|
||||||
|
if comment then
|
||||||
dv.doc:set_selections(idx, line_comment(comment, line1, col1, line2, col2))
|
dv.doc:set_selections(idx, line_comment(comment, line1, col1, line2, col2))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -538,7 +630,7 @@ local commands = {
|
||||||
}
|
}
|
||||||
|
|
||||||
command.add(function(x, y)
|
command.add(function(x, y)
|
||||||
if x == nil or y == nil or not core.active_view:is(DocView) then return false end
|
if x == nil or y == nil or not core.active_view:extends(DocView) then return false end
|
||||||
local dv = core.active_view
|
local dv = core.active_view
|
||||||
local x1,y1,x2,y2 = dv.position.x, dv.position.y, dv.position.x + dv.size.x, dv.position.y + dv.size.y
|
local x1,y1,x2,y2 = dv.position.x, dv.position.y, dv.position.x + dv.size.x, dv.position.y + dv.size.y
|
||||||
return x >= x1 + dv:get_gutter_width() and x < x2 and y >= y1 and y < y2, dv, x, y
|
return x >= x1 + dv:get_gutter_width() and x < x2 and y >= y1 and y < y2, dv, x, y
|
||||||
|
|
|
@ -216,7 +216,7 @@ command.add("core.docview!", {
|
||||||
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
|
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
|
||||||
end
|
end
|
||||||
local result, matches = regex.gsub(regex.compile(old, "m"), text, new)
|
local result, matches = regex.gsub(regex.compile(old, "m"), text, new)
|
||||||
return result, #matches
|
return result, matches
|
||||||
end)
|
end)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
|
@ -123,5 +123,13 @@ command.add(nil, {
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
|
end,
|
||||||
|
["root:horizontal-scroll"] = function(delta)
|
||||||
|
local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view
|
||||||
|
if view and view.scrollable then
|
||||||
|
view.scroll.to.x = view.scroll.to.x + delta * -config.mouse_wheel_scroll
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
|
@ -88,6 +88,10 @@ function CommandView:get_scrollable_size()
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function CommandView:get_h_scrollable_size()
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function CommandView:scroll_to_make_visible()
|
function CommandView:scroll_to_make_visible()
|
||||||
-- no-op function to disable this functionality
|
-- no-op function to disable this functionality
|
||||||
|
@ -155,7 +159,7 @@ end
|
||||||
function CommandView:submit()
|
function CommandView:submit()
|
||||||
local suggestion = self.suggestions[self.suggestion_idx]
|
local suggestion = self.suggestions[self.suggestion_idx]
|
||||||
local text = self:get_text()
|
local text = self:get_text()
|
||||||
if self.state.validate(text) then
|
if self.state.validate(text, suggestion) then
|
||||||
local submit = self.state.submit
|
local submit = self.state.submit
|
||||||
self:exit(true)
|
self:exit(true)
|
||||||
submit(text, suggestion)
|
submit(text, suggestion)
|
||||||
|
|
|
@ -6,8 +6,19 @@ config.message_timeout = 5
|
||||||
config.mouse_wheel_scroll = 50 * SCALE
|
config.mouse_wheel_scroll = 50 * SCALE
|
||||||
config.animate_drag_scroll = false
|
config.animate_drag_scroll = false
|
||||||
config.scroll_past_end = true
|
config.scroll_past_end = true
|
||||||
|
---@type "expanded" | "contracted" | false @Force the scrollbar status of the DocView
|
||||||
|
config.force_scrollbar_status = false
|
||||||
config.file_size_limit = 10
|
config.file_size_limit = 10
|
||||||
config.ignore_files = { "^%." }
|
config.ignore_files = {
|
||||||
|
-- folders
|
||||||
|
"^%.svn/", "^%.git/", "^%.hg/", "^CVS/", "^%.Trash/", "^%.Trash%-.*/",
|
||||||
|
"^node_modules/", "^%.cache/", "^__pycache__/",
|
||||||
|
-- files
|
||||||
|
"%.pyc$", "%.pyo$", "%.exe$", "%.dll$", "%.obj$", "%.o$",
|
||||||
|
"%.a$", "%.lib$", "%.so$", "%.dylib$", "%.ncb$", "%.sdf$",
|
||||||
|
"%.suo$", "%.pdb$", "%.idb$", "%.class$", "%.psd$", "%.db$",
|
||||||
|
"^desktop%.ini$", "^%.DS_Store$", "^%.directory$",
|
||||||
|
}
|
||||||
config.symbol_pattern = "[%a_][%w_]*"
|
config.symbol_pattern = "[%a_][%w_]*"
|
||||||
config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
|
config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
|
||||||
config.undo_merge_timeout = 0.3
|
config.undo_merge_timeout = 0.3
|
||||||
|
@ -19,6 +30,7 @@ config.highlight_current_line = true
|
||||||
config.line_height = 1.2
|
config.line_height = 1.2
|
||||||
config.indent_size = 2
|
config.indent_size = 2
|
||||||
config.tab_type = "soft"
|
config.tab_type = "soft"
|
||||||
|
config.keep_newline_whitespace = false
|
||||||
config.line_limit = 80
|
config.line_limit = 80
|
||||||
config.max_project_files = 2000
|
config.max_project_files = 2000
|
||||||
config.transitions = true
|
config.transitions = true
|
||||||
|
@ -47,8 +59,9 @@ config.plugins = {}
|
||||||
-- Allow you to set plugin configs even if we haven't seen the plugin before.
|
-- Allow you to set plugin configs even if we haven't seen the plugin before.
|
||||||
setmetatable(config.plugins, {
|
setmetatable(config.plugins, {
|
||||||
__index = function(t, k)
|
__index = function(t, k)
|
||||||
if rawget(t, k) == nil then rawset(t, k, {}) end
|
local v = rawget(t, k)
|
||||||
return rawget(t, k)
|
if v == true or v == nil then v = {} rawset(t, k, v) end
|
||||||
|
return v
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ local View = require "core.view"
|
||||||
|
|
||||||
local border_width = 1
|
local border_width = 1
|
||||||
local divider_width = 1
|
local divider_width = 1
|
||||||
|
local divider_padding = 5
|
||||||
local DIVIDER = {}
|
local DIVIDER = {}
|
||||||
|
|
||||||
---@class core.contextmenu : core.object
|
---@class core.contextmenu : core.object
|
||||||
|
@ -29,7 +30,7 @@ local function get_item_size(item)
|
||||||
local lw, lh
|
local lw, lh
|
||||||
if item == DIVIDER then
|
if item == DIVIDER then
|
||||||
lw = 0
|
lw = 0
|
||||||
lh = divider_width
|
lh = divider_width + divider_padding * SCALE * 2
|
||||||
else
|
else
|
||||||
lw = style.font:get_width(item.text)
|
lw = style.font:get_width(item.text)
|
||||||
if item.info then
|
if item.info then
|
||||||
|
@ -82,12 +83,8 @@ function ContextMenu:show(x, y)
|
||||||
local w, h = self.items.width, self.items.height
|
local w, h = self.items.width, self.items.height
|
||||||
|
|
||||||
-- by default the box is opened on the right and below
|
-- by default the box is opened on the right and below
|
||||||
if x + w >= core.root_view.size.x then
|
x = common.clamp(x, 0, core.root_view.size.x - w - style.padding.x)
|
||||||
x = x - w
|
y = common.clamp(y, 0, core.root_view.size.y - h)
|
||||||
end
|
|
||||||
if y + h >= core.root_view.size.y then
|
|
||||||
y = y - h
|
|
||||||
end
|
|
||||||
|
|
||||||
self.position.x, self.position.y = x, y
|
self.position.x, self.position.y = x, y
|
||||||
self.show_context_menu = true
|
self.show_context_menu = true
|
||||||
|
@ -224,7 +221,7 @@ function ContextMenu:draw_context_menu()
|
||||||
|
|
||||||
for i, item, x, y, w, h in self:each_item() do
|
for i, item, x, y, w, h in self:each_item() do
|
||||||
if item == DIVIDER then
|
if item == DIVIDER then
|
||||||
renderer.draw_rect(x, y, w, h, style.caret)
|
renderer.draw_rect(x, y + divider_padding * SCALE, w, divider_width, style.divider)
|
||||||
else
|
else
|
||||||
if i == self.selected then
|
if i == self.selected then
|
||||||
renderer.draw_rect(x, y, w, h, style.selection)
|
renderer.draw_rect(x, y, w, h, style.selection)
|
||||||
|
|
|
@ -14,8 +14,8 @@ function dirwatch.new()
|
||||||
watched = {},
|
watched = {},
|
||||||
reverse_watched = {},
|
reverse_watched = {},
|
||||||
monitor = dirmonitor.new(),
|
monitor = dirmonitor.new(),
|
||||||
windows_watch_top = nil,
|
single_watch_top = nil,
|
||||||
windows_watch_count = 0
|
single_watch_count = 0
|
||||||
}
|
}
|
||||||
setmetatable(t, dirwatch)
|
setmetatable(t, dirwatch)
|
||||||
return t
|
return t
|
||||||
|
@ -38,23 +38,23 @@ function dirwatch:watch(directory, bool)
|
||||||
local info = system.get_file_info(directory)
|
local info = system.get_file_info(directory)
|
||||||
if not info then return end
|
if not info then return end
|
||||||
if not self.watched[directory] and not self.scanned[directory] then
|
if not self.watched[directory] and not self.scanned[directory] then
|
||||||
if PLATFORM == "Windows" then
|
if self.monitor:mode() == "single" then
|
||||||
if info.type ~= "dir" then return self:scan(directory) end
|
if info.type ~= "dir" then return self:scan(directory) end
|
||||||
if not self.windows_watch_top or directory:find(self.windows_watch_top, 1, true) ~= 1 then
|
if not self.single_watch_top or directory:find(self.single_watch_top, 1, true) ~= 1 then
|
||||||
-- Get the highest level of directory that is common to this directory, and the original.
|
-- Get the highest level of directory that is common to this directory, and the original.
|
||||||
local target = directory
|
local target = directory
|
||||||
while self.windows_watch_top and self.windows_watch_top:find(target, 1, true) ~= 1 do
|
while self.single_watch_top and self.single_watch_top:find(target, 1, true) ~= 1 do
|
||||||
target = common.dirname(target)
|
target = common.dirname(target)
|
||||||
end
|
end
|
||||||
if target ~= self.windows_watch_top then
|
if target ~= self.single_watch_top then
|
||||||
local value = self.monitor:watch(target)
|
local value = self.monitor:watch(target)
|
||||||
if value and value < 0 then
|
if value and value < 0 then
|
||||||
return self:scan(directory)
|
return self:scan(directory)
|
||||||
end
|
end
|
||||||
self.windows_watch_top = target
|
self.single_watch_top = target
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.windows_watch_count = self.windows_watch_count + 1
|
self.single_watch_count = self.single_watch_count + 1
|
||||||
self.watched[directory] = true
|
self.watched[directory] = true
|
||||||
else
|
else
|
||||||
local value = self.monitor:watch(directory)
|
local value = self.monitor:watch(directory)
|
||||||
|
@ -72,13 +72,13 @@ end
|
||||||
-- this should be an absolute path
|
-- this should be an absolute path
|
||||||
function dirwatch:unwatch(directory)
|
function dirwatch:unwatch(directory)
|
||||||
if self.watched[directory] then
|
if self.watched[directory] then
|
||||||
if PLATFORM ~= "Windows" then
|
if self.monitor:mode() == "multiple" then
|
||||||
self.monitor:unwatch(self.watched[directory])
|
self.monitor:unwatch(self.watched[directory])
|
||||||
self.reverse_watched[directory] = nil
|
self.reverse_watched[directory] = nil
|
||||||
else
|
else
|
||||||
self.windows_watch_count = self.windows_watch_count - 1
|
self.single_watch_count = self.single_watch_count - 1
|
||||||
if self.windows_watch_count == 0 then
|
if self.single_watch_count == 0 then
|
||||||
self.windows_watch_top = nil
|
self.single_watch_top = nil
|
||||||
self.monitor:unwatch(directory)
|
self.monitor:unwatch(directory)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -93,8 +93,12 @@ function dirwatch:check(change_callback, scan_time, wait_time)
|
||||||
local had_change = false
|
local had_change = false
|
||||||
self.monitor:check(function(id)
|
self.monitor:check(function(id)
|
||||||
had_change = true
|
had_change = true
|
||||||
if PLATFORM == "Windows" then
|
if self.monitor:mode() == "single" then
|
||||||
change_callback(common.dirname(self.windows_watch_top .. PATHSEP .. id))
|
local path = common.dirname(id)
|
||||||
|
if not string.match(id, "^/") and not string.match(id, "^%a:[/\\]") then
|
||||||
|
path = common.dirname(self.single_watch_top .. PATHSEP .. id)
|
||||||
|
end
|
||||||
|
change_callback(path)
|
||||||
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
|
||||||
|
@ -162,7 +166,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
local function compare_file(a, b)
|
local function compare_file(a, b)
|
||||||
return a.filename < b.filename
|
return system.path_compare(a.filename, a.type, b.filename, b.type)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,18 +10,17 @@ local Highlighter = Object:extend()
|
||||||
|
|
||||||
function Highlighter:new(doc)
|
function Highlighter:new(doc)
|
||||||
self.doc = doc
|
self.doc = doc
|
||||||
|
self.running = false
|
||||||
self:reset()
|
self:reset()
|
||||||
|
end
|
||||||
|
|
||||||
-- init incremental syntax highlighting
|
-- init incremental syntax highlighting
|
||||||
|
function Highlighter:start()
|
||||||
|
if self.running then return end
|
||||||
|
self.running = true
|
||||||
core.add_thread(function()
|
core.add_thread(function()
|
||||||
while true do
|
while self.first_invalid_line < self.max_wanted_line do
|
||||||
if self.first_invalid_line > self.max_wanted_line then
|
|
||||||
self.max_wanted_line = 0
|
|
||||||
coroutine.yield(1 / config.fps)
|
|
||||||
|
|
||||||
else
|
|
||||||
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
|
||||||
|
@ -42,10 +41,18 @@ function Highlighter:new(doc)
|
||||||
core.redraw = true
|
core.redraw = true
|
||||||
coroutine.yield()
|
coroutine.yield()
|
||||||
end
|
end
|
||||||
end
|
self.max_wanted_line = 0
|
||||||
|
self.running = false
|
||||||
end, self)
|
end, self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function set_max_wanted_lines(self, amount)
|
||||||
|
self.max_wanted_line = amount
|
||||||
|
if self.first_invalid_line < self.max_wanted_line then
|
||||||
|
self:start()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function Highlighter:reset()
|
function Highlighter:reset()
|
||||||
self.lines = {}
|
self.lines = {}
|
||||||
|
@ -62,7 +69,7 @@ end
|
||||||
|
|
||||||
function Highlighter:invalidate(idx)
|
function Highlighter:invalidate(idx)
|
||||||
self.first_invalid_line = math.min(self.first_invalid_line, idx)
|
self.first_invalid_line = math.min(self.first_invalid_line, idx)
|
||||||
self.max_wanted_line = math.min(self.max_wanted_line, #self.doc.lines)
|
set_max_wanted_lines(self, math.min(self.max_wanted_line, #self.doc.lines))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Highlighter:insert_notify(line, n)
|
function Highlighter:insert_notify(line, n)
|
||||||
|
@ -101,7 +108,7 @@ function Highlighter:get_line(idx)
|
||||||
self.lines[idx] = line
|
self.lines[idx] = line
|
||||||
self:update_notify(idx, 0)
|
self:update_notify(idx, 0)
|
||||||
end
|
end
|
||||||
self.max_wanted_line = math.max(self.max_wanted_line, idx)
|
set_max_wanted_lines(self, math.max(self.max_wanted_line, idx))
|
||||||
return line
|
return line
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ end
|
||||||
function Doc:reset()
|
function Doc:reset()
|
||||||
self.lines = { "\n" }
|
self.lines = { "\n" }
|
||||||
self.selections = { 1, 1, 1, 1 }
|
self.selections = { 1, 1, 1, 1 }
|
||||||
|
self.last_selection = 1
|
||||||
self.undo_stack = { idx = 1 }
|
self.undo_stack = { idx = 1 }
|
||||||
self.redo_stack = { idx = 1 }
|
self.redo_stack = { idx = 1 }
|
||||||
self.clean_change_id = 1
|
self.clean_change_id = 1
|
||||||
|
@ -141,15 +142,39 @@ function Doc:get_change_id()
|
||||||
return self.undo_stack.idx
|
return self.undo_stack.idx
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function sort_positions(line1, col1, line2, col2)
|
||||||
|
if line1 > line2 or line1 == line2 and col1 > col2 then
|
||||||
|
return line2, col2, line1, col1, true
|
||||||
|
end
|
||||||
|
return line1, col1, line2, col2, false
|
||||||
|
end
|
||||||
|
|
||||||
-- Cursor section. Cursor indices are *only* valid during a get_selections() call.
|
-- Cursor section. Cursor indices are *only* valid during a get_selections() call.
|
||||||
-- Cursors will always be iterated in order from top to bottom. Through normal operation
|
-- Cursors will always be iterated in order from top to bottom. Through normal operation
|
||||||
-- curors can never swap positions; only merge or split, or change their position in cursor
|
-- curors can never swap positions; only merge or split, or change their position in cursor
|
||||||
-- order.
|
-- order.
|
||||||
function Doc:get_selection(sort)
|
function Doc:get_selection(sort)
|
||||||
local idx, line1, col1, line2, col2, swap = self:get_selections(sort)({ self.selections, sort }, 0)
|
local line1, col1, line2, col2, swap = self:get_selection_idx(self.last_selection, sort)
|
||||||
|
if not line1 then
|
||||||
|
line1, col1, line2, col2, swap = self:get_selection_idx(1, sort)
|
||||||
|
end
|
||||||
return line1, col1, line2, col2, swap
|
return line1, col1, line2, col2, swap
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Get the selection specified by `idx`
|
||||||
|
---@param idx integer @the index of the selection to retrieve
|
||||||
|
---@param sort? boolean @whether to sort the selection returned
|
||||||
|
---@return integer,integer,integer,integer,boolean? @line1, col1, line2, col2, was the selection sorted
|
||||||
|
function Doc:get_selection_idx(idx, sort)
|
||||||
|
local line1, col1, line2, col2 = self.selections[idx*4-3], self.selections[idx*4-2], self.selections[idx*4-1], self.selections[idx*4]
|
||||||
|
if line1 and sort then
|
||||||
|
return sort_positions(line1, col1, line2, col2)
|
||||||
|
else
|
||||||
|
return line1, col1, line2, col2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function Doc:get_selection_text(limit)
|
function Doc:get_selection_text(limit)
|
||||||
limit = limit or math.huge
|
limit = limit or math.huge
|
||||||
local result = {}
|
local result = {}
|
||||||
|
@ -181,13 +206,6 @@ function Doc:sanitize_selection()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function sort_positions(line1, col1, line2, col2)
|
|
||||||
if line1 > line2 or line1 == line2 and col1 > col2 then
|
|
||||||
return line2, col2, line1, col1, true
|
|
||||||
end
|
|
||||||
return line1, col1, line2, col2, false
|
|
||||||
end
|
|
||||||
|
|
||||||
function Doc:set_selections(idx, line1, col1, line2, col2, swap, rm)
|
function Doc:set_selections(idx, line1, col1, line2, col2, swap, rm)
|
||||||
assert(not line2 == not col2, "expected 3 or 5 arguments")
|
assert(not line2 == not col2, "expected 3 or 5 arguments")
|
||||||
if swap then line1, col1, line2, col2 = line2, col2, line1, col1 end
|
if swap then line1, col1, line2, col2 = line2, col2, line1, col1 end
|
||||||
|
@ -206,10 +224,14 @@ function Doc:add_selection(line1, col1, line2, col2, swap)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self:set_selections(target, line1, col1, line2, col2, swap, 0)
|
self:set_selections(target, line1, col1, line2, col2, swap, 0)
|
||||||
|
self.last_selection = target
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:remove_selection(idx)
|
function Doc:remove_selection(idx)
|
||||||
|
if self.last_selection >= idx then
|
||||||
|
self.last_selection = self.last_selection - 1
|
||||||
|
end
|
||||||
common.splice(self.selections, (idx - 1) * 4 + 1, 4)
|
common.splice(self.selections, (idx - 1) * 4 + 1, 4)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -217,6 +239,7 @@ end
|
||||||
function Doc:set_selection(line1, col1, line2, col2, swap)
|
function Doc:set_selection(line1, col1, line2, col2, swap)
|
||||||
self.selections = {}
|
self.selections = {}
|
||||||
self:set_selections(1, line1, col1, line2, col2, swap)
|
self:set_selections(1, line1, col1, line2, col2, swap)
|
||||||
|
self.last_selection = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
function Doc:merge_cursors(idx)
|
function Doc:merge_cursors(idx)
|
||||||
|
@ -225,6 +248,9 @@ function Doc:merge_cursors(idx)
|
||||||
if self.selections[i] == self.selections[j] and
|
if self.selections[i] == self.selections[j] and
|
||||||
self.selections[i+1] == self.selections[j+1] then
|
self.selections[i+1] == self.selections[j+1] then
|
||||||
common.splice(self.selections, i, 4)
|
common.splice(self.selections, i, 4)
|
||||||
|
if self.last_selection >= (i+3)/4 then
|
||||||
|
self.last_selection = self.last_selection - 1
|
||||||
|
end
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -456,6 +482,18 @@ function Doc:text_input(text, idx)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Doc:ime_text_editing(text, start, length, idx)
|
||||||
|
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
|
||||||
|
if line1 ~= line2 or col1 ~= col2 then
|
||||||
|
self:delete_to_cursor(sidx)
|
||||||
|
end
|
||||||
|
self:insert(line1, col1, text)
|
||||||
|
self:set_selections(sidx, line1, col1 + #text, line1, col1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
|
function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
|
||||||
local old_text = self:get_text(line1, col1, line2, col2)
|
local old_text = self:get_text(line1, col1, line2, col2)
|
||||||
local new_text, res = fn(old_text)
|
local new_text, res = fn(old_text)
|
||||||
|
|
|
@ -4,6 +4,7 @@ local config = require "core.config"
|
||||||
local style = require "core.style"
|
local style = require "core.style"
|
||||||
local keymap = require "core.keymap"
|
local keymap = require "core.keymap"
|
||||||
local translate = require "core.doc.translate"
|
local translate = require "core.doc.translate"
|
||||||
|
local ime = require "core.ime"
|
||||||
local View = require "core.view"
|
local View = require "core.view"
|
||||||
|
|
||||||
---@class core.docview : core.view
|
---@class core.docview : core.view
|
||||||
|
@ -60,6 +61,11 @@ function DocView:new(doc)
|
||||||
self.doc = assert(doc)
|
self.doc = assert(doc)
|
||||||
self.font = "code_font"
|
self.font = "code_font"
|
||||||
self.last_x_offset = {}
|
self.last_x_offset = {}
|
||||||
|
self.ime_selection = { from = 0, size = 0 }
|
||||||
|
self.ime_status = false
|
||||||
|
self.hovering_gutter = false
|
||||||
|
self.v_scrollbar:set_forced_status(config.force_scrollbar_status)
|
||||||
|
self.h_scrollbar:set_forced_status(config.force_scrollbar_status)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,6 +117,10 @@ function DocView:get_scrollable_size()
|
||||||
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
|
||||||
|
|
||||||
|
function DocView:get_h_scrollable_size()
|
||||||
|
return math.huge
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function DocView:get_font()
|
function DocView:get_font()
|
||||||
return style[self.font]
|
return style[self.font]
|
||||||
|
@ -239,8 +249,14 @@ end
|
||||||
function DocView:on_mouse_moved(x, y, ...)
|
function DocView:on_mouse_moved(x, y, ...)
|
||||||
DocView.super.on_mouse_moved(self, x, y, ...)
|
DocView.super.on_mouse_moved(self, x, y, ...)
|
||||||
|
|
||||||
if self.hovered_scrollbar_track or self.dragging_scrollbar then
|
self.hovering_gutter = false
|
||||||
|
local gw = self:get_gutter_width()
|
||||||
|
|
||||||
|
if self:scrollbar_hovering() or self:scrollbar_dragging() then
|
||||||
self.cursor = "arrow"
|
self.cursor = "arrow"
|
||||||
|
elseif gw > 0 and x >= self.position.x and x <= (self.position.x + gw) then
|
||||||
|
self.cursor = "arrow"
|
||||||
|
self.hovering_gutter = true
|
||||||
else
|
else
|
||||||
self.cursor = "ibeam"
|
self.cursor = "ibeam"
|
||||||
end
|
end
|
||||||
|
@ -282,6 +298,29 @@ function DocView:mouse_selection(doc, snap_type, line1, col1, line2, col2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function DocView:on_mouse_pressed(button, x, y, clicks)
|
||||||
|
if button ~= "left" or not self.hovering_gutter then
|
||||||
|
return DocView.super.on_mouse_pressed(self, button, x, y, clicks)
|
||||||
|
end
|
||||||
|
local line = self:resolve_screen_position(x, y)
|
||||||
|
if keymap.modkeys["shift"] then
|
||||||
|
local sline, scol, sline2, scol2 = self.doc:get_selection(true)
|
||||||
|
if line > sline then
|
||||||
|
self.doc:set_selection(sline, 1, line, #self.doc.lines[line])
|
||||||
|
else
|
||||||
|
self.doc:set_selection(line, 1, sline2, #self.doc.lines[sline2])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if clicks == 1 then
|
||||||
|
self.doc:set_selection(line, 1, line, 1)
|
||||||
|
elseif clicks == 2 then
|
||||||
|
self.doc:set_selection(line, 1, line, #self.doc.lines[line])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function DocView:on_mouse_released(...)
|
function DocView:on_mouse_released(...)
|
||||||
DocView.super.on_mouse_released(self, ...)
|
DocView.super.on_mouse_released(self, ...)
|
||||||
self.mouse_selecting = nil
|
self.mouse_selecting = nil
|
||||||
|
@ -292,13 +331,53 @@ function DocView:on_text_input(text)
|
||||||
self.doc:text_input(text)
|
self.doc:text_input(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function DocView:on_ime_text_editing(text, start, length)
|
||||||
|
self.doc:ime_text_editing(text, start, length)
|
||||||
|
self.ime_status = #text > 0
|
||||||
|
self.ime_selection.from = start
|
||||||
|
self.ime_selection.size = length
|
||||||
|
|
||||||
|
-- Set the composition bounding box that the system IME
|
||||||
|
-- will consider when drawing its interface
|
||||||
|
local line1, col1, line2, col2 = self.doc:get_selection(true)
|
||||||
|
local col = math.min(col1, col2)
|
||||||
|
self:update_ime_location()
|
||||||
|
self:scroll_to_make_visible(line1, col + start)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Update the composition bounding box that the system IME
|
||||||
|
---will consider when drawing its interface
|
||||||
|
function DocView:update_ime_location()
|
||||||
|
if not self.ime_status then return end
|
||||||
|
|
||||||
|
local line1, col1, line2, col2 = self.doc:get_selection(true)
|
||||||
|
local x, y = self:get_line_screen_position(line1)
|
||||||
|
local h = self:get_line_height()
|
||||||
|
local col = math.min(col1, col2)
|
||||||
|
|
||||||
|
local x1, x2 = 0, 0
|
||||||
|
|
||||||
|
if self.ime_selection.size > 0 then
|
||||||
|
-- focus on a part of the text
|
||||||
|
local from = col + self.ime_selection.from
|
||||||
|
local to = from + self.ime_selection.size
|
||||||
|
x1 = self:get_col_x_offset(line1, from)
|
||||||
|
x2 = self:get_col_x_offset(line1, to)
|
||||||
|
else
|
||||||
|
-- focus the whole text
|
||||||
|
x1 = self:get_col_x_offset(line1, col1)
|
||||||
|
x2 = self:get_col_x_offset(line2, col2)
|
||||||
|
end
|
||||||
|
|
||||||
|
ime.set_location(x + x1, y, x2 - x1, h)
|
||||||
|
end
|
||||||
|
|
||||||
function DocView:update()
|
function DocView:update()
|
||||||
-- scroll to make caret visible and reset blink timer if it moved
|
-- scroll to make caret visible and reset blink timer if it moved
|
||||||
local line1, col1, line2, col2 = self.doc:get_selection()
|
local line1, col1, line2, col2 = self.doc:get_selection()
|
||||||
if (line1 ~= self.last_line1 or col1 ~= self.last_col1 or
|
if (line1 ~= self.last_line1 or col1 ~= self.last_col1 or
|
||||||
line2 ~= self.last_line2 or col2 ~= self.last_col2) and self.size.x > 0 then
|
line2 ~= self.last_line2 or col2 ~= self.last_col2) and self.size.x > 0 then
|
||||||
if core.active_view == self then
|
if core.active_view == self and not ime.editing then
|
||||||
self:scroll_to_make_visible(line1, col1)
|
self:scroll_to_make_visible(line1, col1)
|
||||||
end
|
end
|
||||||
core.blink_reset()
|
core.blink_reset()
|
||||||
|
@ -316,6 +395,8 @@ function DocView:update()
|
||||||
core.blink_timer = tb
|
core.blink_timer = tb
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self:update_ime_location()
|
||||||
|
|
||||||
DocView.super.update(self)
|
DocView.super.update(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -329,9 +410,17 @@ end
|
||||||
function DocView:draw_line_text(line, x, y)
|
function DocView:draw_line_text(line, x, y)
|
||||||
local default_font = self:get_font()
|
local default_font = self:get_font()
|
||||||
local tx, ty = x, y + self:get_line_text_y_offset()
|
local tx, ty = x, y + self:get_line_text_y_offset()
|
||||||
for _, type, text in self.doc.highlighter:each_token(line) do
|
local last_token = nil
|
||||||
|
local tokens = self.doc.highlighter:get_line(line).tokens
|
||||||
|
local tokens_count = #tokens
|
||||||
|
if string.sub(tokens[tokens_count], -1) == "\n" then
|
||||||
|
last_token = tokens_count - 1
|
||||||
|
end
|
||||||
|
for tidx, type, text in self.doc.highlighter:each_token(line) do
|
||||||
local color = style.syntax[type]
|
local color = style.syntax[type]
|
||||||
local font = style.syntax_fonts[type] or default_font
|
local font = style.syntax_fonts[type] or default_font
|
||||||
|
-- do not render newline, fixes issue #1164
|
||||||
|
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)
|
||||||
end
|
end
|
||||||
return self:get_line_height()
|
return self:get_line_height()
|
||||||
|
@ -399,17 +488,45 @@ function DocView:draw_line_gutter(line, x, y, width)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function DocView:draw_ime_decoration(line1, col1, line2, col2)
|
||||||
|
local x, y = self:get_line_screen_position(line1)
|
||||||
|
local line_size = math.max(1, SCALE)
|
||||||
|
local lh = self:get_line_height()
|
||||||
|
|
||||||
|
-- Draw IME underline
|
||||||
|
local x1 = self:get_col_x_offset(line1, col1)
|
||||||
|
local x2 = self:get_col_x_offset(line2, col2)
|
||||||
|
renderer.draw_rect(x + math.min(x1, x2), y + lh - line_size, math.abs(x1 - x2), line_size, style.text)
|
||||||
|
|
||||||
|
-- Draw IME selection
|
||||||
|
local col = math.min(col1, col2)
|
||||||
|
local from = col + self.ime_selection.from
|
||||||
|
local to = from + self.ime_selection.size
|
||||||
|
x1 = self:get_col_x_offset(line1, from)
|
||||||
|
if from ~= to then
|
||||||
|
x2 = self:get_col_x_offset(line1, to)
|
||||||
|
line_size = style.caret_width
|
||||||
|
renderer.draw_rect(x + math.min(x1, x2), y + lh - line_size, math.abs(x1 - x2), line_size, style.caret)
|
||||||
|
end
|
||||||
|
self:draw_caret(x + x1, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function DocView:draw_overlay()
|
function DocView:draw_overlay()
|
||||||
if core.active_view == self then
|
if core.active_view == self then
|
||||||
local minline, maxline = self:get_visible_line_range()
|
local minline, maxline = self:get_visible_line_range()
|
||||||
-- draw caret if it overlaps this line
|
-- draw caret if it overlaps this line
|
||||||
local T = config.blink_period
|
local T = config.blink_period
|
||||||
for _, line, col in self.doc:get_selections() do
|
for _, line1, col1, line2, col2 in self.doc:get_selections() do
|
||||||
if line >= minline and line <= maxline
|
if line1 >= minline and line1 <= maxline
|
||||||
and system.window_has_focus() then
|
and system.window_has_focus() then
|
||||||
|
if ime.editing then
|
||||||
|
self:draw_ime_decoration(line1, col1, line2, col2)
|
||||||
|
else
|
||||||
if config.disable_blink
|
if config.disable_blink
|
||||||
or (core.blink_timer - core.blink_start) % T < T / 2 then
|
or (core.blink_timer - core.blink_start) % T < T / 2 then
|
||||||
self:draw_caret(self:get_line_screen_position(line, col))
|
self:draw_caret(self:get_line_screen_position(line1, col1))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,9 +7,15 @@ local View = require "core.view"
|
||||||
local EmptyView = View:extend()
|
local EmptyView = View:extend()
|
||||||
|
|
||||||
local function draw_text(x, y, color)
|
local function draw_text(x, y, color)
|
||||||
|
local lines = {
|
||||||
|
{ fmt = "%s to run a command", cmd = "core:find-command" },
|
||||||
|
{ fmt = "%s to open a file from the project", cmd = "core:find-file" },
|
||||||
|
{ fmt = "%s to change project folder", cmd = "core:change-project-folder" },
|
||||||
|
{ fmt = "%s to open a project folder", cmd = "core:open-project-folder" },
|
||||||
|
}
|
||||||
local th = style.big_font:get_height()
|
local th = style.big_font:get_height()
|
||||||
local dh = 2 * th + style.padding.y * 2
|
local dh = 2 * th + style.padding.y * 2
|
||||||
local x1, y1 = x, y + (dh - th) / 2
|
local x1, y1 = x, y + ((dh - th) / #lines)
|
||||||
local xv = x1
|
local xv = x1
|
||||||
local title = "Lite XL"
|
local title = "Lite XL"
|
||||||
local version = "version " .. VERSION
|
local version = "version " .. VERSION
|
||||||
|
@ -24,12 +30,6 @@ local function draw_text(x, y, color)
|
||||||
renderer.draw_text(style.font, version, xv, y1 + th, color)
|
renderer.draw_text(style.font, version, xv, y1 + th, color)
|
||||||
x = x + style.padding.x
|
x = x + style.padding.x
|
||||||
renderer.draw_rect(x, y, math.ceil(1 * SCALE), dh, color)
|
renderer.draw_rect(x, y, math.ceil(1 * SCALE), dh, color)
|
||||||
local lines = {
|
|
||||||
{ fmt = "%s to run a command", cmd = "core:find-command" },
|
|
||||||
{ fmt = "%s to open a file from the project", cmd = "core:find-file" },
|
|
||||||
{ fmt = "%s to change project folder", cmd = "core:change-project-folder" },
|
|
||||||
{ fmt = "%s to open a project folder", cmd = "core:open-project-folder" },
|
|
||||||
}
|
|
||||||
th = style.font:get_height()
|
th = style.font:get_height()
|
||||||
y = y + (dh - (th + style.padding.y) * #lines) / 2
|
y = y + (dh - (th + style.padding.y) * #lines) / 2
|
||||||
local w = 0
|
local w = 0
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
local core = require "core"
|
||||||
|
|
||||||
|
local ime = { }
|
||||||
|
|
||||||
|
function ime.reset()
|
||||||
|
ime.editing = false
|
||||||
|
ime.last_location = { x = 0, y = 0, w = 0, h = 0 }
|
||||||
|
end
|
||||||
|
|
||||||
|
---Convert from utf-8 offset and length (from SDL) to byte offsets
|
||||||
|
---@param text string @Textediting string
|
||||||
|
---@param start integer @0-based utf-8 offset of the starting position of the selection
|
||||||
|
---@param length integer @Size of the utf-8 length of the selection
|
||||||
|
function ime.ingest(text, start, length)
|
||||||
|
if #text == 0 then
|
||||||
|
-- finished textediting
|
||||||
|
ime.reset()
|
||||||
|
return "", 0, 0
|
||||||
|
end
|
||||||
|
|
||||||
|
ime.editing = true
|
||||||
|
|
||||||
|
if start < 0 then
|
||||||
|
-- we assume no selection and caret at the end
|
||||||
|
return text, #text, 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- start is 0-based, so we use start + 1
|
||||||
|
local start_byte = utf8.offset(text, start + 1)
|
||||||
|
if not start_byte then
|
||||||
|
-- bad start offset
|
||||||
|
-- we assume it meant the last byte of the text
|
||||||
|
start_byte = #text
|
||||||
|
else
|
||||||
|
start_byte = math.min(start_byte - 1, #text)
|
||||||
|
end
|
||||||
|
|
||||||
|
if length < 0 then
|
||||||
|
-- caret only
|
||||||
|
return text, start_byte, 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local end_byte = utf8.offset(text, start + length + 1)
|
||||||
|
if not end_byte or end_byte - 1 < start_byte then
|
||||||
|
-- bad length, assume caret only
|
||||||
|
return text, start_byte, 0
|
||||||
|
end
|
||||||
|
|
||||||
|
end_byte = math.min(end_byte - 1, #text)
|
||||||
|
return text, start_byte, end_byte - start_byte
|
||||||
|
end
|
||||||
|
|
||||||
|
---Forward the given textediting SDL event data to Views.
|
||||||
|
---@param text string @Textediting string
|
||||||
|
---@param start integer @0-based utf-8 offset of the starting position of the selection
|
||||||
|
---@param length integer @Size of the utf-8 length of the selection
|
||||||
|
function ime.on_text_editing(text, start, length, ...)
|
||||||
|
if ime.editing or #text > 0 then
|
||||||
|
core.root_view:on_ime_text_editing(ime.ingest(text, start, length, ...))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Stop IME composition.
|
||||||
|
---Might not completely work on every platform.
|
||||||
|
function ime.stop()
|
||||||
|
if ime.editing then
|
||||||
|
-- SDL_ClearComposition for now doesn't work everywhere
|
||||||
|
system.clear_ime()
|
||||||
|
ime.on_text_editing("", 0, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Set the bounding box of the text pertaining the IME.
|
||||||
|
---The IME will draw its interface based on this info.
|
||||||
|
---@param x number
|
||||||
|
---@param y number
|
||||||
|
---@param w number
|
||||||
|
---@param h number
|
||||||
|
function ime.set_location(x, y, w, h)
|
||||||
|
if not ime.last_location or
|
||||||
|
ime.last_location.x ~= x or
|
||||||
|
ime.last_location.y ~= y or
|
||||||
|
ime.last_location.w ~= w or
|
||||||
|
ime.last_location.h ~= h
|
||||||
|
then
|
||||||
|
ime.last_location.x, ime.last_location.y, ime.last_location.w, ime.last_location.h = x, y, w, h
|
||||||
|
system.set_text_input_rect(x, y, w, h)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ime.reset()
|
||||||
|
return ime
|
|
@ -6,6 +6,7 @@ local style = require "colors.default"
|
||||||
local command
|
local command
|
||||||
local keymap
|
local keymap
|
||||||
local dirwatch
|
local dirwatch
|
||||||
|
local ime
|
||||||
local RootView
|
local RootView
|
||||||
local StatusView
|
local StatusView
|
||||||
local TitleView
|
local TitleView
|
||||||
|
@ -295,7 +296,19 @@ function core.add_project_directory(path)
|
||||||
end
|
end
|
||||||
return refresh_directory(topdir, dirpath)
|
return refresh_directory(topdir, dirpath)
|
||||||
end, 0.01, 0.01)
|
end, 0.01, 0.01)
|
||||||
|
-- properly exit coroutine if project not open anymore to clear dir watch
|
||||||
|
local project_dir_open = false
|
||||||
|
for _, prj in ipairs(core.project_directories) do
|
||||||
|
if topdir == prj then
|
||||||
|
project_dir_open = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if project_dir_open then
|
||||||
coroutine.yield(changed and 0.05 or 0)
|
coroutine.yield(changed and 0.05 or 0)
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -361,6 +374,11 @@ end
|
||||||
function core.update_project_subdir(dir, filename, expanded)
|
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
|
||||||
|
dir.watch:watch(dir.name .. PATHSEP .. filename)
|
||||||
|
else
|
||||||
|
dir.watch:unwatch(dir.name .. PATHSEP .. filename)
|
||||||
|
end
|
||||||
return refresh_directory(dir, filename)
|
return refresh_directory(dir, filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -477,6 +495,9 @@ local style = require "core.style"
|
||||||
-- key binding:
|
-- key binding:
|
||||||
-- keymap.add { ["ctrl+escape"] = "core:quit" }
|
-- keymap.add { ["ctrl+escape"] = "core:quit" }
|
||||||
|
|
||||||
|
-- pass 'true' for second parameter to overwrite an existing binding
|
||||||
|
-- keymap.add({ ["ctrl+pageup"] = "root:switch-to-previous-tab" }, true)
|
||||||
|
-- keymap.add({ ["ctrl+pagedown"] = "root:switch-to-next-tab" }, true)
|
||||||
|
|
||||||
------------------------------- Fonts ----------------------------------------
|
------------------------------- Fonts ----------------------------------------
|
||||||
|
|
||||||
|
@ -512,11 +533,26 @@ local style = require "core.style"
|
||||||
|
|
||||||
-- enable or disable plugin loading setting config entries:
|
-- enable or disable plugin loading setting config entries:
|
||||||
|
|
||||||
-- enable plugins.trimwhitespace, otherwise it is disable by default:
|
-- enable plugins.trimwhitespace, otherwise it is disabled by default:
|
||||||
-- config.plugins.trimwhitespace = true
|
-- config.plugins.trimwhitespace = true
|
||||||
--
|
--
|
||||||
-- disable detectindent, otherwise it is enabled by default
|
-- disable detectindent, otherwise it is enabled by default
|
||||||
-- config.plugins.detectindent = false
|
-- config.plugins.detectindent = false
|
||||||
|
|
||||||
|
---------------------------- Miscellaneous -------------------------------------
|
||||||
|
|
||||||
|
-- modify list of files to ignore when indexing the project:
|
||||||
|
-- config.ignore_files = {
|
||||||
|
-- -- folders
|
||||||
|
-- "^%.svn/", "^%.git/", "^%.hg/", "^CVS/", "^%.Trash/", "^%.Trash%-.*/",
|
||||||
|
-- "^node_modules/", "^%.cache/", "^__pycache__/",
|
||||||
|
-- -- files
|
||||||
|
-- "%.pyc$", "%.pyo$", "%.exe$", "%.dll$", "%.obj$", "%.o$",
|
||||||
|
-- "%.a$", "%.lib$", "%.so$", "%.dylib$", "%.ncb$", "%.sdf$",
|
||||||
|
-- "%.suo$", "%.pdb$", "%.idb$", "%.class$", "%.psd$", "%.db$",
|
||||||
|
-- "^desktop%.ini$", "^%.DS_Store$", "^%.directory$",
|
||||||
|
-- }
|
||||||
|
|
||||||
]])
|
]])
|
||||||
init_file:close()
|
init_file:close()
|
||||||
end
|
end
|
||||||
|
@ -558,7 +594,7 @@ local config = require "core.config"
|
||||||
-- "^/build.*/" match any top level directory whose name begins with "build"
|
-- "^/build.*/" match any top level directory whose name begins with "build"
|
||||||
-- "^/subprojects/.+/" 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 pre-project base 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
|
||||||
]])
|
]])
|
||||||
init_file:close()
|
init_file:close()
|
||||||
|
@ -640,6 +676,7 @@ function core.init()
|
||||||
command = require "core.command"
|
command = require "core.command"
|
||||||
keymap = require "core.keymap"
|
keymap = require "core.keymap"
|
||||||
dirwatch = require "core.dirwatch"
|
dirwatch = require "core.dirwatch"
|
||||||
|
ime = require "core.ime"
|
||||||
RootView = require "core.rootview"
|
RootView = require "core.rootview"
|
||||||
StatusView = require "core.statusview"
|
StatusView = require "core.statusview"
|
||||||
TitleView = require "core.titleview"
|
TitleView = require "core.titleview"
|
||||||
|
@ -1034,6 +1071,8 @@ end
|
||||||
|
|
||||||
function core.set_active_view(view)
|
function core.set_active_view(view)
|
||||||
assert(view, "Tried to set active view to nil")
|
assert(view, "Tried to set active view to nil")
|
||||||
|
-- Reset the IME even if the focus didn't change
|
||||||
|
ime.stop()
|
||||||
if view ~= core.active_view then
|
if view ~= core.active_view then
|
||||||
if core.active_view and core.active_view.force_focus then
|
if core.active_view and core.active_view.force_focus then
|
||||||
core.next_active_view = view
|
core.next_active_view = view
|
||||||
|
@ -1136,7 +1175,7 @@ function core.custom_log(level, show, backtrace, fmt, ...)
|
||||||
text = text,
|
text = text,
|
||||||
time = os.time(),
|
time = os.time(),
|
||||||
at = at,
|
at = at,
|
||||||
info = backtrace and debug.traceback(nil, 2):gsub("\t", "")
|
info = backtrace and debug.traceback("", 2):gsub("\t", "")
|
||||||
}
|
}
|
||||||
table.insert(core.log_items, item)
|
table.insert(core.log_items, item)
|
||||||
if #core.log_items > config.max_log_items then
|
if #core.log_items > config.max_log_items then
|
||||||
|
@ -1185,7 +1224,7 @@ function core.try(fn, ...)
|
||||||
local err
|
local err
|
||||||
local ok, res = xpcall(fn, function(msg)
|
local ok, res = xpcall(fn, function(msg)
|
||||||
local item = core.error("%s", msg)
|
local item = core.error("%s", msg)
|
||||||
item.info = debug.traceback(nil, 2):gsub("\t", "")
|
item.info = debug.traceback("", 2):gsub("\t", "")
|
||||||
err = msg
|
err = msg
|
||||||
end, ...)
|
end, ...)
|
||||||
if ok then
|
if ok then
|
||||||
|
@ -1198,6 +1237,8 @@ function core.on_event(type, ...)
|
||||||
local did_keymap = false
|
local did_keymap = false
|
||||||
if type == "textinput" then
|
if type == "textinput" then
|
||||||
core.root_view:on_text_input(...)
|
core.root_view:on_text_input(...)
|
||||||
|
elseif type == "textediting" then
|
||||||
|
ime.on_text_editing(...)
|
||||||
elseif type == "keypressed" then
|
elseif type == "keypressed" then
|
||||||
did_keymap = keymap.on_key_pressed(...)
|
did_keymap = keymap.on_key_pressed(...)
|
||||||
elseif type == "keyreleased" then
|
elseif type == "keyreleased" then
|
||||||
|
@ -1384,7 +1425,7 @@ function core.on_error(err)
|
||||||
-- write error to file
|
-- write error to file
|
||||||
local fp = io.open(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(nil, 4) .. "\n")
|
fp:write(debug.traceback("", 4) .. "\n")
|
||||||
fp:close()
|
fp:close()
|
||||||
-- save copy of all unsaved documents
|
-- save copy of all unsaved documents
|
||||||
for _, doc in ipairs(core.docs) do
|
for _, doc in ipairs(core.docs) do
|
||||||
|
|
|
@ -6,7 +6,7 @@ local function keymap_macos(keymap)
|
||||||
["cmd+n"] = "core:new-doc",
|
["cmd+n"] = "core:new-doc",
|
||||||
["cmd+shift+c"] = "core:change-project-folder",
|
["cmd+shift+c"] = "core:change-project-folder",
|
||||||
["cmd+shift+o"] = "core:open-project-folder",
|
["cmd+shift+o"] = "core:open-project-folder",
|
||||||
["cmd+shift+r"] = "core:restart",
|
["cmd+option+r"] = "core:restart",
|
||||||
["cmd+ctrl+return"] = "core:toggle-fullscreen",
|
["cmd+ctrl+return"] = "core:toggle-fullscreen",
|
||||||
|
|
||||||
["cmd+ctrl+shift+j"] = "root:split-left",
|
["cmd+ctrl+shift+j"] = "root:split-left",
|
||||||
|
@ -34,6 +34,8 @@ local function keymap_macos(keymap)
|
||||||
["cmd+8"] = "root:switch-to-tab-8",
|
["cmd+8"] = "root:switch-to-tab-8",
|
||||||
["cmd+9"] = "root:switch-to-tab-9",
|
["cmd+9"] = "root:switch-to-tab-9",
|
||||||
["wheel"] = "root:scroll",
|
["wheel"] = "root:scroll",
|
||||||
|
["hwheel"] = "root:horizontal-scroll",
|
||||||
|
["shift+hwheel"] = "root:horizontal-scroll",
|
||||||
|
|
||||||
["cmd+f"] = "find-replace:find",
|
["cmd+f"] = "find-replace:find",
|
||||||
["cmd+r"] = "find-replace:replace",
|
["cmd+r"] = "find-replace:replace",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
local core = require "core"
|
local core = require "core"
|
||||||
local command = require "core.command"
|
local command = require "core.command"
|
||||||
local config = require "core.config"
|
local config = require "core.config"
|
||||||
|
local ime = require "core.ime"
|
||||||
local keymap = {}
|
local keymap = {}
|
||||||
|
|
||||||
---@alias keymap.shortcut string
|
---@alias keymap.shortcut string
|
||||||
|
@ -179,6 +180,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
|
||||||
|
@ -209,9 +214,32 @@ function keymap.on_key_pressed(k, ...)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function keymap.on_mouse_wheel(delta, ...)
|
function keymap.on_mouse_wheel(delta_y, delta_x, ...)
|
||||||
return not (keymap.on_key_pressed("wheel" .. (delta > 0 and "up" or "down"), delta, ...)
|
local y_direction = delta_y > 0 and "up" or "down"
|
||||||
or keymap.on_key_pressed("wheel", delta, ...))
|
local x_direction = delta_x > 0 and "left" or "right"
|
||||||
|
-- Try sending a "cumulative" event for both scroll directions
|
||||||
|
if delta_y ~= 0 and delta_x ~= 0 then
|
||||||
|
local result = keymap.on_key_pressed("wheel" .. y_direction .. x_direction, delta_y, delta_x, ...)
|
||||||
|
if not result then
|
||||||
|
result = keymap.on_key_pressed("wheelyx", delta_y, delta_x, ...)
|
||||||
|
end
|
||||||
|
if result then return true end
|
||||||
|
end
|
||||||
|
-- Otherwise send each direction as its own separate event
|
||||||
|
local y_result, x_result
|
||||||
|
if delta_y ~= 0 then
|
||||||
|
y_result = keymap.on_key_pressed("wheel" .. y_direction, delta_y, ...)
|
||||||
|
if not y_result then
|
||||||
|
y_result = keymap.on_key_pressed("wheel", delta_y, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if delta_x ~= 0 then
|
||||||
|
x_result = keymap.on_key_pressed("wheel" .. x_direction, delta_x, ...)
|
||||||
|
if not x_result then
|
||||||
|
x_result = keymap.on_key_pressed("hwheel", delta_x, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return y_result or x_result
|
||||||
end
|
end
|
||||||
|
|
||||||
function keymap.on_mouse_pressed(button, x, y, clicks)
|
function keymap.on_mouse_pressed(button, x, y, clicks)
|
||||||
|
@ -274,6 +302,8 @@ keymap.add_direct {
|
||||||
["alt+8"] = "root:switch-to-tab-8",
|
["alt+8"] = "root:switch-to-tab-8",
|
||||||
["alt+9"] = "root:switch-to-tab-9",
|
["alt+9"] = "root:switch-to-tab-9",
|
||||||
["wheel"] = "root:scroll",
|
["wheel"] = "root:scroll",
|
||||||
|
["hwheel"] = "root:horizontal-scroll",
|
||||||
|
["shift+wheel"] = "root:horizontal-scroll",
|
||||||
|
|
||||||
["ctrl+f"] = "find-replace:find",
|
["ctrl+f"] = "find-replace:find",
|
||||||
["ctrl+r"] = "find-replace:replace",
|
["ctrl+r"] = "find-replace:replace",
|
||||||
|
|
|
@ -323,15 +323,14 @@ end
|
||||||
function Node:get_scroll_button_rect(index)
|
function Node:get_scroll_button_rect(index)
|
||||||
local w, pad = get_scroll_button_width()
|
local w, pad = get_scroll_button_width()
|
||||||
local h = style.font:get_height() + style.padding.y * 2
|
local h = style.font:get_height() + style.padding.y * 2
|
||||||
local x = self.position.x + (index == 1 and 0 or self.size.x - w)
|
local x = self.position.x + (index == 1 and self.size.x - w * 2 or self.size.x - w)
|
||||||
return x, self.position.y, w, h, pad
|
return x, self.position.y, w, h, pad
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Node:get_tab_rect(idx)
|
function Node:get_tab_rect(idx)
|
||||||
local sbw = get_scroll_button_width()
|
local maxw = self.size.x
|
||||||
local maxw = self.size.x - 2 * sbw
|
local x0 = self.position.x
|
||||||
local x0 = self.position.x + sbw
|
|
||||||
local x1 = x0 + common.clamp(self.tab_width * (idx - 1) - self.tab_shift, 0, maxw)
|
local x1 = x0 + common.clamp(self.tab_width * (idx - 1) - self.tab_shift, 0, maxw)
|
||||||
local x2 = x0 + common.clamp(self.tab_width * idx - self.tab_shift, 0, maxw)
|
local x2 = x0 + common.clamp(self.tab_width * idx - self.tab_shift, 0, maxw)
|
||||||
local h = style.font:get_height() + style.padding.y * 2
|
local h = style.font:get_height() + style.padding.y * 2
|
||||||
|
@ -469,7 +468,10 @@ end
|
||||||
|
|
||||||
function Node:target_tab_width()
|
function Node:target_tab_width()
|
||||||
local n = self:get_visible_tabs_number()
|
local n = self:get_visible_tabs_number()
|
||||||
local w = self.size.x - get_scroll_button_width() * 2
|
local w = self.size.x
|
||||||
|
if #self.views > n then
|
||||||
|
w = self.size.x - get_scroll_button_width() * 2
|
||||||
|
end
|
||||||
return common.clamp(style.tab_width, w / config.max_tabs, w / n)
|
return common.clamp(style.tab_width, w / config.max_tabs, w / n)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -547,24 +549,14 @@ function Node:draw_tab(view, is_active, is_hovered, is_close_hovered, x, y, w, h
|
||||||
end
|
end
|
||||||
|
|
||||||
function Node:draw_tabs()
|
function Node:draw_tabs()
|
||||||
local x, y, w, h, scroll_padding = self:get_scroll_button_rect(1)
|
local _, y, w, h, scroll_padding = self:get_scroll_button_rect(1)
|
||||||
|
local x = self.position.x
|
||||||
local ds = style.divider_size
|
local ds = style.divider_size
|
||||||
local dots_width = style.font:get_width("…")
|
local dots_width = style.font:get_width("…")
|
||||||
core.push_clip_rect(x, y, self.size.x, h)
|
core.push_clip_rect(x, y, self.size.x, h)
|
||||||
renderer.draw_rect(x, y, self.size.x, h, style.background2)
|
renderer.draw_rect(x, y, self.size.x, h, style.background2)
|
||||||
renderer.draw_rect(x, y + h - ds, self.size.x, ds, style.divider)
|
renderer.draw_rect(x, y + h - ds, self.size.x, ds, style.divider)
|
||||||
|
|
||||||
if self.tab_offset > 1 then
|
|
||||||
local button_style = self.hovered_scroll_button == 1 and style.text or style.dim
|
|
||||||
common.draw_text(style.icon_font, button_style, "<", nil, x + scroll_padding, y, 0, h)
|
|
||||||
end
|
|
||||||
|
|
||||||
local tabs_number = self:get_visible_tabs_number()
|
local tabs_number = self:get_visible_tabs_number()
|
||||||
if #self.views > self.tab_offset + tabs_number - 1 then
|
|
||||||
local xrb, yrb, wrb = self:get_scroll_button_rect(2)
|
|
||||||
local button_style = self.hovered_scroll_button == 2 and style.text or style.dim
|
|
||||||
common.draw_text(style.icon_font, button_style, ">", nil, xrb + scroll_padding, yrb, 0, h)
|
|
||||||
end
|
|
||||||
|
|
||||||
for i = self.tab_offset, self.tab_offset + tabs_number - 1 do
|
for i = self.tab_offset, self.tab_offset + tabs_number - 1 do
|
||||||
local view = self.views[i]
|
local view = self.views[i]
|
||||||
|
@ -574,6 +566,18 @@ function Node:draw_tabs()
|
||||||
x, y, w, h)
|
x, y, w, h)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if #self.views > tabs_number then
|
||||||
|
local _, pad = get_scroll_button_width()
|
||||||
|
local xrb, yrb, wrb, hrb = self:get_scroll_button_rect(1)
|
||||||
|
renderer.draw_rect(xrb + pad, yrb, wrb * 2, hrb, style.background2)
|
||||||
|
local left_button_style = (self.hovered_scroll_button == 1 and self.tab_offset > 1) and style.text or style.dim
|
||||||
|
common.draw_text(style.icon_font, left_button_style, "<", nil, xrb + scroll_padding, yrb, 0, h)
|
||||||
|
|
||||||
|
xrb, yrb, wrb = self:get_scroll_button_rect(2)
|
||||||
|
local right_button_style = (self.hovered_scroll_button == 2 and #self.views > self.tab_offset + tabs_number - 1) and style.text or style.dim
|
||||||
|
common.draw_text(style.icon_font, right_button_style, ">", nil, xrb + scroll_padding, yrb, 0, h)
|
||||||
|
end
|
||||||
|
|
||||||
core.pop_clip_rect()
|
core.pop_clip_rect()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,70 +2,81 @@
|
||||||
-- pattern:gsub(string).
|
-- pattern:gsub(string).
|
||||||
regex.__index = function(table, key) return regex[key]; end
|
regex.__index = function(table, key) return regex[key]; end
|
||||||
|
|
||||||
regex.match = function(pattern_string, string, offset, options)
|
---Looks for the first match of `pattern` in the string `str`.
|
||||||
local pattern = type(pattern_string) == "table" and
|
---If it finds a match, it returns the indices of `str` where this occurrence
|
||||||
pattern_string or regex.compile(pattern_string)
|
---starts and ends; otherwise, it returns `nil`.
|
||||||
local res = { regex.cmatch(pattern, string, offset or 1, options or 0) }
|
---If the pattern has captures, the captured start and end indexes are returned,
|
||||||
res[2] = res[2] and res[2] - 1
|
---after the two initial ones.
|
||||||
|
---
|
||||||
|
---@param pattern string|table The regex pattern to use, either as a simple string or precompiled.
|
||||||
|
---@param str string The string to search for valid matches.
|
||||||
|
---@param offset? integer The position on the subject to start searching.
|
||||||
|
---@param options? integer A bit field of matching options, eg: regex.NOTBOL | regex.NOTEMPTY
|
||||||
|
---
|
||||||
|
---@return integer? start Offset where the first match was found; `nil` if no match.
|
||||||
|
---@return integer? end Offset where the first match ends; `nil` if no match.
|
||||||
|
---@return integer? ... #Captured matches offsets.
|
||||||
|
regex.find_offsets = function(pattern, str, offset, options)
|
||||||
|
if type(pattern) ~= "table" then
|
||||||
|
pattern = regex.compile(pattern)
|
||||||
|
end
|
||||||
|
local res = { regex.cmatch(pattern, str, offset or 1, options or 0) }
|
||||||
|
-- Reduce every end delimiter by 1
|
||||||
|
for i = 2,#res,2 do
|
||||||
|
res[i] = res[i] - 1
|
||||||
|
end
|
||||||
return table.unpack(res)
|
return table.unpack(res)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Will iterate back through any UTF-8 bytes so that we don't replace bits
|
---Behaves like `string.match`.
|
||||||
-- mid character.
|
---Looks for the first match of `pattern` in the string `str`.
|
||||||
local function previous_character(str, index)
|
---If it finds a match, it returns the matched string; otherwise, it returns `nil`.
|
||||||
local byte
|
---If the pattern has captures, only the captured strings are returned.
|
||||||
repeat
|
---If a capture is empty, its offset is returned instead.
|
||||||
index = index - 1
|
---
|
||||||
byte = string.byte(str, index)
|
---@param pattern string|table The regex pattern to use, either as a simple string or precompiled.
|
||||||
until byte < 128 or byte >= 192
|
---@param str string The string to search for valid matches.
|
||||||
return index
|
---@param offset? integer The position on the subject to start searching.
|
||||||
|
---@param options? integer A bit field of matching options, eg: regex.NOTBOL | regex.NOTEMPTY
|
||||||
|
---
|
||||||
|
---@return (string|integer)? ... #List of captured matches; the entire match if no matches were specified; if the match is empty, its offset is returned instead.
|
||||||
|
regex.match = function(pattern, str, offset, options)
|
||||||
|
local res = { regex.find(pattern, str, offset, options) }
|
||||||
|
if #res == 0 then return end
|
||||||
|
-- If available, only return captures
|
||||||
|
if #res > 2 then return table.unpack(res, 3) end
|
||||||
|
return string.sub(str, res[1], res[2])
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Moves to the end of the identified character.
|
---Behaves like `string.find`.
|
||||||
local function end_character(str, index)
|
---Looks for the first match of `pattern` in the string `str`.
|
||||||
local byte = string.byte(str, index + 1)
|
---If it finds a match, it returns the indices of `str` where this occurrence
|
||||||
while byte and byte >= 128 and byte < 192 do
|
---starts and ends; otherwise, it returns `nil`.
|
||||||
index = index + 1
|
---If the pattern has captures, the captured strings are returned,
|
||||||
byte = string.byte(str, index + 1)
|
---after the two indexes ones.
|
||||||
end
|
---If a capture is empty, its offset is returned instead.
|
||||||
return index
|
---
|
||||||
end
|
---@param pattern string|table The regex pattern to use, either as a simple string or precompiled.
|
||||||
|
---@param str string The string to search for valid matches.
|
||||||
-- Build off matching. For now, only support basic replacements, but capture
|
---@param offset? integer The position on the subject to start searching.
|
||||||
-- groupings should be doable. We can even have custom group replacements and
|
---@param options? integer A bit field of matching options, eg: regex.NOTBOL | regex.NOTEMPTY
|
||||||
-- transformations and stuff in lua. Currently, this takes group replacements
|
---
|
||||||
-- as \1 - \9.
|
---@return integer? start Offset where the first match was found; `nil` if no match.
|
||||||
-- Should work on UTF-8 text.
|
---@return integer? end Offset where the first match ends; `nil` if no match.
|
||||||
regex.gsub = function(pattern_string, str, replacement)
|
---@return (string|integer)? ... #List of captured matches; if the match is empty, its offset is returned instead.
|
||||||
local pattern = type(pattern_string) == "table" and
|
regex.find = function(pattern, str, offset, options)
|
||||||
pattern_string or regex.compile(pattern_string)
|
local res = { regex.find_offsets(pattern, str, offset, options) }
|
||||||
local result, indices = ""
|
local out = { }
|
||||||
local matches, replacements = {}, {}
|
if #res == 0 then return end
|
||||||
repeat
|
out[1] = res[1]
|
||||||
indices = { regex.cmatch(pattern, str) }
|
out[2] = res[2]
|
||||||
if #indices > 0 then
|
for i = 3,#res,2 do
|
||||||
table.insert(matches, indices)
|
if res[i] > res[i+1] then
|
||||||
local currentReplacement = replacement
|
-- Like in string.find, if the group has size 0, return the index
|
||||||
if #indices > 2 then
|
table.insert(out, res[i])
|
||||||
for i = 1, (#indices/2 - 1) do
|
|
||||||
currentReplacement = string.gsub(
|
|
||||||
currentReplacement,
|
|
||||||
"\\" .. i,
|
|
||||||
str:sub(indices[i*2+1], end_character(str,indices[i*2+2]-1))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
currentReplacement = string.gsub(currentReplacement, "\\%d", "")
|
|
||||||
table.insert(replacements, { indices[1], #currentReplacement+indices[1] })
|
|
||||||
if indices[1] > 1 then
|
|
||||||
result = result ..
|
|
||||||
str:sub(1, previous_character(str, indices[1])) .. currentReplacement
|
|
||||||
else
|
else
|
||||||
result = result .. currentReplacement
|
table.insert(out, string.sub(str, res[i], res[i+1]))
|
||||||
end
|
end
|
||||||
str = str:sub(indices[2])
|
|
||||||
end
|
end
|
||||||
until #indices == 0 or indices[1] == indices[2]
|
return table.unpack(out)
|
||||||
return result .. str, matches, replacements
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -334,6 +334,9 @@ function RootView:on_text_input(...)
|
||||||
core.active_view:on_text_input(...)
|
core.active_view:on_text_input(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function RootView:on_ime_text_editing(...)
|
||||||
|
core.active_view:on_ime_text_editing(...)
|
||||||
|
end
|
||||||
|
|
||||||
function RootView:on_focus_lost(...)
|
function RootView:on_focus_lost(...)
|
||||||
-- We force a redraw so documents can redraw without the cursor.
|
-- We force a redraw so documents can redraw without the cursor.
|
||||||
|
|
|
@ -0,0 +1,342 @@
|
||||||
|
local core = require "core"
|
||||||
|
local common = require "core.common"
|
||||||
|
local config = require "core.config"
|
||||||
|
local style = require "core.style"
|
||||||
|
local Object = require "core.object"
|
||||||
|
|
||||||
|
---Scrollbar
|
||||||
|
---Use Scrollbar:set_size to set the bounding box of the view the scrollbar belongs to.
|
||||||
|
---Use Scrollbar:update to update the scrollbar animations.
|
||||||
|
---Use Scrollbar:draw to draw the scrollbar.
|
||||||
|
---Use Scrollbar:on_mouse_pressed, Scrollbar:on_mouse_released,
|
||||||
|
---Scrollbar:on_mouse_moved and Scrollbar:on_mouse_left to react to mouse movements;
|
||||||
|
---the scrollbar won't update automatically.
|
||||||
|
---Use Scrollbar:set_percent to set the scrollbar location externally.
|
||||||
|
---
|
||||||
|
---To manage all the orientations, the scrollbar changes the coordinates system
|
||||||
|
---accordingly. The "normal" coordinate system adapts the scrollbar coordinates
|
||||||
|
---as if it's always a vertical scrollbar, positioned at the end of the bounding box.
|
||||||
|
---@class core.scrollbar : core.object
|
||||||
|
local Scrollbar = Object:extend()
|
||||||
|
|
||||||
|
---@class ScrollbarOptions
|
||||||
|
---@field direction "v" | "h" @Vertical or Horizontal
|
||||||
|
---@field alignment "s" | "e" @Start or End (left to right, top to bottom)
|
||||||
|
---@field force_status "expanded" | "contracted" | false @Force the scrollbar status
|
||||||
|
---@field expanded_size number? @Override the default value specified by `style.expanded_scrollbar_size`
|
||||||
|
---@field contracted_size number? @Override the default value specified by `style.scrollbar_size`
|
||||||
|
|
||||||
|
---@param options ScrollbarOptions
|
||||||
|
function Scrollbar:new(options)
|
||||||
|
---Position information of the owner
|
||||||
|
self.rect = {
|
||||||
|
x = 0, y = 0, w = 0, h = 0,
|
||||||
|
---Scrollable size
|
||||||
|
scrollable = 0
|
||||||
|
}
|
||||||
|
self.normal_rect = {
|
||||||
|
across = 0,
|
||||||
|
along = 0,
|
||||||
|
across_size = 0,
|
||||||
|
along_size = 0,
|
||||||
|
scrollable = 0
|
||||||
|
}
|
||||||
|
---@type integer @Position in percent [0-1]
|
||||||
|
self.percent = 0
|
||||||
|
---@type boolean @Scrollbar dragging status
|
||||||
|
self.dragging = false
|
||||||
|
---@type integer @Private. Used to offset the start of the drag from the top of the thumb
|
||||||
|
self.drag_start_offset = 0
|
||||||
|
---What is currently being hovered. `thumb` implies` track`
|
||||||
|
self.hovering = { track = false, thumb = false }
|
||||||
|
---@type "v" | "h"@Vertical or Horizontal
|
||||||
|
self.direction = options.direction or "v"
|
||||||
|
---@type "s" | "e" @Start or End (left to right, top to bottom)
|
||||||
|
self.alignment = options.alignment or "e"
|
||||||
|
---@type number @Private. Used to keep track of animations
|
||||||
|
self.expand_percent = 0
|
||||||
|
---@type "expanded" | "contracted" | false @Force the scrollbar status
|
||||||
|
self.force_status = options.force_status
|
||||||
|
self:set_forced_status(options.force_status)
|
||||||
|
---@type number? @Override the default value specified by `style.expanded_scrollbar_size`
|
||||||
|
self.contracted_size = options.contracted_size
|
||||||
|
---@type number? @Override the default value specified by `style.scrollbar_size`
|
||||||
|
self.expanded_size = options.expanded_size
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Set the status the scrollbar is forced to keep
|
||||||
|
---@param status "expanded" | "contracted" | false @The status to force
|
||||||
|
function Scrollbar:set_forced_status(status)
|
||||||
|
self.force_status = status
|
||||||
|
if self.force_status == "expanded" then
|
||||||
|
self.expand_percent = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Scrollbar:real_to_normal(x, y, w, h)
|
||||||
|
x, y, w, h = x or 0, y or 0, w or 0, h or 0
|
||||||
|
if self.direction == "v" then
|
||||||
|
if self.alignment == "s" then
|
||||||
|
x = (self.rect.x + self.rect.w) - x - w
|
||||||
|
end
|
||||||
|
return x, y, w, h
|
||||||
|
else
|
||||||
|
if self.alignment == "s" then
|
||||||
|
y = (self.rect.y + self.rect.h) - y - h
|
||||||
|
end
|
||||||
|
return y, x, h, w
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Scrollbar:normal_to_real(x, y, w, h)
|
||||||
|
x, y, w, h = x or 0, y or 0, w or 0, h or 0
|
||||||
|
if self.direction == "v" then
|
||||||
|
if self.alignment == "s" then
|
||||||
|
x = (self.rect.x + self.rect.w) - x - w
|
||||||
|
end
|
||||||
|
return x, y, w, h
|
||||||
|
else
|
||||||
|
if self.alignment == "s" then
|
||||||
|
x = (self.rect.y + self.rect.h) - x - w
|
||||||
|
end
|
||||||
|
return y, x, h, w
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Scrollbar:_get_thumb_rect_normal()
|
||||||
|
local nr = self.normal_rect
|
||||||
|
local sz = nr.scrollable
|
||||||
|
if sz == math.huge or sz <= nr.along_size
|
||||||
|
then
|
||||||
|
return 0, 0, 0, 0
|
||||||
|
end
|
||||||
|
local scrollbar_size = self.contracted_size or style.scrollbar_size
|
||||||
|
local expanded_scrollbar_size = self.expanded_size or style.expanded_scrollbar_size
|
||||||
|
local along_size = math.max(20, nr.along_size * nr.along_size / sz)
|
||||||
|
local across_size = scrollbar_size
|
||||||
|
across_size = across_size + (expanded_scrollbar_size - scrollbar_size) * self.expand_percent
|
||||||
|
return
|
||||||
|
nr.across + nr.across_size - across_size,
|
||||||
|
nr.along + self.percent * nr.scrollable * (nr.along_size - along_size) / (sz - nr.along_size),
|
||||||
|
across_size,
|
||||||
|
along_size
|
||||||
|
end
|
||||||
|
|
||||||
|
---Get the thumb rect (the part of the scrollbar that can be dragged)
|
||||||
|
---@return integer,integer,integer,integer @x, y, w, h
|
||||||
|
function Scrollbar:get_thumb_rect()
|
||||||
|
return self:normal_to_real(self:_get_thumb_rect_normal())
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Scrollbar:_get_track_rect_normal()
|
||||||
|
local nr = self.normal_rect
|
||||||
|
local sz = nr.scrollable
|
||||||
|
if sz <= nr.along_size or sz == math.huge then
|
||||||
|
return 0, 0, 0, 0
|
||||||
|
end
|
||||||
|
local scrollbar_size = self.contracted_size or style.scrollbar_size
|
||||||
|
local expanded_scrollbar_size = self.expanded_size or style.expanded_scrollbar_size
|
||||||
|
local across_size = scrollbar_size
|
||||||
|
across_size = across_size + (expanded_scrollbar_size - scrollbar_size) * self.expand_percent
|
||||||
|
return
|
||||||
|
nr.across + nr.across_size - across_size,
|
||||||
|
nr.along,
|
||||||
|
across_size,
|
||||||
|
nr.along_size
|
||||||
|
end
|
||||||
|
|
||||||
|
---Get the track rect (the "background" of the scrollbar)
|
||||||
|
---@return number,number,number,number @x, y, w, h
|
||||||
|
function Scrollbar:get_track_rect()
|
||||||
|
return self:normal_to_real(self:_get_track_rect_normal())
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Scrollbar:_overlaps_normal(x, y)
|
||||||
|
local sx, sy, sw, sh = self:_get_thumb_rect_normal()
|
||||||
|
local scrollbar_size = self.contracted_size or style.scrollbar_size
|
||||||
|
local result
|
||||||
|
if x >= sx - scrollbar_size * 3 and x <= sx + sw and y >= sy and y <= sy + sh then
|
||||||
|
result = "thumb"
|
||||||
|
else
|
||||||
|
sx, sy, sw, sh = self:_get_track_rect_normal()
|
||||||
|
if x >= sx - scrollbar_size * 3 and x <= sx + sw and y >= sy and y <= sy + sh then
|
||||||
|
result = "track"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
---Get what part of the scrollbar the coordinates overlap
|
||||||
|
---@return "thumb"|"track"|nil
|
||||||
|
function Scrollbar:overlaps(x, y)
|
||||||
|
x, y = self:real_to_normal(x, y)
|
||||||
|
return self:_overlaps_normal(x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Scrollbar:_on_mouse_pressed_normal(button, x, y, clicks)
|
||||||
|
local overlaps = self:_overlaps_normal(x, y)
|
||||||
|
if overlaps then
|
||||||
|
local _, along, _, along_size = self:_get_thumb_rect_normal()
|
||||||
|
self.dragging = true
|
||||||
|
if overlaps == "thumb" then
|
||||||
|
self.drag_start_offset = along - y
|
||||||
|
return true
|
||||||
|
elseif overlaps == "track" then
|
||||||
|
self.drag_start_offset = - along_size / 2
|
||||||
|
return (y - self.normal_rect.along - along_size / 2) / self.normal_rect.along_size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Updates the scrollbar with mouse pressed info.
|
||||||
|
---Won't update the scrollbar position automatically.
|
||||||
|
---Use Scrollbar:set_percent to update it.
|
||||||
|
---
|
||||||
|
---This sets the dragging status if needed.
|
||||||
|
---
|
||||||
|
---Returns a falsy value if the event happened outside the scrollbar.
|
||||||
|
---Returns `true` if the thumb was pressed.
|
||||||
|
---If the track was pressed this returns a value between 0 and 1
|
||||||
|
---representing the percent of the position.
|
||||||
|
---@return boolean|number
|
||||||
|
function Scrollbar:on_mouse_pressed(button, x, y, clicks)
|
||||||
|
if button ~= "left" then return end
|
||||||
|
x, y = self:real_to_normal(x, y)
|
||||||
|
return self:_on_mouse_pressed_normal(button, x, y, clicks)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Updates the scrollbar hover status.
|
||||||
|
---This gets called by other functions and shouldn't be called manually
|
||||||
|
function Scrollbar:_update_hover_status_normal(x, y)
|
||||||
|
local overlaps = self:_overlaps_normal(x, y)
|
||||||
|
self.hovering.thumb = overlaps == "thumb"
|
||||||
|
self.hovering.track = self.hovering.thumb or overlaps == "track"
|
||||||
|
return self.hovering.track or self.hovering.thumb
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scrollbar:_on_mouse_released_normal(button, x, y)
|
||||||
|
self.dragging = false
|
||||||
|
return self:_update_hover_status_normal(x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Updates the scrollbar dragging status
|
||||||
|
function Scrollbar:on_mouse_released(button, x, y)
|
||||||
|
if button ~= "left" then return end
|
||||||
|
x, y = self:real_to_normal(x, y)
|
||||||
|
return self:_on_mouse_released_normal(button, x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Scrollbar:_on_mouse_moved_normal(x, y, dx, dy)
|
||||||
|
if self.dragging then
|
||||||
|
local nr = self.normal_rect
|
||||||
|
return common.clamp((y - nr.along + self.drag_start_offset) / nr.along_size, 0, 1)
|
||||||
|
end
|
||||||
|
return self:_update_hover_status_normal(x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Updates the scrollbar with mouse moved info.
|
||||||
|
---Won't update the scrollbar position automatically.
|
||||||
|
---Use Scrollbar:set_percent to update it.
|
||||||
|
---
|
||||||
|
---This updates the hovering status.
|
||||||
|
---
|
||||||
|
---Returns a falsy value if the event happened outside the scrollbar.
|
||||||
|
---Returns `true` if the scrollbar is hovered.
|
||||||
|
---If the scrollbar was being dragged, this returns a value between 0 and 1
|
||||||
|
---representing the percent of the position.
|
||||||
|
---@return boolean|number
|
||||||
|
function Scrollbar:on_mouse_moved(x, y, dx, dy)
|
||||||
|
x, y = self:real_to_normal(x, y)
|
||||||
|
dx, dy = self:real_to_normal(dx, dy) -- TODO: do we need this? (is this even correct?)
|
||||||
|
return self:_on_mouse_moved_normal(x, y, dx, dy)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Updates the scrollbar hovering status
|
||||||
|
function Scrollbar:on_mouse_left()
|
||||||
|
self.hovering.track, self.hovering.thumb = false, false
|
||||||
|
end
|
||||||
|
|
||||||
|
---Updates the bounding box of the view the scrollbar belongs to.
|
||||||
|
---@param x number
|
||||||
|
---@param y number
|
||||||
|
---@param w number
|
||||||
|
---@param h number
|
||||||
|
---@param scrollable number @size of the scrollable area
|
||||||
|
function Scrollbar:set_size(x, y, w, h, scrollable)
|
||||||
|
self.rect.x, self.rect.y, self.rect.w, self.rect.h = x, y, w, h
|
||||||
|
self.rect.scrollable = scrollable
|
||||||
|
|
||||||
|
local nr = self.normal_rect
|
||||||
|
nr.across, nr.along, nr.across_size, nr.along_size = self:real_to_normal(x, y, w, h)
|
||||||
|
nr.scrollable = scrollable
|
||||||
|
end
|
||||||
|
|
||||||
|
---Updates the scrollbar location
|
||||||
|
---@param percent number @number between 0 and 1 representing the position of the middle part of the thumb
|
||||||
|
function Scrollbar:set_percent(percent)
|
||||||
|
self.percent = percent
|
||||||
|
end
|
||||||
|
|
||||||
|
---Updates the scrollbar animations
|
||||||
|
function Scrollbar:update()
|
||||||
|
-- TODO: move the animation code to its own class
|
||||||
|
if not self.force_status then
|
||||||
|
local dest = (self.hovering.track or self.dragging) and 1 or 0
|
||||||
|
local diff = math.abs(self.expand_percent - dest)
|
||||||
|
if not config.transitions or diff < 0.05 or config.disabled_transitions["scroll"] then
|
||||||
|
self.expand_percent = dest
|
||||||
|
else
|
||||||
|
local rate = 0.3
|
||||||
|
if config.fps ~= 60 or config.animation_rate ~= 1 then
|
||||||
|
local dt = 60 / config.fps
|
||||||
|
rate = 1 - common.clamp(1 - rate, 1e-8, 1 - 1e-8)^(config.animation_rate * dt)
|
||||||
|
end
|
||||||
|
self.expand_percent = common.lerp(self.expand_percent, dest, rate)
|
||||||
|
end
|
||||||
|
if diff > 1e-8 then
|
||||||
|
core.redraw = true
|
||||||
|
end
|
||||||
|
elseif self.force_status == "expanded" then
|
||||||
|
self.expand_percent = 1
|
||||||
|
elseif self.force_status == "contracted" then
|
||||||
|
self.expand_percent = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---Draw the scrollbar track
|
||||||
|
function Scrollbar:draw_track()
|
||||||
|
if not (self.hovering.track or self.dragging)
|
||||||
|
and self.expand_percent == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local color = { table.unpack(style.scrollbar_track) }
|
||||||
|
color[4] = color[4] * self.expand_percent
|
||||||
|
local x, y, w, h = self:get_track_rect()
|
||||||
|
renderer.draw_rect(x, y, w, h, color)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Draw the scrollbar thumb
|
||||||
|
function Scrollbar:draw_thumb()
|
||||||
|
local highlight = self.hovering.thumb or self.dragging
|
||||||
|
local color = highlight and style.scrollbar2 or style.scrollbar
|
||||||
|
local x, y, w, h = self:get_thumb_rect()
|
||||||
|
renderer.draw_rect(x, y, w, h, color)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Draw both the scrollbar track and thumb
|
||||||
|
function Scrollbar:draw()
|
||||||
|
self:draw_track()
|
||||||
|
self:draw_thumb()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return Scrollbar
|
|
@ -10,11 +10,12 @@ if MACOS_RESOURCES then
|
||||||
DATADIR = MACOS_RESOURCES
|
DATADIR = MACOS_RESOURCES
|
||||||
else
|
else
|
||||||
local prefix = EXEDIR:match("^(.+)[/\\]bin$")
|
local prefix = EXEDIR:match("^(.+)[/\\]bin$")
|
||||||
DATADIR = prefix and (prefix .. '/share/lite-xl') or (EXEDIR .. '/data')
|
DATADIR = prefix and (prefix .. PATHSEP .. 'share' .. PATHSEP .. 'lite-xl') or (EXEDIR .. PATHSEP .. 'data')
|
||||||
end
|
end
|
||||||
USERDIR = (system.get_file_info(EXEDIR .. '/user') and (EXEDIR .. '/user'))
|
USERDIR = (system.get_file_info(EXEDIR .. PATHSEP .. 'user') and (EXEDIR .. PATHSEP .. 'user'))
|
||||||
or ((os.getenv("XDG_CONFIG_HOME") and os.getenv("XDG_CONFIG_HOME") .. "/lite-xl"))
|
or os.getenv("LITE_USERDIR")
|
||||||
or (HOME and (HOME .. '/.config/lite-xl'))
|
or ((os.getenv("XDG_CONFIG_HOME") and os.getenv("XDG_CONFIG_HOME") .. PATHSEP .. "lite-xl"))
|
||||||
|
or (HOME and (HOME .. PATHSEP .. '.config' .. PATHSEP .. 'lite-xl'))
|
||||||
|
|
||||||
package.path = DATADIR .. '/?.lua;'
|
package.path = DATADIR .. '/?.lua;'
|
||||||
package.path = DATADIR .. '/?/init.lua;' .. package.path
|
package.path = DATADIR .. '/?/init.lua;' .. package.path
|
||||||
|
|
|
@ -11,26 +11,27 @@ local Object = require "core.object"
|
||||||
|
|
||||||
|
|
||||||
---@alias core.statusview.styledtext table<integer, renderer.font|renderer.color|string>
|
---@alias core.statusview.styledtext table<integer, renderer.font|renderer.color|string>
|
||||||
|
---@alias core.statusview.position '"left"' | '"right"'
|
||||||
|
|
||||||
---A status bar implementation for lite, check core.status_view.
|
---A status bar implementation for lite, check core.status_view.
|
||||||
---@class core.statusview : core.view
|
---@class core.statusview : core.view
|
||||||
---@field public super core.view
|
---@field super core.view
|
||||||
---@field private items core.statusview.item[]
|
---@field items core.statusview.item[]
|
||||||
---@field private active_items core.statusview.item[]
|
---@field active_items core.statusview.item[]
|
||||||
---@field private hovered_item core.statusview.item
|
---@field hovered_item core.statusview.item
|
||||||
---@field private message_timeout number
|
---@field message_timeout number
|
||||||
---@field private message core.statusview.styledtext
|
---@field message core.statusview.styledtext
|
||||||
---@field private tooltip_mode boolean
|
---@field tooltip_mode boolean
|
||||||
---@field private tooltip core.statusview.styledtext
|
---@field tooltip core.statusview.styledtext
|
||||||
---@field private left_width number
|
---@field left_width number
|
||||||
---@field private right_width number
|
---@field right_width number
|
||||||
---@field private r_left_width number
|
---@field r_left_width number
|
||||||
---@field private r_right_width number
|
---@field r_right_width number
|
||||||
---@field private left_xoffset number
|
---@field left_xoffset number
|
||||||
---@field private right_xoffset number
|
---@field right_xoffset number
|
||||||
---@field private dragged_panel '"left"' | '"right"'
|
---@field dragged_panel '""' | core.statusview.position
|
||||||
---@field private hovered_panel '"left"' | '"right"'
|
---@field hovered_panel '""' | core.statusview.position
|
||||||
---@field private hide_messages boolean
|
---@field hide_messages boolean
|
||||||
local StatusView = View:extend()
|
local StatusView = View:extend()
|
||||||
|
|
||||||
---Space separator
|
---Space separator
|
||||||
|
@ -42,81 +43,73 @@ StatusView.separator = " "
|
||||||
StatusView.separator2 = " | "
|
StatusView.separator2 = " | "
|
||||||
|
|
||||||
---@alias core.statusview.item.separator
|
---@alias core.statusview.item.separator
|
||||||
---|>'core.statusview.separator' # Space separator
|
---|>`StatusView.separator`
|
||||||
---| 'core.statusview.separator2' # Pipe separator
|
---| `StatusView.separator2`
|
||||||
|
|
||||||
---@alias core.statusview.item.predicate fun():boolean
|
---@alias core.statusview.item.predicate fun():boolean
|
||||||
---@alias core.statusview.item.onclick fun(button: string, x: number, y: number)
|
---@alias core.statusview.item.onclick fun(button: string, x: number, y: number)
|
||||||
---@alias core.statusview.item.get_item fun():core.statusview.styledtext,core.statusview.styledtext
|
---@alias core.statusview.item.get_item fun(self: core.statusview.item):core.statusview.styledtext?,core.statusview.styledtext?
|
||||||
---@alias core.statusview.item.ondraw fun(x, y, h, hovered: boolean, calc_only?: boolean):number
|
---@alias core.statusview.item.ondraw fun(x, y, h, hovered: boolean, calc_only?: boolean):number
|
||||||
|
|
||||||
---@class core.statusview.item : core.object
|
---@class core.statusview.item : core.object
|
||||||
---@field name string
|
---@field name string
|
||||||
---@field predicate core.statusview.item.predicate
|
---@field predicate core.statusview.item.predicate
|
||||||
---@field alignment core.statusview.item.alignment
|
---@field alignment core.statusview.item.alignment
|
||||||
---@field tooltip string | nil
|
---@field tooltip string
|
||||||
---@field command string | nil @Command to perform when the item is clicked.
|
---@field command string | nil @Command to perform when the item is clicked.
|
||||||
---@field on_click core.statusview.item.onclick | nil @Function called when item is clicked and no command is set.
|
---Function called when item is clicked and no command is set.
|
||||||
---@field on_draw core.statusview.item.ondraw | nil @Custom drawing that when passed calc true should return the needed width for drawing and when false should draw.
|
---@field on_click core.statusview.item.onclick | nil
|
||||||
|
---Custom drawing that when passed calc true should return the needed width for
|
||||||
|
---drawing and when false should draw.
|
||||||
|
---@field on_draw core.statusview.item.ondraw | nil
|
||||||
---@field background_color renderer.color | nil
|
---@field background_color renderer.color | nil
|
||||||
---@field background_color_hover renderer.color | nil
|
---@field background_color_hover renderer.color | nil
|
||||||
---@field visible boolean
|
---@field visible boolean
|
||||||
---@field separator core.statusview.item.separator
|
---@field separator core.statusview.item.separator
|
||||||
---@field private active boolean
|
---@field active boolean
|
||||||
---@field private x number
|
---@field x number
|
||||||
---@field private w number
|
---@field w number
|
||||||
---@field private cached_item core.statusview.styledtext
|
---@field cached_item core.statusview.styledtext
|
||||||
local StatusViewItem = Object:extend()
|
local StatusViewItem = Object:extend()
|
||||||
|
|
||||||
|
|
||||||
---Available StatusViewItem options.
|
---Available StatusViewItem options.
|
||||||
---@class core.statusview.item.options : table
|
---@class core.statusview.item.options : table
|
||||||
|
---A condition to evaluate if the item should be displayed. If a string
|
||||||
|
---is given it is treated as a require import that should return a valid object
|
||||||
|
---which is checked against the current active view, the sames applies if a
|
||||||
|
---table is given. A function that returns a boolean can be used instead to
|
||||||
|
---perform a custom evaluation, setting to nil means always evaluates to true.
|
||||||
---@field predicate string | table | core.statusview.item.predicate
|
---@field predicate string | table | core.statusview.item.predicate
|
||||||
---@field name string
|
---A unique name to identify the item on the status bar.
|
||||||
|
---@field name string @A unique name to identify the item on the status bar.
|
||||||
---@field alignment core.statusview.item.alignment
|
---@field alignment core.statusview.item.alignment
|
||||||
|
---A function that should return a core.statusview.styledtext element,
|
||||||
|
---returning an empty table is allowed.
|
||||||
---@field get_item core.statusview.item.get_item
|
---@field get_item core.statusview.item.get_item
|
||||||
---@field command? string | core.statusview.item.onclick
|
---The name of a valid registered command or a callback function to execute
|
||||||
|
---when the item is clicked.
|
||||||
|
---@field command string | core.statusview.item.onclick | nil
|
||||||
|
---The position in which to insert the given item on the internal table,
|
||||||
|
---a value of -1 inserts the item at the end which is the default. A value
|
||||||
|
---of 1 will insert the item at the beggining.
|
||||||
---@field position? integer
|
---@field position? integer
|
||||||
---@field tooltip? string
|
---@field tooltip? string @Text displayed when mouse hovers the item.
|
||||||
|
---@field visible boolean @Flag to show or hide the item
|
||||||
|
---The type of separator rendered to the right of the item if another item
|
||||||
|
---follows it.
|
||||||
---@field separator? core.statusview.item.separator
|
---@field separator? core.statusview.item.separator
|
||||||
local StatusViewItemOptions = {
|
|
||||||
---A condition to evaluate if the item should be displayed. If a string
|
|
||||||
---is given it is treated as a require import that should return a valid object
|
|
||||||
---which is checked against the current active view, the sames applies if a
|
|
||||||
---table is given. A function that returns a boolean can be used instead to
|
|
||||||
---perform a custom evaluation, setting to nil means always evaluates to true.
|
|
||||||
predicate = nil,
|
|
||||||
---A unique name to identify the item on the status bar.
|
|
||||||
name = nil,
|
|
||||||
alignment = nil,
|
|
||||||
---A function that should return a core.statusview.styledtext element,
|
|
||||||
---returning empty table is allowed.
|
|
||||||
get_item = nil,
|
|
||||||
---The name of a valid registered command or a callback function to execute
|
|
||||||
---when the item is clicked.
|
|
||||||
command = nil,
|
|
||||||
---The position in which to insert the given item on the internal table,
|
|
||||||
---a value of -1 inserts the item at the end which is the default. A value
|
|
||||||
---of 1 will insert the item at the beggining.
|
|
||||||
position = nil,
|
|
||||||
---Displayed when mouse hovers the item
|
|
||||||
tooltip = nil,
|
|
||||||
separator = nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusViewItem.options = StatusViewItemOptions
|
|
||||||
|
|
||||||
---Flag to tell the item should me aligned on left side of status bar.
|
---Flag to tell the item should me aligned on left side of status bar.
|
||||||
---@type number
|
---@type integer
|
||||||
StatusViewItem.LEFT = 1
|
StatusViewItem.LEFT = 1
|
||||||
|
|
||||||
---Flag to tell the item should me aligned on right side of status bar.
|
---Flag to tell the item should me aligned on right side of status bar.
|
||||||
---@type number
|
---@type integer
|
||||||
StatusViewItem.RIGHT = 2
|
StatusViewItem.RIGHT = 2
|
||||||
|
|
||||||
---@alias core.statusview.item.alignment
|
---@alias core.statusview.item.alignment
|
||||||
---|>'core.statusview.item.LEFT'
|
---|>`StatusView.Item.LEFT`
|
||||||
---| 'core.statusview.item.RIGHT'
|
---| `StatusView.Item.RIGHT`
|
||||||
|
|
||||||
---Constructor
|
---Constructor
|
||||||
---@param options core.statusview.item.options
|
---@param options core.statusview.item.options
|
||||||
|
@ -210,7 +203,7 @@ function StatusView:register_docview_items()
|
||||||
return {
|
return {
|
||||||
dv.doc:is_dirty() and style.accent or style.text, style.icon_font, "f",
|
dv.doc:is_dirty() and style.accent or style.text, style.icon_font, "f",
|
||||||
style.dim, style.font, self.separator2, style.text,
|
style.dim, style.font, self.separator2, style.text,
|
||||||
dv.doc.filename and style.text or style.dim, dv.doc:get_name()
|
dv.doc.filename and style.text or style.dim, common.home_encode(dv.doc:get_name())
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
@ -470,7 +463,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
---Hides the given items from the status view or all if no names given.
|
---Hides the given items from the status view or all if no names given.
|
||||||
---@param names table<integer, string> | string | nil
|
---@param names? table<integer, string> | string
|
||||||
function StatusView:hide_items(names)
|
function StatusView:hide_items(names)
|
||||||
if type(names) == "string" then
|
if type(names) == "string" then
|
||||||
names = {names}
|
names = {names}
|
||||||
|
@ -489,7 +482,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
---Shows the given items from the status view or all if no names given.
|
---Shows the given items from the status view or all if no names given.
|
||||||
---@param names table<integer, string> | string | nil
|
---@param names? table<integer, string> | string
|
||||||
function StatusView:show_items(names)
|
function StatusView:show_items(names)
|
||||||
if type(names) == "string" then
|
if type(names) == "string" then
|
||||||
names = {names}
|
names = {names}
|
||||||
|
@ -607,8 +600,8 @@ function StatusView:draw_item_tooltip(item)
|
||||||
local x = self.pointer.x - (w / 2) - (style.padding.x * 2)
|
local x = self.pointer.x - (w / 2) - (style.padding.x * 2)
|
||||||
|
|
||||||
if x < 0 then x = 0 end
|
if x < 0 then x = 0 end
|
||||||
if x + w + (style.padding.x * 2) > self.size.x then
|
if (x + w + (style.padding.x * 3)) > self.size.x then
|
||||||
x = self.size.x - w - (style.padding.x * 2)
|
x = self.size.x - w - (style.padding.x * 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
renderer.draw_rect(
|
renderer.draw_rect(
|
||||||
|
@ -783,7 +776,7 @@ function StatusView:update_active_items()
|
||||||
item.cached_item = {}
|
item.cached_item = {}
|
||||||
if item.visible and item:predicate() then
|
if item.visible and item:predicate() then
|
||||||
local styled_text = type(item.get_item) == "function"
|
local styled_text = type(item.get_item) == "function"
|
||||||
and item.get_item(self) or item.get_item
|
and item.get_item(item) or item.get_item
|
||||||
|
|
||||||
if #styled_text > 0 then
|
if #styled_text > 0 then
|
||||||
remove_spacing(self, styled_text)
|
remove_spacing(self, styled_text)
|
||||||
|
@ -881,7 +874,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
---Drag the given panel if possible.
|
---Drag the given panel if possible.
|
||||||
---@param panel '"left"' | '"right"'
|
---@param panel core.statusview.position
|
||||||
---@param dx number
|
---@param dx number
|
||||||
function StatusView:drag_panel(panel, dx)
|
function StatusView:drag_panel(panel, dx)
|
||||||
if panel == "left" and self.r_left_width > self.left_width then
|
if panel == "left" and self.r_left_width > self.left_width then
|
||||||
|
@ -915,10 +908,8 @@ end
|
||||||
function StatusView:get_hovered_panel(x, y)
|
function StatusView:get_hovered_panel(x, y)
|
||||||
if y >= self.position.y and x <= self.left_width + style.padding.x then
|
if y >= self.position.y and x <= self.left_width + style.padding.x then
|
||||||
return "left"
|
return "left"
|
||||||
else
|
|
||||||
return "right"
|
|
||||||
end
|
end
|
||||||
return ""
|
return "right"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -1053,9 +1044,13 @@ function StatusView:on_mouse_released(button, x, y)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function StatusView:on_mouse_wheel(y)
|
function StatusView:on_mouse_wheel(y, x)
|
||||||
if not self.visible then return end
|
if not self.visible or self.hovered_panel == "" then return end
|
||||||
|
if x ~= 0 then
|
||||||
|
self:drag_panel(self.hovered_panel, x * self.left_width / 10)
|
||||||
|
else
|
||||||
self:drag_panel(self.hovered_panel, y * self.left_width / 10)
|
self:drag_panel(self.hovered_panel, y * self.left_width / 10)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -30,16 +30,21 @@ end
|
||||||
|
|
||||||
|
|
||||||
local function find(string, field)
|
local function find(string, field)
|
||||||
|
local best_match = 0
|
||||||
|
local best_syntax
|
||||||
for i = #syntax.items, 1, -1 do
|
for i = #syntax.items, 1, -1 do
|
||||||
local t = syntax.items[i]
|
local t = syntax.items[i]
|
||||||
if common.match_pattern(string, t[field] or {}) then
|
local s, e = common.match_pattern(string, t[field] or {})
|
||||||
return t
|
if s and e - s > best_match then
|
||||||
|
best_match = e - s
|
||||||
|
best_syntax = t
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return best_syntax
|
||||||
end
|
end
|
||||||
|
|
||||||
function syntax.get(filename, header)
|
function syntax.get(filename, header)
|
||||||
return 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
|
||||||
|
|
|
@ -3,6 +3,14 @@ local common = require "core.common"
|
||||||
local style = require "core.style"
|
local style = require "core.style"
|
||||||
local View = require "core.view"
|
local View = require "core.view"
|
||||||
|
|
||||||
|
local icon_colors = {
|
||||||
|
bg = { common.color "#2e2e32ff" },
|
||||||
|
color6 = { common.color "#e1e1e6ff" },
|
||||||
|
color7 = { common.color "#ffa94dff" },
|
||||||
|
color8 = { common.color "#93ddfaff" },
|
||||||
|
color9 = { common.color "#f7c95cff" }
|
||||||
|
};
|
||||||
|
|
||||||
local restore_command = {
|
local restore_command = {
|
||||||
symbol = "w", action = function() system.set_window_mode("normal") end
|
symbol = "w", action = function() system.set_window_mode("normal") end
|
||||||
}
|
}
|
||||||
|
@ -43,6 +51,10 @@ function TitleView:configure_hit_test(borderless)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function TitleView:on_scale_change()
|
||||||
|
self:configure_hit_test(self.visible)
|
||||||
|
end
|
||||||
|
|
||||||
function TitleView:update()
|
function TitleView:update()
|
||||||
self.size.y = self.visible and title_view_height() or 0
|
self.size.y = self.visible and title_view_height() or 0
|
||||||
title_commands[2] = core.window_mode == "maximized" and restore_command or maximize_command
|
title_commands[2] = core.window_mode == "maximized" and restore_command or maximize_command
|
||||||
|
@ -55,7 +67,11 @@ function TitleView:draw_window_title()
|
||||||
local ox, oy = self:get_content_offset()
|
local ox, oy = self:get_content_offset()
|
||||||
local color = style.text
|
local color = style.text
|
||||||
local x, y = ox + style.padding.x, oy + style.padding.y
|
local x, y = ox + style.padding.x, oy + style.padding.y
|
||||||
x = common.draw_text(style.icon_font, color, "M ", nil, x, y, 0, h)
|
common.draw_text(style.icon_font, icon_colors.bg, "5", nil, x, y, 0, h)
|
||||||
|
common.draw_text(style.icon_font, icon_colors.color6, "6", nil, x, y, 0, h)
|
||||||
|
common.draw_text(style.icon_font, icon_colors.color7, "7", nil, x, y, 0, h)
|
||||||
|
common.draw_text(style.icon_font, icon_colors.color8, "8", nil, x, y, 0, h)
|
||||||
|
x = common.draw_text(style.icon_font, icon_colors.color9, "9 ", nil, x, y, 0, h)
|
||||||
local title = core.compose_window_title(core.window_title)
|
local title = core.compose_window_title(core.window_title)
|
||||||
common.draw_text(style.font, color, title, nil, x, y, 0, h)
|
common.draw_text(style.font, color, title, nil, x, y, 0, h)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
local core = require "core"
|
local core = require "core"
|
||||||
local syntax = require "core.syntax"
|
local syntax = require "core.syntax"
|
||||||
local common = require "core.common"
|
|
||||||
|
|
||||||
local tokenizer = {}
|
local tokenizer = {}
|
||||||
local bad_patterns = {}
|
local bad_patterns = {}
|
||||||
|
@ -51,31 +50,37 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- State is a string of bytes, where the count of bytes represents the depth
|
||||||
|
-- of the subsyntax we are currently in. Each individual byte represents the
|
||||||
|
-- index of the pattern for the current subsyntax in relation to its parent
|
||||||
|
-- syntax. Using a string of bytes allows us to have as many subsyntaxes as
|
||||||
|
-- bytes can be stored on a string while keeping some level of performance in
|
||||||
|
-- comparison to a Lua table. The only limitation is that a syntax would not
|
||||||
|
-- be able to contain more than 255 patterns.
|
||||||
|
--
|
||||||
|
-- Lets say a state contains 2 bytes byte #1 with value `3` and byte #2 with
|
||||||
|
-- a value of `5`. This would mean that on the parent syntax at index `3` a
|
||||||
|
-- pattern subsyntax that matched current text was found, then inside that
|
||||||
|
-- subsyntax another subsyntax pattern at index `5` that matched current text
|
||||||
|
-- was also found.
|
||||||
|
|
||||||
-- State is a 32-bit number that is four separate bytes, illustrating how many
|
-- Calling `push_subsyntax` appends the current subsyntax pattern index to the
|
||||||
-- differnet delimiters we have open, and which subsyntaxes we have active.
|
-- state and increases the stack depth. Calling `pop_subsyntax` clears the
|
||||||
-- At most, there are 3 subsyntaxes active at the same time. Beyond that,
|
-- last appended subsyntax and decreases the stack.
|
||||||
-- does not support further highlighting.
|
|
||||||
|
|
||||||
-- You can think of it as a maximum 4 integer (0-255) stack. It always has
|
|
||||||
-- 1 integer in it. Calling `push_subsyntax` increases the stack depth. Calling
|
|
||||||
-- `pop_subsyntax` decreases it. The integers represent the index of a pattern
|
|
||||||
-- that we're following in the syntax. The top of the stack can be any valid
|
|
||||||
-- pattern index, any integer lower in the stack must represent a pattern that
|
|
||||||
-- specifies a subsyntax.
|
|
||||||
|
|
||||||
-- If you do not have subsyntaxes in your syntax, the three most
|
|
||||||
-- singificant numbers will always be 0, the stack will only ever be length 1
|
|
||||||
-- and the state variable will only ever range from 0-255.
|
|
||||||
local function retrieve_syntax_state(incoming_syntax, state)
|
local function retrieve_syntax_state(incoming_syntax, state)
|
||||||
local current_syntax, subsyntax_info, current_pattern_idx, current_level =
|
local current_syntax, subsyntax_info, current_pattern_idx, current_level =
|
||||||
incoming_syntax, nil, state, 0
|
incoming_syntax, nil, state:byte(1) or 0, 1
|
||||||
if state > 0 and (state > 255 or current_syntax.patterns[state].syntax) then
|
if
|
||||||
-- If we have higher bits, then decode them one at a time, and find which
|
current_pattern_idx > 0
|
||||||
|
and
|
||||||
|
current_syntax.patterns[current_pattern_idx]
|
||||||
|
then
|
||||||
|
-- If the state is not empty we iterate over each byte, and find which
|
||||||
-- syntax we're using. Rather than walking the bytes, and calling into
|
-- syntax we're using. Rather than walking the bytes, and calling into
|
||||||
-- `syntax` each time, we could probably cache this in a single table.
|
-- `syntax` each time, we could probably cache this in a single table.
|
||||||
for i = 0, 2 do
|
for i = 1, #state do
|
||||||
local target = bit32.extract(state, i*8, 8)
|
local target = state:byte(i)
|
||||||
if target ~= 0 then
|
if target ~= 0 then
|
||||||
if current_syntax.patterns[target].syntax then
|
if current_syntax.patterns[target].syntax then
|
||||||
subsyntax_info = current_syntax.patterns[target]
|
subsyntax_info = current_syntax.patterns[target]
|
||||||
|
@ -95,6 +100,21 @@ local function retrieve_syntax_state(incoming_syntax, state)
|
||||||
return current_syntax, subsyntax_info, current_pattern_idx, current_level
|
return current_syntax, subsyntax_info, current_pattern_idx, current_level
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Return the list of syntaxes used in the specified state.
|
||||||
|
---@param base_syntax table @The initial base syntax (the syntax of the file)
|
||||||
|
---@param state string @The state of the tokenizer to extract from
|
||||||
|
---@return table @Array of syntaxes starting from the innermost one
|
||||||
|
function tokenizer.extract_subsyntaxes(base_syntax, state)
|
||||||
|
local current_syntax
|
||||||
|
local t = {}
|
||||||
|
repeat
|
||||||
|
current_syntax = retrieve_syntax_state(base_syntax, state)
|
||||||
|
table.insert(t, current_syntax)
|
||||||
|
state = string.sub(state, 2)
|
||||||
|
until #state == 0
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
local function report_bad_pattern(log_fn, syntax, pattern_idx, msg, ...)
|
local function report_bad_pattern(log_fn, syntax, pattern_idx, msg, ...)
|
||||||
if not bad_patterns[syntax] then
|
if not bad_patterns[syntax] then
|
||||||
bad_patterns[syntax] = { }
|
bad_patterns[syntax] = { }
|
||||||
|
@ -107,7 +127,7 @@ end
|
||||||
|
|
||||||
---@param incoming_syntax table
|
---@param incoming_syntax table
|
||||||
---@param text string
|
---@param text string
|
||||||
---@param state integer
|
---@param state string
|
||||||
function tokenizer.tokenize(incoming_syntax, text, state)
|
function tokenizer.tokenize(incoming_syntax, text, state)
|
||||||
local res = {}
|
local res = {}
|
||||||
local i = 1
|
local i = 1
|
||||||
|
@ -116,9 +136,9 @@ function tokenizer.tokenize(incoming_syntax, text, state)
|
||||||
return { "normal", text }
|
return { "normal", text }
|
||||||
end
|
end
|
||||||
|
|
||||||
state = state or 0
|
state = state or string.char(0)
|
||||||
-- incoming_syntax : the parent syntax of the file.
|
-- incoming_syntax : the parent syntax of the file.
|
||||||
-- state : a 32-bit number representing syntax state (see above)
|
-- state : a string of bytes representing syntax state (see above)
|
||||||
|
|
||||||
-- current_syntax : the syntax we're currently in.
|
-- current_syntax : the syntax we're currently in.
|
||||||
-- subsyntax_info : info about the delimiters of this subsyntax.
|
-- subsyntax_info : info about the delimiters of this subsyntax.
|
||||||
|
@ -130,7 +150,18 @@ function tokenizer.tokenize(incoming_syntax, text, state)
|
||||||
-- Should be used to set the state variable. Don't modify it directly.
|
-- Should be used to set the state variable. Don't modify it directly.
|
||||||
local function set_subsyntax_pattern_idx(pattern_idx)
|
local function set_subsyntax_pattern_idx(pattern_idx)
|
||||||
current_pattern_idx = pattern_idx
|
current_pattern_idx = pattern_idx
|
||||||
state = bit32.replace(state, pattern_idx, current_level*8, 8)
|
local state_len = #state
|
||||||
|
if current_level > state_len then
|
||||||
|
state = state .. string.char(pattern_idx)
|
||||||
|
elseif state_len == 1 then
|
||||||
|
state = string.char(pattern_idx)
|
||||||
|
else
|
||||||
|
state = ("%s%s%s"):format(
|
||||||
|
state:sub(1,current_level-1),
|
||||||
|
string.char(pattern_idx),
|
||||||
|
state:sub(current_level+1)
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -144,8 +175,8 @@ function tokenizer.tokenize(incoming_syntax, text, state)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function pop_subsyntax()
|
local function pop_subsyntax()
|
||||||
set_subsyntax_pattern_idx(0)
|
|
||||||
current_level = current_level - 1
|
current_level = current_level - 1
|
||||||
|
state = string.sub(state, 1, current_level)
|
||||||
set_subsyntax_pattern_idx(0)
|
set_subsyntax_pattern_idx(0)
|
||||||
current_syntax, subsyntax_info, current_pattern_idx, current_level =
|
current_syntax, subsyntax_info, current_pattern_idx, current_level =
|
||||||
retrieve_syntax_state(incoming_syntax, state)
|
retrieve_syntax_state(incoming_syntax, state)
|
||||||
|
@ -183,23 +214,12 @@ function tokenizer.tokenize(incoming_syntax, text, state)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
res = p.pattern and { text:ufind((at_start or p.whole_line[p_idx]) and "^" .. code or code, next) }
|
res = p.pattern and { text:ufind((at_start or p.whole_line[p_idx]) and "^" .. code or code, next) }
|
||||||
or { regex.match(code, text, text:ucharpos(next), (at_start or p.whole_line[p_idx]) and regex.ANCHORED or 0) }
|
or { regex.find(code, text, text:ucharpos(next), (at_start or p.whole_line[p_idx]) and regex.ANCHORED or 0) }
|
||||||
if p.regex and #res > 0 then -- set correct utf8 len for regex result
|
if p.regex and #res > 0 then -- set correct utf8 len for regex result
|
||||||
local char_pos_1 = string.ulen(text:sub(1, res[1]))
|
local char_pos_1 = res[1] > next and string.ulen(text:sub(1, res[1])) or next
|
||||||
local char_pos_2 = char_pos_1 + string.ulen(text:sub(res[1], res[2])) - 1
|
local char_pos_2 = string.ulen(text:sub(1, res[2]))
|
||||||
-- `regex.match` returns group results as a series of `begin, end`
|
for i=3,#res do
|
||||||
-- we only want `begin`s
|
res[i] = string.ulen(text:sub(1, res[i] - 1)) + 1
|
||||||
if #res >= 3 then
|
|
||||||
res[3] = char_pos_1 + string.ulen(text:sub(res[1], res[3])) - 1
|
|
||||||
end
|
|
||||||
for i=1,(#res-3) do
|
|
||||||
local curr = i + 3
|
|
||||||
local from = i * 2 + 3
|
|
||||||
if from < #res then
|
|
||||||
res[curr] = char_pos_1 + string.ulen(text:sub(res[1], res[from])) - 1
|
|
||||||
else
|
|
||||||
res[curr] = nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
res[1] = char_pos_1
|
res[1] = char_pos_1
|
||||||
res[2] = char_pos_2
|
res[2] = char_pos_2
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
local core = require "core"
|
local core = require "core"
|
||||||
local config = require "core.config"
|
local config = require "core.config"
|
||||||
local style = require "core.style"
|
|
||||||
local common = require "core.common"
|
local common = require "core.common"
|
||||||
local Object = require "core.object"
|
local Object = require "core.object"
|
||||||
|
local Scrollbar = require "core.scrollbar"
|
||||||
|
|
||||||
---@class core.view.position
|
---@class core.view.position
|
||||||
---@field x number
|
---@field x number
|
||||||
|
@ -28,10 +28,6 @@ local Object = require "core.object"
|
||||||
---@field w core.view.thumbtrackwidth
|
---@field w core.view.thumbtrackwidth
|
||||||
---@field h core.view.thumbtrack
|
---@field h core.view.thumbtrack
|
||||||
|
|
||||||
---@class core.view.increment
|
|
||||||
---@field value number
|
|
||||||
---@field to number
|
|
||||||
|
|
||||||
---@alias core.view.cursor "'arrow'" | "'ibeam'" | "'sizeh'" | "'sizev'" | "'hand'"
|
---@alias core.view.cursor "'arrow'" | "'ibeam'" | "'sizeh'" | "'sizev'" | "'hand'"
|
||||||
|
|
||||||
---@alias core.view.mousebutton "'left'" | "'right'"
|
---@alias core.view.mousebutton "'left'" | "'right'"
|
||||||
|
@ -47,8 +43,9 @@ local Object = require "core.object"
|
||||||
---@field scroll core.view.scroll
|
---@field scroll core.view.scroll
|
||||||
---@field cursor core.view.cursor
|
---@field cursor core.view.cursor
|
||||||
---@field scrollable boolean
|
---@field scrollable boolean
|
||||||
---@field scrollbar core.view.scrollbar
|
---@field v_scrollbar core.scrollbar
|
||||||
---@field scrollbar_alpha core.view.increment
|
---@field h_scrollbar core.scrollbar
|
||||||
|
---@field current_scale number
|
||||||
local View = Object:extend()
|
local View = Object:extend()
|
||||||
|
|
||||||
-- context can be "application" or "session". The instance of objects
|
-- context can be "application" or "session". The instance of objects
|
||||||
|
@ -62,13 +59,9 @@ function View:new()
|
||||||
self.scroll = { x = 0, y = 0, to = { x = 0, y = 0 } }
|
self.scroll = { x = 0, y = 0, to = { x = 0, y = 0 } }
|
||||||
self.cursor = "arrow"
|
self.cursor = "arrow"
|
||||||
self.scrollable = false
|
self.scrollable = false
|
||||||
self.scrollbar = {
|
self.v_scrollbar = Scrollbar({direction = "v", alignment = "e"})
|
||||||
x = { thumb = 0, track = 0 },
|
self.h_scrollbar = Scrollbar({direction = "h", alignment = "e"})
|
||||||
y = { thumb = 0, track = 0 },
|
self.current_scale = SCALE
|
||||||
w = { thumb = 0, track = 0, to = { thumb = 0, track = 0 } },
|
|
||||||
h = { thumb = 0, track = 0 },
|
|
||||||
}
|
|
||||||
self.scrollbar_alpha = { value = 0, to = 0 }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function View:move_towards(t, k, dest, rate, name)
|
function View:move_towards(t, k, dest, rate, name)
|
||||||
|
@ -109,47 +102,9 @@ function View:get_scrollable_size()
|
||||||
return math.huge
|
return math.huge
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@return number
|
||||||
---@return number x
|
function View:get_h_scrollable_size()
|
||||||
---@return number y
|
return 0
|
||||||
---@return number width
|
|
||||||
---@return number height
|
|
||||||
function View:get_scrollbar_track_rect()
|
|
||||||
local sz = self:get_scrollable_size()
|
|
||||||
if sz <= self.size.y or sz == math.huge then
|
|
||||||
return 0, 0, 0, 0
|
|
||||||
end
|
|
||||||
local width = style.scrollbar_size
|
|
||||||
if self.hovered_scrollbar_track or self.dragging_scrollbar then
|
|
||||||
width = style.expanded_scrollbar_size
|
|
||||||
end
|
|
||||||
return
|
|
||||||
self.position.x + self.size.x - width,
|
|
||||||
self.position.y,
|
|
||||||
width,
|
|
||||||
self.size.y
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
---@return number x
|
|
||||||
---@return number y
|
|
||||||
---@return number width
|
|
||||||
---@return number height
|
|
||||||
function View:get_scrollbar_rect()
|
|
||||||
local sz = self:get_scrollable_size()
|
|
||||||
if sz <= self.size.y or sz == math.huge then
|
|
||||||
return 0, 0, 0, 0
|
|
||||||
end
|
|
||||||
local h = math.max(20, self.size.y * self.size.y / sz)
|
|
||||||
local width = style.scrollbar_size
|
|
||||||
if self.hovered_scrollbar_track or self.dragging_scrollbar then
|
|
||||||
width = style.expanded_scrollbar_size
|
|
||||||
end
|
|
||||||
return
|
|
||||||
self.position.x + self.size.x - width,
|
|
||||||
self.position.y + self.scroll.y * (self.size.y - h) / (sz - self.size.y),
|
|
||||||
width,
|
|
||||||
h
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,16 +112,19 @@ end
|
||||||
---@param y number
|
---@param y number
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function View:scrollbar_overlaps_point(x, y)
|
function View:scrollbar_overlaps_point(x, y)
|
||||||
local sx, sy, sw, sh = self:get_scrollbar_rect()
|
return not (not (self.v_scrollbar:overlaps(x, y) or self.h_scrollbar:overlaps(x, y)))
|
||||||
return x >= sx - style.scrollbar_size * 3 and x < sx + sw and y > sy and y <= sy + sh
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param x number
|
|
||||||
---@param y number
|
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function View:scrollbar_track_overlaps_point(x, y)
|
function View:scrollbar_dragging()
|
||||||
local sx, sy, sw, sh = self:get_scrollbar_track_rect()
|
return self.v_scrollbar.dragging or self.h_scrollbar.dragging
|
||||||
return x >= sx - style.scrollbar_size * 3 and x < sx + sw and y > sy and y <= sy + sh
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@return boolean
|
||||||
|
function View:scrollbar_hovering()
|
||||||
|
return self.v_scrollbar.hovering.track or self.h_scrollbar.hovering.track
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -176,14 +134,18 @@ end
|
||||||
---@param clicks integer
|
---@param clicks integer
|
||||||
---return boolean
|
---return boolean
|
||||||
function View:on_mouse_pressed(button, x, y, clicks)
|
function View:on_mouse_pressed(button, x, y, clicks)
|
||||||
if self:scrollbar_track_overlaps_point(x, y) then
|
if not self.scrollable then return end
|
||||||
if self:scrollbar_overlaps_point(x, y) then
|
local result = self.v_scrollbar:on_mouse_pressed(button, x, y, clicks)
|
||||||
self.dragging_scrollbar = true
|
if result then
|
||||||
else
|
if result ~= true then
|
||||||
local _, _, _, sh = self:get_scrollbar_rect()
|
self.scroll.to.y = result * self:get_scrollable_size()
|
||||||
local ly = (y - self.position.y) - sh / 2
|
end
|
||||||
local pct = common.clamp(ly / self.size.y, 0, 100)
|
return true
|
||||||
self.scroll.to.y = self:get_scrollable_size() * pct
|
end
|
||||||
|
result = self.h_scrollbar:on_mouse_pressed(button, x, y, clicks)
|
||||||
|
if result then
|
||||||
|
if result ~= true then
|
||||||
|
self.scroll.to.x = result * self:get_h_scrollable_size()
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -194,7 +156,9 @@ end
|
||||||
---@param x number
|
---@param x number
|
||||||
---@param y number
|
---@param y number
|
||||||
function View:on_mouse_released(button, x, y)
|
function View:on_mouse_released(button, x, y)
|
||||||
self.dragging_scrollbar = false
|
if not self.scrollable then return end
|
||||||
|
self.v_scrollbar:on_mouse_released(button, x, y)
|
||||||
|
self.h_scrollbar:on_mouse_released(button, x, y)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -203,22 +167,41 @@ end
|
||||||
---@param dx number
|
---@param dx number
|
||||||
---@param dy number
|
---@param dy number
|
||||||
function View:on_mouse_moved(x, y, dx, dy)
|
function View:on_mouse_moved(x, y, dx, dy)
|
||||||
if self.dragging_scrollbar then
|
if not self.scrollable then return end
|
||||||
local delta = self:get_scrollable_size() / self.size.y * dy
|
local result
|
||||||
self.scroll.to.y = self.scroll.to.y + delta
|
if self.h_scrollbar.dragging then goto skip_v_scrollbar end
|
||||||
|
result = self.v_scrollbar:on_mouse_moved(x, y, dx, dy)
|
||||||
|
if result then
|
||||||
|
if result ~= true then
|
||||||
|
self.scroll.to.y = result * self:get_scrollable_size()
|
||||||
if not config.animate_drag_scroll then
|
if not config.animate_drag_scroll then
|
||||||
self:clamp_scroll_position()
|
self:clamp_scroll_position()
|
||||||
self.scroll.y = self.scroll.to.y
|
self.scroll.y = self.scroll.to.y
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.hovered_scrollbar = self:scrollbar_overlaps_point(x, y)
|
-- hide horizontal scrollbar
|
||||||
self.hovered_scrollbar_track = self.hovered_scrollbar or self:scrollbar_track_overlaps_point(x, y)
|
self.h_scrollbar:on_mouse_left()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
::skip_v_scrollbar::
|
||||||
|
result = self.h_scrollbar:on_mouse_moved(x, y, dx, dy)
|
||||||
|
if result then
|
||||||
|
if result ~= true then
|
||||||
|
self.scroll.to.x = result * self:get_h_scrollable_size()
|
||||||
|
if not config.animate_drag_scroll then
|
||||||
|
self:clamp_scroll_position()
|
||||||
|
self.scroll.x = self.scroll.to.x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function View:on_mouse_left()
|
function View:on_mouse_left()
|
||||||
self.hovered_scrollbar = false
|
if not self.scrollable then return end
|
||||||
self.hovered_scrollbar_track = false
|
self.v_scrollbar:on_mouse_left()
|
||||||
|
self.h_scrollbar:on_mouse_left()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,12 +219,25 @@ function View:on_text_input(text)
|
||||||
-- no-op
|
-- no-op
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param y number
|
|
||||||
---@return boolean
|
|
||||||
function View:on_mouse_wheel(y)
|
|
||||||
|
|
||||||
|
function View:on_ime_text_editing(text, start, length)
|
||||||
|
-- no-op
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param y number @Vertical scroll delta; positive is "up"
|
||||||
|
---@param x number @Horizontal scroll delta; positive is "left"
|
||||||
|
---@return boolean @Capture event
|
||||||
|
function View:on_mouse_wheel(y, x)
|
||||||
|
-- no-op
|
||||||
|
end
|
||||||
|
|
||||||
|
---Can be overriden to listen for scale change events to apply
|
||||||
|
---any neccesary changes in sizes, padding, etc...
|
||||||
|
---@param new_scale number
|
||||||
|
---@param prev_scale number
|
||||||
|
function View:on_scale_change(new_scale, prev_scale) end
|
||||||
|
|
||||||
function View:get_content_bounds()
|
function View:get_content_bounds()
|
||||||
local x = self.scroll.x
|
local x = self.scroll.x
|
||||||
local y = self.scroll.y
|
local y = self.scroll.y
|
||||||
|
@ -261,35 +257,35 @@ end
|
||||||
function View:clamp_scroll_position()
|
function View:clamp_scroll_position()
|
||||||
local max = self:get_scrollable_size() - self.size.y
|
local max = self:get_scrollable_size() - self.size.y
|
||||||
self.scroll.to.y = common.clamp(self.scroll.to.y, 0, max)
|
self.scroll.to.y = common.clamp(self.scroll.to.y, 0, max)
|
||||||
|
|
||||||
|
max = self:get_h_scrollable_size() - self.size.x
|
||||||
|
self.scroll.to.x = common.clamp(self.scroll.to.x, 0, max)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function View:update_scrollbar()
|
function View:update_scrollbar()
|
||||||
local x, y, w, h = self:get_scrollbar_rect()
|
local v_scrollable = self:get_scrollable_size()
|
||||||
self.scrollbar.w.to.thumb = w
|
self.v_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, v_scrollable)
|
||||||
self:move_towards(self.scrollbar.w, "thumb", self.scrollbar.w.to.thumb, 0.3, "scroll")
|
self.v_scrollbar:set_percent(self.scroll.y/v_scrollable)
|
||||||
self.scrollbar.x.thumb = x + w - self.scrollbar.w.thumb
|
self.v_scrollbar:update()
|
||||||
self.scrollbar.y.thumb = y
|
|
||||||
self.scrollbar.h.thumb = h
|
|
||||||
|
|
||||||
local x, y, w, h = self:get_scrollbar_track_rect()
|
local h_scrollable = self:get_h_scrollable_size()
|
||||||
self.scrollbar.w.to.track = w
|
self.h_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, h_scrollable)
|
||||||
self:move_towards(self.scrollbar.w, "track", self.scrollbar.w.to.track, 0.3, "scroll")
|
self.h_scrollbar:set_percent(self.scroll.x/h_scrollable)
|
||||||
self.scrollbar.x.track = x + w - self.scrollbar.w.track
|
self.h_scrollbar:update()
|
||||||
self.scrollbar.y.track = y
|
|
||||||
self.scrollbar.h.track = h
|
|
||||||
|
|
||||||
-- we use 100 for a smoother transition
|
|
||||||
self.scrollbar_alpha.to = (self.hovered_scrollbar_track or self.dragging_scrollbar) and 100 or 0
|
|
||||||
self:move_towards(self.scrollbar_alpha, "value", self.scrollbar_alpha.to, 0.3, "scroll")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function View:update()
|
function View:update()
|
||||||
|
if self.current_scale ~= SCALE then
|
||||||
|
self:on_scale_change(SCALE, self.current_scale)
|
||||||
|
self.current_scale = SCALE
|
||||||
|
end
|
||||||
|
|
||||||
self:clamp_scroll_position()
|
self:clamp_scroll_position()
|
||||||
self:move_towards(self.scroll, "x", self.scroll.to.x, 0.3, "scroll")
|
self:move_towards(self.scroll, "x", self.scroll.to.x, 0.3, "scroll")
|
||||||
self:move_towards(self.scroll, "y", self.scroll.to.y, 0.3, "scroll")
|
self:move_towards(self.scroll, "y", self.scroll.to.y, 0.3, "scroll")
|
||||||
|
if not self.scrollable then return end
|
||||||
self:update_scrollbar()
|
self:update_scrollbar()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -302,29 +298,9 @@ function View:draw_background(color)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function View:draw_scrollbar_track()
|
|
||||||
if not (self.hovered_scrollbar_track or self.dragging_scrollbar)
|
|
||||||
and self.scrollbar_alpha.value == 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local color = { table.unpack(style.scrollbar_track) }
|
|
||||||
color[4] = color[4] * self.scrollbar_alpha.value / 100
|
|
||||||
renderer.draw_rect(self.scrollbar.x.track, self.scrollbar.y.track,
|
|
||||||
self.scrollbar.w.track, self.scrollbar.h.track, color)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function View:draw_scrollbar_thumb()
|
|
||||||
local highlight = self.hovered_scrollbar or self.dragging_scrollbar
|
|
||||||
local color = highlight and style.scrollbar2 or style.scrollbar
|
|
||||||
renderer.draw_rect(self.scrollbar.x.thumb, self.scrollbar.y.thumb,
|
|
||||||
self.scrollbar.w.thumb, self.scrollbar.h.thumb, color)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function View:draw_scrollbar()
|
function View:draw_scrollbar()
|
||||||
self:draw_scrollbar_track()
|
self.v_scrollbar:draw()
|
||||||
self:draw_scrollbar_thumb()
|
self.h_scrollbar:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -588,8 +588,11 @@ function autocomplete.open(on_close)
|
||||||
end
|
end
|
||||||
|
|
||||||
local av = get_active_view()
|
local av = get_active_view()
|
||||||
|
if av then
|
||||||
|
partial = get_partial_symbol()
|
||||||
last_line, last_col = av.doc:get_selection()
|
last_line, last_col = av.doc:get_selection()
|
||||||
update_suggestions()
|
update_suggestions()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function autocomplete.close()
|
function autocomplete.close()
|
||||||
|
@ -645,11 +648,11 @@ command.add(predicate, {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["autocomplete:previous"] = function()
|
["autocomplete:previous"] = function()
|
||||||
suggestions_idx = math.max(suggestions_idx - 1, 1)
|
suggestions_idx = (suggestions_idx - 2) % #suggestions + 1
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["autocomplete:next"] = function()
|
["autocomplete:next"] = function()
|
||||||
suggestions_idx = math.min(suggestions_idx + 1, #suggestions)
|
suggestions_idx = (suggestions_idx % #suggestions) + 1
|
||||||
end,
|
end,
|
||||||
|
|
||||||
["autocomplete:cycle"] = function()
|
["autocomplete:cycle"] = function()
|
||||||
|
|
|
@ -75,7 +75,9 @@ local function escape_comment_tokens(token)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function get_comment_patterns(syntax)
|
local function get_comment_patterns(syntax, _loop)
|
||||||
|
_loop = _loop or 1
|
||||||
|
if _loop > 5 then return end
|
||||||
if comments_cache[syntax] then
|
if comments_cache[syntax] then
|
||||||
if #comments_cache[syntax] > 0 then
|
if #comments_cache[syntax] > 0 then
|
||||||
return comments_cache[syntax]
|
return comments_cache[syntax]
|
||||||
|
@ -125,7 +127,7 @@ local function get_comment_patterns(syntax)
|
||||||
elseif pattern.syntax then
|
elseif pattern.syntax then
|
||||||
local subsyntax = type(pattern.syntax) == 'table' and pattern.syntax
|
local subsyntax = type(pattern.syntax) == 'table' and pattern.syntax
|
||||||
or core_syntax.get("file"..pattern.syntax, "")
|
or core_syntax.get("file"..pattern.syntax, "")
|
||||||
local sub_comments = get_comment_patterns(subsyntax)
|
local sub_comments = get_comment_patterns(subsyntax, _loop + 1)
|
||||||
if sub_comments then
|
if sub_comments then
|
||||||
for s=1, #sub_comments do
|
for s=1, #sub_comments do
|
||||||
table.insert(comments, sub_comments[s])
|
table.insert(comments, sub_comments[s])
|
||||||
|
@ -190,11 +192,11 @@ local function get_non_empty_lines(syntax, lines)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if comment[3] then
|
if comment[3] then
|
||||||
local start, ending = regex.match(
|
local start, ending = regex.find_offsets(
|
||||||
comment[2], line, 1, regex.ANCHORED
|
comment[2], line, 1, regex.ANCHORED
|
||||||
)
|
)
|
||||||
if start then
|
if start then
|
||||||
if not regex.match(
|
if not regex.find_offsets(
|
||||||
comment[3], line, ending+1, regex.ANCHORED
|
comment[3], line, ending+1, regex.ANCHORED
|
||||||
)
|
)
|
||||||
then
|
then
|
||||||
|
@ -204,7 +206,7 @@ local function get_non_empty_lines(syntax, lines)
|
||||||
end
|
end
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
elseif regex.match(comment[2], line, 1, regex.ANCHORED) then
|
elseif regex.find_offsets(comment[2], line, 1, regex.ANCHORED) then
|
||||||
is_comment = true
|
is_comment = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
@ -214,7 +216,7 @@ local function get_non_empty_lines(syntax, lines)
|
||||||
is_comment = true
|
is_comment = true
|
||||||
inside_comment = false
|
inside_comment = false
|
||||||
end_pattern = nil
|
end_pattern = nil
|
||||||
elseif end_regex and regex.match(end_regex, line) then
|
elseif end_regex and regex.find_offsets(end_regex, line) then
|
||||||
is_comment = true
|
is_comment = true
|
||||||
inside_comment = false
|
inside_comment = false
|
||||||
end_regex = nil
|
end_regex = nil
|
||||||
|
|
|
@ -244,7 +244,7 @@ function DocView:draw_line_text(idx, x, y)
|
||||||
local color = base_color
|
local color = base_color
|
||||||
local draw = false
|
local draw = false
|
||||||
|
|
||||||
if e == #text - 1 then
|
if e >= #text - 1 then
|
||||||
draw = show_trailing
|
draw = show_trailing
|
||||||
color = trailing_color
|
color = trailing_color
|
||||||
elseif s == 1 then
|
elseif s == 1 then
|
||||||
|
@ -290,14 +290,15 @@ function DocView:draw_line_text(idx, x, y)
|
||||||
local ty = y + self:get_line_text_y_offset()
|
local ty = y + self:get_line_text_y_offset()
|
||||||
local cache = ws_cache[self.doc.highlighter][idx]
|
local cache = ws_cache[self.doc.highlighter][idx]
|
||||||
for i=1,#cache,4 do
|
for i=1,#cache,4 do
|
||||||
local sub = cache[i]
|
|
||||||
local tx = cache[i + 1] + x
|
local tx = cache[i + 1] + x
|
||||||
local tw = cache[i + 2]
|
local tw = cache[i + 2]
|
||||||
|
if tx <= x2 then
|
||||||
|
local sub = cache[i]
|
||||||
local color = cache[i + 3]
|
local color = cache[i + 3]
|
||||||
if tx + tw >= x1 then
|
if tx + tw >= x1 then
|
||||||
tx = renderer.draw_text(font, sub, tx, ty, color)
|
tx = renderer.draw_text(font, sub, tx, ty, color)
|
||||||
end
|
end
|
||||||
if tx > x2 then break end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return draw_line_text(self, idx, x, y)
|
return draw_line_text(self, idx, x, y)
|
||||||
|
|
|
@ -42,9 +42,9 @@ syntax.add {
|
||||||
-- blockquote
|
-- blockquote
|
||||||
{ pattern = "^%s*>+%s", type = "string" },
|
{ pattern = "^%s*>+%s", type = "string" },
|
||||||
-- alternative bold italic formats
|
-- alternative bold italic formats
|
||||||
{ pattern = { "%s___", "___%f[%s]" }, type = "markdown_bold_italic" },
|
{ pattern = { "%s___", "___" }, type = "markdown_bold_italic" },
|
||||||
{ pattern = { "%s__", "__%f[%s]" }, type = "markdown_bold" },
|
{ pattern = { "%s__", "__" }, type = "markdown_bold" },
|
||||||
{ pattern = { "%s_[%S]", "_%f[%s]" }, type = "markdown_italic" },
|
{ pattern = { "%s_[%S]", "_" }, type = "markdown_italic" },
|
||||||
-- reference links
|
-- reference links
|
||||||
{
|
{
|
||||||
pattern = "^%s*%[%^()["..in_squares_match.."]+()%]: ",
|
pattern = "^%s*%[%^()["..in_squares_match.."]+()%]: ",
|
||||||
|
@ -112,6 +112,7 @@ syntax.add {
|
||||||
{ pattern = { "%$%$", "%$%$", "\\" }, type = "string", syntax = ".tex"},
|
{ pattern = { "%$%$", "%$%$", "\\" }, type = "string", syntax = ".tex"},
|
||||||
{ regex = { "\\$", [[\$|(?=\\*\n)]], "\\" }, type = "string", syntax = ".tex"},
|
{ regex = { "\\$", [[\$|(?=\\*\n)]], "\\" }, type = "string", syntax = ".tex"},
|
||||||
-- code blocks
|
-- code blocks
|
||||||
|
{ pattern = { "```caddyfile", "```" }, type = "string", syntax = "Caddyfile" },
|
||||||
{ pattern = { "```c++", "```" }, type = "string", syntax = ".cpp" },
|
{ pattern = { "```c++", "```" }, type = "string", syntax = ".cpp" },
|
||||||
{ pattern = { "```cpp", "```" }, type = "string", syntax = ".cpp" },
|
{ pattern = { "```cpp", "```" }, type = "string", syntax = ".cpp" },
|
||||||
{ pattern = { "```python", "```" }, type = "string", syntax = ".py" },
|
{ pattern = { "```python", "```" }, type = "string", syntax = ".py" },
|
||||||
|
@ -149,14 +150,15 @@ syntax.add {
|
||||||
{ pattern = { "```", "```" }, type = "string" },
|
{ pattern = { "```", "```" }, type = "string" },
|
||||||
{ pattern = { "``", "``" }, type = "string" },
|
{ pattern = { "``", "``" }, type = "string" },
|
||||||
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" },
|
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" },
|
||||||
|
-- lines
|
||||||
|
{ pattern = "^%-%-%-+\n" , type = "comment" },
|
||||||
|
{ pattern = "^%*%*%*+\n", type = "comment" },
|
||||||
|
{ pattern = "^___+\n", type = "comment" },
|
||||||
|
{ pattern = "^===+\n", type = "comment" },
|
||||||
-- strike
|
-- strike
|
||||||
{ pattern = { "~~", "~~" }, type = "keyword2" },
|
{ pattern = { "~~", "~~" }, type = "keyword2" },
|
||||||
-- highlight
|
-- highlight
|
||||||
{ pattern = { "==", "==" }, type = "literal" },
|
{ pattern = { "==", "==" }, type = "literal" },
|
||||||
-- lines
|
|
||||||
{ pattern = "^%-%-%-+$" , type = "comment" },
|
|
||||||
{ pattern = "^%*%*%*+$", type = "comment" },
|
|
||||||
{ pattern = "^___+$", type = "comment" },
|
|
||||||
-- bold and italic
|
-- bold and italic
|
||||||
{ pattern = { "%*%*%*%S", "%*%*%*" }, type = "markdown_bold_italic" },
|
{ pattern = { "%*%*%*%S", "%*%*%*" }, type = "markdown_bold_italic" },
|
||||||
{ pattern = { "%*%*%S", "%*%*" }, type = "markdown_bold" },
|
{ pattern = { "%*%*%S", "%*%*" }, type = "markdown_bold" },
|
||||||
|
@ -166,16 +168,16 @@ syntax.add {
|
||||||
type = "markdown_italic"
|
type = "markdown_italic"
|
||||||
},
|
},
|
||||||
-- alternative bold italic formats
|
-- alternative bold italic formats
|
||||||
{ pattern = "^___[%s%p%w]+___%s" , type = "markdown_bold_italic" },
|
{ pattern = "^___[%s%p%w]+___" , type = "markdown_bold_italic" },
|
||||||
{ pattern = "^__[%s%p%w]+__%s" , type = "markdown_bold" },
|
{ pattern = "^__[%s%p%w]+__" , type = "markdown_bold" },
|
||||||
{ pattern = "^_[%s%p%w]+_%s" , type = "markdown_italic" },
|
{ pattern = "^_[%s%p%w]+_" , type = "markdown_italic" },
|
||||||
-- heading with custom id
|
-- heading with custom id
|
||||||
{
|
{
|
||||||
pattern = "^#+%s[%w%s%p]+(){()#[%w%-]+()}",
|
pattern = "^#+%s[%w%s%p]+(){()#[%w%-]+()}",
|
||||||
type = { "keyword", "function", "string", "function" }
|
type = { "keyword", "function", "string", "function" }
|
||||||
},
|
},
|
||||||
-- headings
|
-- headings
|
||||||
{ pattern = "^#+%s.+$", type = "keyword" },
|
{ pattern = "^#+%s.+\n", type = "keyword" },
|
||||||
-- superscript and subscript
|
-- superscript and subscript
|
||||||
{
|
{
|
||||||
pattern = "%^()%d+()%^",
|
pattern = "%^()%d+()%^",
|
||||||
|
|
|
@ -86,7 +86,7 @@ function DocView:draw_overlay(...)
|
||||||
and
|
and
|
||||||
config.plugins.lineguide.enabled
|
config.plugins.lineguide.enabled
|
||||||
and
|
and
|
||||||
not self:is(CommandView)
|
self:is(DocView)
|
||||||
then
|
then
|
||||||
local line_x = self:get_line_screen_position(1)
|
local line_x = self:get_line_screen_position(1)
|
||||||
local character_width = self:get_font():get_width("n")
|
local character_width = self:get_font():get_width("n")
|
||||||
|
|
|
@ -219,7 +219,7 @@ function LineWrapping.draw_guide(docview)
|
||||||
end
|
end
|
||||||
|
|
||||||
function LineWrapping.update_docview_breaks(docview)
|
function LineWrapping.update_docview_breaks(docview)
|
||||||
local x,y,w,h = docview:get_scrollbar_rect()
|
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
|
||||||
|
@ -355,18 +355,34 @@ function DocView:get_scrollable_size()
|
||||||
return self:get_line_height() * (get_total_wrapped_lines(self) - 1) + self.size.y
|
return self:get_line_height() * (get_total_wrapped_lines(self) - 1) + self.size.y
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local old_get_h_scrollable_size = DocView.get_h_scrollable_size
|
||||||
|
function DocView:get_h_scrollable_size(...)
|
||||||
|
if self.wrapping_enabled then return 0 end
|
||||||
|
return old_get_h_scrollable_size(self, ...)
|
||||||
|
end
|
||||||
|
|
||||||
local old_new = DocView.new
|
local old_new = DocView.new
|
||||||
function DocView:new(doc)
|
function DocView:new(doc)
|
||||||
old_new(self, doc)
|
old_new(self, doc)
|
||||||
if not open_files[doc] then open_files[doc] = {} end
|
if not open_files[doc] then open_files[doc] = {} end
|
||||||
table.insert(open_files[doc], self)
|
table.insert(open_files[doc], self)
|
||||||
if config.plugins.linewrapping.enable_by_default then
|
if config.plugins.linewrapping.enable_by_default then
|
||||||
|
self.wrapping_enabled = true
|
||||||
LineWrapping.update_docview_breaks(self)
|
LineWrapping.update_docview_breaks(self)
|
||||||
|
else
|
||||||
|
self.wrapping_enabled = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local old_scroll_to_line = DocView.scroll_to_line
|
||||||
|
function DocView:scroll_to_line(...)
|
||||||
|
if self.wrapping_enabled then LineWrapping.update_docview_breaks(self) end
|
||||||
|
old_scroll_to_line(self, ...)
|
||||||
|
end
|
||||||
|
|
||||||
local old_scroll_to_make_visible = DocView.scroll_to_make_visible
|
local old_scroll_to_make_visible = DocView.scroll_to_make_visible
|
||||||
function DocView:scroll_to_make_visible(line, col)
|
function DocView:scroll_to_make_visible(line, col)
|
||||||
|
if self.wrapping_enabled then LineWrapping.update_docview_breaks(self) end
|
||||||
old_scroll_to_make_visible(self, line, col)
|
old_scroll_to_make_visible(self, line, col)
|
||||||
if self.wrapped_settings then self.scroll.to.x = 0 end
|
if self.wrapped_settings then self.scroll.to.x = 0 end
|
||||||
end
|
end
|
||||||
|
@ -557,11 +573,13 @@ end
|
||||||
command.add(nil, {
|
command.add(nil, {
|
||||||
["line-wrapping:enable"] = function()
|
["line-wrapping:enable"] = function()
|
||||||
if core.active_view and core.active_view.doc then
|
if core.active_view and core.active_view.doc then
|
||||||
|
core.active_view.wrapping_enabled = true
|
||||||
LineWrapping.update_docview_breaks(core.active_view)
|
LineWrapping.update_docview_breaks(core.active_view)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
["line-wrapping:disable"] = function()
|
["line-wrapping:disable"] = function()
|
||||||
if core.active_view and core.active_view.doc then
|
if core.active_view and core.active_view.doc then
|
||||||
|
core.active_view.wrapping_enabled = false
|
||||||
LineWrapping.reconstruct_breaks(core.active_view, core.active_view:get_font(), math.huge)
|
LineWrapping.reconstruct_breaks(core.active_view, core.active_view:get_font(), math.huge)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
|
@ -6,7 +6,7 @@ local command = require "core.command"
|
||||||
local style = require "core.style"
|
local style = require "core.style"
|
||||||
local View = require "core.view"
|
local View = require "core.view"
|
||||||
|
|
||||||
|
---@class plugins.projectsearch.resultsview : core.view
|
||||||
local ResultsView = View:extend()
|
local ResultsView = View:extend()
|
||||||
|
|
||||||
ResultsView.context = "session"
|
ResultsView.context = "session"
|
||||||
|
@ -219,6 +219,10 @@ function ResultsView:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param path string
|
||||||
|
---@param text string
|
||||||
|
---@param fn fun(line_text:string):...
|
||||||
|
---@return plugins.projectsearch.resultsview?
|
||||||
local function begin_search(path, text, fn)
|
local function begin_search(path, text, fn)
|
||||||
if text == "" then
|
if text == "" then
|
||||||
core.error("Expected non-empty string")
|
core.error("Expected non-empty string")
|
||||||
|
@ -226,6 +230,7 @@ local function begin_search(path, text, fn)
|
||||||
end
|
end
|
||||||
local rv = ResultsView(path, text, fn)
|
local rv = ResultsView(path, text, fn)
|
||||||
core.root_view:get_active_node_default():add_view(rv)
|
core.root_view:get_active_node_default():add_view(rv)
|
||||||
|
return rv
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -249,6 +254,59 @@ local function normalize_path(path)
|
||||||
return path
|
return path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@class plugins.projectsearch
|
||||||
|
local projectsearch = {}
|
||||||
|
|
||||||
|
---@type plugins.projectsearch.resultsview
|
||||||
|
projectsearch.ResultsView = ResultsView
|
||||||
|
|
||||||
|
---@param text string
|
||||||
|
---@param path string
|
||||||
|
---@param insensitive? boolean
|
||||||
|
---@return plugins.projectsearch.resultsview?
|
||||||
|
function projectsearch.search_plain(text, path, insensitive)
|
||||||
|
if insensitive then text = text:lower() end
|
||||||
|
return begin_search(path, text, function(line_text)
|
||||||
|
if insensitive then
|
||||||
|
return line_text:lower():find(text, nil, true)
|
||||||
|
else
|
||||||
|
return line_text:find(text, nil, true)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param text string
|
||||||
|
---@param path string
|
||||||
|
---@param insensitive? boolean
|
||||||
|
---@return plugins.projectsearch.resultsview?
|
||||||
|
function projectsearch.search_regex(text, path, insensitive)
|
||||||
|
local re, errmsg
|
||||||
|
if insensitive then
|
||||||
|
re, errmsg = regex.compile(text, "i")
|
||||||
|
else
|
||||||
|
re, errmsg = regex.compile(text)
|
||||||
|
end
|
||||||
|
if not re then core.log("%s", errmsg) return end
|
||||||
|
return begin_search(path, text, function(line_text)
|
||||||
|
return regex.cmatch(re, line_text)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param text string
|
||||||
|
---@param path string
|
||||||
|
---@param insensitive? boolean
|
||||||
|
---@return plugins.projectsearch.resultsview?
|
||||||
|
function projectsearch.search_fuzzy(text, path, insensitive)
|
||||||
|
if insensitive then text = text:lower() end
|
||||||
|
return begin_search(path, text, function(line_text)
|
||||||
|
if insensitive then
|
||||||
|
return common.fuzzy_match(line_text:lower(), text) and 1
|
||||||
|
else
|
||||||
|
return common.fuzzy_match(line_text, text) and 1
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
command.add(nil, {
|
command.add(nil, {
|
||||||
["project-search:find"] = function(path)
|
["project-search:find"] = function(path)
|
||||||
|
@ -256,10 +314,7 @@ command.add(nil, {
|
||||||
text = get_selected_text(),
|
text = get_selected_text(),
|
||||||
select_text = true,
|
select_text = true,
|
||||||
submit = function(text)
|
submit = function(text)
|
||||||
text = text:lower()
|
projectsearch.search_plain(text, path, true)
|
||||||
begin_search(path, text, function(line_text)
|
|
||||||
return line_text:lower():find(text, nil, true)
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
|
@ -267,10 +322,7 @@ command.add(nil, {
|
||||||
["project-search:find-regex"] = function(path)
|
["project-search:find-regex"] = function(path)
|
||||||
core.command_view:enter("Find Regex In " .. (normalize_path(path) or "Project"), {
|
core.command_view:enter("Find Regex In " .. (normalize_path(path) or "Project"), {
|
||||||
submit = function(text)
|
submit = function(text)
|
||||||
local re = regex.compile(text, "i")
|
projectsearch.search_regex(text, path, true)
|
||||||
begin_search(path, text, function(line_text)
|
|
||||||
return regex.cmatch(re, line_text)
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
|
@ -280,9 +332,7 @@ command.add(nil, {
|
||||||
text = get_selected_text(),
|
text = get_selected_text(),
|
||||||
select_text = true,
|
select_text = true,
|
||||||
submit = function(text)
|
submit = function(text)
|
||||||
begin_search(path, text, function(line_text)
|
projectsearch.search_fuzzy(text, path, true)
|
||||||
return common.fuzzy_match(line_text, text) and 1
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
|
@ -344,3 +394,6 @@ keymap.add {
|
||||||
["home"] = "project-search:move-to-start-of-doc",
|
["home"] = "project-search:move-to-start-of-doc",
|
||||||
["end"] = "project-search:move-to-end-of-doc"
|
["end"] = "project-search:move-to-end-of-doc"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return projectsearch
|
||||||
|
|
|
@ -39,6 +39,11 @@ end
|
||||||
|
|
||||||
function ToolbarView:toggle_visible()
|
function ToolbarView:toggle_visible()
|
||||||
self.visible = not self.visible
|
self.visible = not self.visible
|
||||||
|
if self.tooltip then
|
||||||
|
core.status_view:remove_tooltip()
|
||||||
|
self.tooltip = false
|
||||||
|
end
|
||||||
|
self.hovered_item = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function ToolbarView:get_icon_width()
|
function ToolbarView:get_icon_width()
|
||||||
|
@ -73,6 +78,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
function ToolbarView:draw()
|
function ToolbarView:draw()
|
||||||
|
if not self.visible then return end
|
||||||
self:draw_background(style.background2)
|
self:draw_background(style.background2)
|
||||||
|
|
||||||
for item, x, y, w, h in self:each_item() do
|
for item, x, y, w, h in self:each_item() do
|
||||||
|
@ -83,6 +89,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
function ToolbarView:on_mouse_pressed(button, x, y, clicks)
|
function ToolbarView:on_mouse_pressed(button, x, y, clicks)
|
||||||
|
if not self.visible then return end
|
||||||
local caught = ToolbarView.super.on_mouse_pressed(self, button, x, y, clicks)
|
local caught = ToolbarView.super.on_mouse_pressed(self, button, x, y, clicks)
|
||||||
if caught then return caught end
|
if caught then return caught end
|
||||||
core.set_active_view(core.last_active_view)
|
core.set_active_view(core.last_active_view)
|
||||||
|
@ -94,6 +101,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
function ToolbarView:on_mouse_moved(px, py, ...)
|
function ToolbarView:on_mouse_moved(px, py, ...)
|
||||||
|
if not self.visible then return end
|
||||||
ToolbarView.super.on_mouse_moved(self, px, py, ...)
|
ToolbarView.super.on_mouse_moved(self, px, py, ...)
|
||||||
self.hovered_item = nil
|
self.hovered_item = nil
|
||||||
local x_min, x_max, y_min, y_max = self.size.x, 0, self.size.y, 0
|
local x_min, x_max, y_min, y_max = self.size.x, 0, self.size.y, 0
|
||||||
|
|
|
@ -50,20 +50,6 @@ function TreeView:new()
|
||||||
|
|
||||||
self.item_icon_width = 0
|
self.item_icon_width = 0
|
||||||
self.item_text_spacing = 0
|
self.item_text_spacing = 0
|
||||||
|
|
||||||
self:add_core_hooks()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function TreeView:add_core_hooks()
|
|
||||||
-- When a file or directory is deleted we delete the corresponding cache entry
|
|
||||||
-- because if the entry is recreated we may use wrong information from cache.
|
|
||||||
local on_delete = core.on_dirmonitor_delete
|
|
||||||
core.on_dirmonitor_delete = function(dir, filepath)
|
|
||||||
local cache = self.cache[dir.name]
|
|
||||||
if cache then cache[filepath] = nil end
|
|
||||||
on_delete(dir, filepath)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,7 +72,7 @@ function TreeView:get_cached(dir, item, dirname)
|
||||||
-- used only to identify the entry into the cache.
|
-- used only to identify the entry into the cache.
|
||||||
local cache_name = item.filename .. (item.topdir and ":" or "")
|
local cache_name = item.filename .. (item.topdir and ":" or "")
|
||||||
local t = dir_cache[cache_name]
|
local t = dir_cache[cache_name]
|
||||||
if not t then
|
if not t or t.type ~= item.type then
|
||||||
t = {}
|
t = {}
|
||||||
local basename = common.basename(item.filename)
|
local basename = common.basename(item.filename)
|
||||||
if item.topdir then
|
if item.topdir then
|
||||||
|
@ -209,10 +195,10 @@ end
|
||||||
|
|
||||||
function TreeView:on_mouse_moved(px, py, ...)
|
function TreeView:on_mouse_moved(px, py, ...)
|
||||||
if not self.visible then return end
|
if not self.visible then return end
|
||||||
TreeView.super.on_mouse_moved(self, px, py, ...)
|
|
||||||
self.cursor_pos.x = px
|
self.cursor_pos.x = px
|
||||||
self.cursor_pos.y = py
|
self.cursor_pos.y = py
|
||||||
if self.dragging_scrollbar then
|
if TreeView.super.on_mouse_moved(self, px, py, ...) then
|
||||||
|
-- mouse movement handled by the View (scrollbar)
|
||||||
self.hovered_item = nil
|
self.hovered_item = nil
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -728,16 +714,8 @@ command.add(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
end
|
end,
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
command.add(function()
|
|
||||||
if not (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) then return end
|
|
||||||
if core.root_view.overlapping_node.active_view ~= view then return end
|
|
||||||
local item = treeitem()
|
|
||||||
return item ~= nil, item
|
|
||||||
end, {
|
|
||||||
["treeview:rename"] = function(item)
|
["treeview:rename"] = function(item)
|
||||||
local old_filename = item.filename
|
local old_filename = item.filename
|
||||||
local old_abs_filename = item.abs_filename
|
local old_abs_filename = item.abs_filename
|
||||||
|
|
|
@ -1,10 +1,53 @@
|
||||||
-- mod-version:3
|
-- mod-version:3
|
||||||
local core = require "core"
|
local common = require "core.common"
|
||||||
|
local config = require "core.config"
|
||||||
local command = require "core.command"
|
local command = require "core.command"
|
||||||
local Doc = require "core.doc"
|
local Doc = require "core.doc"
|
||||||
|
|
||||||
|
---@class config.plugins.trimwhitespace
|
||||||
|
---@field enabled boolean
|
||||||
|
---@field trim_empty_end_lines boolean
|
||||||
|
config.plugins.trimwhitespace = common.merge({
|
||||||
|
enabled = true,
|
||||||
|
trim_empty_end_lines = false,
|
||||||
|
config_spec = {
|
||||||
|
name = "Trim Whitespace",
|
||||||
|
{
|
||||||
|
label = "Enabled",
|
||||||
|
description = "Disable or enable the trimming of white spaces by default.",
|
||||||
|
path = "enabled",
|
||||||
|
type = "toggle",
|
||||||
|
default = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label = "Trim Empty End Lines",
|
||||||
|
description = "Remove any empty new lines at the end of documents.",
|
||||||
|
path = "trim_empty_end_lines",
|
||||||
|
type = "toggle",
|
||||||
|
default = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, config.plugins.trimwhitespace)
|
||||||
|
|
||||||
local function trim_trailing_whitespace(doc)
|
---@class plugins.trimwhitespace
|
||||||
|
local trimwhitespace = {}
|
||||||
|
|
||||||
|
---Disable whitespace trimming for a specific document.
|
||||||
|
---@param doc core.doc
|
||||||
|
function trimwhitespace.disable(doc)
|
||||||
|
doc.disable_trim_whitespace = true
|
||||||
|
end
|
||||||
|
|
||||||
|
---Re-enable whitespace trimming if previously disabled.
|
||||||
|
---@param doc core.doc
|
||||||
|
function trimwhitespace.enable(doc)
|
||||||
|
doc.disable_trim_whitespace = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
---Perform whitespace trimming in all lines of a document except the
|
||||||
|
---line where the caret is currently positioned.
|
||||||
|
---@param doc core.doc
|
||||||
|
function trimwhitespace.trim(doc)
|
||||||
local cline, ccol = doc:get_selection()
|
local cline, ccol = doc:get_selection()
|
||||||
for i = 1, #doc.lines do
|
for i = 1, #doc.lines do
|
||||||
local old_text = doc:get_text(i, 1, i, math.huge)
|
local old_text = doc:get_text(i, 1, i, math.huge)
|
||||||
|
@ -22,16 +65,54 @@ local function trim_trailing_whitespace(doc)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Removes all empty new lines at the end of the document.
|
||||||
|
---@param doc core.doc
|
||||||
|
---@param raw_remove? boolean Perform the removal not registering to undo stack
|
||||||
|
function trimwhitespace.trim_empty_end_lines(doc, raw_remove)
|
||||||
|
for _=#doc.lines, 1, -1 do
|
||||||
|
local l = #doc.lines
|
||||||
|
if l > 1 and doc.lines[l] == "\n" then
|
||||||
|
local current_line = doc:get_selection()
|
||||||
|
if current_line == l then
|
||||||
|
doc:set_selection(l-1, math.huge, l-1, math.huge)
|
||||||
|
end
|
||||||
|
if not raw_remove then
|
||||||
|
doc:remove(l-1, math.huge, l, math.huge)
|
||||||
|
else
|
||||||
|
table.remove(doc.lines, l)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
command.add("core.docview", {
|
command.add("core.docview", {
|
||||||
["trim-whitespace:trim-trailing-whitespace"] = function(dv)
|
["trim-whitespace:trim-trailing-whitespace"] = function(dv)
|
||||||
trim_trailing_whitespace(dv.doc)
|
trimwhitespace.trim(dv.doc)
|
||||||
|
end,
|
||||||
|
|
||||||
|
["trim-whitespace:trim-empty-end-lines"] = function(dv)
|
||||||
|
trimwhitespace.trim_empty_end_lines(dv.doc)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
local save = Doc.save
|
local doc_save = Doc.save
|
||||||
Doc.save = function(self, ...)
|
Doc.save = function(self, ...)
|
||||||
trim_trailing_whitespace(self)
|
if
|
||||||
save(self, ...)
|
config.plugins.trimwhitespace.enabled
|
||||||
|
and
|
||||||
|
not self.disable_trim_whitespace
|
||||||
|
then
|
||||||
|
trimwhitespace.trim(self)
|
||||||
|
if config.plugins.trimwhitespace.trim_empty_end_lines then
|
||||||
|
trimwhitespace.trim_empty_end_lines(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
doc_save(self, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return trimwhitespace
|
||||||
|
|
|
@ -92,7 +92,8 @@ local function save_view(view)
|
||||||
return {
|
return {
|
||||||
type = "view",
|
type = "view",
|
||||||
active = (core.active_view == view),
|
active = (core.active_view == view),
|
||||||
module = name
|
module = name,
|
||||||
|
scroll = { x = view.scroll.to.x, y = view.scroll.to.y, to = { x = view.scroll.to.x, y = view.scroll.to.y } },
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -162,6 +163,9 @@ local function load_node(node, t)
|
||||||
if t.active_view == i then
|
if t.active_view == i then
|
||||||
active_view = view
|
active_view = view
|
||||||
end
|
end
|
||||||
|
if not view:is(DocView) then
|
||||||
|
view.scroll = v.scroll
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if active_view then
|
if active_view then
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
---@meta
|
||||||
|
|
||||||
|
---
|
||||||
|
---Functionality that allows to monitor a directory or file for changes
|
||||||
|
---using the native facilities provided by the current operating system
|
||||||
|
---for better efficiency and performance.
|
||||||
|
---@class dirmonitor
|
||||||
|
dirmonitor = {}
|
||||||
|
|
||||||
|
---@alias dirmonitor.callback fun(fd_or_path:integer|string)
|
||||||
|
|
||||||
|
---
|
||||||
|
---Creates a new dirmonitor object.
|
||||||
|
---
|
||||||
|
---@return dirmonitor
|
||||||
|
function dirmonitor.new() end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Monitors a directory or file for changes.
|
||||||
|
---
|
||||||
|
---In "multiple" mode you will need to call this method more than once to
|
||||||
|
---recursively monitor directories and files.
|
||||||
|
---
|
||||||
|
---In "single" mode you will only need to call this method for the parent
|
||||||
|
---directory and every sub directory and files will get automatically monitored.
|
||||||
|
---
|
||||||
|
---@param path string
|
||||||
|
---
|
||||||
|
---@return integer fd The file descriptor id assigned to the monitored path when
|
||||||
|
---the mode is "multiple", in "single" mode: 1 for success or -1 on failure.
|
||||||
|
function dirmonitor:watch(path) end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Stops monitoring a file descriptor in "multiple" mode
|
||||||
|
---or in "single" mode a directory path.
|
||||||
|
---
|
||||||
|
---@param fd_or_path integer | string A file descriptor or path.
|
||||||
|
function dirmonitor:unwatch(fd_or_path) end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Verify if the resources registered for monitoring have changed, should
|
||||||
|
---be called periodically to check for changes.
|
||||||
|
---
|
||||||
|
---The callback will be called for each file or directory that was:
|
||||||
|
---edited, removed or added. A file descriptor will be passed to the
|
||||||
|
---callback in "multiple" mode or a path in "single" mode.
|
||||||
|
---
|
||||||
|
---@param callback dirmonitor.callback
|
||||||
|
---
|
||||||
|
---@return boolean? changes True when changes were detected.
|
||||||
|
function dirmonitor:check(callback) end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Get the working mode for the current file system monitoring backend.
|
||||||
|
---
|
||||||
|
---"multiple": various file descriptors are needed to recursively monitor a
|
||||||
|
---directory contents, backends: inotify and kqueue.
|
||||||
|
---
|
||||||
|
---"single": a single process takes care of monitoring a path recursively
|
||||||
|
---so no individual file descriptors are used, backends: win32 and fsevents.
|
||||||
|
---
|
||||||
|
---@return "single" | "multiple"
|
||||||
|
function dirmonitor:mode() end
|
||||||
|
|
||||||
|
|
||||||
|
return dirmonitor
|
|
@ -10,7 +10,7 @@ ARGS = {}
|
||||||
ARCH = "Architecture-OperatingSystem"
|
ARCH = "Architecture-OperatingSystem"
|
||||||
|
|
||||||
---The current operating system.
|
---The current operating system.
|
||||||
---@type string | "'Windows'" | "'Mac OS X'" | "'Linux'" | "'iOS'" | "'Android'"
|
---@type string | "Windows" | "Mac OS X" | "Linux" | "iOS" | "Android"
|
||||||
PLATFORM = "Operating System"
|
PLATFORM = "Operating System"
|
||||||
|
|
||||||
---The current text or ui scale.
|
---The current text or ui scale.
|
||||||
|
|
|
@ -81,27 +81,27 @@ process.REDIRECT_DISCARD = 3
|
||||||
process.REDIRECT_STDOUT = 4
|
process.REDIRECT_STDOUT = 4
|
||||||
|
|
||||||
---@alias process.errortype
|
---@alias process.errortype
|
||||||
---|>'process.ERROR_PIPE'
|
---| `process.ERROR_PIPE`
|
||||||
---| 'process.ERROR_WOULDBLOCK'
|
---| `process.ERROR_WOULDBLOCK`
|
||||||
---| 'process.ERROR_TIMEDOUT'
|
---| `process.ERROR_TIMEDOUT`
|
||||||
---| 'process.ERROR_INVAL'
|
---| `process.ERROR_INVAL`
|
||||||
---| 'process.ERROR_NOMEM'
|
---| `process.ERROR_NOMEM`
|
||||||
|
|
||||||
---@alias process.streamtype
|
---@alias process.streamtype
|
||||||
---|>'process.STREAM_STDIN'
|
---| `process.STREAM_STDIN`
|
||||||
---| 'process.STREAM_STDOUT'
|
---| `process.STREAM_STDOUT`
|
||||||
---| 'process.STREAM_STDERR'
|
---| `process.STREAM_STDERR`
|
||||||
|
|
||||||
---@alias process.waittype
|
---@alias process.waittype
|
||||||
---|>'process.WAIT_INFINITE'
|
---| `process.WAIT_INFINITE`
|
||||||
---| 'process.WAIT_DEADLINE'
|
---| `process.WAIT_DEADLINE`
|
||||||
|
|
||||||
---@alias process.redirecttype
|
---@alias process.redirecttype
|
||||||
---|>'process.REDIRECT_DEFAULT'
|
---| `process.REDIRECT_DEFAULT`
|
||||||
---| 'process.REDIRECT_PIPE'
|
---| `process.REDIRECT_PIPE`
|
||||||
---| 'process.REDIRECT_PARENT'
|
---| `process.REDIRECT_PARENT`
|
||||||
---| 'process.REDIRECT_DISCARD'
|
---| `process.REDIRECT_DISCARD`
|
||||||
---| 'process.REDIRECT_STDOUT'
|
---| `process.REDIRECT_STDOUT`
|
||||||
|
|
||||||
---
|
---
|
||||||
--- Options that can be passed to process.start()
|
--- Options that can be passed to process.start()
|
||||||
|
@ -112,7 +112,6 @@ process.REDIRECT_STDOUT = 4
|
||||||
---@field public stdout process.redirecttype
|
---@field public stdout process.redirecttype
|
||||||
---@field public stderr process.redirecttype
|
---@field public stderr process.redirecttype
|
||||||
---@field public env table<string, string>
|
---@field public env table<string, string>
|
||||||
process.options = {}
|
|
||||||
|
|
||||||
---
|
---
|
||||||
---Create and start a new process
|
---Create and start a new process
|
||||||
|
@ -233,3 +232,6 @@ function process:returncode() end
|
||||||
---
|
---
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function process:running() end
|
function process:running() end
|
||||||
|
|
||||||
|
|
||||||
|
return process
|
||||||
|
|
|
@ -31,9 +31,9 @@ regex.NOTEMPTY = 0x00000004
|
||||||
regex.NOTEMPTY_ATSTART = 0x00000008
|
regex.NOTEMPTY_ATSTART = 0x00000008
|
||||||
|
|
||||||
---@alias regex.modifiers
|
---@alias regex.modifiers
|
||||||
---|>'"i"' # Case insesitive matching
|
---| "i" # Case insesitive matching
|
||||||
---| '"m"' # Multiline matching
|
---| "m" # Multiline matching
|
||||||
---| '"s"' # Match all characters with dot (.) metacharacter even new lines
|
---| "s" # Match all characters with dot (.) metacharacter even new lines
|
||||||
|
|
||||||
---
|
---
|
||||||
---Compiles a regular expression pattern that can be used to search in strings.
|
---Compiles a regular expression pattern that can be used to search in strings.
|
||||||
|
@ -41,8 +41,8 @@ regex.NOTEMPTY_ATSTART = 0x00000008
|
||||||
---@param pattern string
|
---@param pattern string
|
||||||
---@param options? regex.modifiers A string of one or more pattern modifiers.
|
---@param options? regex.modifiers A string of one or more pattern modifiers.
|
||||||
---
|
---
|
||||||
---@return regex|string regex Ready to use regular expression object or error
|
---@return regex? regex Ready to use regular expression object or nil on error.
|
||||||
---message if compiling the pattern failed.
|
---@return string? error The error message if compiling the pattern failed.
|
||||||
function regex.compile(pattern, options) end
|
function regex.compile(pattern, options) end
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -53,5 +53,42 @@ function regex.compile(pattern, options) end
|
||||||
---@param options? integer A bit field of matching options, eg:
|
---@param options? integer A bit field of matching options, eg:
|
||||||
---regex.NOTBOL | regex.NOTEMPTY
|
---regex.NOTBOL | regex.NOTEMPTY
|
||||||
---
|
---
|
||||||
---@return table<integer, integer> list List of offsets where a match was found.
|
---@return integer? ... List of offsets where a match was found.
|
||||||
function regex:cmatch(subject, offset, options) end
|
function regex:cmatch(subject, offset, options) end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Returns an iterator function that, each time it is called, returns the
|
||||||
|
---next captures from `pattern` over the string subject.
|
||||||
|
---
|
||||||
|
---Example:
|
||||||
|
---```lua
|
||||||
|
--- s = "hello world hello world"
|
||||||
|
--- for hello, world in regex.gmatch("(hello)\\s+(world)", s) do
|
||||||
|
--- print(hello .. " " .. world)
|
||||||
|
--- end
|
||||||
|
---```
|
||||||
|
---
|
||||||
|
---@param pattern string
|
||||||
|
---@param subject string
|
||||||
|
---@param offset? integer
|
||||||
|
---
|
||||||
|
---@return fun():string, ...
|
||||||
|
function regex.gmatch(pattern, subject, offset) end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Replaces the matched pattern globally on the subject with the given
|
||||||
|
---replacement, supports named captures ((?'name'<pattern>), ${name}) and
|
||||||
|
---$[1-9][0-9]* substitutions. Raises an error when failing to compile the
|
||||||
|
---pattern or by a substitution mistake.
|
||||||
|
---
|
||||||
|
---@param pattern regex|string
|
||||||
|
---@param subject string
|
||||||
|
---@param replacement string
|
||||||
|
---@param limit? integer Limits the number of substitutions that will be done.
|
||||||
|
---
|
||||||
|
---@return string? replaced_subject
|
||||||
|
---@return integer? total_replacements
|
||||||
|
function regex.gsub(pattern, subject, replacement, limit) end
|
||||||
|
|
||||||
|
|
||||||
|
return regex
|
||||||
|
|
|
@ -14,19 +14,17 @@ renderer = {}
|
||||||
---@field public g number Green
|
---@field public g number Green
|
||||||
---@field public b number Blue
|
---@field public b number Blue
|
||||||
---@field public a number Alpha
|
---@field public a number Alpha
|
||||||
renderer.color = {}
|
|
||||||
|
|
||||||
---
|
---
|
||||||
---Represent options that affect a font's rendering.
|
---Represent options that affect a font's rendering.
|
||||||
---@class renderer.fontoptions
|
---@class renderer.fontoptions
|
||||||
---@field public antialiasing "'none'" | "'grayscale'" | "'subpixel'"
|
---@field public antialiasing "none" | "grayscale" | "subpixel"
|
||||||
---@field public hinting "'slight'" | "'none'" | '"full"'
|
---@field public hinting "slight" | "none" | "full"
|
||||||
-- @field public bold boolean
|
---@field public bold boolean
|
||||||
-- @field public italic boolean
|
---@field public italic boolean
|
||||||
-- @field public underline boolean
|
---@field public underline boolean
|
||||||
-- @field public smoothing boolean
|
---@field public smoothing boolean
|
||||||
-- @field public strikethrough boolean
|
---@field public strikethrough boolean
|
||||||
renderer.fontoptions = {}
|
|
||||||
|
|
||||||
---
|
---
|
||||||
---@class renderer.font
|
---@class renderer.font
|
||||||
|
@ -154,3 +152,6 @@ function renderer.draw_rect(x, y, width, height, color) end
|
||||||
---
|
---
|
||||||
---@return number x
|
---@return number x
|
||||||
function renderer.draw_text(font, text, x, y, color) end
|
function renderer.draw_text(font, text, x, y, color) end
|
||||||
|
|
||||||
|
|
||||||
|
return renderer
|
||||||
|
|
|
@ -101,14 +101,14 @@ function string.unext(s, charpos, index) end
|
||||||
---@param s string
|
---@param s string
|
||||||
---@param idx? integer
|
---@param idx? integer
|
||||||
---@param substring string
|
---@param substring string
|
||||||
---return string new_string
|
---@return string new_string
|
||||||
function string.uinsert(s, idx, substring) end
|
function string.uinsert(s, idx, substring) end
|
||||||
|
|
||||||
---Equivalent to utf8.remove()
|
---Equivalent to utf8.remove()
|
||||||
---@param s string
|
---@param s string
|
||||||
---@param start? integer
|
---@param start? integer
|
||||||
---@param stop? integer
|
---@param stop? integer
|
||||||
---return string new_string
|
---@return string new_string
|
||||||
function string.uremove(s, start, stop) end
|
function string.uremove(s, start, stop) end
|
||||||
|
|
||||||
---Equivalent to utf8.width()
|
---Equivalent to utf8.width()
|
||||||
|
@ -130,12 +130,12 @@ function string.uwidthindex(s, location, ambi_is_double, default_width) end
|
||||||
|
|
||||||
---Equivalent to utf8.title()
|
---Equivalent to utf8.title()
|
||||||
---@param s string
|
---@param s string
|
||||||
---return string new_string
|
---@return string new_string
|
||||||
function string.utitle(s) end
|
function string.utitle(s) end
|
||||||
|
|
||||||
---Equivalent to utf8.fold()
|
---Equivalent to utf8.fold()
|
||||||
---@param s string
|
---@param s string
|
||||||
---return string new_string
|
---@return string new_string
|
||||||
function string.ufold(s) end
|
function string.ufold(s) end
|
||||||
|
|
||||||
---Equivalent to utf8.ncasecmp()
|
---Equivalent to utf8.ncasecmp()
|
||||||
|
|
|
@ -6,15 +6,15 @@
|
||||||
system = {}
|
system = {}
|
||||||
|
|
||||||
---@alias system.fileinfotype
|
---@alias system.fileinfotype
|
||||||
---|>'"file"' # It is a file.
|
---| "file" # It is a file.
|
||||||
---| '"dir"' # It is a directory.
|
---| "dir" # It is a directory.
|
||||||
|
|
||||||
---
|
---
|
||||||
---@class system.fileinfo
|
---@class system.fileinfo
|
||||||
---@field public modified number A timestamp in seconds.
|
---@field public modified number A timestamp in seconds.
|
||||||
---@field public size number Size in bytes.
|
---@field public size number Size in bytes.
|
||||||
---@field public type system.fileinfotype Type of file
|
---@field public type system.fileinfotype Type of file
|
||||||
system.fileinfo = {}
|
---@field public symlink boolean The directory is a symlink. This field is only set on Linux and on directories.
|
||||||
|
|
||||||
---
|
---
|
||||||
---Core function used to retrieve the current event been triggered by SDL.
|
---Core function used to retrieve the current event been triggered by SDL.
|
||||||
|
@ -24,7 +24,7 @@ system.fileinfo = {}
|
||||||
---
|
---
|
||||||
---Window events:
|
---Window events:
|
||||||
--- * "quit"
|
--- * "quit"
|
||||||
--- * "resized" -> width, height
|
--- * "resized" -> width, height (in points)
|
||||||
--- * "exposed"
|
--- * "exposed"
|
||||||
--- * "minimized"
|
--- * "minimized"
|
||||||
--- * "maximized"
|
--- * "maximized"
|
||||||
|
@ -38,12 +38,18 @@ system.fileinfo = {}
|
||||||
--- * "keypressed" -> key_name
|
--- * "keypressed" -> key_name
|
||||||
--- * "keyreleased" -> key_name
|
--- * "keyreleased" -> key_name
|
||||||
--- * "textinput" -> text
|
--- * "textinput" -> text
|
||||||
|
--- * "textediting" -> text, start, length
|
||||||
---
|
---
|
||||||
---Mouse events:
|
---Mouse events:
|
||||||
--- * "mousepressed" -> button_name, x, y, amount_of_clicks
|
--- * "mousepressed" -> button_name, x, y, amount_of_clicks
|
||||||
--- * "mousereleased" -> button_name, x, y
|
--- * "mousereleased" -> button_name, x, y
|
||||||
--- * "mousemoved" -> x, y, relative_x, relative_y
|
--- * "mousemoved" -> x, y, relative_x, relative_y
|
||||||
--- * "mousewheel" -> y
|
--- * "mousewheel" -> y, x
|
||||||
|
---
|
||||||
|
---Touch events:
|
||||||
|
--- * "touchpressed" -> x, y, finger_id
|
||||||
|
--- * "touchreleased" -> x, y, finger_id
|
||||||
|
--- * "touchmoved" -> x, y, distance_x, distance_y, finger_id
|
||||||
---
|
---
|
||||||
---@return string type
|
---@return string type
|
||||||
---@return any? arg1
|
---@return any? arg1
|
||||||
|
@ -64,7 +70,7 @@ function system.wait_event(timeout) end
|
||||||
---
|
---
|
||||||
---Change the cursor type displayed on screen.
|
---Change the cursor type displayed on screen.
|
||||||
---
|
---
|
||||||
---@param type string | "'arrow'" | "'ibeam'" | "'sizeh'" | "'sizev'" | "'hand'"
|
---@param type string | "arrow" | "ibeam" | "sizeh" | "sizev" | "hand"
|
||||||
function system.set_cursor(type) end
|
function system.set_cursor(type) end
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -74,10 +80,10 @@ function system.set_cursor(type) end
|
||||||
function system.set_window_title(title) end
|
function system.set_window_title(title) end
|
||||||
|
|
||||||
---@alias system.windowmode
|
---@alias system.windowmode
|
||||||
---|>'"normal"'
|
---| "normal"
|
||||||
---| '"minimized"'
|
---| "minimized"
|
||||||
---| '"maximized"'
|
---| "maximized"
|
||||||
---| '"fullscreen"'
|
---| "fullscreen"
|
||||||
|
|
||||||
---
|
---
|
||||||
---Change the window mode.
|
---Change the window mode.
|
||||||
|
@ -103,7 +109,7 @@ function system.set_window_bordered(bordered) end
|
||||||
---for custom window management.
|
---for custom window management.
|
||||||
---
|
---
|
||||||
---@param title_height number
|
---@param title_height number
|
||||||
---@param controls_width number This is for minimize, maximize, close, 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
|
||||||
|
|
||||||
|
@ -131,6 +137,30 @@ function system.set_window_size(width, height, x, y) end
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function system.window_has_focus() end
|
function system.window_has_focus() end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Gets the mode of the window.
|
||||||
|
---
|
||||||
|
---@return system.windowmode
|
||||||
|
function system.get_window_mode() end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Sets the position of the IME composition window.
|
||||||
|
---
|
||||||
|
---@param x number
|
||||||
|
---@param y number
|
||||||
|
---@param width number
|
||||||
|
---@param height number
|
||||||
|
function system.set_text_input_rect(x, y, width, height) end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Clears any ongoing composition on the IME
|
||||||
|
function system.clear_ime() end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Raise the main window and give it input focus.
|
||||||
|
---Note: may not always be obeyed by the users window manager.
|
||||||
|
function system.raise_window() end
|
||||||
|
|
||||||
---
|
---
|
||||||
---Opens a message box to display an error message.
|
---Opens a message box to display an error message.
|
||||||
---
|
---
|
||||||
|
@ -138,6 +168,14 @@ function system.window_has_focus() end
|
||||||
---@param message string
|
---@param message string
|
||||||
function system.show_fatal_error(title, message) end
|
function system.show_fatal_error(title, message) end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Deletes an empty directory.
|
||||||
|
---
|
||||||
|
---@param path string
|
||||||
|
---@return boolean success True if the operation suceeded, false otherwise
|
||||||
|
---@return string? message An error message if the operation failed
|
||||||
|
function system.rmdir(path) end
|
||||||
|
|
||||||
---
|
---
|
||||||
---Change the current directory path which affects relative file operations.
|
---Change the current directory path which affects relative file operations.
|
||||||
---This function raises an error if the path doesn't exists.
|
---This function raises an error if the path doesn't exists.
|
||||||
|
@ -152,6 +190,7 @@ function system.chdir(path) end
|
||||||
---@param directory_path string
|
---@param directory_path string
|
||||||
---
|
---
|
||||||
---@return boolean created True on success or false on failure.
|
---@return boolean created True on success or false on failure.
|
||||||
|
---@return string? message The error message if the operation failed.
|
||||||
function system.mkdir(directory_path) end
|
function system.mkdir(directory_path) end
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -168,7 +207,7 @@ function system.list_dir(path) end
|
||||||
---
|
---
|
||||||
---@param path string
|
---@param path string
|
||||||
---
|
---
|
||||||
---@return string
|
---@return string? abspath
|
||||||
function system.absolute_path(path) end
|
function system.absolute_path(path) end
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -180,6 +219,28 @@ function system.absolute_path(path) end
|
||||||
---@return string? message Error message in case of error.
|
---@return string? message Error message in case of error.
|
||||||
function system.get_file_info(path) end
|
function system.get_file_info(path) end
|
||||||
|
|
||||||
|
|
||||||
|
---@alias system.fstype
|
||||||
|
---| "ext2/ext3"
|
||||||
|
---| "nfs"
|
||||||
|
---| "fuse"
|
||||||
|
---| "smb"
|
||||||
|
---| "smb2"
|
||||||
|
---| "reiserfs"
|
||||||
|
---| "tmpfs"
|
||||||
|
---| "ramfs"
|
||||||
|
---| "ntfs"
|
||||||
|
|
||||||
|
---
|
||||||
|
---Gets the filesystem type of a path.
|
||||||
|
---Note: This only works on Linux.
|
||||||
|
---
|
||||||
|
---@param path string Can be path to a directory or a file
|
||||||
|
---
|
||||||
|
---@return system.fstype
|
||||||
|
function system.get_fs_type(path) end
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
---Retrieve the text currently stored on the clipboard.
|
---Retrieve the text currently stored on the clipboard.
|
||||||
---
|
---
|
||||||
|
@ -193,7 +254,7 @@ function system.get_clipboard() end
|
||||||
function system.set_clipboard(text) end
|
function system.set_clipboard(text) end
|
||||||
|
|
||||||
---
|
---
|
||||||
---Get the process id of lite-xl it self.
|
---Get the process id of lite-xl itself.
|
||||||
---
|
---
|
||||||
---@return integer
|
---@return integer
|
||||||
function system.get_process_id() end
|
function system.get_process_id() end
|
||||||
|
@ -215,7 +276,9 @@ function system.sleep(seconds) end
|
||||||
---Similar to os.execute() but does not return the exit status of the
|
---Similar to os.execute() but does not return the exit status of the
|
||||||
---executed command and executes the process in a non blocking way by
|
---executed command and executes the process in a non blocking way by
|
||||||
---forking it to the background.
|
---forking it to the background.
|
||||||
|
---Note: Do not use this function, use the Process API instead.
|
||||||
---
|
---
|
||||||
|
---@deprecated
|
||||||
---@param command string The command to execute.
|
---@param command string The command to execute.
|
||||||
function system.exec(command) end
|
function system.exec(command) end
|
||||||
|
|
||||||
|
@ -237,4 +300,25 @@ function system.fuzzy_match(haystack, needle, file) end
|
||||||
---
|
---
|
||||||
---@param opacity number A value from 0.0 to 1.0, the lower the value
|
---@param opacity number A value from 0.0 to 1.0, the lower the value
|
||||||
---the less visible the window will be.
|
---the less visible the window will be.
|
||||||
|
---@return boolean success True if the operation suceeded.
|
||||||
function system.set_window_opacity(opacity) end
|
function system.set_window_opacity(opacity) end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Loads a lua native module using the default Lua API or lite-xl native plugin API.
|
||||||
|
---Note: Never use this function directly.
|
||||||
|
---
|
||||||
|
---@param name string the name of the module
|
||||||
|
---@param path string the path to the shared library file
|
||||||
|
---@return number nargs the return value of the entrypoint
|
||||||
|
function system.load_native_plugin(name, path) end
|
||||||
|
|
||||||
|
---
|
||||||
|
---Compares two paths in the order used by TreeView.
|
||||||
|
---
|
||||||
|
---@param path1 string
|
||||||
|
---@param path2 string
|
||||||
|
---@return boolean compare_result True if path1 < path2
|
||||||
|
function system.path_compare(path1, path2) end
|
||||||
|
|
||||||
|
|
||||||
|
return system
|
||||||
|
|
|
@ -131,7 +131,7 @@ function utf8extra.next(s, charpos, index) end
|
||||||
---@param s string
|
---@param s string
|
||||||
---@param idx? integer
|
---@param idx? integer
|
||||||
---@param substring string
|
---@param substring string
|
||||||
---return string new_string
|
---@return string new_string
|
||||||
function utf8extra.insert(s, idx, substring) end
|
function utf8extra.insert(s, idx, substring) end
|
||||||
|
|
||||||
---Delete a substring in s. If neither start nor stop is given, delete the last
|
---Delete a substring in s. If neither start nor stop is given, delete the last
|
||||||
|
@ -141,7 +141,7 @@ function utf8extra.insert(s, idx, substring) end
|
||||||
---@param s string
|
---@param s string
|
||||||
---@param start? integer
|
---@param start? integer
|
||||||
---@param stop? integer
|
---@param stop? integer
|
||||||
---return string new_string
|
---@return string new_string
|
||||||
function utf8extra.remove(s, start, stop) end
|
function utf8extra.remove(s, start, stop) end
|
||||||
|
|
||||||
---Calculate the width of UTF-8 string s. if ambi_is_double is given, the
|
---Calculate the width of UTF-8 string s. if ambi_is_double is given, the
|
||||||
|
@ -174,14 +174,14 @@ function utf8extra.widthindex(s, location, ambi_is_double, default_width) end
|
||||||
---is a number, it's treat as a code point and return a convert code point
|
---is a number, it's treat as a code point and return a convert code point
|
||||||
---(number). utf8.lower/utf8.pper has the same extension.
|
---(number). utf8.lower/utf8.pper has the same extension.
|
||||||
---@param s string
|
---@param s string
|
||||||
---return string new_string
|
---@return string new_string
|
||||||
function utf8extra.title(s) end
|
function utf8extra.title(s) end
|
||||||
|
|
||||||
---Convert UTF-8 string s to folded case, used to compare by ignore case. if s
|
---Convert UTF-8 string s to folded case, used to compare by ignore case. if s
|
||||||
---is a number, it's treat as a code point and return a convert code point
|
---is a number, it's treat as a code point and return a convert code point
|
||||||
---(number). utf8.lower/utf8.pper has the same extension.
|
---(number). utf8.lower/utf8.pper has the same extension.
|
||||||
---@param s string
|
---@param s string
|
||||||
---return string new_string
|
---@return string new_string
|
||||||
function utf8extra.fold(s) end
|
function utf8extra.fold(s) end
|
||||||
|
|
||||||
---Compare a and b without case, -1 means a < b, 0 means a == b and 1 means a > b.
|
---Compare a and b without case, -1 means a < b, 0 means a == b and 1 means a > b.
|
||||||
|
@ -189,3 +189,6 @@ function utf8extra.fold(s) end
|
||||||
---@param b string
|
---@param b string
|
||||||
---@return integer result
|
---@return integer result
|
||||||
function utf8extra.ncasecmp(a, b) end
|
function utf8extra.ncasecmp(a, b) end
|
||||||
|
|
||||||
|
|
||||||
|
return utf8extra
|
||||||
|
|
20
meson.build
20
meson.build
|
@ -1,8 +1,8 @@
|
||||||
project('lite-xl',
|
project('lite-xl',
|
||||||
['c'],
|
['c'],
|
||||||
version : '2.1.0',
|
version : '2.1.1',
|
||||||
license : 'MIT',
|
license : 'MIT',
|
||||||
meson_version : '>= 0.47',
|
meson_version : '>= 0.56',
|
||||||
default_options : [
|
default_options : [
|
||||||
'c_std=gnu11',
|
'c_std=gnu11',
|
||||||
'wrap_mode=nofallback'
|
'wrap_mode=nofallback'
|
||||||
|
@ -52,6 +52,13 @@ lite_cargs = ['-DSDL_MAIN_HANDLED', '-DPCRE2_STATIC']
|
||||||
if get_option('renderer') or host_machine.system() == 'darwin'
|
if get_option('renderer') or host_machine.system() == 'darwin'
|
||||||
lite_cargs += '-DLITE_USE_SDL_RENDERER'
|
lite_cargs += '-DLITE_USE_SDL_RENDERER'
|
||||||
endif
|
endif
|
||||||
|
if get_option('arch_tuple') != ''
|
||||||
|
arch_tuple = get_option('arch_tuple')
|
||||||
|
else
|
||||||
|
arch_tuple = '@0@-@1@'.format(target_machine.cpu_family(), target_machine.system())
|
||||||
|
endif
|
||||||
|
lite_cargs += '-DLITE_ARCH_TUPLE="@0@"'.format(arch_tuple)
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Linker Settings
|
# Linker Settings
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
|
@ -82,13 +89,20 @@ if not get_option('source-only')
|
||||||
|
|
||||||
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 : last_lua,
|
lua_dep = dependency(lua, fallback: last_lua ? ['lua', 'lua_dep'] : [], required : false,
|
||||||
version: '>= 5.4',
|
version: '>= 5.4',
|
||||||
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']
|
||||||
)
|
)
|
||||||
if lua_dep.found()
|
if lua_dep.found()
|
||||||
break
|
break
|
||||||
endif
|
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
|
endforeach
|
||||||
|
|
||||||
pcre2_dep = dependency('libpcre2-8', fallback: ['pcre2', 'libpcre2_8'],
|
pcre2_dep = dependency('libpcre2-8', fallback: ['pcre2', 'libpcre2_8'],
|
||||||
|
|
|
@ -2,4 +2,5 @@ option('bundle', type : 'boolean', value : false, description: 'Build a macOS bu
|
||||||
option('source-only', type : 'boolean', value : false, description: 'Configure source files only, doesn\'t checks for dependencies')
|
option('source-only', type : 'boolean', value : false, description: 'Configure source files only, doesn\'t checks for dependencies')
|
||||||
option('portable', type : 'boolean', value : false, description: 'Portable install')
|
option('portable', type : 'boolean', value : false, description: 'Portable install')
|
||||||
option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer')
|
option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer')
|
||||||
option('dirmonitor_backend', type : 'combo', value : '', choices : ['', 'inotify', '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')
|
|
@ -29,6 +29,6 @@
|
||||||
</provides>
|
</provides>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
<release version="2.0.1" date="2021-08-28" />
|
<release version="2.1.1" date="2022-12-29" />
|
||||||
</releases>
|
</releases>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -7,4 +7,4 @@ Icon=lite-xl
|
||||||
Terminal=false
|
Terminal=false
|
||||||
StartupWMClass=lite-xl
|
StartupWMClass=lite-xl
|
||||||
Categories=Development;IDE;
|
Categories=Development;IDE;
|
||||||
MimeType=text/plain;
|
MimeType=text/plain;inode/directory;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
The lite_xl plugin API is quite simple. Any shared library can be a plugin file, so long
|
The lite_xl plugin API is quite simple. Any shared library can be a plugin file, so long
|
||||||
as it has an entrypoint that looks like the following, where xxxxx is the plugin name:
|
as it has an entrypoint that looks like the following, where xxxxx is the plugin name:
|
||||||
#include "lite_xl_plugin_api.h"
|
#include "lite_xl_plugin_api.h"
|
||||||
int lua_open_lite_xl_xxxxx(lua_State* L, void* XL) {
|
int luaopen_lite_xl_xxxxx(lua_State* L, void* XL) {
|
||||||
lite_xl_plugin_init(XL);
|
lite_xl_plugin_init(XL);
|
||||||
...
|
...
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -12,6 +12,11 @@ int lua_open_lite_xl_xxxxx(lua_State* L, void* XL) {
|
||||||
In linux, to compile this file, you'd do: 'gcc -o xxxxx.so -shared xxxxx.c'. Simple!
|
In linux, to compile this file, you'd do: 'gcc -o xxxxx.so -shared xxxxx.c'. Simple!
|
||||||
Due to the way the API is structured, you *should not* link or include lua libraries.
|
Due to the way the API is structured, you *should not* link or include lua libraries.
|
||||||
This file was automatically generated. DO NOT MODIFY DIRECTLY.
|
This file was automatically generated. DO NOT MODIFY DIRECTLY.
|
||||||
|
|
||||||
|
UNLESS you're us, and you had to modify this file manually to get it ready for 2.1.
|
||||||
|
|
||||||
|
Go figure.
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
@ -231,9 +236,6 @@ static int (*lua_absindex) (lua_State *L, int idx);
|
||||||
static int (*lua_gettop) (lua_State *L);
|
static int (*lua_gettop) (lua_State *L);
|
||||||
static void (*lua_settop) (lua_State *L, int idx);
|
static void (*lua_settop) (lua_State *L, int idx);
|
||||||
static void (*lua_pushvalue) (lua_State *L, int idx);
|
static void (*lua_pushvalue) (lua_State *L, int idx);
|
||||||
static void (*lua_remove) (lua_State *L, int idx);
|
|
||||||
static void (*lua_insert) (lua_State *L, int idx);
|
|
||||||
static void (*lua_replace) (lua_State *L, int idx);
|
|
||||||
static void (*lua_copy) (lua_State *L, int fromidx, int toidx);
|
static void (*lua_copy) (lua_State *L, int fromidx, int toidx);
|
||||||
static int (*lua_checkstack) (lua_State *L, int sz);
|
static int (*lua_checkstack) (lua_State *L, int sz);
|
||||||
static void (*lua_xmove) (lua_State *from, lua_State *to, int n);
|
static void (*lua_xmove) (lua_State *from, lua_State *to, int n);
|
||||||
|
@ -276,8 +278,10 @@ static void (*lua_rawgeti) (lua_State *L, int idx, int n);
|
||||||
static void (*lua_rawgetp) (lua_State *L, int idx, const void *p);
|
static void (*lua_rawgetp) (lua_State *L, int idx, const void *p);
|
||||||
static void (*lua_createtable) (lua_State *L, int narr, int nrec);
|
static void (*lua_createtable) (lua_State *L, int narr, int nrec);
|
||||||
static void *(*lua_newuserdata) (lua_State *L, size_t sz);
|
static void *(*lua_newuserdata) (lua_State *L, size_t sz);
|
||||||
|
static void *(*lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue);
|
||||||
static int (*lua_getmetatable) (lua_State *L, int objindex);
|
static int (*lua_getmetatable) (lua_State *L, int objindex);
|
||||||
static void (*lua_getuservalue) (lua_State *L, int idx);
|
static void (*lua_getuservalue) (lua_State *L, int idx);
|
||||||
|
static void (*lua_getiuservalue) (lua_State *L, int idx, int n);
|
||||||
static void (*lua_setglobal) (lua_State *L, const char *var);
|
static void (*lua_setglobal) (lua_State *L, const char *var);
|
||||||
static void (*lua_settable) (lua_State *L, int idx);
|
static void (*lua_settable) (lua_State *L, int idx);
|
||||||
static void (*lua_setfield) (lua_State *L, int idx, const char *k);
|
static void (*lua_setfield) (lua_State *L, int idx, const char *k);
|
||||||
|
@ -286,11 +290,12 @@ static void (*lua_rawseti) (lua_State *L, int idx, int n);
|
||||||
static void (*lua_rawsetp) (lua_State *L, int idx, const void *p);
|
static void (*lua_rawsetp) (lua_State *L, int idx, const void *p);
|
||||||
static int (*lua_setmetatable) (lua_State *L, int objindex);
|
static int (*lua_setmetatable) (lua_State *L, int objindex);
|
||||||
static void (*lua_setuservalue) (lua_State *L, int idx);
|
static void (*lua_setuservalue) (lua_State *L, int idx);
|
||||||
|
static void (*lua_setiuservalue) (lua_State *L, int idx, int n);
|
||||||
static void (*lua_callk) (lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k);
|
static void (*lua_callk) (lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k);
|
||||||
static int (*lua_getctx) (lua_State *L, int *ctx);
|
static int (*lua_getctx) (lua_State *L, int *ctx);
|
||||||
static int (*lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k);
|
static int (*lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k);
|
||||||
static int (*lua_load) (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode);
|
static int (*lua_load) (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode);
|
||||||
static int (*lua_dump) (lua_State *L, lua_Writer writer, void *data);
|
static int (*lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip);
|
||||||
static int (*lua_yieldk) (lua_State *L, int nresults, int ctx, lua_CFunction k);
|
static int (*lua_yieldk) (lua_State *L, int nresults, int ctx, lua_CFunction k);
|
||||||
static int (*lua_resume) (lua_State *L, lua_State *from, int narg);
|
static int (*lua_resume) (lua_State *L, lua_State *from, int narg);
|
||||||
static int (*lua_status) (lua_State *L);
|
static int (*lua_status) (lua_State *L);
|
||||||
|
@ -391,6 +396,9 @@ static int (*lua_gethookcount) (lua_State *L);
|
||||||
#define lua_pushliteral(L, s) lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)
|
#define lua_pushliteral(L, s) lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)
|
||||||
#define lua_pushglobaltable(L) lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
|
#define lua_pushglobaltable(L) lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)
|
||||||
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
|
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
|
||||||
|
#define lua_insert(L,idx) lua_rotate(L, (idx), 1)
|
||||||
|
#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1))
|
||||||
|
#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1))
|
||||||
#define LUA_HOOKCALL 0
|
#define LUA_HOOKCALL 0
|
||||||
#define LUA_HOOKRET 1
|
#define LUA_HOOKRET 1
|
||||||
#define LUA_HOOKLINE 2
|
#define LUA_HOOKLINE 2
|
||||||
|
@ -409,9 +417,6 @@ static int __lite_xl_fallback_lua_absindex (lua_State *L, int idx) { fputs("war
|
||||||
static int __lite_xl_fallback_lua_gettop (lua_State *L) { fputs("warning: lua_gettop is a stub", stderr); }
|
static int __lite_xl_fallback_lua_gettop (lua_State *L) { fputs("warning: lua_gettop is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_lua_settop (lua_State *L, int idx) { fputs("warning: lua_settop is a stub", stderr); }
|
static void __lite_xl_fallback_lua_settop (lua_State *L, int idx) { fputs("warning: lua_settop is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_lua_pushvalue (lua_State *L, int idx) { fputs("warning: lua_pushvalue is a stub", stderr); }
|
static void __lite_xl_fallback_lua_pushvalue (lua_State *L, int idx) { fputs("warning: lua_pushvalue is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_lua_remove (lua_State *L, int idx) { fputs("warning: lua_remove is a stub", stderr); }
|
|
||||||
static void __lite_xl_fallback_lua_insert (lua_State *L, int idx) { fputs("warning: lua_insert is a stub", stderr); }
|
|
||||||
static void __lite_xl_fallback_lua_replace (lua_State *L, int idx) { fputs("warning: lua_replace is a stub", stderr); }
|
|
||||||
static void __lite_xl_fallback_lua_copy (lua_State *L, int fromidx, int toidx) { fputs("warning: lua_copy is a stub", stderr); }
|
static void __lite_xl_fallback_lua_copy (lua_State *L, int fromidx, int toidx) { fputs("warning: lua_copy is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_lua_checkstack (lua_State *L, int sz) { fputs("warning: lua_checkstack is a stub", stderr); }
|
static int __lite_xl_fallback_lua_checkstack (lua_State *L, int sz) { fputs("warning: lua_checkstack is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_lua_xmove (lua_State *from, lua_State *to, int n) { fputs("warning: lua_xmove is a stub", stderr); }
|
static void __lite_xl_fallback_lua_xmove (lua_State *from, lua_State *to, int n) { fputs("warning: lua_xmove is a stub", stderr); }
|
||||||
|
@ -454,8 +459,10 @@ static void __lite_xl_fallback_lua_rawgeti (lua_State *L, int idx, int n) { fpu
|
||||||
static void __lite_xl_fallback_lua_rawgetp (lua_State *L, int idx, const void *p) { fputs("warning: lua_rawgetp is a stub", stderr); }
|
static void __lite_xl_fallback_lua_rawgetp (lua_State *L, int idx, const void *p) { fputs("warning: lua_rawgetp is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_lua_createtable (lua_State *L, int narr, int nrec) { fputs("warning: lua_createtable is a stub", stderr); }
|
static void __lite_xl_fallback_lua_createtable (lua_State *L, int narr, int nrec) { fputs("warning: lua_createtable is a stub", stderr); }
|
||||||
static void * __lite_xl_fallback_lua_newuserdata (lua_State *L, size_t sz) { fputs("warning: lua_newuserdata is a stub", stderr); }
|
static void * __lite_xl_fallback_lua_newuserdata (lua_State *L, size_t sz) { fputs("warning: lua_newuserdata is a stub", stderr); }
|
||||||
|
static void * __lite_xl_fallback_lua_newuserdatauv (lua_State *L, size_t sz, int nuvalue) { fputs("warning: lua_newuserdatauv is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_lua_getmetatable (lua_State *L, int objindex) { fputs("warning: lua_getmetatable is a stub", stderr); }
|
static int __lite_xl_fallback_lua_getmetatable (lua_State *L, int objindex) { fputs("warning: lua_getmetatable is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_lua_getuservalue (lua_State *L, int idx) { fputs("warning: lua_getuservalue is a stub", stderr); }
|
static void __lite_xl_fallback_lua_getuservalue (lua_State *L, int idx) { fputs("warning: lua_getuservalue is a stub", stderr); }
|
||||||
|
static void __lite_xl_fallback_lua_getiuservalue (lua_State *L, int idx, int n) { fputs("warning: lua_getiuservalue is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_lua_setglobal (lua_State *L, const char *var) { fputs("warning: lua_setglobal is a stub", stderr); }
|
static void __lite_xl_fallback_lua_setglobal (lua_State *L, const char *var) { fputs("warning: lua_setglobal is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_lua_settable (lua_State *L, int idx) { fputs("warning: lua_settable is a stub", stderr); }
|
static void __lite_xl_fallback_lua_settable (lua_State *L, int idx) { fputs("warning: lua_settable is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_lua_setfield (lua_State *L, int idx, const char *k) { fputs("warning: lua_setfield is a stub", stderr); }
|
static void __lite_xl_fallback_lua_setfield (lua_State *L, int idx, const char *k) { fputs("warning: lua_setfield is a stub", stderr); }
|
||||||
|
@ -464,11 +471,12 @@ static void __lite_xl_fallback_lua_rawseti (lua_State *L, int idx, int n) { fpu
|
||||||
static void __lite_xl_fallback_lua_rawsetp (lua_State *L, int idx, const void *p) { fputs("warning: lua_rawsetp is a stub", stderr); }
|
static void __lite_xl_fallback_lua_rawsetp (lua_State *L, int idx, const void *p) { fputs("warning: lua_rawsetp is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_lua_setmetatable (lua_State *L, int objindex) { fputs("warning: lua_setmetatable is a stub", stderr); }
|
static int __lite_xl_fallback_lua_setmetatable (lua_State *L, int objindex) { fputs("warning: lua_setmetatable is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_lua_setuservalue (lua_State *L, int idx) { fputs("warning: lua_setuservalue is a stub", stderr); }
|
static void __lite_xl_fallback_lua_setuservalue (lua_State *L, int idx) { fputs("warning: lua_setuservalue is a stub", stderr); }
|
||||||
|
static void __lite_xl_fallback_lua_setiuservalue (lua_State *L, int idx, int n) { fputs("warning: lua_setiuservalue is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_lua_callk (lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k) { fputs("warning: lua_callk is a stub", stderr); }
|
static void __lite_xl_fallback_lua_callk (lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k) { fputs("warning: lua_callk is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_lua_getctx (lua_State *L, int *ctx) { fputs("warning: lua_getctx is a stub", stderr); }
|
static int __lite_xl_fallback_lua_getctx (lua_State *L, int *ctx) { fputs("warning: lua_getctx is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k) { fputs("warning: lua_pcallk is a stub", stderr); }
|
static int __lite_xl_fallback_lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k) { fputs("warning: lua_pcallk is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_lua_load (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode) { fputs("warning: lua_load is a stub", stderr); }
|
static int __lite_xl_fallback_lua_load (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode) { fputs("warning: lua_load is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_lua_dump (lua_State *L, lua_Writer writer, void *data) { fputs("warning: lua_dump is a stub", stderr); }
|
static int __lite_xl_fallback_lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { fputs("warning: lua_dump is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) { fputs("warning: lua_yieldk is a stub", stderr); }
|
static int __lite_xl_fallback_lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) { fputs("warning: lua_yieldk is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_lua_resume (lua_State *L, lua_State *from, int narg) { fputs("warning: lua_resume is a stub", stderr); }
|
static int __lite_xl_fallback_lua_resume (lua_State *L, lua_State *from, int narg) { fputs("warning: lua_resume is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_lua_status (lua_State *L) { fputs("warning: lua_status is a stub", stderr); }
|
static int __lite_xl_fallback_lua_status (lua_State *L) { fputs("warning: lua_status is a stub", stderr); }
|
||||||
|
@ -492,6 +500,7 @@ static lua_Hook __lite_xl_fallback_lua_gethook (lua_State *L) { fputs("warning:
|
||||||
static int __lite_xl_fallback_lua_gethookmask (lua_State *L) { fputs("warning: lua_gethookmask is a stub", stderr); }
|
static int __lite_xl_fallback_lua_gethookmask (lua_State *L) { fputs("warning: lua_gethookmask is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_lua_gethookcount (lua_State *L) { fputs("warning: lua_gethookcount is a stub", stderr); }
|
static int __lite_xl_fallback_lua_gethookcount (lua_State *L) { fputs("warning: lua_gethookcount is a stub", stderr); }
|
||||||
|
|
||||||
|
|
||||||
/** lauxlib.h **/
|
/** lauxlib.h **/
|
||||||
|
|
||||||
typedef struct luaL_Reg {
|
typedef struct luaL_Reg {
|
||||||
|
@ -531,6 +540,7 @@ static void *(*luaL_testudata) (lua_State *L, int ud, const char *tname);
|
||||||
static void *(*luaL_checkudata) (lua_State *L, int ud, const char *tname);
|
static void *(*luaL_checkudata) (lua_State *L, int ud, const char *tname);
|
||||||
static void (*luaL_where) (lua_State *L, int lvl);
|
static void (*luaL_where) (lua_State *L, int lvl);
|
||||||
static int (*luaL_error) (lua_State *L, const char *fmt, ...);
|
static int (*luaL_error) (lua_State *L, const char *fmt, ...);
|
||||||
|
static int (*luaL_typeerror) (lua_State *L, int narg, const char *tname);
|
||||||
static int (*luaL_checkoption) (lua_State *L, int narg, const char *def, const char *const lst[]);
|
static int (*luaL_checkoption) (lua_State *L, int narg, const char *def, const char *const lst[]);
|
||||||
static int (*luaL_fileresult) (lua_State *L, int stat, const char *fname);
|
static int (*luaL_fileresult) (lua_State *L, int stat, const char *fname);
|
||||||
static int (*luaL_execresult) (lua_State *L, int stat);
|
static int (*luaL_execresult) (lua_State *L, int stat);
|
||||||
|
@ -554,6 +564,7 @@ static void (*luaL_addvalue) (luaL_Buffer *B);
|
||||||
static void (*luaL_pushresult) (luaL_Buffer *B);
|
static void (*luaL_pushresult) (luaL_Buffer *B);
|
||||||
static void (*luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
|
static void (*luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
|
||||||
static char *(*luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
|
static char *(*luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
|
||||||
|
static void (*luaL_openlibs) (lua_State *L);
|
||||||
#define lauxlib_h
|
#define lauxlib_h
|
||||||
#define LUA_ERRFILE (LUA_ERRERR+1)
|
#define LUA_ERRFILE (LUA_ERRERR+1)
|
||||||
#define luaL_checkversion(L) luaL_checkversion_(L, LUA_VERSION_NUM)
|
#define luaL_checkversion(L) luaL_checkversion_(L, LUA_VERSION_NUM)
|
||||||
|
@ -601,6 +612,7 @@ static void * __lite_xl_fallback_luaL_testudata (lua_State *L, int ud, const cha
|
||||||
static void * __lite_xl_fallback_luaL_checkudata (lua_State *L, int ud, const char *tname) { fputs("warning: luaL_checkudata is a stub", stderr); }
|
static void * __lite_xl_fallback_luaL_checkudata (lua_State *L, int ud, const char *tname) { fputs("warning: luaL_checkudata is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_luaL_where (lua_State *L, int lvl) { fputs("warning: luaL_where is a stub", stderr); }
|
static void __lite_xl_fallback_luaL_where (lua_State *L, int lvl) { fputs("warning: luaL_where is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_luaL_error (lua_State *L, const char *fmt, ...) { fputs("warning: luaL_error is a stub", stderr); }
|
static int __lite_xl_fallback_luaL_error (lua_State *L, const char *fmt, ...) { fputs("warning: luaL_error is a stub", stderr); }
|
||||||
|
static int __lite_xl_fallback_luaL_typeerror (lua_State *L, int narg, const char *tname) { fputs("warning: luaL_typeerror is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_luaL_checkoption (lua_State *L, int narg, const char *def, const char *const lst[]) { fputs("warning: luaL_checkoption is a stub", stderr); }
|
static int __lite_xl_fallback_luaL_checkoption (lua_State *L, int narg, const char *def, const char *const lst[]) { fputs("warning: luaL_checkoption is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_luaL_fileresult (lua_State *L, int stat, const char *fname) { fputs("warning: luaL_fileresult is a stub", stderr); }
|
static int __lite_xl_fallback_luaL_fileresult (lua_State *L, int stat, const char *fname) { fputs("warning: luaL_fileresult is a stub", stderr); }
|
||||||
static int __lite_xl_fallback_luaL_execresult (lua_State *L, int stat) { fputs("warning: luaL_execresult is a stub", stderr); }
|
static int __lite_xl_fallback_luaL_execresult (lua_State *L, int stat) { fputs("warning: luaL_execresult is a stub", stderr); }
|
||||||
|
@ -624,6 +636,7 @@ static void __lite_xl_fallback_luaL_addvalue (luaL_Buffer *B) { fputs("warning:
|
||||||
static void __lite_xl_fallback_luaL_pushresult (luaL_Buffer *B) { fputs("warning: luaL_pushresult is a stub", stderr); }
|
static void __lite_xl_fallback_luaL_pushresult (luaL_Buffer *B) { fputs("warning: luaL_pushresult is a stub", stderr); }
|
||||||
static void __lite_xl_fallback_luaL_pushresultsize (luaL_Buffer *B, size_t sz) { fputs("warning: luaL_pushresultsize is a stub", stderr); }
|
static void __lite_xl_fallback_luaL_pushresultsize (luaL_Buffer *B, size_t sz) { fputs("warning: luaL_pushresultsize is a stub", stderr); }
|
||||||
static char * __lite_xl_fallback_luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { fputs("warning: luaL_buffinitsize is a stub", stderr); }
|
static char * __lite_xl_fallback_luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { fputs("warning: luaL_buffinitsize is a stub", stderr); }
|
||||||
|
static void __lite_xl_fallback_luaL_openlibs (lua_State *L) { fputs("warning: luaL_openlibs is a stub", stderr); }
|
||||||
|
|
||||||
#define IMPORT_SYMBOL(name, ret, ...) name = (name = (ret (*) (__VA_ARGS__)) symbol(#name), name == NULL ? &__lite_xl_fallback_##name : name)
|
#define IMPORT_SYMBOL(name, ret, ...) name = (name = (ret (*) (__VA_ARGS__)) symbol(#name), name == NULL ? &__lite_xl_fallback_##name : name)
|
||||||
static void lite_xl_plugin_init(void *XL) {
|
static void lite_xl_plugin_init(void *XL) {
|
||||||
|
@ -637,9 +650,6 @@ static void lite_xl_plugin_init(void *XL) {
|
||||||
IMPORT_SYMBOL(lua_gettop, int , lua_State *L);
|
IMPORT_SYMBOL(lua_gettop, int , lua_State *L);
|
||||||
IMPORT_SYMBOL(lua_settop, void , lua_State *L, int idx);
|
IMPORT_SYMBOL(lua_settop, void , lua_State *L, int idx);
|
||||||
IMPORT_SYMBOL(lua_pushvalue, void , lua_State *L, int idx);
|
IMPORT_SYMBOL(lua_pushvalue, void , lua_State *L, int idx);
|
||||||
IMPORT_SYMBOL(lua_remove, void , lua_State *L, int idx);
|
|
||||||
IMPORT_SYMBOL(lua_insert, void , lua_State *L, int idx);
|
|
||||||
IMPORT_SYMBOL(lua_replace, void , lua_State *L, int idx);
|
|
||||||
IMPORT_SYMBOL(lua_copy, void , lua_State *L, int fromidx, int toidx);
|
IMPORT_SYMBOL(lua_copy, void , lua_State *L, int fromidx, int toidx);
|
||||||
IMPORT_SYMBOL(lua_checkstack, int , lua_State *L, int sz);
|
IMPORT_SYMBOL(lua_checkstack, int , lua_State *L, int sz);
|
||||||
IMPORT_SYMBOL(lua_xmove, void , lua_State *from, lua_State *to, int n);
|
IMPORT_SYMBOL(lua_xmove, void , lua_State *from, lua_State *to, int n);
|
||||||
|
@ -682,8 +692,10 @@ static void lite_xl_plugin_init(void *XL) {
|
||||||
IMPORT_SYMBOL(lua_rawgetp, void , lua_State *L, int idx, const void *p);
|
IMPORT_SYMBOL(lua_rawgetp, void , lua_State *L, int idx, const void *p);
|
||||||
IMPORT_SYMBOL(lua_createtable, void , lua_State *L, int narr, int nrec);
|
IMPORT_SYMBOL(lua_createtable, void , lua_State *L, int narr, int nrec);
|
||||||
IMPORT_SYMBOL(lua_newuserdata, void *, lua_State *L, size_t sz);
|
IMPORT_SYMBOL(lua_newuserdata, void *, lua_State *L, size_t sz);
|
||||||
|
IMPORT_SYMBOL(lua_newuserdatauv, void *, lua_State *L, size_t sz, int nuvalue);
|
||||||
IMPORT_SYMBOL(lua_getmetatable, int , lua_State *L, int objindex);
|
IMPORT_SYMBOL(lua_getmetatable, int , lua_State *L, int objindex);
|
||||||
IMPORT_SYMBOL(lua_getuservalue, void , lua_State *L, int idx);
|
IMPORT_SYMBOL(lua_getuservalue, void , lua_State *L, int idx);
|
||||||
|
IMPORT_SYMBOL(lua_getiuservalue, void , lua_State *L, int idx, int n);
|
||||||
IMPORT_SYMBOL(lua_setglobal, void , lua_State *L, const char *var);
|
IMPORT_SYMBOL(lua_setglobal, void , lua_State *L, const char *var);
|
||||||
IMPORT_SYMBOL(lua_settable, void , lua_State *L, int idx);
|
IMPORT_SYMBOL(lua_settable, void , lua_State *L, int idx);
|
||||||
IMPORT_SYMBOL(lua_setfield, void , lua_State *L, int idx, const char *k);
|
IMPORT_SYMBOL(lua_setfield, void , lua_State *L, int idx, const char *k);
|
||||||
|
@ -692,11 +704,12 @@ static void lite_xl_plugin_init(void *XL) {
|
||||||
IMPORT_SYMBOL(lua_rawsetp, void , lua_State *L, int idx, const void *p);
|
IMPORT_SYMBOL(lua_rawsetp, void , lua_State *L, int idx, const void *p);
|
||||||
IMPORT_SYMBOL(lua_setmetatable, int , lua_State *L, int objindex);
|
IMPORT_SYMBOL(lua_setmetatable, int , lua_State *L, int objindex);
|
||||||
IMPORT_SYMBOL(lua_setuservalue, void , lua_State *L, int idx);
|
IMPORT_SYMBOL(lua_setuservalue, void , lua_State *L, int idx);
|
||||||
|
IMPORT_SYMBOL(lua_setiuservalue, void , lua_State *L, int idx, int n);
|
||||||
IMPORT_SYMBOL(lua_callk, void , lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k);
|
IMPORT_SYMBOL(lua_callk, void , lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k);
|
||||||
IMPORT_SYMBOL(lua_getctx, int , lua_State *L, int *ctx);
|
IMPORT_SYMBOL(lua_getctx, int , lua_State *L, int *ctx);
|
||||||
IMPORT_SYMBOL(lua_pcallk, int , lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k);
|
IMPORT_SYMBOL(lua_pcallk, int , lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k);
|
||||||
IMPORT_SYMBOL(lua_load, int , lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode);
|
IMPORT_SYMBOL(lua_load, int , lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode);
|
||||||
IMPORT_SYMBOL(lua_dump, int , lua_State *L, lua_Writer writer, void *data);
|
IMPORT_SYMBOL(lua_dump, int , lua_State *L, lua_Writer writer, void *data, int strip);
|
||||||
IMPORT_SYMBOL(lua_yieldk, int , lua_State *L, int nresults, int ctx, lua_CFunction k);
|
IMPORT_SYMBOL(lua_yieldk, int , lua_State *L, int nresults, int ctx, lua_CFunction k);
|
||||||
IMPORT_SYMBOL(lua_resume, int , lua_State *L, lua_State *from, int narg);
|
IMPORT_SYMBOL(lua_resume, int , lua_State *L, lua_State *from, int narg);
|
||||||
IMPORT_SYMBOL(lua_status, int , lua_State *L);
|
IMPORT_SYMBOL(lua_status, int , lua_State *L);
|
||||||
|
@ -741,6 +754,7 @@ static void lite_xl_plugin_init(void *XL) {
|
||||||
IMPORT_SYMBOL(luaL_checkudata, void *, lua_State *L, int ud, const char *tname);
|
IMPORT_SYMBOL(luaL_checkudata, void *, lua_State *L, int ud, const char *tname);
|
||||||
IMPORT_SYMBOL(luaL_where, void , lua_State *L, int lvl);
|
IMPORT_SYMBOL(luaL_where, void , lua_State *L, int lvl);
|
||||||
IMPORT_SYMBOL(luaL_error, int , lua_State *L, const char *fmt, ...);
|
IMPORT_SYMBOL(luaL_error, int , lua_State *L, const char *fmt, ...);
|
||||||
|
IMPORT_SYMBOL(luaL_typeerror, int , lua_State *L, int narg, const char *tname);
|
||||||
IMPORT_SYMBOL(luaL_checkoption, int , lua_State *L, int narg, const char *def, const char *const lst[]);
|
IMPORT_SYMBOL(luaL_checkoption, int , lua_State *L, int narg, const char *def, const char *const lst[]);
|
||||||
IMPORT_SYMBOL(luaL_fileresult, int , lua_State *L, int stat, const char *fname);
|
IMPORT_SYMBOL(luaL_fileresult, int , lua_State *L, int stat, const char *fname);
|
||||||
IMPORT_SYMBOL(luaL_execresult, int , lua_State *L, int stat);
|
IMPORT_SYMBOL(luaL_execresult, int , lua_State *L, int stat);
|
||||||
|
@ -764,5 +778,6 @@ static void lite_xl_plugin_init(void *XL) {
|
||||||
IMPORT_SYMBOL(luaL_pushresult, void , luaL_Buffer *B);
|
IMPORT_SYMBOL(luaL_pushresult, void , luaL_Buffer *B);
|
||||||
IMPORT_SYMBOL(luaL_pushresultsize, void , luaL_Buffer *B, size_t sz);
|
IMPORT_SYMBOL(luaL_pushresultsize, void , luaL_Buffer *B, size_t sz);
|
||||||
IMPORT_SYMBOL(luaL_buffinitsize, char *, lua_State *L, luaL_Buffer *B, size_t sz);
|
IMPORT_SYMBOL(luaL_buffinitsize, char *, lua_State *L, luaL_Buffer *B, size_t sz);
|
||||||
|
IMPORT_SYMBOL(luaL_openlibs, void, lua_State* L);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>@PROJECT_VERSION@</string>
|
<string>@PROJECT_VERSION@</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>© 2019-2021 Francesco Abbate</string>
|
<string>© 2019-2022 Lite XL Team</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
[host_machine]
|
||||||
|
system = 'darwin'
|
||||||
|
cpu_family = 'aarch64'
|
||||||
|
cpu = 'arm64'
|
||||||
|
endian = 'little'
|
||||||
|
|
||||||
|
[binaries]
|
||||||
|
c = ['clang']
|
||||||
|
cpp = ['clang++']
|
||||||
|
objc = ['clang']
|
||||||
|
objcpp = ['clang++']
|
||||||
|
ar = ['ar']
|
||||||
|
strip = ['strip']
|
||||||
|
pkgconfig = ['pkg-config']
|
||||||
|
|
||||||
|
[built-in options]
|
||||||
|
c_args = ['-arch', 'arm64']
|
||||||
|
cpp_args = ['-stdlib=libc++', '-arch', 'arm64']
|
||||||
|
objc_args = ['-arch', 'arm64']
|
||||||
|
objcpp_args = ['-stdlib=libc++', '-arch', 'arm64']
|
||||||
|
c_link_args = ['-arch', 'arm64']
|
||||||
|
cpp_link_args = ['-arch', 'arm64']
|
||||||
|
objc_link_args = ['-arch', 'arm64']
|
||||||
|
objcpp_link_args = ['-arch', 'arm64']
|
|
@ -1,19 +1,18 @@
|
||||||
diff -ruN lua-5.4.3/meson.build newlua/meson.build
|
diff -ruN lua-5.4.4/meson.build lua-5.4.4-mod/meson.build
|
||||||
--- lua-5.4.3/meson.build 2022-05-29 21:04:17.850449500 +0800
|
--- lua-5.4.4/meson.build 2022-11-16 10:33:38.424383300 +0800
|
||||||
+++ newlua/meson.build 2022-06-10 19:23:55.685139800 +0800
|
+++ lua-5.4.4-mod/meson.build 2022-11-16 09:40:57.697918000 +0800
|
||||||
@@ -82,6 +82,7 @@
|
@@ -85,6 +85,7 @@
|
||||||
'src/lutf8lib.c',
|
'src/lutf8lib.c',
|
||||||
'src/lvm.c',
|
'src/lvm.c',
|
||||||
'src/lzio.c',
|
'src/lzio.c',
|
||||||
+ 'src/utf8_wrappers.c',
|
+ 'src/utf8_wrappers.c',
|
||||||
dependencies: lua_lib_deps,
|
dependencies: lua_lib_deps,
|
||||||
override_options: project_options,
|
version: meson.project_version(),
|
||||||
implicit_include_directories: false,
|
soversion: lua_versions[0] + '.' + lua_versions[1],
|
||||||
Binary files lua-5.4.3/src/lua54.dll and newlua/src/lua54.dll differ
|
diff -ruN lua-5.4.4/src/luaconf.h lua-5.4.4-mod/src/luaconf.h
|
||||||
diff -ruN lua-5.4.3/src/luaconf.h newlua/src/luaconf.h
|
--- lua-5.4.4/src/luaconf.h 2022-01-13 19:24:43.000000000 +0800
|
||||||
--- lua-5.4.3/src/luaconf.h 2021-03-15 21:32:52.000000000 +0800
|
+++ lua-5.4.4-mod/src/luaconf.h 2022-11-16 09:40:57.703926000 +0800
|
||||||
+++ newlua/src/luaconf.h 2022-06-10 19:15:03.014745300 +0800
|
@@ -782,5 +782,15 @@
|
||||||
@@ -786,5 +786,15 @@
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,9 +28,9 @@ diff -ruN lua-5.4.3/src/luaconf.h newlua/src/luaconf.h
|
||||||
+
|
+
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
diff -ruN lua-5.4.3/src/Makefile newlua/src/Makefile
|
diff -ruN lua-5.4.4/src/Makefile lua-5.4.4-mod/src/Makefile
|
||||||
--- lua-5.4.3/src/Makefile 2021-02-10 02:47:17.000000000 +0800
|
--- lua-5.4.4/src/Makefile 2021-07-15 22:01:52.000000000 +0800
|
||||||
+++ newlua/src/Makefile 2022-06-10 19:22:45.267931400 +0800
|
+++ lua-5.4.4-mod/src/Makefile 2022-11-16 09:40:57.708921800 +0800
|
||||||
@@ -33,7 +33,7 @@
|
@@ -33,7 +33,7 @@
|
||||||
PLATS= guess aix bsd c89 freebsd generic linux linux-readline macosx mingw posix solaris
|
PLATS= guess aix bsd c89 freebsd generic linux linux-readline macosx mingw posix solaris
|
||||||
|
|
||||||
|
@ -41,10 +40,9 @@ diff -ruN lua-5.4.3/src/Makefile newlua/src/Makefile
|
||||||
LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
|
LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
|
||||||
BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)
|
BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)
|
||||||
|
|
||||||
|
diff -ruN lua-5.4.4/src/utf8_wrappers.c lua-5.4.4-mod/src/utf8_wrappers.c
|
||||||
diff -ruN lua-5.4.3/src/utf8_wrappers.c newlua/src/utf8_wrappers.c
|
--- lua-5.4.4/src/utf8_wrappers.c 1970-01-01 07:30:00.000000000 +0730
|
||||||
--- lua-5.4.3/src/utf8_wrappers.c 1970-01-01 07:30:00.000000000 +0730
|
+++ lua-5.4.4-mod/src/utf8_wrappers.c 2022-11-16 10:09:04.583866600 +0800
|
||||||
+++ newlua/src/utf8_wrappers.c 2022-06-10 19:13:11.904613300 +0800
|
|
||||||
@@ -0,0 +1,101 @@
|
@@ -0,0 +1,101 @@
|
||||||
+/**
|
+/**
|
||||||
+ * Wrappers to provide Unicode (UTF-8) support on Windows.
|
+ * Wrappers to provide Unicode (UTF-8) support on Windows.
|
||||||
|
@ -147,10 +145,10 @@ diff -ruN lua-5.4.3/src/utf8_wrappers.c newlua/src/utf8_wrappers.c
|
||||||
+ return LoadLibraryExW(pathname_w, hFile, dwFlags);
|
+ return LoadLibraryExW(pathname_w, hFile, dwFlags);
|
||||||
+}
|
+}
|
||||||
+#endif
|
+#endif
|
||||||
diff -ruN lua-5.4.3/src/utf8_wrappers.h newlua/src/utf8_wrappers.h
|
diff -ruN lua-5.4.4/src/utf8_wrappers.h lua-5.4.4-mod/src/utf8_wrappers.h
|
||||||
--- lua-5.4.3/src/utf8_wrappers.h 1970-01-01 07:30:00.000000000 +0730
|
--- lua-5.4.4/src/utf8_wrappers.h 1970-01-01 07:30:00.000000000 +0730
|
||||||
+++ newlua/src/utf8_wrappers.h 2022-06-10 19:22:53.554879400 +0800
|
+++ lua-5.4.4-mod/src/utf8_wrappers.h 2022-11-16 10:29:46.044102000 +0800
|
||||||
@@ -0,0 +1,42 @@
|
@@ -0,0 +1,44 @@
|
||||||
+/**
|
+/**
|
||||||
+ * Wrappers to provide Unicode (UTF-8) support on Windows.
|
+ * Wrappers to provide Unicode (UTF-8) support on Windows.
|
||||||
+ *
|
+ *
|
||||||
|
@ -167,6 +165,7 @@ diff -ruN lua-5.4.3/src/utf8_wrappers.h newlua/src/utf8_wrappers.h
|
||||||
+#endif
|
+#endif
|
||||||
+
|
+
|
||||||
+#ifdef lauxlib_c
|
+#ifdef lauxlib_c
|
||||||
|
+#include <stdio.h>
|
||||||
+FILE *freopen_utf8(const char *pathname, const char *mode, FILE *stream);
|
+FILE *freopen_utf8(const char *pathname, const char *mode, FILE *stream);
|
||||||
+#define freopen freopen_utf8
|
+#define freopen freopen_utf8
|
||||||
+#endif
|
+#endif
|
||||||
|
@ -177,6 +176,7 @@ diff -ruN lua-5.4.3/src/utf8_wrappers.h newlua/src/utf8_wrappers.h
|
||||||
+#endif
|
+#endif
|
||||||
+
|
+
|
||||||
+#ifdef loslib_c
|
+#ifdef loslib_c
|
||||||
|
+#include <stdio.h>
|
||||||
+int remove_utf8(const char *pathname);
|
+int remove_utf8(const char *pathname);
|
||||||
+int rename_utf8(const char *oldpath, const char *newpath);
|
+int rename_utf8(const char *oldpath, const char *newpath);
|
||||||
+int system_utf8(const char *command);
|
+int system_utf8(const char *command);
|
||||||
|
|
|
@ -31,6 +31,7 @@ show_help() {
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
local platform="$(get_platform_name)"
|
local platform="$(get_platform_name)"
|
||||||
|
local arch="$(get_platform_arch)"
|
||||||
local build_dir="$(get_default_build_dir)"
|
local build_dir="$(get_default_build_dir)"
|
||||||
local build_type="debug"
|
local build_type="debug"
|
||||||
local prefix=/
|
local prefix=/
|
||||||
|
@ -106,11 +107,27 @@ main() {
|
||||||
portable=""
|
portable=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ $CROSS_ARCH != "" ]]; then
|
||||||
|
if [[ $platform == "macos" ]]; then
|
||||||
|
macos_version_min=10.11
|
||||||
|
if [[ $CROSS_ARCH == "arm64" ]]; then
|
||||||
|
cross_file="--cross-file resources/macos/macos_arm64.conf"
|
||||||
|
macos_version_min=11.0
|
||||||
|
fi
|
||||||
|
export MACOSX_DEPLOYMENT_TARGET=$macos_version_min
|
||||||
|
export MIN_SUPPORTED_MACOSX_DEPLOYMENT_TARGET=$macos_version_min
|
||||||
|
export CFLAGS=-mmacosx-version-min=$macos_version_min
|
||||||
|
export CXXFLAGS=-mmacosx-version-min=$macos_version_min
|
||||||
|
export LDFLAGS=-mmacosx-version-min=$macos_version_min
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
rm -rf "${build_dir}"
|
rm -rf "${build_dir}"
|
||||||
|
|
||||||
CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS meson setup \
|
CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS meson setup \
|
||||||
--buildtype=$build_type \
|
--buildtype=$build_type \
|
||||||
--prefix "$prefix" \
|
--prefix "$prefix" \
|
||||||
|
$cross_file \
|
||||||
$force_fallback \
|
$force_fallback \
|
||||||
$bundle \
|
$bundle \
|
||||||
$portable \
|
$portable \
|
||||||
|
@ -124,7 +141,7 @@ main() {
|
||||||
|
|
||||||
meson compile -C "${build_dir}"
|
meson compile -C "${build_dir}"
|
||||||
|
|
||||||
if [ ! -z ${pgo+x} ]; then
|
if [[ $pgo != "" ]]; then
|
||||||
cp -r data "${build_dir}/src"
|
cp -r data "${build_dir}/src"
|
||||||
"${build_dir}/src/lite-xl"
|
"${build_dir}/src/lite-xl"
|
||||||
meson configure -Db_pgo=use "${build_dir}"
|
meson configure -Db_pgo=use "${build_dir}"
|
||||||
|
|
|
@ -27,16 +27,17 @@ addons_download() {
|
||||||
-o "${build_dir}/lite-xl-widgets.zip"
|
-o "${build_dir}/lite-xl-widgets.zip"
|
||||||
|
|
||||||
unzip "${build_dir}/lite-xl-widgets.zip" -d "${build_dir}"
|
unzip "${build_dir}/lite-xl-widgets.zip" -d "${build_dir}"
|
||||||
mv "${build_dir}/lite-xl-widgets-master" "${build_dir}/third/data/widget"
|
mkdir -p "${build_dir}/third/data/libraries"
|
||||||
|
mv "${build_dir}/lite-xl-widgets-master" "${build_dir}/third/data/libraries/widget"
|
||||||
|
|
||||||
# Downlaod thirdparty plugins
|
# Downlaod thirdparty plugins
|
||||||
curl --insecure \
|
curl --insecure \
|
||||||
-L "https://github.com/lite-xl/lite-xl-plugins/archive/2.1.zip" \
|
-L "https://github.com/lite-xl/lite-xl-plugins/archive/master.zip" \
|
||||||
-o "${build_dir}/lite-xl-plugins.zip"
|
-o "${build_dir}/lite-xl-plugins.zip"
|
||||||
|
|
||||||
unzip "${build_dir}/lite-xl-plugins.zip" -d "${build_dir}"
|
unzip "${build_dir}/lite-xl-plugins.zip" -d "${build_dir}"
|
||||||
mv "${build_dir}/lite-xl-plugins-2.1/plugins" "${build_dir}/third/data"
|
mv "${build_dir}/lite-xl-plugins-master/plugins" "${build_dir}/third/data"
|
||||||
rm -rf "${build_dir}/lite-xl-plugins-2.1"
|
rm -rf "${build_dir}/lite-xl-plugins-master"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Addons installation: some distributions forbid external downloads
|
# Addons installation: some distributions forbid external downloads
|
||||||
|
@ -45,7 +46,7 @@ addons_install() {
|
||||||
local build_dir="$1"
|
local build_dir="$1"
|
||||||
local data_dir="$2"
|
local data_dir="$2"
|
||||||
|
|
||||||
for module_name in colors widget; do
|
for module_name in colors libraries; do
|
||||||
cp -r "${build_dir}/third/data/$module_name" "${data_dir}"
|
cp -r "${build_dir}/third/data/$module_name" "${data_dir}"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -81,6 +82,8 @@ get_platform_arch() {
|
||||||
else
|
else
|
||||||
arch=i686
|
arch=i686
|
||||||
fi
|
fi
|
||||||
|
elif [[ $CROSS_ARCH != "" ]]; then
|
||||||
|
arch=$CROSS_ARCH
|
||||||
fi
|
fi
|
||||||
echo "$arch"
|
echo "$arch"
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,76 @@
|
||||||
"css": "right-open",
|
"css": "right-open",
|
||||||
"code": 62,
|
"code": 62,
|
||||||
"src": "fontawesome"
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "ebf4bfe82c54f9beb94c5221a7bdd975",
|
||||||
|
"css": "lite-xlbg",
|
||||||
|
"code": 53,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M83.3 0H916.7C962.7 0 1000 37.3 1000 83.3V916.7C1000 962.7 962.7 1000 916.7 1000H83.3C37.3 1000 0 962.7 0 916.7V83.3C0 37.3 37.3 0 83.3 0Z",
|
||||||
|
"width": 1000
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"lite-xlbg"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "cde50fad6c2cd7a805a8d5026939d645",
|
||||||
|
"css": "lite-xl1",
|
||||||
|
"code": 54,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M395.8 562.5V312.5C395.8 289.5 377.2 270.8 354.2 270.8H270.8V625C270.8 682.5 317.5 729.2 375 729.2H729.2V645.8C729.2 622.8 710.5 604.2 687.5 604.2H437.5C414.5 604.2 395.8 585.5 395.8 562.5Z",
|
||||||
|
"width": 1000
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"lite-xl1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "526539ec7fcda0b3e7e7ceaa6ae416e6",
|
||||||
|
"css": "lite-xl2",
|
||||||
|
"code": 55,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M729.2 270.8H437.5L729.2 562.5Z",
|
||||||
|
"width": 1000
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"lite-xl2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "cccbe047c5cb17c63c41a1fb1fc022f4",
|
||||||
|
"css": "lite-xl3",
|
||||||
|
"code": 57,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M666.7 333.3H500L666.7 500Z",
|
||||||
|
"width": 1000
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"lite-xl3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "09d781dca1f50fcae90e67df9cc2f8dd",
|
||||||
|
"css": "lite-xl4",
|
||||||
|
"code": 56,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M500 458.3V333.3L604.2 395.8 666.7 500H541.7C518.7 500 500 481.3 500 458.3Z",
|
||||||
|
"width": 1000
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"lite-xl4"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
##### CONFIG
|
##### CONFIG
|
||||||
|
|
||||||
# symbols to ignore
|
# symbols to ignore
|
||||||
IGNORE_SYM='luaL_pushmodule\|luaL_openlib'
|
IGNORE_SYM='luaL_pushmodule'
|
||||||
|
|
||||||
##### CONFIG
|
##### CONFIG
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ generate_header() {
|
||||||
echo "The lite_xl plugin API is quite simple. Any shared library can be a plugin file, so long"
|
echo "The lite_xl plugin API is quite simple. Any shared library can be a plugin file, so long"
|
||||||
echo "as it has an entrypoint that looks like the following, where xxxxx is the plugin name:"
|
echo "as it has an entrypoint that looks like the following, where xxxxx is the plugin name:"
|
||||||
echo '#include "lite_xl_plugin_api.h"'
|
echo '#include "lite_xl_plugin_api.h"'
|
||||||
echo "int lua_open_lite_xl_xxxxx(lua_State* L, void* XL) {"
|
echo "int luaopen_lite_xl_xxxxx(lua_State* L, void* XL) {"
|
||||||
echo " lite_xl_plugin_init(XL);"
|
echo " lite_xl_plugin_init(XL);"
|
||||||
echo " ..."
|
echo " ..."
|
||||||
echo " return 1;"
|
echo " return 1;"
|
||||||
|
@ -98,6 +98,8 @@ generate_header() {
|
||||||
decl "$LUA_PATH/lua.h"
|
decl "$LUA_PATH/lua.h"
|
||||||
echo
|
echo
|
||||||
decl "$LUA_PATH/lauxlib.h"
|
decl "$LUA_PATH/lauxlib.h"
|
||||||
|
echo "static void (*luaL_openlibs) (lua_State *L);"
|
||||||
|
echo 'static void __lite_xl_fallback_luaL_openlibs (lua_State *L) { fputs("warning: luaL_openlibs is a stub", stderr); }'
|
||||||
echo
|
echo
|
||||||
|
|
||||||
echo "#define IMPORT_SYMBOL(name, ret, ...) name = (name = (ret (*) (__VA_ARGS__)) symbol(#name), name == NULL ? &__lite_xl_fallback_##name : name)"
|
echo "#define IMPORT_SYMBOL(name, ret, ...) name = (name = (ret (*) (__VA_ARGS__)) symbol(#name), name == NULL ? &__lite_xl_fallback_##name : name)"
|
||||||
|
@ -106,6 +108,7 @@ generate_header() {
|
||||||
|
|
||||||
decl_import "$LUA_PATH/lua.h"
|
decl_import "$LUA_PATH/lua.h"
|
||||||
decl_import "$LUA_PATH/lauxlib.h"
|
decl_import "$LUA_PATH/lauxlib.h"
|
||||||
|
echo -e "\tIMPORT_SYMBOL(luaL_openlibs, void, lua_State* L);"
|
||||||
|
|
||||||
echo "}"
|
echo "}"
|
||||||
echo "#endif"
|
echo "#endif"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#define API_TYPE_FONT "Font"
|
#define API_TYPE_FONT "Font"
|
||||||
#define API_TYPE_PROCESS "Process"
|
#define API_TYPE_PROCESS "Process"
|
||||||
#define API_TYPE_DIRMONITOR "Dirmonitor"
|
#define API_TYPE_DIRMONITOR "Dirmonitor"
|
||||||
|
#define API_TYPE_NATIVE_PLUGIN "NativePlugin"
|
||||||
|
|
||||||
#define API_CONSTANT_DEFINE(L, idx, key, n) (lua_pushnumber(L, n), lua_setfield(L, idx - 1, key))
|
#define API_CONSTANT_DEFINE(L, idx, key, n) (lua_pushnumber(L, n), lua_setfield(L, idx - 1, key))
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#include "api.h"
|
#include "api.h"
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
@ -23,6 +21,7 @@ int get_changes_dirmonitor(struct dirmonitor_internal*, char*, int);
|
||||||
int translate_changes_dirmonitor(struct dirmonitor_internal*, char*, int, int (*)(int, const char*, void*), void*);
|
int translate_changes_dirmonitor(struct dirmonitor_internal*, char*, int, int (*)(int, const char*, void*), void*);
|
||||||
int add_dirmonitor(struct dirmonitor_internal*, const char*);
|
int add_dirmonitor(struct dirmonitor_internal*, const char*);
|
||||||
void remove_dirmonitor(struct dirmonitor_internal*, int);
|
void remove_dirmonitor(struct dirmonitor_internal*, int);
|
||||||
|
int get_mode_dirmonitor();
|
||||||
|
|
||||||
|
|
||||||
static int f_check_dir_callback(int watch_id, const char* path, void* L) {
|
static int f_check_dir_callback(int watch_id, const char* path, void* L) {
|
||||||
|
@ -62,6 +61,7 @@ static int f_dirmonitor_new(lua_State* L) {
|
||||||
struct dirmonitor* monitor = lua_newuserdata(L, sizeof(struct dirmonitor));
|
struct dirmonitor* monitor = lua_newuserdata(L, sizeof(struct dirmonitor));
|
||||||
luaL_setmetatable(L, API_TYPE_DIRMONITOR);
|
luaL_setmetatable(L, API_TYPE_DIRMONITOR);
|
||||||
memset(monitor, 0, sizeof(struct dirmonitor));
|
memset(monitor, 0, sizeof(struct dirmonitor));
|
||||||
|
monitor->mutex = SDL_CreateMutex();
|
||||||
monitor->internal = init_dirmonitor();
|
monitor->internal = init_dirmonitor();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -111,12 +111,23 @@ static int f_dirmonitor_check(lua_State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int f_dirmonitor_mode(lua_State* L) {
|
||||||
|
int mode = get_mode_dirmonitor();
|
||||||
|
if (mode == 1)
|
||||||
|
lua_pushstring(L, "single");
|
||||||
|
else
|
||||||
|
lua_pushstring(L, "multiple");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static const luaL_Reg dirmonitor_lib[] = {
|
static const luaL_Reg dirmonitor_lib[] = {
|
||||||
{ "new", f_dirmonitor_new },
|
{ "new", f_dirmonitor_new },
|
||||||
{ "__gc", f_dirmonitor_gc },
|
{ "__gc", f_dirmonitor_gc },
|
||||||
{ "watch", f_dirmonitor_watch },
|
{ "watch", f_dirmonitor_watch },
|
||||||
{ "unwatch", f_dirmonitor_unwatch },
|
{ "unwatch", f_dirmonitor_unwatch },
|
||||||
{ "check", f_dirmonitor_check },
|
{ "check", f_dirmonitor_check },
|
||||||
|
{ "mode", f_dirmonitor_mode },
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,3 +6,4 @@ int get_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, si
|
||||||
int translate_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, int size, int (*callback)(int, const char*, void*), void* data) { return -1; }
|
int translate_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, int size, int (*callback)(int, const char*, void*), void* data) { return -1; }
|
||||||
int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) { return -1; }
|
int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) { return -1; }
|
||||||
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) { }
|
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) { }
|
||||||
|
int get_mode_dirmonitor() { return 1; }
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <CoreServices/CoreServices.h>
|
||||||
|
|
||||||
|
struct dirmonitor_internal {
|
||||||
|
SDL_mutex* lock;
|
||||||
|
char** changes;
|
||||||
|
size_t count;
|
||||||
|
FSEventStreamRef stream;
|
||||||
|
int fds[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
CFRunLoopRef main_run_loop;
|
||||||
|
|
||||||
|
|
||||||
|
struct dirmonitor_internal* init_dirmonitor() {
|
||||||
|
static bool mainloop_registered = false;
|
||||||
|
if (!mainloop_registered) {
|
||||||
|
main_run_loop = CFRunLoopGetCurrent();
|
||||||
|
mainloop_registered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirmonitor_internal* monitor = malloc(sizeof(struct dirmonitor_internal));
|
||||||
|
monitor->stream = NULL;
|
||||||
|
monitor->changes = NULL;
|
||||||
|
monitor->count = 0;
|
||||||
|
monitor->lock = NULL;
|
||||||
|
|
||||||
|
return monitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void stop_monitor_stream(struct dirmonitor_internal* monitor) {
|
||||||
|
if (monitor->stream) {
|
||||||
|
FSEventStreamStop(monitor->stream);
|
||||||
|
FSEventStreamUnscheduleFromRunLoop(
|
||||||
|
monitor->stream, main_run_loop, kCFRunLoopDefaultMode
|
||||||
|
);
|
||||||
|
FSEventStreamInvalidate(monitor->stream);
|
||||||
|
FSEventStreamRelease(monitor->stream);
|
||||||
|
monitor->stream = NULL;
|
||||||
|
|
||||||
|
SDL_LockMutex(monitor->lock);
|
||||||
|
write(monitor->fds[1], "", 1);
|
||||||
|
close(monitor->fds[0]);
|
||||||
|
close(monitor->fds[1]);
|
||||||
|
if (monitor->count > 0) {
|
||||||
|
for (size_t i = 0; i<monitor->count; i++) {
|
||||||
|
free(monitor->changes[i]);
|
||||||
|
}
|
||||||
|
free(monitor->changes);
|
||||||
|
monitor->changes = NULL;
|
||||||
|
monitor->count = 0;
|
||||||
|
}
|
||||||
|
SDL_UnlockMutex(monitor->lock);
|
||||||
|
SDL_DestroyMutex(monitor->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void deinit_dirmonitor(struct dirmonitor_internal* monitor) {
|
||||||
|
stop_monitor_stream(monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void stream_callback(
|
||||||
|
ConstFSEventStreamRef streamRef,
|
||||||
|
void* monitor_ptr,
|
||||||
|
size_t numEvents,
|
||||||
|
void* eventPaths,
|
||||||
|
const FSEventStreamEventFlags eventFlags[],
|
||||||
|
const FSEventStreamEventId eventIds[]
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (numEvents <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirmonitor_internal* monitor = monitor_ptr;
|
||||||
|
char** path_list = eventPaths;
|
||||||
|
|
||||||
|
SDL_LockMutex(monitor->lock);
|
||||||
|
size_t total = 0;
|
||||||
|
if (monitor->count == 0) {
|
||||||
|
total = numEvents;
|
||||||
|
monitor->changes = calloc(numEvents, sizeof(char*));
|
||||||
|
} else {
|
||||||
|
total = monitor->count + numEvents;
|
||||||
|
monitor->changes = realloc(
|
||||||
|
monitor->changes,
|
||||||
|
sizeof(char*) * total
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (size_t idx = monitor->count; idx < total; idx++) {
|
||||||
|
size_t pidx = idx - monitor->count;
|
||||||
|
monitor->changes[idx] = malloc(strlen(path_list[pidx])+1);
|
||||||
|
strcpy(monitor->changes[idx], path_list[pidx]);
|
||||||
|
}
|
||||||
|
monitor->count = total;
|
||||||
|
|
||||||
|
if (total > 0)
|
||||||
|
write(monitor->fds[1], "", 1);
|
||||||
|
SDL_UnlockMutex(monitor->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int get_changes_dirmonitor(
|
||||||
|
struct dirmonitor_internal* monitor,
|
||||||
|
char* buffer,
|
||||||
|
int buffer_size
|
||||||
|
) {
|
||||||
|
char response[1];
|
||||||
|
read(monitor->fds[0], response, 1);
|
||||||
|
|
||||||
|
size_t results = 0;
|
||||||
|
SDL_LockMutex(monitor->lock);
|
||||||
|
results = monitor->count;
|
||||||
|
SDL_UnlockMutex(monitor->lock);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int translate_changes_dirmonitor(
|
||||||
|
struct dirmonitor_internal* monitor,
|
||||||
|
char* buffer,
|
||||||
|
int buffer_size,
|
||||||
|
int (*change_callback)(int, const char*, void*),
|
||||||
|
void* L
|
||||||
|
) {
|
||||||
|
SDL_LockMutex(monitor->lock);
|
||||||
|
if (monitor->count > 0) {
|
||||||
|
for (size_t i = 0; i<monitor->count; i++) {
|
||||||
|
change_callback(strlen(monitor->changes[i]), monitor->changes[i], L);
|
||||||
|
free(monitor->changes[i]);
|
||||||
|
}
|
||||||
|
free(monitor->changes);
|
||||||
|
monitor->changes = NULL;
|
||||||
|
monitor->count = 0;
|
||||||
|
}
|
||||||
|
SDL_UnlockMutex(monitor->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) {
|
||||||
|
stop_monitor_stream(monitor);
|
||||||
|
|
||||||
|
monitor->lock = SDL_CreateMutex();
|
||||||
|
pipe(monitor->fds);
|
||||||
|
|
||||||
|
FSEventStreamContext context = {
|
||||||
|
.info = monitor,
|
||||||
|
.retain = NULL,
|
||||||
|
.release = NULL,
|
||||||
|
.copyDescription = NULL,
|
||||||
|
.version = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
CFStringRef paths[] = {
|
||||||
|
CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8)
|
||||||
|
};
|
||||||
|
|
||||||
|
monitor->stream = FSEventStreamCreate(
|
||||||
|
NULL,
|
||||||
|
stream_callback,
|
||||||
|
&context,
|
||||||
|
CFArrayCreate(NULL, (const void **)&paths, 1, NULL),
|
||||||
|
kFSEventStreamEventIdSinceNow,
|
||||||
|
0,
|
||||||
|
kFSEventStreamCreateFlagNone
|
||||||
|
| kFSEventStreamCreateFlagWatchRoot
|
||||||
|
| kFSEventStreamCreateFlagFileEvents
|
||||||
|
);
|
||||||
|
|
||||||
|
FSEventStreamScheduleWithRunLoop(
|
||||||
|
monitor->stream, main_run_loop, kCFRunLoopDefaultMode
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!FSEventStreamStart(monitor->stream)) {
|
||||||
|
stop_monitor_stream(monitor);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) {
|
||||||
|
stop_monitor_stream(monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int get_mode_dirmonitor() { return 1; }
|
|
@ -13,7 +13,7 @@ struct dirmonitor_internal {
|
||||||
|
|
||||||
|
|
||||||
struct dirmonitor_internal* init_dirmonitor() {
|
struct dirmonitor_internal* init_dirmonitor() {
|
||||||
struct dirmonitor_internal* monitor = calloc(sizeof(struct dirmonitor_internal), 1);
|
struct dirmonitor_internal* monitor = calloc(1, sizeof(struct dirmonitor_internal));
|
||||||
monitor->fd = inotify_init();
|
monitor->fd = inotify_init();
|
||||||
pipe(monitor->sig);
|
pipe(monitor->sig);
|
||||||
fcntl(monitor->sig[0], F_SETFD, FD_CLOEXEC);
|
fcntl(monitor->sig[0], F_SETFD, FD_CLOEXEC);
|
||||||
|
@ -51,3 +51,6 @@ int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) {
|
||||||
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) {
|
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) {
|
||||||
inotify_rm_watch(monitor->fd, fd);
|
inotify_rm_watch(monitor->fd, fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int get_mode_dirmonitor() { return 2; }
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
struct dirmonitor_internal {
|
struct dirmonitor_internal {
|
||||||
int fd;
|
int fd;
|
||||||
|
@ -11,7 +12,7 @@ struct dirmonitor_internal {
|
||||||
|
|
||||||
|
|
||||||
struct dirmonitor_internal* init_dirmonitor() {
|
struct dirmonitor_internal* init_dirmonitor() {
|
||||||
struct dirmonitor_internal* monitor = calloc(sizeof(struct dirmonitor_internal), 1);
|
struct dirmonitor_internal* monitor = calloc(1, sizeof(struct dirmonitor_internal));
|
||||||
monitor->fd = kqueue();
|
monitor->fd = kqueue();
|
||||||
return monitor;
|
return monitor;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +24,9 @@ void deinit_dirmonitor(struct dirmonitor_internal* monitor) {
|
||||||
|
|
||||||
|
|
||||||
int get_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, int buffer_size) {
|
int get_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, int buffer_size) {
|
||||||
int nev = kevent(monitor->fd, NULL, 0, (struct kevent*)buffer, buffer_size / sizeof(kevent), NULL);
|
struct timespec ts = { 0, 100 * 1000000 }; // 100 ms
|
||||||
|
|
||||||
|
int nev = kevent(monitor->fd, NULL, 0, (struct kevent*)buffer, buffer_size / sizeof(kevent), &ts);
|
||||||
if (nev == -1)
|
if (nev == -1)
|
||||||
return -1;
|
return -1;
|
||||||
if (nev <= 0)
|
if (nev <= 0)
|
||||||
|
@ -43,8 +46,11 @@ int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) {
|
||||||
int fd = open(path, O_RDONLY);
|
int fd = open(path, O_RDONLY);
|
||||||
struct kevent change;
|
struct kevent change;
|
||||||
|
|
||||||
|
// a timeout of zero should make kevent return immediately
|
||||||
|
struct timespec ts = { 0, 0 }; // 0 s
|
||||||
|
|
||||||
EV_SET(&change, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME, 0, (void*)path);
|
EV_SET(&change, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME, 0, (void*)path);
|
||||||
kevent(monitor->fd, &change, 1, NULL, 0, NULL);
|
kevent(monitor->fd, &change, 1, NULL, 0, &ts);
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
@ -53,3 +59,6 @@ int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) {
|
||||||
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) {
|
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) {
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int get_mode_dirmonitor() { return 2; }
|
||||||
|
|
|
@ -19,7 +19,7 @@ int get_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, in
|
||||||
|
|
||||||
|
|
||||||
struct dirmonitor* init_dirmonitor() {
|
struct dirmonitor* init_dirmonitor() {
|
||||||
return calloc(sizeof(struct dirmonitor_internal), 1);
|
return calloc(1, sizeof(struct dirmonitor_internal));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,8 +40,8 @@ void deinit_dirmonitor(struct dirmonitor_internal* monitor) {
|
||||||
|
|
||||||
int translate_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, int buffer_size, int (*change_callback)(int, const char*, void*), void* data) {
|
int translate_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, int buffer_size, int (*change_callback)(int, const char*, void*), void* data) {
|
||||||
for (FILE_NOTIFY_INFORMATION* info = (FILE_NOTIFY_INFORMATION*)buffer; (char*)info < buffer + buffer_size; info = (FILE_NOTIFY_INFORMATION*)(((char*)info) + info->NextEntryOffset)) {
|
for (FILE_NOTIFY_INFORMATION* info = (FILE_NOTIFY_INFORMATION*)buffer; (char*)info < buffer + buffer_size; info = (FILE_NOTIFY_INFORMATION*)(((char*)info) + info->NextEntryOffset)) {
|
||||||
char transform_buffer[PATH_MAX*4];
|
char transform_buffer[MAX_PATH*4];
|
||||||
int count = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)info->FileName, info->FileNameLength, transform_buffer, PATH_MAX*4 - 1, NULL, NULL);
|
int count = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)info->FileName, info->FileNameLength / 2, transform_buffer, MAX_PATH*4 - 1, NULL, NULL);
|
||||||
change_callback(count, transform_buffer, data);
|
change_callback(count, transform_buffer, data);
|
||||||
if (!info->NextEntryOffset)
|
if (!info->NextEntryOffset)
|
||||||
break;
|
break;
|
||||||
|
@ -60,3 +60,6 @@ int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) {
|
||||||
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) {
|
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) {
|
||||||
close_monitor_handle(monitor);
|
close_monitor_handle(monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int get_mode_dirmonitor() { return 1; }
|
||||||
|
|
|
@ -29,7 +29,7 @@ typedef int process_handle;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool running;
|
bool running, detached;
|
||||||
int returncode, deadline;
|
int returncode, deadline;
|
||||||
long pid;
|
long pid;
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
|
@ -65,7 +65,7 @@ typedef enum {
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static volatile long PipeSerialNumber;
|
static volatile long PipeSerialNumber;
|
||||||
static void close_fd(HANDLE* handle) { if (*handle) CloseHandle(*handle); *handle = NULL; }
|
static void close_fd(HANDLE* handle) { if (*handle) CloseHandle(*handle); *handle = INVALID_HANDLE_VALUE; }
|
||||||
#else
|
#else
|
||||||
static void close_fd(int* fd) { if (*fd) close(*fd); *fd = 0; }
|
static void close_fd(int* fd) { if (*fd) close(*fd); *fd = 0; }
|
||||||
#endif
|
#endif
|
||||||
|
@ -125,22 +125,26 @@ static int process_start(lua_State* L) {
|
||||||
int retval = 1;
|
int retval = 1;
|
||||||
size_t env_len = 0, key_len, val_len;
|
size_t env_len = 0, key_len, val_len;
|
||||||
const char *cmd[256] = { NULL }, *env_names[256] = { NULL }, *env_values[256] = { NULL }, *cwd = NULL;
|
const char *cmd[256] = { NULL }, *env_names[256] = { NULL }, *env_values[256] = { NULL }, *cwd = NULL;
|
||||||
bool detach = false;
|
bool detach = false, literal = false;
|
||||||
int deadline = 10, new_fds[3] = { STDIN_FD, STDOUT_FD, STDERR_FD };
|
int deadline = 10, new_fds[3] = { STDIN_FD, STDOUT_FD, STDERR_FD };
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
size_t arg_len = lua_gettop(L), cmd_len;
|
||||||
|
if (lua_type(L, 1) == LUA_TTABLE) {
|
||||||
#if LUA_VERSION_NUM > 501
|
#if LUA_VERSION_NUM > 501
|
||||||
lua_len(L, 1);
|
lua_len(L, 1);
|
||||||
#else
|
#else
|
||||||
lua_pushinteger(L, (int)lua_objlen(L, 1));
|
lua_pushinteger(L, (int)lua_objlen(L, 1));
|
||||||
#endif
|
#endif
|
||||||
size_t cmd_len = luaL_checknumber(L, -1); lua_pop(L, 1);
|
cmd_len = luaL_checknumber(L, -1); lua_pop(L, 1);
|
||||||
size_t arg_len = lua_gettop(L);
|
|
||||||
for (size_t i = 1; i <= cmd_len; ++i) {
|
for (size_t i = 1; i <= cmd_len; ++i) {
|
||||||
lua_pushinteger(L, i);
|
lua_pushinteger(L, i);
|
||||||
lua_rawget(L, 1);
|
lua_rawget(L, 1);
|
||||||
cmd[i-1] = luaL_checkstring(L, -1);
|
cmd[i-1] = luaL_checkstring(L, -1);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
literal = true;
|
||||||
|
cmd[0] = luaL_checkstring(L, 1);
|
||||||
|
cmd_len = 1;
|
||||||
|
}
|
||||||
// this should never trip
|
// this should never trip
|
||||||
// but if it does we are in deep trouble
|
// but if it does we are in deep trouble
|
||||||
assert(cmd[0]);
|
assert(cmd[0]);
|
||||||
|
@ -169,11 +173,8 @@ static int process_start(lua_State* L) {
|
||||||
lua_getfield(L, 2, "stderr"); new_fds[STDERR_FD] = luaL_optnumber(L, -1, STDERR_FD);
|
lua_getfield(L, 2, "stderr"); new_fds[STDERR_FD] = luaL_optnumber(L, -1, STDERR_FD);
|
||||||
for (int stream = STDIN_FD; stream <= STDERR_FD; ++stream) {
|
for (int stream = STDIN_FD; stream <= STDERR_FD; ++stream) {
|
||||||
if (new_fds[stream] > STDERR_FD || new_fds[stream] < REDIRECT_PARENT) {
|
if (new_fds[stream] > STDERR_FD || new_fds[stream] < REDIRECT_PARENT) {
|
||||||
for (size_t i = 0; i < env_len; ++i) {
|
lua_pushfstring(L, "redirect to handles, FILE* and paths are not supported");
|
||||||
free((char*)env_names[i]);
|
retval = -1;
|
||||||
free((char*)env_values[i]);
|
|
||||||
}
|
|
||||||
retval = luaL_error(L, "redirect to handles, FILE* and paths are not supported");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,6 +184,7 @@ static int process_start(lua_State* L) {
|
||||||
memset(self, 0, sizeof(process_t));
|
memset(self, 0, sizeof(process_t));
|
||||||
luaL_setmetatable(L, API_TYPE_PROCESS);
|
luaL_setmetatable(L, API_TYPE_PROCESS);
|
||||||
self->deadline = deadline;
|
self->deadline = deadline;
|
||||||
|
self->detached = detach;
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
switch (new_fds[i]) {
|
switch (new_fds[i]) {
|
||||||
|
@ -205,18 +207,21 @@ static int process_start(lua_State* L) {
|
||||||
self->child_pipes[i][0] = CreateNamedPipeA(pipeNameBuffer, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
|
self->child_pipes[i][0] = CreateNamedPipeA(pipeNameBuffer, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
|
||||||
PIPE_TYPE_BYTE | PIPE_WAIT, 1, READ_BUF_SIZE, READ_BUF_SIZE, 0, NULL);
|
PIPE_TYPE_BYTE | PIPE_WAIT, 1, READ_BUF_SIZE, READ_BUF_SIZE, 0, NULL);
|
||||||
if (self->child_pipes[i][0] == INVALID_HANDLE_VALUE) {
|
if (self->child_pipes[i][0] == INVALID_HANDLE_VALUE) {
|
||||||
retval = luaL_error(L, "Error creating read pipe: %d.", GetLastError());
|
lua_pushfstring(L, "Error creating read pipe: %d.", GetLastError());
|
||||||
|
retval = -1;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
self->child_pipes[i][1] = CreateFileA(pipeNameBuffer, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
self->child_pipes[i][1] = CreateFileA(pipeNameBuffer, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
if (self->child_pipes[i][1] == INVALID_HANDLE_VALUE) {
|
if (self->child_pipes[i][1] == INVALID_HANDLE_VALUE) {
|
||||||
CloseHandle(self->child_pipes[i][0]);
|
CloseHandle(self->child_pipes[i][0]);
|
||||||
retval = luaL_error(L, "Error creating write pipe: %d.", GetLastError());
|
lua_pushfstring(L, "Error creating write pipe: %d.", GetLastError());
|
||||||
|
retval = -1;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
if (!SetHandleInformation(self->child_pipes[i][i == STDIN_FD ? 1 : 0], HANDLE_FLAG_INHERIT, 0) ||
|
if (!SetHandleInformation(self->child_pipes[i][i == STDIN_FD ? 1 : 0], HANDLE_FLAG_INHERIT, 0) ||
|
||||||
!SetHandleInformation(self->child_pipes[i][i == STDIN_FD ? 0 : 1], HANDLE_FLAG_INHERIT, 1)) {
|
!SetHandleInformation(self->child_pipes[i][i == STDIN_FD ? 0 : 1], HANDLE_FLAG_INHERIT, 1)) {
|
||||||
retval = luaL_error(L, "Error inheriting pipes: %d.", GetLastError());
|
lua_pushfstring(L, "Error inheriting pipes: %d.", GetLastError());
|
||||||
|
retval = -1;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,15 +243,35 @@ static int process_start(lua_State* L) {
|
||||||
siStartInfo.hStdOutput = self->child_pipes[STDOUT_FD][1];
|
siStartInfo.hStdOutput = self->child_pipes[STDOUT_FD][1];
|
||||||
siStartInfo.hStdError = self->child_pipes[STDERR_FD][1];
|
siStartInfo.hStdError = self->child_pipes[STDERR_FD][1];
|
||||||
char commandLine[32767] = { 0 }, environmentBlock[32767], wideEnvironmentBlock[32767*2];
|
char commandLine[32767] = { 0 }, environmentBlock[32767], wideEnvironmentBlock[32767*2];
|
||||||
strcpy(commandLine, cmd[0]);
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (size_t i = 1; i < cmd_len; ++i) {
|
if (!literal) {
|
||||||
|
for (size_t i = 0; i < cmd_len; ++i) {
|
||||||
size_t len = strlen(cmd[i]);
|
size_t len = strlen(cmd[i]);
|
||||||
offset += len + 1;
|
if (offset + len + 2 >= sizeof(commandLine)) break;
|
||||||
if (offset >= sizeof(commandLine))
|
if (i > 0)
|
||||||
break;
|
commandLine[offset++] = ' ';
|
||||||
strcat(commandLine, " ");
|
commandLine[offset++] = '"';
|
||||||
strcat(commandLine, cmd[i]);
|
int backslashCount = 0; // Yes, this is necessary.
|
||||||
|
for (size_t j = 0; j < len && offset + 2 + backslashCount < sizeof(commandLine); ++j) {
|
||||||
|
if (cmd[i][j] == '\\')
|
||||||
|
++backslashCount;
|
||||||
|
else if (cmd[i][j] == '"') {
|
||||||
|
for (size_t k = 0; k < backslashCount; ++k)
|
||||||
|
commandLine[offset++] = '\\';
|
||||||
|
commandLine[offset++] = '\\';
|
||||||
|
backslashCount = 0;
|
||||||
|
} else
|
||||||
|
backslashCount = 0;
|
||||||
|
commandLine[offset++] = cmd[i][j];
|
||||||
|
}
|
||||||
|
if (offset + 1 + backslashCount >= sizeof(commandLine)) break;
|
||||||
|
for (size_t k = 0; k < backslashCount; ++k)
|
||||||
|
commandLine[offset++] = '\\';
|
||||||
|
commandLine[offset++] = '"';
|
||||||
|
}
|
||||||
|
commandLine[offset] = 0;
|
||||||
|
} else {
|
||||||
|
strncpy(commandLine, cmd[0], sizeof(commandLine));
|
||||||
}
|
}
|
||||||
offset = 0;
|
offset = 0;
|
||||||
for (size_t i = 0; i < env_len; ++i) {
|
for (size_t i = 0; i < env_len; ++i) {
|
||||||
|
@ -259,7 +284,8 @@ static int process_start(lua_State* L) {
|
||||||
if (env_len > 0)
|
if (env_len > 0)
|
||||||
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, environmentBlock, offset, (LPWSTR)wideEnvironmentBlock, sizeof(wideEnvironmentBlock));
|
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, environmentBlock, offset, (LPWSTR)wideEnvironmentBlock, sizeof(wideEnvironmentBlock));
|
||||||
if (!CreateProcess(NULL, commandLine, NULL, NULL, true, (detach ? DETACHED_PROCESS : CREATE_NO_WINDOW) | CREATE_UNICODE_ENVIRONMENT, env_len > 0 ? wideEnvironmentBlock : NULL, cwd, &siStartInfo, &self->process_information)) {
|
if (!CreateProcess(NULL, commandLine, NULL, NULL, true, (detach ? DETACHED_PROCESS : CREATE_NO_WINDOW) | CREATE_UNICODE_ENVIRONMENT, env_len > 0 ? wideEnvironmentBlock : NULL, cwd, &siStartInfo, &self->process_information)) {
|
||||||
retval = luaL_error(L, "Error creating a process: %d.", GetLastError());
|
lua_pushfstring(L, "Error creating a process: %d.", GetLastError());
|
||||||
|
retval = -1;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
self->pid = (long)self->process_information.dwProcessId;
|
self->pid = (long)self->process_information.dwProcessId;
|
||||||
|
@ -269,15 +295,18 @@ static int process_start(lua_State* L) {
|
||||||
#else
|
#else
|
||||||
for (int i = 0; i < 3; ++i) { // Make only the parents fd's non-blocking. Children should block.
|
for (int i = 0; i < 3; ++i) { // Make only the parents fd's non-blocking. Children should block.
|
||||||
if (pipe(self->child_pipes[i]) || fcntl(self->child_pipes[i][i == STDIN_FD ? 1 : 0], F_SETFL, O_NONBLOCK) == -1) {
|
if (pipe(self->child_pipes[i]) || fcntl(self->child_pipes[i][i == STDIN_FD ? 1 : 0], F_SETFL, O_NONBLOCK) == -1) {
|
||||||
retval = luaL_error(L, "Error creating pipes: %s", strerror(errno));
|
lua_pushfstring(L, "Error creating pipes: %s", strerror(errno));
|
||||||
|
retval = -1;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self->pid = (long)fork();
|
self->pid = (long)fork();
|
||||||
if (self->pid < 0) {
|
if (self->pid < 0) {
|
||||||
retval = luaL_error(L, "Error running fork: %s.", strerror(errno));
|
lua_pushfstring(L, "Error running fork: %s.", strerror(errno));
|
||||||
|
retval = -1;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
} else if (!self->pid) {
|
} else if (!self->pid) {
|
||||||
|
if (!detach)
|
||||||
setpgid(0,0);
|
setpgid(0,0);
|
||||||
for (int stream = 0; stream < 3; ++stream) {
|
for (int stream = 0; stream < 3; ++stream) {
|
||||||
if (new_fds[stream] == REDIRECT_DISCARD) { // Close the stream if we don't want it.
|
if (new_fds[stream] == REDIRECT_DISCARD) { // Close the stream if we don't want it.
|
||||||
|
@ -307,6 +336,9 @@ static int process_start(lua_State* L) {
|
||||||
close_fd(pipe);
|
close_fd(pipe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (retval == -1)
|
||||||
|
return lua_error(L);
|
||||||
|
|
||||||
self->running = true;
|
self->running = true;
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
@ -454,6 +486,7 @@ static int f_kill(lua_State* L) { return self_signal(L, SIGNAL_KILL); }
|
||||||
static int f_interrupt(lua_State* L) { return self_signal(L, SIGNAL_INTERRUPT); }
|
static int f_interrupt(lua_State* L) { return self_signal(L, SIGNAL_INTERRUPT); }
|
||||||
static int f_gc(lua_State* L) {
|
static int f_gc(lua_State* L) {
|
||||||
process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS);
|
process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS);
|
||||||
|
if (!self->detached)
|
||||||
signal_process(self, SIGNAL_TERM);
|
signal_process(self, SIGNAL_TERM);
|
||||||
close_fd(&self->child_pipes[STDIN_FD ][1]);
|
close_fd(&self->child_pipes[STDIN_FD ][1]);
|
||||||
close_fd(&self->child_pipes[STDOUT_FD][0]);
|
close_fd(&self->child_pipes[STDOUT_FD][0]);
|
||||||
|
|
266
src/api/regex.c
266
src/api/regex.c
|
@ -4,6 +4,126 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <pcre2.h>
|
#include <pcre2.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct RegexState {
|
||||||
|
pcre2_code* re;
|
||||||
|
pcre2_match_data* match_data;
|
||||||
|
const char* subject;
|
||||||
|
size_t subject_len;
|
||||||
|
size_t offset;
|
||||||
|
bool regex_compiled;
|
||||||
|
bool found;
|
||||||
|
} RegexState;
|
||||||
|
|
||||||
|
static pcre2_code* regex_get_pattern(lua_State *L, bool* should_free) {
|
||||||
|
pcre2_code* re = NULL;
|
||||||
|
*should_free = false;
|
||||||
|
|
||||||
|
if (lua_type(L, 1) == LUA_TTABLE) {
|
||||||
|
lua_rawgeti(L, 1, 1);
|
||||||
|
re = (pcre2_code*)lua_touserdata(L, -1);
|
||||||
|
lua_settop(L, -2);
|
||||||
|
} else {
|
||||||
|
int errornumber;
|
||||||
|
PCRE2_SIZE erroroffset;
|
||||||
|
size_t pattern_len = 0;
|
||||||
|
const char* pattern = luaL_checklstring(L, 1, &pattern_len);
|
||||||
|
|
||||||
|
re = pcre2_compile(
|
||||||
|
(PCRE2_SPTR)pattern,
|
||||||
|
pattern_len, PCRE2_UTF,
|
||||||
|
&errornumber, &erroroffset, NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (re == NULL) {
|
||||||
|
PCRE2_UCHAR errmsg[256];
|
||||||
|
pcre2_get_error_message(errornumber, errmsg, sizeof(errmsg));
|
||||||
|
luaL_error(
|
||||||
|
L, "regex pattern error at offset %d: %s",
|
||||||
|
(int)erroroffset, errmsg
|
||||||
|
);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pcre2_jit_compile(re, PCRE2_JIT_COMPLETE);
|
||||||
|
|
||||||
|
*should_free = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regex_gmatch_iterator(lua_State *L) {
|
||||||
|
RegexState *state = (RegexState*)lua_touserdata(L, lua_upvalueindex(3));
|
||||||
|
|
||||||
|
if (state->found) {
|
||||||
|
int rc = pcre2_match(
|
||||||
|
state->re,
|
||||||
|
(PCRE2_SPTR)state->subject, state->subject_len,
|
||||||
|
state->offset, 0, state->match_data, NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
if (rc != PCRE2_ERROR_NOMATCH) {
|
||||||
|
PCRE2_UCHAR buffer[120];
|
||||||
|
pcre2_get_error_message(rc, buffer, sizeof(buffer));
|
||||||
|
luaL_error(L, "regex matching error %d: %s", rc, buffer);
|
||||||
|
}
|
||||||
|
goto clean;
|
||||||
|
} else {
|
||||||
|
size_t ovector_count = pcre2_get_ovector_count(state->match_data);
|
||||||
|
if (ovector_count > 0) {
|
||||||
|
PCRE2_SIZE* ovector = pcre2_get_ovector_pointer(state->match_data);
|
||||||
|
if (ovector[0] > ovector[1]) {
|
||||||
|
/* We must guard against patterns such as /(?=.\K)/ that use \K in an
|
||||||
|
assertion to set the start of a match later than its end. In the editor,
|
||||||
|
we just detect this case and give up. */
|
||||||
|
luaL_error(L, "regex matching error: \\K was used in an assertion to "
|
||||||
|
" set the match start after its end");
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
if (ovector_count > 1) index = 2;
|
||||||
|
|
||||||
|
int total = 0;
|
||||||
|
int total_results = ovector_count * 2;
|
||||||
|
size_t last_offset = 0;
|
||||||
|
for (int i = index; i < total_results; i+=2) {
|
||||||
|
lua_pushlstring(L, state->subject+ovector[i], ovector[i+1] - ovector[i]);
|
||||||
|
last_offset = ovector[i+1];
|
||||||
|
total++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_offset - 1 < state->subject_len)
|
||||||
|
state->offset = last_offset;
|
||||||
|
else
|
||||||
|
state->found = false;
|
||||||
|
|
||||||
|
return total;
|
||||||
|
} else {
|
||||||
|
state->found = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clean:
|
||||||
|
if (state->regex_compiled) pcre2_code_free(state->re);
|
||||||
|
pcre2_match_data_free(state->match_data);
|
||||||
|
|
||||||
|
return 0; /* not found */
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t regex_offset_relative(lua_Integer pos, size_t len) {
|
||||||
|
if (pos > 0)
|
||||||
|
return (size_t)pos;
|
||||||
|
else if (pos == 0)
|
||||||
|
return 1;
|
||||||
|
else if (pos < -(lua_Integer)len) /* inverted comparison */
|
||||||
|
return 1; /* clip to 1 */
|
||||||
|
else return len + (size_t)pos + 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int f_pcre_gc(lua_State* L) {
|
static int f_pcre_gc(lua_State* L) {
|
||||||
lua_rawgeti(L, -1, 1);
|
lua_rawgeti(L, -1, 1);
|
||||||
|
@ -37,6 +157,7 @@ static int f_pcre_compile(lua_State *L) {
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
if (re) {
|
if (re) {
|
||||||
|
pcre2_jit_compile(re, PCRE2_JIT_COMPLETE);
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
lua_pushlightuserdata(L, re);
|
lua_pushlightuserdata(L, re);
|
||||||
lua_rawseti(L, -2, 1);
|
lua_rawseti(L, -2, 1);
|
||||||
|
@ -56,19 +177,21 @@ static int f_pcre_compile(lua_State *L) {
|
||||||
// (including the whole match), if a match was found.
|
// (including the whole match), if a match was found.
|
||||||
static int f_pcre_match(lua_State *L) {
|
static int f_pcre_match(lua_State *L) {
|
||||||
size_t len, offset = 1, opts = 0;
|
size_t len, offset = 1, opts = 0;
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
bool regex_compiled = false;
|
||||||
|
pcre2_code* re = regex_get_pattern(L, ®ex_compiled);
|
||||||
|
if (!re) return 0 ;
|
||||||
const char* str = luaL_checklstring(L, 2, &len);
|
const char* str = luaL_checklstring(L, 2, &len);
|
||||||
if (lua_gettop(L) > 2)
|
if (lua_gettop(L) > 2)
|
||||||
offset = luaL_checknumber(L, 3);
|
offset = regex_offset_relative(luaL_checknumber(L, 3), len);
|
||||||
offset -= 1;
|
offset -= 1;
|
||||||
len -= offset;
|
len -= offset;
|
||||||
if (lua_gettop(L) > 3)
|
if (lua_gettop(L) > 3)
|
||||||
opts = luaL_checknumber(L, 4);
|
opts = luaL_checknumber(L, 4);
|
||||||
lua_rawgeti(L, 1, 1);
|
lua_rawgeti(L, 1, 1);
|
||||||
pcre2_code* re = (pcre2_code*)lua_touserdata(L, -1);
|
|
||||||
pcre2_match_data* md = pcre2_match_data_create_from_pattern(re, NULL);
|
pcre2_match_data* md = pcre2_match_data_create_from_pattern(re, NULL);
|
||||||
int rc = pcre2_match(re, (PCRE2_SPTR)&str[offset], len, 0, opts, md, NULL);
|
int rc = pcre2_match(re, (PCRE2_SPTR)&str[offset], len, 0, opts, md, NULL);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
|
if (regex_compiled) pcre2_code_free(re);
|
||||||
pcre2_match_data_free(md);
|
pcre2_match_data_free(md);
|
||||||
if (rc != PCRE2_ERROR_NOMATCH) {
|
if (rc != PCRE2_ERROR_NOMATCH) {
|
||||||
PCRE2_UCHAR buffer[120];
|
PCRE2_UCHAR buffer[120];
|
||||||
|
@ -84,18 +207,155 @@ static int f_pcre_match(lua_State *L) {
|
||||||
we just detect this case and give up. */
|
we just detect this case and give up. */
|
||||||
luaL_error(L, "regex matching error: \\K was used in an assertion to "
|
luaL_error(L, "regex matching error: \\K was used in an assertion to "
|
||||||
" set the match start after its end");
|
" set the match start after its end");
|
||||||
|
if (regex_compiled) pcre2_code_free(re);
|
||||||
pcre2_match_data_free(md);
|
pcre2_match_data_free(md);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < rc*2; i++)
|
for (int i = 0; i < rc*2; i++)
|
||||||
lua_pushinteger(L, ovector[i]+offset+1);
|
lua_pushinteger(L, ovector[i]+offset+1);
|
||||||
|
if (regex_compiled) pcre2_code_free(re);
|
||||||
pcre2_match_data_free(md);
|
pcre2_match_data_free(md);
|
||||||
return rc*2;
|
return rc*2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int f_pcre_gmatch(lua_State *L) {
|
||||||
|
/* pattern param */
|
||||||
|
bool regex_compiled = false;
|
||||||
|
pcre2_code* re = regex_get_pattern(L, ®ex_compiled);
|
||||||
|
if (!re) return 0;
|
||||||
|
size_t subject_len = 0;
|
||||||
|
|
||||||
|
/* subject param */
|
||||||
|
const char* subject = luaL_checklstring(L, 2, &subject_len);
|
||||||
|
|
||||||
|
/* offset param */
|
||||||
|
size_t offset = regex_offset_relative(
|
||||||
|
luaL_optnumber(L, 3, 1), subject_len
|
||||||
|
) - 1;
|
||||||
|
|
||||||
|
/* keep strings on closure to avoid being collected */
|
||||||
|
lua_settop(L, 2);
|
||||||
|
|
||||||
|
RegexState *state;
|
||||||
|
state = (RegexState*)lua_newuserdata(L, sizeof(RegexState));
|
||||||
|
|
||||||
|
state->re = re;
|
||||||
|
state->match_data = pcre2_match_data_create_from_pattern(re, NULL);
|
||||||
|
state->subject = subject;
|
||||||
|
state->subject_len = subject_len;
|
||||||
|
state->offset = offset;
|
||||||
|
state->found = true;
|
||||||
|
state->regex_compiled = regex_compiled;
|
||||||
|
|
||||||
|
lua_pushcclosure(L, regex_gmatch_iterator, 3);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int f_pcre_gsub(lua_State *L) {
|
||||||
|
size_t subject_len = 0, replacement_len = 0;
|
||||||
|
|
||||||
|
bool regex_compiled = false;
|
||||||
|
pcre2_code* re = regex_get_pattern(L, ®ex_compiled);
|
||||||
|
if (!re) return 0 ;
|
||||||
|
|
||||||
|
char* subject = (char*) luaL_checklstring(L, 2, &subject_len);
|
||||||
|
const char* replacement = luaL_checklstring(L, 3, &replacement_len);
|
||||||
|
int limit = luaL_optinteger(L, 4, 0);
|
||||||
|
if (limit < 0 ) limit = 0;
|
||||||
|
|
||||||
|
pcre2_match_data* match_data = pcre2_match_data_create_from_pattern(re, NULL);
|
||||||
|
|
||||||
|
size_t buffer_size = 1024;
|
||||||
|
char *output = (char *)malloc(buffer_size);
|
||||||
|
|
||||||
|
int options = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH | PCRE2_SUBSTITUTE_EXTENDED;
|
||||||
|
if (limit == 0) options |= PCRE2_SUBSTITUTE_GLOBAL;
|
||||||
|
|
||||||
|
int results_count = 0;
|
||||||
|
int limit_count = 0;
|
||||||
|
bool done = false;
|
||||||
|
size_t offset = 0;
|
||||||
|
PCRE2_SIZE outlen = buffer_size;
|
||||||
|
while (!done) {
|
||||||
|
results_count = pcre2_substitute(
|
||||||
|
re,
|
||||||
|
(PCRE2_SPTR)subject, subject_len,
|
||||||
|
offset, options,
|
||||||
|
match_data, NULL,
|
||||||
|
(PCRE2_SPTR)replacement, replacement_len,
|
||||||
|
(PCRE2_UCHAR*)output, &outlen
|
||||||
|
);
|
||||||
|
|
||||||
|
if (results_count != PCRE2_ERROR_NOMEMORY || buffer_size >= outlen) {
|
||||||
|
/* PCRE2_SUBSTITUTE_GLOBAL code path (fastest) */
|
||||||
|
if(limit == 0) {
|
||||||
|
done = true;
|
||||||
|
/* non PCRE2_SUBSTITUTE_GLOBAL with limit code path (slower) */
|
||||||
|
} else {
|
||||||
|
size_t ovector_count = pcre2_get_ovector_count(match_data);
|
||||||
|
if (results_count > 0 && ovector_count > 0) {
|
||||||
|
limit_count++;
|
||||||
|
PCRE2_SIZE* ovector = pcre2_get_ovector_pointer(match_data);
|
||||||
|
if (outlen > subject_len) {
|
||||||
|
offset = ovector[1] + (outlen - subject_len);
|
||||||
|
} else {
|
||||||
|
offset = ovector[1] - (subject_len - outlen);
|
||||||
|
}
|
||||||
|
if (limit_count > 1) free(subject);
|
||||||
|
if (limit_count == limit || offset-1 == outlen) {
|
||||||
|
done = true;
|
||||||
|
results_count = limit_count;
|
||||||
|
} else {
|
||||||
|
subject = output;
|
||||||
|
subject_len = outlen;
|
||||||
|
output = (char *)malloc(buffer_size);
|
||||||
|
outlen = buffer_size;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (limit_count > 1) {
|
||||||
|
free(subject);
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
results_count = limit_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buffer_size = outlen;
|
||||||
|
output = (char *)realloc(output, buffer_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int return_count = 0;
|
||||||
|
|
||||||
|
if (results_count > 0) {
|
||||||
|
lua_pushlstring(L, (const char*) output, outlen);
|
||||||
|
lua_pushinteger(L, results_count);
|
||||||
|
return_count = 2;
|
||||||
|
} else if (results_count == 0) {
|
||||||
|
lua_pushlstring(L, subject, subject_len);
|
||||||
|
lua_pushinteger(L, 0);
|
||||||
|
return_count = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(output);
|
||||||
|
pcre2_match_data_free(match_data);
|
||||||
|
if (regex_compiled)
|
||||||
|
pcre2_code_free(re);
|
||||||
|
|
||||||
|
if (results_count < 0) {
|
||||||
|
PCRE2_UCHAR errmsg[256];
|
||||||
|
pcre2_get_error_message(results_count, errmsg, sizeof(errmsg));
|
||||||
|
return luaL_error(L, "regex substitute error: %s", errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return return_count;
|
||||||
|
}
|
||||||
|
|
||||||
static const luaL_Reg lib[] = {
|
static const luaL_Reg lib[] = {
|
||||||
{ "compile", f_pcre_compile },
|
{ "compile", f_pcre_compile },
|
||||||
{ "cmatch", f_pcre_match },
|
{ "cmatch", f_pcre_match },
|
||||||
|
{ "gmatch", f_pcre_gmatch },
|
||||||
|
{ "gsub", f_pcre_gsub },
|
||||||
{ "__gc", f_pcre_gc },
|
{ "__gc", f_pcre_gc },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "api.h"
|
#include "api.h"
|
||||||
|
<<<<<<< HEAD
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
#include "rencache.h"
|
#include "rencache.h"
|
||||||
|
=======
|
||||||
|
#include "../renderer.h"
|
||||||
|
#include "../rencache.h"
|
||||||
|
#include "lua.h"
|
||||||
|
|
||||||
|
// a reference index to a table that stores the fonts
|
||||||
|
static int RENDERER_FONT_REF = LUA_NOREF;
|
||||||
|
>>>>>>> master
|
||||||
|
|
||||||
static int font_get_options(
|
static int font_get_options(
|
||||||
lua_State *L,
|
lua_State *L,
|
||||||
|
@ -137,7 +146,22 @@ static int f_font_copy(lua_State *L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int f_font_group(lua_State* L) {
|
static int f_font_group(lua_State* L) {
|
||||||
|
int table_size;
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
|
||||||
|
table_size = lua_rawlen(L, 1);
|
||||||
|
if (table_size <= 0)
|
||||||
|
return luaL_error(L, "failed to create font group: table is empty");
|
||||||
|
if (table_size > FONT_FALLBACK_MAX)
|
||||||
|
return luaL_error(L, "failed to create font group: table size too large");
|
||||||
|
|
||||||
|
// we also need to ensure that there are no fontgroups inside it
|
||||||
|
for (int i = 1; i <= table_size; i++) {
|
||||||
|
if (lua_rawgeti(L, 1, i) != LUA_TUSERDATA)
|
||||||
|
return luaL_typeerror(L, -1, API_TYPE_FONT "(userdata)");
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
luaL_setmetatable(L, API_TYPE_FONT);
|
luaL_setmetatable(L, API_TYPE_FONT);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -176,7 +200,10 @@ static int f_font_gc(lua_State *L) {
|
||||||
|
|
||||||
static int f_font_get_width(lua_State *L) {
|
static int f_font_get_width(lua_State *L) {
|
||||||
RenFont* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1);
|
RenFont* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1);
|
||||||
lua_pushnumber(L, ren_font_group_get_width(fonts, luaL_checkstring(L, 2)));
|
size_t len;
|
||||||
|
const char *text = luaL_checklstring(L, 2, &len);
|
||||||
|
|
||||||
|
lua_pushnumber(L, ren_font_group_get_width(fonts, text, len));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,19 +226,47 @@ static int f_font_set_size(lua_State *L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int color_value_error(lua_State *L, int idx, int table_idx) {
|
||||||
|
const char *type, *msg;
|
||||||
|
// generate an appropriate error message
|
||||||
|
if (luaL_getmetafield(L, -1, "__name") == LUA_TSTRING) {
|
||||||
|
type = lua_tostring(L, -1); // metatable name
|
||||||
|
} else if (lua_type(L, -1) == LUA_TLIGHTUSERDATA) {
|
||||||
|
type = "light userdata"; // special name for light userdata
|
||||||
|
} else {
|
||||||
|
type = lua_typename(L, lua_type(L, -1)); // default name
|
||||||
|
}
|
||||||
|
// the reason it went through so much hoops is to generate the correct error
|
||||||
|
// message (with function name and proper index).
|
||||||
|
msg = lua_pushfstring(L, "table[%d]: %s expected, got %s", table_idx, lua_typename(L, LUA_TNUMBER), type);
|
||||||
|
return luaL_argerror(L, idx, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_color_value(lua_State *L, int idx, int table_idx) {
|
||||||
|
lua_rawgeti(L, idx, table_idx);
|
||||||
|
return lua_isnumber(L, -1) ? lua_tonumber(L, -1) : color_value_error(L, idx, table_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_color_value_opt(lua_State *L, int idx, int table_idx, int default_value) {
|
||||||
|
lua_rawgeti(L, idx, table_idx);
|
||||||
|
if (lua_isnoneornil(L, -1))
|
||||||
|
return default_value;
|
||||||
|
else if (lua_isnumber(L, -1))
|
||||||
|
return lua_tonumber(L, -1);
|
||||||
|
else
|
||||||
|
return color_value_error(L, idx, table_idx);
|
||||||
|
}
|
||||||
|
|
||||||
static RenColor checkcolor(lua_State *L, int idx, int def) {
|
static RenColor checkcolor(lua_State *L, int idx, int def) {
|
||||||
RenColor color;
|
RenColor color;
|
||||||
if (lua_isnoneornil(L, idx)) {
|
if (lua_isnoneornil(L, idx)) {
|
||||||
return (RenColor) { def, def, def, 255 };
|
return (RenColor) { def, def, def, 255 };
|
||||||
}
|
}
|
||||||
lua_rawgeti(L, idx, 1);
|
luaL_checktype(L, idx, LUA_TTABLE);
|
||||||
lua_rawgeti(L, idx, 2);
|
color.r = get_color_value(L, idx, 1);
|
||||||
lua_rawgeti(L, idx, 3);
|
color.g = get_color_value(L, idx, 2);
|
||||||
lua_rawgeti(L, idx, 4);
|
color.b = get_color_value(L, idx, 3);
|
||||||
color.r = luaL_checknumber(L, -4);
|
color.a = get_color_value_opt(L, idx, 4, 255);
|
||||||
color.g = luaL_checknumber(L, -3);
|
|
||||||
color.b = luaL_checknumber(L, -2);
|
|
||||||
color.a = luaL_optnumber(L, -1, 255);
|
|
||||||
lua_pop(L, 4);
|
lua_pop(L, 4);
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
@ -241,6 +296,9 @@ static int f_begin_frame(UNUSED lua_State *L) {
|
||||||
|
|
||||||
static int f_end_frame(UNUSED lua_State *L) {
|
static int f_end_frame(UNUSED lua_State *L) {
|
||||||
rencache_end_frame();
|
rencache_end_frame();
|
||||||
|
// clear the font reference table
|
||||||
|
lua_newtable(L);
|
||||||
|
lua_rawseti(L, LUA_REGISTRYINDEX, RENDERER_FONT_REF);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,11 +335,25 @@ static int f_draw_rect(lua_State *L) {
|
||||||
static int f_draw_text(lua_State *L) {
|
static int f_draw_text(lua_State *L) {
|
||||||
RenFont* fonts[FONT_FALLBACK_MAX];
|
RenFont* fonts[FONT_FALLBACK_MAX];
|
||||||
font_retrieve(L, fonts, 1);
|
font_retrieve(L, fonts, 1);
|
||||||
const char *text = luaL_checkstring(L, 2);
|
|
||||||
|
// stores a reference to this font to the reference table
|
||||||
|
lua_rawgeti(L, LUA_REGISTRYINDEX, RENDERER_FONT_REF);
|
||||||
|
if (lua_istable(L, -1))
|
||||||
|
{
|
||||||
|
lua_pushvalue(L, 1);
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "warning: failed to reference count fonts\n");
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
size_t len;
|
||||||
|
const char *text = luaL_checklstring(L, 2, &len);
|
||||||
float x = luaL_checknumber(L, 3);
|
float x = luaL_checknumber(L, 3);
|
||||||
int y = luaL_checknumber(L, 4);
|
int y = luaL_checknumber(L, 4);
|
||||||
RenColor color = checkcolor(L, 5, 255);
|
RenColor color = checkcolor(L, 5, 255);
|
||||||
x = rencache_draw_text(fonts, text, x, y, color);
|
x = rencache_draw_text(fonts, text, len, x, y, color);
|
||||||
lua_pushnumber(L, x);
|
lua_pushnumber(L, x);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -312,6 +384,10 @@ static const luaL_Reg fontLib[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
int luaopen_renderer(lua_State *L) {
|
int luaopen_renderer(lua_State *L) {
|
||||||
|
// gets a reference on the registry to store font data
|
||||||
|
lua_newtable(L);
|
||||||
|
RENDERER_FONT_REF = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
luaL_newlib(L, lib);
|
luaL_newlib(L, lib);
|
||||||
luaL_newmetatable(L, API_TYPE_FONT);
|
luaL_newmetatable(L, API_TYPE_FONT);
|
||||||
luaL_setfuncs(L, fontLib, 0);
|
luaL_setfuncs(L, fontLib, 0);
|
||||||
|
|
164
src/api/system.c
164
src/api/system.c
|
@ -12,6 +12,20 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <fileapi.h>
|
#include <fileapi.h>
|
||||||
#include "../utfconv.h"
|
#include "../utfconv.h"
|
||||||
|
|
||||||
|
// Windows does not define the S_ISREG and S_ISDIR macros in stat.h, so we do.
|
||||||
|
// We have to define _CRT_INTERNAL_NONSTDC_NAMES 1 before #including sys/stat.h
|
||||||
|
// in order for Microsoft's stat.h to define names like S_IFMT, S_IFREG, and S_IFDIR,
|
||||||
|
// rather than just defining _S_IFMT, _S_IFREG, and _S_IFDIR as it normally does.
|
||||||
|
#define _CRT_INTERNAL_NONSTDC_NAMES 1
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
|
||||||
|
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||||
|
#endif
|
||||||
|
#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
|
||||||
|
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
@ -169,8 +183,9 @@ static void push_win32_error(lua_State *L, DWORD rc) {
|
||||||
|
|
||||||
static int f_poll_event(lua_State *L) {
|
static int f_poll_event(lua_State *L) {
|
||||||
char buf[16];
|
char buf[16];
|
||||||
int mx, my, wx, wy;
|
int mx, my, w, h;
|
||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
|
SDL_Event event_plus;
|
||||||
|
|
||||||
top:
|
top:
|
||||||
if ( !SDL_PollEvent(&e) ) {
|
if ( !SDL_PollEvent(&e) ) {
|
||||||
|
@ -220,12 +235,11 @@ top:
|
||||||
goto top;
|
goto top;
|
||||||
|
|
||||||
case SDL_DROPFILE:
|
case SDL_DROPFILE:
|
||||||
SDL_GetGlobalMouseState(&mx, &my);
|
SDL_GetMouseState(&mx, &my);
|
||||||
SDL_GetWindowPosition(window, &wx, &wy);
|
|
||||||
lua_pushstring(L, "filedropped");
|
lua_pushstring(L, "filedropped");
|
||||||
lua_pushstring(L, e.drop.file);
|
lua_pushstring(L, e.drop.file);
|
||||||
lua_pushinteger(L, mx - wx);
|
lua_pushinteger(L, mx);
|
||||||
lua_pushinteger(L, my - wy);
|
lua_pushinteger(L, my);
|
||||||
SDL_free(e.drop.file);
|
SDL_free(e.drop.file);
|
||||||
return 4;
|
return 4;
|
||||||
|
|
||||||
|
@ -261,6 +275,23 @@ top:
|
||||||
lua_pushstring(L, e.text.text);
|
lua_pushstring(L, e.text.text);
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
|
case SDL_TEXTEDITING:
|
||||||
|
lua_pushstring(L, "textediting");
|
||||||
|
lua_pushstring(L, e.edit.text);
|
||||||
|
lua_pushinteger(L, e.edit.start);
|
||||||
|
lua_pushinteger(L, e.edit.length);
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 22)
|
||||||
|
case SDL_TEXTEDITING_EXT:
|
||||||
|
lua_pushstring(L, "textediting");
|
||||||
|
lua_pushstring(L, e.editExt.text);
|
||||||
|
lua_pushinteger(L, e.editExt.start);
|
||||||
|
lua_pushinteger(L, e.editExt.length);
|
||||||
|
SDL_free(e.editExt.text);
|
||||||
|
return 4;
|
||||||
|
#endif
|
||||||
|
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
case SDL_MOUSEBUTTONDOWN:
|
||||||
if (e.button.button == 1) { SDL_CaptureMouse(1); }
|
if (e.button.button == 1) { SDL_CaptureMouse(1); }
|
||||||
lua_pushstring(L, "mousepressed");
|
lua_pushstring(L, "mousepressed");
|
||||||
|
@ -280,7 +311,6 @@ top:
|
||||||
|
|
||||||
case SDL_MOUSEMOTION:
|
case SDL_MOUSEMOTION:
|
||||||
SDL_PumpEvents();
|
SDL_PumpEvents();
|
||||||
SDL_Event event_plus;
|
|
||||||
while (SDL_PeepEvents(&event_plus, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
|
while (SDL_PeepEvents(&event_plus, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
|
||||||
e.motion.x = event_plus.motion.x;
|
e.motion.x = event_plus.motion.x;
|
||||||
e.motion.y = event_plus.motion.y;
|
e.motion.y = event_plus.motion.y;
|
||||||
|
@ -296,8 +326,51 @@ top:
|
||||||
|
|
||||||
case SDL_MOUSEWHEEL:
|
case SDL_MOUSEWHEEL:
|
||||||
lua_pushstring(L, "mousewheel");
|
lua_pushstring(L, "mousewheel");
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
||||||
|
lua_pushnumber(L, e.wheel.preciseY);
|
||||||
|
// Use -x to keep consistency with vertical scrolling values (e.g. shift+scroll)
|
||||||
|
lua_pushnumber(L, -e.wheel.preciseX);
|
||||||
|
#else
|
||||||
lua_pushinteger(L, e.wheel.y);
|
lua_pushinteger(L, e.wheel.y);
|
||||||
return 2;
|
lua_pushinteger(L, -e.wheel.x);
|
||||||
|
#endif
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
case SDL_FINGERDOWN:
|
||||||
|
SDL_GetWindowSize(window, &w, &h);
|
||||||
|
|
||||||
|
lua_pushstring(L, "touchpressed");
|
||||||
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
|
||||||
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.y * h));
|
||||||
|
lua_pushinteger(L, e.tfinger.fingerId);
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case SDL_FINGERUP:
|
||||||
|
SDL_GetWindowSize(window, &w, &h);
|
||||||
|
|
||||||
|
lua_pushstring(L, "touchreleased");
|
||||||
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
|
||||||
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.y * h));
|
||||||
|
lua_pushinteger(L, e.tfinger.fingerId);
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case SDL_FINGERMOTION:
|
||||||
|
SDL_PumpEvents();
|
||||||
|
while (SDL_PeepEvents(&event_plus, 1, SDL_GETEVENT, SDL_FINGERMOTION, SDL_FINGERMOTION) > 0) {
|
||||||
|
e.tfinger.x = event_plus.tfinger.x;
|
||||||
|
e.tfinger.y = event_plus.tfinger.y;
|
||||||
|
e.tfinger.dx += event_plus.tfinger.dx;
|
||||||
|
e.tfinger.dy += event_plus.tfinger.dy;
|
||||||
|
}
|
||||||
|
SDL_GetWindowSize(window, &w, &h);
|
||||||
|
|
||||||
|
lua_pushstring(L, "touchmoved");
|
||||||
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
|
||||||
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.y * h));
|
||||||
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.dx * w));
|
||||||
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.dy * h));
|
||||||
|
lua_pushinteger(L, e.tfinger.fingerId);
|
||||||
|
return 6;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
goto top;
|
goto top;
|
||||||
|
@ -441,6 +514,36 @@ static int f_get_window_mode(lua_State *L) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int f_set_text_input_rect(lua_State *L) {
|
||||||
|
SDL_Rect rect;
|
||||||
|
rect.x = luaL_checknumber(L, 1);
|
||||||
|
rect.y = luaL_checknumber(L, 2);
|
||||||
|
rect.w = luaL_checknumber(L, 3);
|
||||||
|
rect.h = luaL_checknumber(L, 4);
|
||||||
|
SDL_SetTextInputRect(&rect);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int f_clear_ime(lua_State *L) {
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 22)
|
||||||
|
SDL_ClearComposition();
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int f_raise_window(lua_State *L) {
|
||||||
|
/*
|
||||||
|
SDL_RaiseWindow should be enough but on some window managers like the
|
||||||
|
one used on Gnome the window needs to first have input focus in order
|
||||||
|
to allow the window to be focused. Also on wayland the raise window event
|
||||||
|
may not always be obeyed.
|
||||||
|
*/
|
||||||
|
SDL_SetWindowInputFocus(window);
|
||||||
|
SDL_RaiseWindow(window);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int f_show_fatal_error(lua_State *L) {
|
static int f_show_fatal_error(lua_State *L) {
|
||||||
const char *title = luaL_checkstring(L, 1);
|
const char *title = luaL_checkstring(L, 1);
|
||||||
|
@ -448,19 +551,8 @@ static int f_show_fatal_error(lua_State *L) {
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
MessageBox(0, msg, title, MB_OK | MB_ICONERROR);
|
MessageBox(0, msg, title, MB_OK | MB_ICONERROR);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
SDL_MessageBoxButtonData buttons[] = {
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, msg, NULL);
|
||||||
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 0, "Ok" },
|
|
||||||
};
|
|
||||||
SDL_MessageBoxData data = {
|
|
||||||
.title = title,
|
|
||||||
.message = msg,
|
|
||||||
.numbuttons = 1,
|
|
||||||
.buttons = buttons,
|
|
||||||
};
|
|
||||||
int buttonid;
|
|
||||||
SDL_ShowMessageBox(&data, &buttonid);
|
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -517,7 +609,7 @@ static int f_list_dir(lua_State *L) {
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
lua_settop(L, 1);
|
lua_settop(L, 1);
|
||||||
if (strchr("\\/", path[strlen(path) - 2]) != NULL)
|
if (path[0] == 0 || strchr("\\/", path[strlen(path) - 1]) != NULL)
|
||||||
lua_pushstring(L, "*");
|
lua_pushstring(L, "*");
|
||||||
else
|
else
|
||||||
lua_pushstring(L, "/*");
|
lua_pushstring(L, "/*");
|
||||||
|
@ -843,7 +935,7 @@ typedef struct lua_function_node {
|
||||||
#define P(FUNC) { "lua_" #FUNC, (fptr)(lua_##FUNC) }
|
#define P(FUNC) { "lua_" #FUNC, (fptr)(lua_##FUNC) }
|
||||||
#define U(FUNC) { "luaL_" #FUNC, (fptr)(luaL_##FUNC) }
|
#define U(FUNC) { "luaL_" #FUNC, (fptr)(luaL_##FUNC) }
|
||||||
static void* api_require(const char* symbol) {
|
static void* api_require(const char* symbol) {
|
||||||
static lua_function_node nodes[] = {
|
static const lua_function_node nodes[] = {
|
||||||
P(atpanic), P(checkstack),
|
P(atpanic), P(checkstack),
|
||||||
P(close), P(concat), P(copy), P(createtable), P(dump),
|
P(close), P(concat), P(copy), P(createtable), P(dump),
|
||||||
P(error), P(gc), P(getallocf), P(getfield),
|
P(error), P(gc), P(getallocf), P(getfield),
|
||||||
|
@ -866,16 +958,21 @@ static void* api_require(const char* symbol) {
|
||||||
U(newmetatable), U(setmetatable), U(testudata), U(checkudata), U(where),
|
U(newmetatable), U(setmetatable), U(testudata), U(checkudata), U(where),
|
||||||
U(error), U(fileresult), U(execresult), U(ref), U(unref), U(loadstring),
|
U(error), U(fileresult), U(execresult), U(ref), U(unref), U(loadstring),
|
||||||
U(newstate), U(setfuncs), U(buffinit), U(addlstring), U(addstring),
|
U(newstate), U(setfuncs), U(buffinit), U(addlstring), U(addstring),
|
||||||
U(addvalue), U(pushresult), {"api_load_libs", (void*)(api_load_libs)},
|
U(addvalue), U(pushresult), U(openlibs), {"api_load_libs", (void*)(api_load_libs)},
|
||||||
#if LUA_VERSION_NUM >= 502
|
#if LUA_VERSION_NUM >= 502
|
||||||
P(absindex), P(arith), P(callk), P(compare), P(getglobal),
|
P(absindex), P(arith), P(callk), P(compare), P(getglobal),
|
||||||
P(len), P(pcallk), P(rawgetp), P(rawlen), P(rawsetp), P(setglobal),
|
P(len), P(pcallk), P(rawgetp), P(rawlen), P(rawsetp), P(setglobal),
|
||||||
P(iscfunction), P(yieldk),
|
P(iscfunction), P(yieldk),
|
||||||
U(checkversion_), U(tolstring), U(len), U(getsubtable), U(prepbuffsize),
|
U(checkversion_), U(tolstring), U(len), U(getsubtable), U(prepbuffsize),
|
||||||
U(pushresultsize), U(buffinitsize), U(checklstring), U(checkoption), U(gsub), U(loadbufferx),
|
U(pushresultsize), U(buffinitsize), U(checklstring), U(checkoption), U(gsub), U(loadbufferx),
|
||||||
U(loadfilex), U(optinteger), U(optlstring), U(requiref), U(traceback)
|
U(loadfilex), U(optinteger), U(optlstring), U(requiref), U(traceback),
|
||||||
#else
|
#else
|
||||||
P(objlen)
|
P(objlen),
|
||||||
|
#endif
|
||||||
|
#if LUA_VERSION_NUM >= 504
|
||||||
|
P(newuserdatauv), P(setiuservalue), P(getiuservalue)
|
||||||
|
#else
|
||||||
|
P(newuserdata), P(setuservalue), P(getuservalue)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -886,6 +983,14 @@ static void* api_require(const char* symbol) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int f_library_gc(lua_State *L) {
|
||||||
|
lua_getfield(L, 1, "handle");
|
||||||
|
void* handle = lua_touserdata(L, -1);
|
||||||
|
SDL_UnloadObject(handle);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int f_load_native_plugin(lua_State *L) {
|
static int f_load_native_plugin(lua_State *L) {
|
||||||
char entrypoint_name[512]; entrypoint_name[sizeof(entrypoint_name) - 1] = '\0';
|
char entrypoint_name[512]; entrypoint_name[sizeof(entrypoint_name) - 1] = '\0';
|
||||||
int result;
|
int result;
|
||||||
|
@ -898,9 +1003,12 @@ static int f_load_native_plugin(lua_State *L) {
|
||||||
|
|
||||||
lua_getglobal(L, "package");
|
lua_getglobal(L, "package");
|
||||||
lua_getfield(L, -1, "native_plugins");
|
lua_getfield(L, -1, "native_plugins");
|
||||||
|
lua_newtable(L);
|
||||||
lua_pushlightuserdata(L, library);
|
lua_pushlightuserdata(L, library);
|
||||||
|
lua_setfield(L, -2, "handle");
|
||||||
|
luaL_setmetatable(L, API_TYPE_NATIVE_PLUGIN);
|
||||||
lua_setfield(L, -2, name);
|
lua_setfield(L, -2, name);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 2);
|
||||||
|
|
||||||
const char *basename = strrchr(name, '.');
|
const char *basename = strrchr(name, '.');
|
||||||
basename = !basename ? name : basename + 1;
|
basename = !basename ? name : basename + 1;
|
||||||
|
@ -993,7 +1101,10 @@ static const luaL_Reg lib[] = {
|
||||||
{ "set_window_hit_test", f_set_window_hit_test },
|
{ "set_window_hit_test", f_set_window_hit_test },
|
||||||
{ "get_window_size", f_get_window_size },
|
{ "get_window_size", f_get_window_size },
|
||||||
{ "set_window_size", f_set_window_size },
|
{ "set_window_size", f_set_window_size },
|
||||||
|
{ "set_text_input_rect", f_set_text_input_rect },
|
||||||
|
{ "clear_ime", f_clear_ime },
|
||||||
{ "window_has_focus", f_window_has_focus },
|
{ "window_has_focus", f_window_has_focus },
|
||||||
|
{ "raise_window", f_raise_window },
|
||||||
{ "show_fatal_error", f_show_fatal_error },
|
{ "show_fatal_error", f_show_fatal_error },
|
||||||
{ "rmdir", f_rmdir },
|
{ "rmdir", f_rmdir },
|
||||||
{ "chdir", f_chdir },
|
{ "chdir", f_chdir },
|
||||||
|
@ -1017,6 +1128,9 @@ static const luaL_Reg lib[] = {
|
||||||
|
|
||||||
|
|
||||||
int luaopen_system(lua_State *L) {
|
int luaopen_system(lua_State *L) {
|
||||||
|
luaL_newmetatable(L, API_TYPE_NATIVE_PLUGIN);
|
||||||
|
lua_pushcfunction(L, f_library_gc);
|
||||||
|
lua_setfield(L, -2, "__gc");
|
||||||
luaL_newlib(L, lib);
|
luaL_newlib(L, lib);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,27 @@
|
||||||
/*
|
/*
|
||||||
* Integration of https://github.com/starwing/luautf8
|
* Integration of https://github.com/starwing/luautf8
|
||||||
*
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
* Copyright (c) 2018 Xavier Wang
|
* Copyright (c) 2018 Xavier Wang
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
* copyright notice and this permission notice appear in all copies.
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
* copies or substantial portions of the Software.
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
*
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
|
@ -369,7 +377,7 @@ static int Lutf8_codepoint (lua_State *L) {
|
||||||
luaL_checkstack(L, n, "string slice too long");
|
luaL_checkstack(L, n, "string slice too long");
|
||||||
n = 0; /* count the number of returns */
|
n = 0; /* count the number of returns */
|
||||||
se = s + pose; /* string end */
|
se = s + pose; /* string end */
|
||||||
for (n = 0, s += posi - 1; s < se;) {
|
for (s += posi - 1; s < se;) {
|
||||||
utfint code = 0;
|
utfint code = 0;
|
||||||
s = utf8_safe_decode(L, s, &code);
|
s = utf8_safe_decode(L, s, &code);
|
||||||
if (!lax && utf8_invalid(code))
|
if (!lax && utf8_invalid(code))
|
||||||
|
|
56
src/main.c
56
src/main.c
|
@ -5,6 +5,8 @@
|
||||||
#include "rencache.h"
|
#include "rencache.h"
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
#if defined(__amigaos4__) || defined(__morphos__)
|
#if defined(__amigaos4__) || defined(__morphos__)
|
||||||
#define VSTRING "Lite XL 2.1.0r1 (10.10.2022)"
|
#define VSTRING "Lite XL 2.1.0r1 (10.10.2022)"
|
||||||
#define VERSTAG "\0$VER: " VSTRING
|
#define VERSTAG "\0$VER: " VSTRING
|
||||||
|
@ -12,19 +14,20 @@
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#elif __linux__ || __FreeBSD__
|
#elif defined(__linux__)
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#elif defined(__APPLE__)
|
||||||
#elif __APPLE__
|
|
||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
#elif __amigaos4__
|
#elif defined(__amigaos4__)
|
||||||
#include "platform/amigaos4.h"
|
#include "platform/amigaos4.h"
|
||||||
static CONST_STRPTR stack USED = "$STACK:102400";
|
static CONST_STRPTR stack USED = "$STACK:102400";
|
||||||
static CONST_STRPTR version USED = VERSTAG;
|
static CONST_STRPTR version USED = VERSTAG;
|
||||||
#elif __morphos__
|
#elif defined(__morphos__)
|
||||||
#include "platform/morphos.h"
|
#include "platform/morphos.h"
|
||||||
unsigned long __stack = 1000000;
|
unsigned long __stack = 1000000;
|
||||||
UBYTE VString[] = VERSTAG;
|
UBYTE VString[] = VERSTAG;
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,7 +49,8 @@ static void get_exe_filename(char *buf, int sz) {
|
||||||
buf[len] = '\0';
|
buf[len] = '\0';
|
||||||
#elif __linux__
|
#elif __linux__
|
||||||
char path[] = "/proc/self/exe";
|
char path[] = "/proc/self/exe";
|
||||||
int len = readlink(path, buf, sz - 1);
|
ssize_t len = readlink(path, buf, sz - 1);
|
||||||
|
if (len > 0)
|
||||||
buf[len] = '\0';
|
buf[len] = '\0';
|
||||||
#elif __APPLE__
|
#elif __APPLE__
|
||||||
/* use realpath to resolve a symlink if the process was launched from one.
|
/* use realpath to resolve a symlink if the process was launched from one.
|
||||||
|
@ -58,8 +62,12 @@ static void get_exe_filename(char *buf, int sz) {
|
||||||
realpath(exepath, buf);
|
realpath(exepath, buf);
|
||||||
#elif defined(__amigaos4__) || defined(__morphos__)
|
#elif defined(__amigaos4__) || defined(__morphos__)
|
||||||
strcpy(buf, _fullpath("./lite"));
|
strcpy(buf, _fullpath("./lite"));
|
||||||
|
#elif __FreeBSD__
|
||||||
|
size_t len = sz;
|
||||||
|
const int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
|
||||||
|
sysctl(mib, 4, buf, &len, NULL, 0);
|
||||||
#else
|
#else
|
||||||
strcpy(buf, "./lite");
|
*buf = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,30 +106,39 @@ void set_macos_bundle_resources(lua_State *L);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef LITE_ARCH_TUPLE
|
#ifndef LITE_ARCH_TUPLE
|
||||||
#if __x86_64__ || _WIN64 || __MINGW64__
|
// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-140
|
||||||
|
#if defined(__x86_64__) || defined(_M_AMD64) || defined(__MINGW64__)
|
||||||
#define ARCH_PROCESSOR "x86_64"
|
#define ARCH_PROCESSOR "x86_64"
|
||||||
#elif __aarch64__
|
|
||||||
#define ARCH_PROCESSOR "aarch64"
|
|
||||||
#elif __arm__
|
#elif __arm__
|
||||||
#define ARCH_PROCESSOR "arm"
|
#define ARCH_PROCESSOR "arm"
|
||||||
#elif __amigaos4__ || __morphos__
|
#elif defined(__amigaos4__) || defined(__morphos__)
|
||||||
#define ARCH_PROCESSOR "ppc"
|
#define ARCH_PROCESSOR "ppc"
|
||||||
#else
|
#elif defined(__i386__) || defined(_M_IX86) || defined(__MINGW32__)
|
||||||
#define ARCH_PROCESSOR "x86"
|
#define ARCH_PROCESSOR "x86"
|
||||||
|
#elif defined(__aarch64__) || defined(_M_ARM64) || defined (_M_ARM64EC)
|
||||||
|
#define ARCH_PROCESSOR "aarch64"
|
||||||
|
#elif defined(__arm__) || defined(_M_ARM)
|
||||||
|
#define ARCH_PROCESSOR "arm"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#define ARCH_PLATFORM "windows"
|
#define ARCH_PLATFORM "windows"
|
||||||
#elif __linux__
|
#elif __linux__
|
||||||
#define ARCH_PLATFORM "linux"
|
#define ARCH_PLATFORM "linux"
|
||||||
|
#elif __FreeBSD__
|
||||||
|
#define ARCH_PLATFORM "freebsd"
|
||||||
#elif __APPLE__
|
#elif __APPLE__
|
||||||
#define ARCH_PLATFORM "darwin"
|
#define ARCH_PLATFORM "darwin"
|
||||||
#elif __amigaos4__
|
#elif __amigaos4__
|
||||||
#define ARCH_PLATFORM "amigaos4"
|
#define ARCH_PLATFORM "amigaos4"
|
||||||
#elif __morphos__
|
#elif __morphos__
|
||||||
#define ARCH_PLATFORM "morphos"
|
#define ARCH_PLATFORM "morphos"
|
||||||
#else
|
#endif
|
||||||
|
|
||||||
|
#if !defined(ARCH_PROCESSOR) || !defined(ARCH_PLATFORM)
|
||||||
#error "Please define -DLITE_ARCH_TUPLE."
|
#error "Please define -DLITE_ARCH_TUPLE."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LITE_ARCH_TUPLE ARCH_PROCESSOR "-" ARCH_PLATFORM
|
#define LITE_ARCH_TUPLE ARCH_PROCESSOR "-" ARCH_PLATFORM
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -130,7 +147,7 @@ int main(int argc, char **argv) {
|
||||||
HINSTANCE lib = LoadLibrary("user32.dll");
|
HINSTANCE lib = LoadLibrary("user32.dll");
|
||||||
int (*SetProcessDPIAware)() = (void*) GetProcAddress(lib, "SetProcessDPIAware");
|
int (*SetProcessDPIAware)() = (void*) GetProcAddress(lib, "SetProcessDPIAware");
|
||||||
SetProcessDPIAware();
|
SetProcessDPIAware();
|
||||||
#elif defined(__morphos__)
|
#elif defined(__amigaos4__) || defined(__morphos__)
|
||||||
setlocale(LC_ALL, "C");
|
setlocale(LC_ALL, "C");
|
||||||
#else
|
#else
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
@ -150,6 +167,12 @@ int main(int argc, char **argv) {
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 5)
|
#if SDL_VERSION_ATLEAST(2, 0, 5)
|
||||||
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
||||||
#endif
|
#endif
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
||||||
|
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||||
|
#endif
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 22)
|
||||||
|
SDL_SetHint(SDL_HINT_IME_SUPPORT_EXTENDED_TEXT, "1");
|
||||||
|
#endif
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 8)
|
#if SDL_VERSION_ATLEAST(2, 0, 8)
|
||||||
/* This hint tells SDL to respect borderless window as a normal window.
|
/* This hint tells SDL to respect borderless window as a normal window.
|
||||||
|
@ -206,7 +229,12 @@ init_lua:
|
||||||
|
|
||||||
char exename[2048];
|
char exename[2048];
|
||||||
get_exe_filename(exename, sizeof(exename));
|
get_exe_filename(exename, sizeof(exename));
|
||||||
|
if (*exename) {
|
||||||
lua_pushstring(L, exename);
|
lua_pushstring(L, exename);
|
||||||
|
} else {
|
||||||
|
// get_exe_filename failed
|
||||||
|
lua_pushstring(L, argv[0]);
|
||||||
|
}
|
||||||
lua_setglobal(L, "EXEFILE");
|
lua_setglobal(L, "EXEFILE");
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|
|
@ -15,6 +15,8 @@ lite_sources = [
|
||||||
if get_option('dirmonitor_backend') == ''
|
if get_option('dirmonitor_backend') == ''
|
||||||
if cc.has_function('inotify_init', prefix : '#include<sys/inotify.h>')
|
if cc.has_function('inotify_init', prefix : '#include<sys/inotify.h>')
|
||||||
dirmonitor_backend = 'inotify'
|
dirmonitor_backend = 'inotify'
|
||||||
|
elif host_machine.system() == 'darwin' and cc.check_header('CoreServices/CoreServices.h')
|
||||||
|
dirmonitor_backend = 'fsevents'
|
||||||
elif cc.has_function('kqueue', prefix : '#include<sys/event.h>')
|
elif cc.has_function('kqueue', prefix : '#include<sys/event.h>')
|
||||||
dirmonitor_backend = 'kqueue'
|
dirmonitor_backend = 'kqueue'
|
||||||
elif dependency('libkqueue', required : false).found()
|
elif dependency('libkqueue', required : false).found()
|
||||||
|
@ -63,5 +65,5 @@ executable('lite-xl',
|
||||||
link_args: lite_link_args,
|
link_args: lite_link_args,
|
||||||
install_dir: lite_bindir,
|
install_dir: lite_bindir,
|
||||||
install: true,
|
install: true,
|
||||||
gui_app: true,
|
win_subsystem: 'windows',
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,9 +2,19 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdalign.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#ifndef alignof
|
||||||
|
#define alignof _Alignof
|
||||||
|
#endif
|
||||||
|
/* max_align_t is a compiler defined type, but
|
||||||
|
** MSVC doesn't provide it, so we'll have to improvise */
|
||||||
|
typedef long double max_align_t;
|
||||||
|
#else
|
||||||
|
#include <stdalign.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
#include "rencache.h"
|
#include "rencache.h"
|
||||||
|
|
||||||
|
@ -16,7 +26,8 @@
|
||||||
#define CELLS_X 80
|
#define CELLS_X 80
|
||||||
#define CELLS_Y 50
|
#define CELLS_Y 50
|
||||||
#define CELL_SIZE 96
|
#define CELL_SIZE 96
|
||||||
#define COMMAND_BUF_SIZE (1024 * 512)
|
#define CMD_BUF_RESIZE_RATE 1.2
|
||||||
|
#define CMD_BUF_INIT_SIZE (1024 * 512)
|
||||||
#define COMMAND_BARE_SIZE offsetof(Command, text)
|
#define COMMAND_BARE_SIZE offsetof(Command, text)
|
||||||
|
|
||||||
enum { SET_CLIP, DRAW_TEXT, DRAW_RECT };
|
enum { SET_CLIP, DRAW_TEXT, DRAW_RECT };
|
||||||
|
@ -29,6 +40,7 @@ typedef struct {
|
||||||
RenColor color;
|
RenColor color;
|
||||||
RenFont *fonts[FONT_FALLBACK_MAX];
|
RenFont *fonts[FONT_FALLBACK_MAX];
|
||||||
float text_x;
|
float text_x;
|
||||||
|
size_t len;
|
||||||
char text[];
|
char text[];
|
||||||
} Command;
|
} Command;
|
||||||
|
|
||||||
|
@ -37,13 +49,15 @@ static unsigned cells_buf2[CELLS_X * CELLS_Y];
|
||||||
static unsigned *cells_prev = cells_buf1;
|
static unsigned *cells_prev = cells_buf1;
|
||||||
static unsigned *cells = cells_buf2;
|
static unsigned *cells = cells_buf2;
|
||||||
static RenRect rect_buf[CELLS_X * CELLS_Y / 2];
|
static RenRect rect_buf[CELLS_X * CELLS_Y / 2];
|
||||||
static char command_buf[COMMAND_BUF_SIZE];
|
size_t command_buf_size = 0;
|
||||||
|
uint8_t *command_buf = NULL;
|
||||||
|
static bool resize_issue;
|
||||||
static int command_buf_idx;
|
static int command_buf_idx;
|
||||||
static RenRect screen_rect;
|
static RenRect screen_rect;
|
||||||
static bool show_debug;
|
static bool show_debug;
|
||||||
|
|
||||||
static inline int min(int a, int b) { return a < b ? a : b; }
|
static inline int rencache_min(int a, int b) { return a < b ? a : b; }
|
||||||
static inline int max(int a, int b) { return a > b ? a : b; }
|
static inline int rencache_max(int a, int b) { return a > b ? a : b; }
|
||||||
|
|
||||||
|
|
||||||
/* 32bit fnv-1a hash */
|
/* 32bit fnv-1a hash */
|
||||||
|
@ -69,32 +83,54 @@ static inline bool rects_overlap(RenRect a, RenRect b) {
|
||||||
|
|
||||||
|
|
||||||
static RenRect intersect_rects(RenRect a, RenRect b) {
|
static RenRect intersect_rects(RenRect a, RenRect b) {
|
||||||
int x1 = max(a.x, b.x);
|
int x1 = rencache_max(a.x, b.x);
|
||||||
int y1 = max(a.y, b.y);
|
int y1 = rencache_max(a.y, b.y);
|
||||||
int x2 = min(a.x + a.width, b.x + b.width);
|
int x2 = rencache_min(a.x + a.width, b.x + b.width);
|
||||||
int y2 = min(a.y + a.height, b.y + b.height);
|
int y2 = rencache_min(a.y + a.height, b.y + b.height);
|
||||||
return (RenRect) { x1, y1, max(0, x2 - x1), max(0, y2 - y1) };
|
return (RenRect) { x1, y1, rencache_max(0, x2 - x1), rencache_max(0, y2 - y1) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static RenRect merge_rects(RenRect a, RenRect b) {
|
static RenRect merge_rects(RenRect a, RenRect b) {
|
||||||
int x1 = min(a.x, b.x);
|
int x1 = rencache_min(a.x, b.x);
|
||||||
int y1 = min(a.y, b.y);
|
int y1 = rencache_min(a.y, b.y);
|
||||||
int x2 = max(a.x + a.width, b.x + b.width);
|
int x2 = rencache_max(a.x + a.width, b.x + b.width);
|
||||||
int y2 = max(a.y + a.height, b.y + b.height);
|
int y2 = rencache_max(a.y + a.height, b.y + b.height);
|
||||||
return (RenRect) { x1, y1, x2 - x1, y2 - y1 };
|
return (RenRect) { x1, y1, x2 - x1, y2 - y1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool expand_command_buffer() {
|
||||||
|
size_t new_size = command_buf_size * CMD_BUF_RESIZE_RATE;
|
||||||
|
if (new_size == 0) {
|
||||||
|
new_size = CMD_BUF_INIT_SIZE;
|
||||||
|
}
|
||||||
|
uint8_t *new_command_buf = realloc(command_buf, new_size);
|
||||||
|
if (!new_command_buf) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
command_buf_size = new_size;
|
||||||
|
command_buf = new_command_buf;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static Command* push_command(int type, int size) {
|
static Command* push_command(int type, int size) {
|
||||||
size_t alignment = alignof(max_align_t) - 1;
|
if (resize_issue) {
|
||||||
size = (size + alignment) & ~alignment;
|
// Don't push new commands as we had problems resizing the command buffer.
|
||||||
Command *cmd = (Command*) (command_buf + command_buf_idx);
|
// Let's wait for the next frame.
|
||||||
int n = command_buf_idx + size;
|
|
||||||
if (n > COMMAND_BUF_SIZE) {
|
|
||||||
fprintf(stderr, "Warning: (" __FILE__ "): exhausted command buffer\n");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
size_t alignment = alignof(max_align_t) - 1;
|
||||||
|
size = (size + alignment) & ~alignment;
|
||||||
|
int n = command_buf_idx + size;
|
||||||
|
while (n > command_buf_size) {
|
||||||
|
if (!expand_command_buffer()) {
|
||||||
|
fprintf(stderr, "Warning: (" __FILE__ "): unable to resize command buffer (%ld)\n",
|
||||||
|
(size_t)(command_buf_size * CMD_BUF_RESIZE_RATE));
|
||||||
|
resize_issue = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command *cmd = (Command*) (command_buf + command_buf_idx);
|
||||||
command_buf_idx = n;
|
command_buf_idx = n;
|
||||||
memset(cmd, 0, size);
|
memset(cmd, 0, size);
|
||||||
cmd->type = type;
|
cmd->type = type;
|
||||||
|
@ -135,12 +171,12 @@ void rencache_draw_rect(RenRect rect, RenColor color) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float rencache_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor color)
|
float rencache_draw_text(RenFont **fonts, const char *text, size_t len, float x, int y, RenColor color)
|
||||||
{
|
{
|
||||||
float width = ren_font_group_get_width(fonts, text);
|
float width = ren_font_group_get_width(fonts, text, len);
|
||||||
RenRect rect = { x, y, (int)width, ren_font_group_get_height(fonts) };
|
RenRect rect = { x, y, (int)width, ren_font_group_get_height(fonts) };
|
||||||
if (rects_overlap(screen_rect, rect)) {
|
if (rects_overlap(screen_rect, rect)) {
|
||||||
int sz = strlen(text) + 1;
|
int sz = len + 1;
|
||||||
Command *cmd = push_command(DRAW_TEXT, COMMAND_BARE_SIZE + sz);
|
Command *cmd = push_command(DRAW_TEXT, COMMAND_BARE_SIZE + sz);
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
memcpy(cmd->text, text, sz);
|
memcpy(cmd->text, text, sz);
|
||||||
|
@ -148,6 +184,7 @@ float rencache_draw_text(RenFont **fonts, const char *text, float x, int y, RenC
|
||||||
memcpy(cmd->fonts, fonts, sizeof(RenFont*)*FONT_FALLBACK_MAX);
|
memcpy(cmd->fonts, fonts, sizeof(RenFont*)*FONT_FALLBACK_MAX);
|
||||||
cmd->rect = rect;
|
cmd->rect = rect;
|
||||||
cmd->text_x = x;
|
cmd->text_x = x;
|
||||||
|
cmd->len = len;
|
||||||
cmd->tab_size = ren_font_group_get_tab_size(fonts);
|
cmd->tab_size = ren_font_group_get_tab_size(fonts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,6 +200,7 @@ void rencache_invalidate(void) {
|
||||||
void rencache_begin_frame() {
|
void rencache_begin_frame() {
|
||||||
/* reset all cells if the screen width/height has changed */
|
/* reset all cells if the screen width/height has changed */
|
||||||
int w, h;
|
int w, h;
|
||||||
|
resize_issue = false;
|
||||||
ren_get_size(&w, &h);
|
ren_get_size(&w, &h);
|
||||||
if (screen_rect.width != w || h != screen_rect.height) {
|
if (screen_rect.width != w || h != screen_rect.height) {
|
||||||
screen_rect.width = w;
|
screen_rect.width = w;
|
||||||
|
@ -256,7 +294,7 @@ void rencache_end_frame() {
|
||||||
break;
|
break;
|
||||||
case DRAW_TEXT:
|
case DRAW_TEXT:
|
||||||
ren_font_group_set_tab_size(cmd->fonts, cmd->tab_size);
|
ren_font_group_set_tab_size(cmd->fonts, cmd->tab_size);
|
||||||
ren_draw_text(cmd->fonts, cmd->text, cmd->text_x, cmd->rect.y, cmd->color);
|
ren_draw_text(cmd->fonts, cmd->text, cmd->len, cmd->text_x, cmd->rect.y, cmd->color);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
void rencache_show_debug(bool enable);
|
void rencache_show_debug(bool enable);
|
||||||
void rencache_set_clip_rect(RenRect rect);
|
void rencache_set_clip_rect(RenRect rect);
|
||||||
void rencache_draw_rect(RenRect rect, RenColor color);
|
void rencache_draw_rect(RenRect rect, RenColor color);
|
||||||
float rencache_draw_text(RenFont **font, const char *text, float x, int y, RenColor color);
|
float rencache_draw_text(RenFont **font, const char *text, size_t len, float x, int y, RenColor color);
|
||||||
void rencache_invalidate(void);
|
void rencache_invalidate(void);
|
||||||
void rencache_begin_frame();
|
void rencache_begin_frame();
|
||||||
void rencache_end_frame();
|
void rencache_end_frame();
|
||||||
|
|
|
@ -8,6 +8,11 @@
|
||||||
#include <freetype/ftoutln.h>
|
#include <freetype/ftoutln.h>
|
||||||
#include FT_FREETYPE_H
|
#include FT_FREETYPE_H
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#include "utfconv.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
#include "renwindow.h"
|
#include "renwindow.h"
|
||||||
|
|
||||||
|
@ -51,7 +56,11 @@ typedef struct RenFont {
|
||||||
ERenFontHinting hinting;
|
ERenFontHinting hinting;
|
||||||
unsigned char style;
|
unsigned char style;
|
||||||
unsigned short underline_thickness;
|
unsigned short underline_thickness;
|
||||||
char path[1];
|
#ifdef _WIN32
|
||||||
|
unsigned char *file;
|
||||||
|
HANDLE file_handle;
|
||||||
|
#endif
|
||||||
|
char path[];
|
||||||
} RenFont;
|
} RenFont;
|
||||||
|
|
||||||
static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
|
static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
|
||||||
|
@ -206,9 +215,48 @@ static void font_clear_glyph_cache(RenFont* font) {
|
||||||
}
|
}
|
||||||
|
|
||||||
RenFont* ren_font_load(const char* path, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style) {
|
RenFont* ren_font_load(const char* path, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style) {
|
||||||
FT_Face face;
|
FT_Face face = NULL;
|
||||||
if (FT_New_Face( library, path, 0, &face))
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
HANDLE file = INVALID_HANDLE_VALUE;
|
||||||
|
DWORD read;
|
||||||
|
int font_file_len = 0;
|
||||||
|
unsigned char *font_file = NULL;
|
||||||
|
wchar_t *wpath = NULL;
|
||||||
|
|
||||||
|
if ((wpath = utfconv_utf8towc(path)) == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
if ((file = CreateFileW(wpath,
|
||||||
|
GENERIC_READ,
|
||||||
|
FILE_SHARE_READ, // or else we can't copy fonts
|
||||||
|
NULL,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
NULL)) == INVALID_HANDLE_VALUE)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
if ((font_file_len = GetFileSize(file, NULL)) == INVALID_FILE_SIZE)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
font_file = check_alloc(malloc(font_file_len * sizeof(unsigned char)));
|
||||||
|
if (!ReadFile(file, font_file, font_file_len, &read, NULL) || read != font_file_len)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
free(wpath);
|
||||||
|
wpath = NULL;
|
||||||
|
|
||||||
|
if (FT_New_Memory_Face(library, font_file, read, 0, &face))
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
if (FT_New_Face(library, path, 0, &face))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||||
if (FT_Set_Pixel_Sizes(face, 0, (int)(size*surface_scale)))
|
if (FT_Set_Pixel_Sizes(face, 0, (int)(size*surface_scale)))
|
||||||
goto failure;
|
goto failure;
|
||||||
|
@ -223,16 +271,31 @@ RenFont* ren_font_load(const char* path, float size, ERenFontAntialiasing antial
|
||||||
font->hinting = hinting;
|
font->hinting = hinting;
|
||||||
font->style = style;
|
font->style = style;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// we need to keep this for freetype
|
||||||
|
font->file = font_file;
|
||||||
|
font->file_handle = file;
|
||||||
|
#endif
|
||||||
|
|
||||||
if(FT_IS_SCALABLE(face))
|
if(FT_IS_SCALABLE(face))
|
||||||
font->underline_thickness = (unsigned short)((face->underline_thickness / (float)face->units_per_EM) * font->size);
|
font->underline_thickness = (unsigned short)((face->underline_thickness / (float)face->units_per_EM) * font->size);
|
||||||
if(!font->underline_thickness) font->underline_thickness = ceil((double) font->height / 14.0);
|
if(!font->underline_thickness) font->underline_thickness = ceil((double) font->height / 14.0);
|
||||||
|
|
||||||
if (FT_Load_Char(face, ' ', font_set_load_options(font)))
|
if (FT_Load_Char(face, ' ', font_set_load_options(font))) {
|
||||||
|
free(font);
|
||||||
goto failure;
|
goto failure;
|
||||||
|
}
|
||||||
font->space_advance = face->glyph->advance.x / 64.0f;
|
font->space_advance = face->glyph->advance.x / 64.0f;
|
||||||
font->tab_advance = font->space_advance * 2;
|
font->tab_advance = font->space_advance * 2;
|
||||||
return font;
|
return font;
|
||||||
failure:
|
|
||||||
|
failure:
|
||||||
|
#ifdef _WIN32
|
||||||
|
free(wpath);
|
||||||
|
free(font_file);
|
||||||
|
if (file != INVALID_HANDLE_VALUE) CloseHandle(file);
|
||||||
|
#endif
|
||||||
|
if (face != NULL)
|
||||||
FT_Done_Face(face);
|
FT_Done_Face(face);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -252,6 +315,10 @@ const char* ren_font_get_path(RenFont *font) {
|
||||||
void ren_font_free(RenFont* font) {
|
void ren_font_free(RenFont* font) {
|
||||||
font_clear_glyph_cache(font);
|
font_clear_glyph_cache(font);
|
||||||
FT_Done_Face(font->face);
|
FT_Done_Face(font->face);
|
||||||
|
#ifdef _WIN32
|
||||||
|
free(font->file);
|
||||||
|
CloseHandle(font->file_handle);
|
||||||
|
#endif
|
||||||
free(font);
|
free(font);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,9 +360,9 @@ int ren_font_group_get_height(RenFont **fonts) {
|
||||||
return fonts[0]->height;
|
return fonts[0]->height;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ren_font_group_get_width(RenFont **fonts, const char *text) {
|
float ren_font_group_get_width(RenFont **fonts, const char *text, size_t len) {
|
||||||
float width = 0;
|
float width = 0;
|
||||||
const char* end = text + strlen(text);
|
const char* end = text + len;
|
||||||
GlyphMetric* metric = NULL; GlyphSet* set = NULL;
|
GlyphMetric* metric = NULL; GlyphSet* set = NULL;
|
||||||
while (text < end) {
|
while (text < end) {
|
||||||
unsigned int codepoint;
|
unsigned int codepoint;
|
||||||
|
@ -309,7 +376,7 @@ float ren_font_group_get_width(RenFont **fonts, const char *text) {
|
||||||
return width / surface_scale;
|
return width / surface_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor color) {
|
float ren_draw_text(RenFont **fonts, const char *text, size_t len, float x, int y, RenColor color) {
|
||||||
SDL_Surface *surface = renwin_get_surface(&window_renderer);
|
SDL_Surface *surface = renwin_get_surface(&window_renderer);
|
||||||
const RenRect clip = window_renderer.clip;
|
const RenRect clip = window_renderer.clip;
|
||||||
|
|
||||||
|
@ -317,11 +384,11 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor
|
||||||
float pen_x = x * surface_scale;
|
float pen_x = x * surface_scale;
|
||||||
y *= surface_scale;
|
y *= surface_scale;
|
||||||
int bytes_per_pixel = surface->format->BytesPerPixel;
|
int bytes_per_pixel = surface->format->BytesPerPixel;
|
||||||
const char* end = text + strlen(text);
|
const char* end = text + len;
|
||||||
uint8_t* destination_pixels = surface->pixels;
|
uint8_t* destination_pixels = surface->pixels;
|
||||||
int clip_end_x = clip.x + clip.width, clip_end_y = clip.y + clip.height;
|
int clip_end_x = clip.x + clip.width, clip_end_y = clip.y + clip.height;
|
||||||
|
|
||||||
RenFont* last;
|
RenFont* last = NULL;
|
||||||
float last_pen_x = x;
|
float last_pen_x = x;
|
||||||
bool underline = fonts[0]->style & FONT_STYLE_UNDERLINE;
|
bool underline = fonts[0]->style & FONT_STYLE_UNDERLINE;
|
||||||
bool strikethrough = fonts[0]->style & FONT_STYLE_STRIKETHROUGH;
|
bool strikethrough = fonts[0]->style & FONT_STYLE_STRIKETHROUGH;
|
||||||
|
@ -458,8 +525,13 @@ void ren_draw_rect(RenRect rect, RenColor color) {
|
||||||
|
|
||||||
/*************** Window Management ****************/
|
/*************** Window Management ****************/
|
||||||
void ren_free_window_resources() {
|
void ren_free_window_resources() {
|
||||||
|
extern uint8_t *command_buf;
|
||||||
|
extern size_t command_buf_size;
|
||||||
renwin_free(&window_renderer);
|
renwin_free(&window_renderer);
|
||||||
SDL_FreeSurface(draw_rect_surface);
|
SDL_FreeSurface(draw_rect_surface);
|
||||||
|
free(command_buf);
|
||||||
|
command_buf = NULL;
|
||||||
|
command_buf_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ren_init(SDL_Window *win) {
|
void ren_init(SDL_Window *win) {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#define UNUSED
|
#define UNUSED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define FONT_FALLBACK_MAX 4
|
#define FONT_FALLBACK_MAX 10
|
||||||
typedef struct RenFont RenFont;
|
typedef struct RenFont RenFont;
|
||||||
typedef enum { FONT_HINTING_NONE, FONT_HINTING_SLIGHT, FONT_HINTING_FULL } ERenFontHinting;
|
typedef enum { FONT_HINTING_NONE, FONT_HINTING_SLIGHT, FONT_HINTING_FULL } ERenFontHinting;
|
||||||
typedef enum { FONT_ANTIALIASING_NONE, FONT_ANTIALIASING_GRAYSCALE, FONT_ANTIALIASING_SUBPIXEL } ERenFontAntialiasing;
|
typedef enum { FONT_ANTIALIASING_NONE, FONT_ANTIALIASING_GRAYSCALE, FONT_ANTIALIASING_SUBPIXEL } ERenFontAntialiasing;
|
||||||
|
@ -28,8 +28,8 @@ int ren_font_group_get_height(RenFont **font);
|
||||||
float ren_font_group_get_size(RenFont **font);
|
float ren_font_group_get_size(RenFont **font);
|
||||||
void ren_font_group_set_size(RenFont **font, float size);
|
void ren_font_group_set_size(RenFont **font, float size);
|
||||||
void ren_font_group_set_tab_size(RenFont **font, int n);
|
void ren_font_group_set_tab_size(RenFont **font, int n);
|
||||||
float ren_font_group_get_width(RenFont **font, const char *text);
|
float ren_font_group_get_width(RenFont **font, const char *text, size_t len);
|
||||||
float ren_draw_text(RenFont **font, const char *text, float x, int y, RenColor color);
|
float ren_draw_text(RenFont **font, const char *text, size_t len, float x, int y, RenColor color);
|
||||||
|
|
||||||
void ren_draw_rect(RenRect rect, RenColor color);
|
void ren_draw_rect(RenRect rect, RenColor color);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
#ifndef MBSEC_H
|
#ifndef MBSEC_H
|
||||||
#define MBSEC_H
|
#define MBSEC_H
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define UNUSED __attribute__((__unused__))
|
||||||
|
#else
|
||||||
|
#define UNUSED
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -8,7 +14,7 @@
|
||||||
|
|
||||||
#define UTFCONV_ERROR_INVALID_CONVERSION "Input contains invalid byte sequences."
|
#define UTFCONV_ERROR_INVALID_CONVERSION "Input contains invalid byte sequences."
|
||||||
|
|
||||||
LPWSTR utfconv_utf8towc(const char *str) {
|
static UNUSED LPWSTR utfconv_utf8towc(const char *str) {
|
||||||
LPWSTR output;
|
LPWSTR output;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
|
@ -30,7 +36,7 @@ LPWSTR utfconv_utf8towc(const char *str) {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *utfconv_wctoutf8(LPCWSTR str) {
|
static UNUSED char *utfconv_wctoutf8(LPCWSTR str) {
|
||||||
char *output;
|
char *output;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
[wrap-file]
|
[wrap-file]
|
||||||
directory = freetype-2.11.1
|
directory = freetype-2.12.1
|
||||||
source_url = https://download.savannah.gnu.org/releases/freetype/freetype-2.11.1.tar.gz
|
source_url = https://download.savannah.gnu.org/releases/freetype/freetype-2.12.1.tar.xz
|
||||||
source_filename = freetype-2.11.1.tar.gz
|
source_filename = freetype-2.12.1.tar.xz
|
||||||
source_hash = f8db94d307e9c54961b39a1cc799a67d46681480696ed72ecf78d4473770f09b
|
source_hash = 4766f20157cc4cf0cd292f80bf917f92d1c439b243ac3018debf6b9140c41a7f
|
||||||
|
wrapdb_version = 2.12.1-2
|
||||||
|
|
||||||
[provide]
|
[provide]
|
||||||
freetype2 = freetype_dep
|
freetype2 = freetype_dep
|
||||||
|
freetype = freetype_dep
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
[wrap-file]
|
[wrap-file]
|
||||||
directory = lua-5.4.3
|
directory = lua-5.4.4
|
||||||
source_url = https://www.lua.org/ftp/lua-5.4.3.tar.gz
|
source_url = https://www.lua.org/ftp/lua-5.4.4.tar.gz
|
||||||
source_filename = lua-5.4.3.tar.gz
|
source_filename = lua-5.4.4.tar.gz
|
||||||
source_hash = f8612276169e3bfcbcfb8f226195bfc6e466fe13042f1076cbde92b7ec96bbfb
|
source_hash = 164c7849653b80ae67bec4b7473b884bf5cc8d2dca05653475ec2ed27b9ebf61
|
||||||
patch_filename = lua_5.4.3-2_patch.zip
|
patch_filename = lua_5.4.4-1_patch.zip
|
||||||
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.3-2/get_patch
|
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.4-1/get_patch
|
||||||
patch_hash = 3c23ec14a3f000d80fe2e2fdddba63a56e13c758d74195daa4ff0da7bfdb02da
|
patch_hash = e61cd965c629d6543176f41a9f1cb9050edfd1566cf00ce768ff211086e40bdc
|
||||||
|
|
||||||
[provide]
|
[provide]
|
||||||
lua-5.4 = lua_dep
|
lua-5.4 = lua_dep
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
[wrap-file]
|
[wrap-file]
|
||||||
directory = pcre2-10.39
|
directory = pcre2-10.42
|
||||||
source_url = https://github.com/PhilipHazel/pcre2/releases/download/pcre2-10.39/pcre2-10.39.tar.bz2
|
source_url = https://github.com/PhilipHazel/pcre2/releases/download/pcre2-10.42/pcre2-10.42.tar.bz2
|
||||||
source_filename = pcre2-10.39.tar.bz2
|
source_filename = pcre2-10.42.tar.bz2
|
||||||
source_hash = 0f03caf57f81d9ff362ac28cd389c055ec2bf0678d277349a1a4bee00ad6d440
|
source_hash = 8d36cd8cb6ea2a4c2bb358ff6411b0c788633a2a45dabbf1aeb4b701d1b5e840
|
||||||
patch_filename = pcre2_10.39-2_patch.zip
|
patch_filename = pcre2_10.42-1_patch.zip
|
||||||
patch_url = https://wrapdb.mesonbuild.com/v2/pcre2_10.39-2/get_patch
|
patch_url = https://wrapdb.mesonbuild.com/v2/pcre2_10.42-1/get_patch
|
||||||
patch_hash = c4cfffff83e7bb239c8c330339b08f4367b019f79bf810f10c415e35fb09cf14
|
patch_hash = 06969e916dfee663c189810df57d98574f15e0754a44cd93f3f0bc7234b05d89
|
||||||
|
wrapdb_version = 10.42-1
|
||||||
|
|
||||||
[provide]
|
[provide]
|
||||||
libpcre2-8 = -libpcre2_8
|
libpcre2-8 = libpcre2_8
|
||||||
libpcre2-16 = -libpcre2_16
|
libpcre2-16 = libpcre2_16
|
||||||
libpcre2-32 = -libpcre2_32
|
libpcre2-32 = libpcre2_32
|
||||||
libpcre2-posix = -libpcre2_posix
|
libpcre2-posix = libpcre2_posix
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
[wrap-file]
|
[wrap-file]
|
||||||
directory = SDL2-2.24.0
|
directory = SDL2-2.26.0
|
||||||
source_url = https://libsdl.org/release/SDL2-2.24.0.tar.gz
|
source_url = https://github.com/libsdl-org/SDL/releases/download/release-2.26.0/SDL2-2.26.0.tar.gz
|
||||||
source_filename = SDL2-2.24.0.tar.gz
|
source_filename = SDL2-2.26.0.tar.gz
|
||||||
source_hash = 91e4c34b1768f92d399b078e171448c6af18cafda743987ed2064a28954d6d97
|
source_hash = 8000d7169febce93c84b6bdf376631f8179132fd69f7015d4dadb8b9c2bdb295
|
||||||
patch_filename = sdl2_2.24.0-2_patch.zip
|
patch_filename = sdl2_2.26.0-1_patch.zip
|
||||||
patch_url = https://wrapdb.mesonbuild.com/v2/sdl2_2.24.0-2/get_patch
|
patch_url = https://wrapdb.mesonbuild.com/v2/sdl2_2.26.0-1/get_patch
|
||||||
patch_hash = ec296ed9a577b42131d2fdbfe5ca73a0cf133793c0290e1ccd825675464bfe32
|
patch_hash = 6fcfd727d71cf7837332723518d5e47ffd64f1e7630681cf4b50e99f2bf7676f
|
||||||
wrapdb_version = 2.24.0-2
|
wrapdb_version = 2.26.0-1
|
||||||
|
|
||||||
[provide]
|
[provide]
|
||||||
sdl2 = sdl2_dep
|
sdl2 = sdl2_dep
|
||||||
|
|
Loading…
Reference in New Issue