Compare commits

...

463 Commits

Author SHA1 Message Date
George Sokianos 2c0f92766f Added changes in README_Amiga 2024-04-24 22:05:01 +01:00
George Sokianos 3f6207b4af Merged the changes from 2.1.4 upstream release tag 2024-04-24 21:24:48 +01:00
George Sokianos 79f42686a7 Added ability to open files with drag 'n drop 2024-03-18 23:06:01 +00:00
George Sokianos 9944a91f55 Changed text about the codesets plugin 2024-03-09 13:51:18 +00:00
George Sokianos 19e95ed791 Fixed ghmarkdown plugin, added tetris plugin, prepare a new release" 2024-03-09 13:11:31 +00:00
George Sokianos 759bccce52 Some changes in makefiles 2024-02-23 22:04:01 +00:00
George Sokianos a7971556d4 Morphos codesets fixes and more 2024-02-23 21:11:13 +00:00
George Sokianos 6807a8e29a s when opening projects or changing paths. 2024-02-19 22:51:41 +00:00
George Sokianos 1be1c7fb0b More fixes with the relative paths 2024-02-19 00:56:45 +00:00
George Sokianos 7efe75fe7d More path fixes 2024-02-18 21:31:24 +00:00
George Sokianos 9c2eec9066 Added AmiUpdate support. Made a lot of changes in paths manipulation 2024-02-17 17:47:15 +00:00
George Sokianos df8eaa64d1 Paths fixes, Open in System changes and a few others done 2024-02-11 22:11:30 +00:00
George Sokianos ad4c221dd8 Manual sync with 2.1.3 tag 2024-02-11 17:51:12 +00:00
George Sokianos 66fb996e76 Merge branch 'v2.1.3-upstream' into amiga2.1 2024-02-11 16:02:19 +00:00
takase1121 c6a7ff98b0
appstream: update release date 2024-01-29 07:47:49 +08:00
takase1121 caf30574ed
changelog: fix formatting issue and release date 2024-01-29 07:43:03 +08:00
takase1121 328ed55f83
update version in metadata 2024-01-21 15:13:26 +08:00
takase1121 b4d750013d
changelog: update changelog for v2.1.3 2024-01-21 14:59:52 +08:00
Takase b856dc371a
process: style changes (#1709) 2024-01-21 14:49:35 +08:00
Takase bbc524ad82
language_js: support binary and octal representation (#1710) 2024-01-21 14:49:28 +08:00
vqn a389adbaf5
autoreload docs only if their filename matches an actual file (#1698) 2024-01-21 14:48:56 +08:00
vqn df12a5dde6
reorder nagview options on doc:save error to be more consistent with other nagview confirmations (#1696) 2024-01-21 14:48:44 +08:00
Fiji e353c5322a
Improve number highlighting for python syntax highlighting (#1704)
* Improve number highlighting for python syntax highlighting

Adds support for octal and binary representation, as well uppercase characters (X, B and O)

* add underscore and negative hex/oct/bin value support

* Removed | from pattern
2024-01-21 14:48:21 +08:00
Chloé Vulquin 4ae92ae128
Memory fixes (#1705)
* fix: free-before-init in renwin_init_surface when using sdl renderer

`ren->rensurface.surface` presupposes zero-initialized rensurface.
Rensurface was not actually zero-initialized.
It is now.

* fix: heap buffer overflow in process_env_free

`process_env_free` presupposed that it was null-terminated.
Pass length to free instead.

* use calloc instead of memset for zero-init

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2024-01-21 14:42:46 +08:00
vqn 2ce8c58bea
Fix doc:create-cursor-previous/next-line with tabs (#1697)
* use DocView.translate to split cursor on previous/next line

* use dv.doc instead of doc()
2024-01-21 14:40:41 +08:00
George Sokianos d0b86296c3 Updated the readme for the 2.1.2r1 release 2023-12-29 11:25:30 +00:00
George Sokianos 71558986d3 innosetup changes 2023-12-29 10:34:37 +00:00
Velosofy d67a951d31 Add "Open with Lite XL" to windows' context menu (#1333)
Closes #423
2023-12-29 10:34:37 +00:00
takase1121 b68efcd9e4
chore(changelog): update changelog for v2.1.2 2023-12-29 09:33:12 +08:00
takase1121 97a49661e0
chore: update versions 2023-12-29 08:47:22 +08:00
vqn 9c21903af7
add autocompletion to multicursor (#1394)
* use Doc:remove
2023-12-29 08:47:21 +08:00
Adam Harrison 1d4e01a192
Fixed a minor bug, should close issue #1680. 2023-12-29 08:47:21 +08:00
ThaCuber f1bdd840a1
fix nagbar failed save message (#1678)
* fix nagbar failed save message

- visually separated statements with a `.`
- first statement slightly rewritten
- use `'` rather than `"`

* yeahhhh no back to `"`
2023-12-29 08:47:05 +08:00
George Sokianos ffc5e25a72 Updated the version and added some info in the readme 2023-12-26 13:58:21 +00:00
George Sokianos 5c983f10b5 Merge branch 'master' into amiga2.1 2023-12-26 13:36:13 +00:00
Jan 7b67a5d81b turn window_renderer into managed pointer (#1683)
* turn window_renderer into managed pointer
this will make it easier to move it into userdata in the future

* remove unused function, remove comment
2023-12-26 13:16:33 +00:00
Adam Harrison cca61ab8ec Fixed a minor bug, should close issue #1680. 2023-12-26 13:16:33 +00:00
ThaCuber c45463459c fix nagbar failed save message (#1678)
* fix nagbar failed save message

- visually separated statements with a `.`
- first statement slightly rewritten
- use `'` rather than `"`

* yeahhhh no back to `"`
2023-12-26 13:16:33 +00:00
Guldoman 39993a6d93 Expose plaintext syntax (#1652) 2023-12-26 13:16:33 +00:00
Guldoman a29327e375 Use `\r\n` for new files on Windows (#1596)
* Use `\r\n` for new files on Windows

* Add `config.line_endings`
2023-12-26 13:16:33 +00:00
Takase 7111b8a6c9 feat(process): allow commands and envs on proces_start (#1477)
* feat(process): allow commands and envs on proces_start

* refactor(process): copy process arguments once whenever possible

Refactors the code to use an arglist type which is just lpCmdline on Windows
and a list in Linux.
The function automatically escapes the command when it is needed, avoiding
a second copy.

This also allows UTF-8 commands btw.

* fix(process): fix invalid dereference

* refactor(process): mark xstrdup as potentially unused

* feat(process): add parent process environment when launching process

* fix(process): fix operator precedence with array operators

* fix(process): fix segfault when freeing random memory

* fix(process): fix wrong check for setenv()

* fix(process): fix accidentally initializing an array by assignment

* fix(process): clear return value if success
2023-12-26 13:16:33 +00:00
takase1121 a9ac33429e chore(deps): update Lua 2023-12-26 13:16:33 +00:00
takase1121 ad1fad2632 chore(deps): update SDL2 2023-12-26 13:16:33 +00:00
takase1121 79bae532b9 chore(deps): update pcre2 2023-12-26 13:16:33 +00:00
takase1121 41813604e1 chore(deps): update freetype 2023-12-26 13:16:33 +00:00
takase1121 7b064bae6b fix(ci,build.sh): un-hardcode lua subproject detection 2023-12-26 13:16:33 +00:00
Takase 2d36359e6e Revert "feat(subprojects): update wraps (#1577)"
This reverts commit a97de87d869c227c2d41595d76ecafdc29e76bef.
2023-12-26 13:16:33 +00:00
Guldoman dac8d1ac8e Improve font/color change detection in `language_md` (#1614)
* Delay setting font for custom `language_md` token types

* Improve font/color change detection in `language_md`
2023-12-26 13:16:33 +00:00
Guldoman 5719f4de6f Use x offset to define render command rect in `rencache_draw_text` (#1618)
* Return x offset for the first character in `ren_font_group_get_width`

* Use x offset to define render command rect in `rencache_draw_text`
2023-12-26 13:16:33 +00:00
Adam e14af4604a Reverted cursor API to something more compatible with old API. (#1674)
* Reverted cursor API to something more compatible with old API.

* Implemented discord discussion.

* Reduced thiccness of overwrite cursor.
2023-12-26 13:16:33 +00:00
ThaCuber f43cfc4a94 Text overwriting (#1495)
* added text overwriting

* rewrote `DocView:draw_caret` to not use the order of draws

* forgot to delete some old code in `DocView:draw_overlay`
also added a temporary solution to overwriting
and added the missing arguments in `DocView:draw_ime_decoration`
and fixed `DocView:draw_caret`

* accidentally broke the `draw_caret` call in `draw_overlay` in the process

* multiline

* fixed calling `Doc:get_char` as a function
that, in turn, crashed the editor because "can't index a number"

* move and rename some stuff

* remove unneeded extra check

I just had to change the `~=` to `<` in the second condition

* overwrite disregards pasting text

* disregard overwrite on selections; doc only removes selection

* Fixed error where `doc` was used, instead of `self`.

---------

Co-authored-by: ThaCuber <70547062+ThaCuber@users.noreply.github.com>
Co-authored-by: Adam Harrison <adamdharrison@gmail.com>
2023-12-26 13:16:33 +00:00
Guldoman 234dd40e49 Fix patterns starting with `^` in `tokenizer` (#1645)
Previously the "dirty" version of the pattern was used, which could 
result in trying to match with multiple `^`, which failed valid matches.
2023-12-26 13:16:33 +00:00
Guldoman ee02d0e0b6 Fix `language_js` regex constant detection (#1581)
* Fix `language_js` regex constant detection

* Simplify regex constant detection in `language_js`

* Add more possessive quantifiers in `language_js` regex constant detection

This avoids more catastrophic backtracking cases.

* Allow `.` after regex constant in `language_js`
2023-12-26 13:16:33 +00:00
Guldoman de043f2e13 Fix editing after undo not clearing the change id (#1574) 2023-12-26 13:16:33 +00:00
Guldoman 9301220d26 Fix selecting newlines with `find-replace:select-add-{next,all}` (#1608)
* Avoid adding existing selections in `select_add_next`

* Use the first available selection as delimiter in `select_add_next`

* Fix returning searches with newlines in `search.find`

* Fix repeat search when the last result spanned multiple lines
2023-12-26 13:16:33 +00:00
Guldoman 2571e17d1b Fix `core.redraw` when window is not focused (#1601)
* Execute at least one step when window has no focus

This way if `core.redraw` is set, it's respected.

* Fully run threads at least once when window has no focus

This allows threads that set `core.redraw` (like `projectsearch`) to 
continue running even after the window loses focus.

"Fully" here means that `run_threads` has gone through *all* the "timed 
out" coroutines at least once.
2023-12-26 13:16:33 +00:00
Guldoman 4e2f70e5ee Scale mouse coordinates by window scale (#1630)
* Update window scale on resize

* Scale mouse coordinates by window scale

* Avoid scaling mouse coordinates while using `LITE_USE_SDL_RENDERER`
2023-12-26 13:16:33 +00:00
Takase 52d224ac6b feat(subprojects): update wraps (#1577)
* feat(subprojects): update SDL2 wrap

* fix(meson.build): add sdl2main as dependency on Windows

* fix(meson.build): don't load sdl2main on non-Windows platforms

* feat(subprojects): update freetype version

* feat(subprojects): update pcre2 to latest version

* feat(subprojects): update lua to latest version

* feat(lite_xl_plugin_api): add lua_closethread to symbols list

* fix(meson.build): fix meson error with features and booleans

* fix(meson.build): fix wrong variable name

* feat(subprojects): update wraps again

* ci(build): fix lua subproject not found

* ci(build): use awk instead of grep and sed
2023-12-26 13:16:33 +00:00
Guldoman 3ee903b16c Fix `dirmonitor` sorting issues (#1599)
* Use `PATHSEP` in path-related functions

* Don't stop on digits when getting the common part in `system.path_compare`

* Avoid sorting multiple times in `dirwatch.get_directory_files`

This also fixes the timeout detection in `recurse_pred`.
2023-12-26 13:16:33 +00:00
Guldoman 1dceaf65f5 Fix running `core.step` when receiving an event while not waiting
When `time_to_wake` was <= 0, so when a coroutine needed to be executed 
as soon as possible, we didn't check for events, so we only performed a 
`core.step` with the blink timer.
This resulted in jerky reactions to input.
2023-12-26 13:16:33 +00:00
Guldoman c4f9542509 Limit `system.{sleep,wait_event}` to timeouts >= 0 (#1666)
Otherwise we might wait forever by mistake.
2023-12-26 13:16:33 +00:00
Daniel Margarido 86b89c402d Fixed issue with set_target_size passing the wrong value to plugins (#1657)
* Fixed issue with set_target_size passing the wrong value to plugins that are split on the right and activated from the settings UI.

* Added position awareness for the all resize_child_node calls.
2023-12-26 13:16:33 +00:00
Guldoman 3972f10059 Fix deleting indentation with multiple cursors (#1670) 2023-12-26 13:16:33 +00:00
Guldoman da64a99f18 Avoid considering single spaces in `detectindent` (#1595) 2023-12-26 13:16:33 +00:00
Takase dc62c59705 refactor(build): use dmgbuild to create dmgs (#1664)
* refactor(appdmg): make dmgs with dmgbuild

* fix(appdmg.sh): typo

* refactor(appdmg.sh): don't generate config on the fly

* fix(dmgbuild): icon file

* fix(gitignore): dmgbuild settings

* chore(resources): update readme with new files

* chore(resources/macos): add missing newline
2023-12-26 13:16:33 +00:00
Takase de05ec374e feat(package): ad-hoc sign macOS bundles (#1656)
* feat(package): ad-hoc sign macOS bundles

* fix(package.sh): syntax error

* docs(readme): add instructions for self-signed builds

* docs(readme): grammar

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-12-26 13:16:33 +00:00
ThaCuber df7cf7e270 ease transparency of nagbar dim (#1658)
* ease transparency of nagbar dim

* tiny changes

* lerp alpha rather than the whole color
2023-12-26 13:16:33 +00:00
Guldoman dc3716f177 Make license time-independent (#1655) 2023-12-26 13:16:33 +00:00
Guldoman 05fbc48e03 Sanitize tab index in `Node:add_view` (#1651)
* Fix `Node:add_view` not adjusting tab index after removing `EmptyView`

* Clamp tab index in `Node:add_view`
2023-12-26 13:16:33 +00:00
Takase 6370968494 fix(dirmonitor): deadlock if error handler jumps somewhere else (#1647)
* fix: deadlock if error handler jumps somewhere else

* docs(dirmonitor): fix wrong data type in error callback

* docs(dirmonitor): clarify coroutines and deadlocks

* docs(dirmonitor): wording

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-12-26 13:16:33 +00:00
Guldoman 05e7fc4e43 Set SDL hint to prefer software render driver (#1646) 2023-12-26 13:16:33 +00:00
Takase e520227d35 ci: fix diff files having "wrong" path separator (#1648)
* ci: fix diff files having "wrong" path separator

* ci(build): use git bash to apply patches

* ci(build): fix step wording

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-12-26 13:16:33 +00:00
Guldoman 9be4583f63 Save in the `workspace` unsaved named files and `crlf` status (#1597)
* Save in the `workspace` unsaved named files

* Save in the `workspace` the `crlf` status and restore it for "new" files
2023-12-26 13:16:33 +00:00
Guldoman 7e20424b29 Ignore keypresses during IME composition (#1573)
Some IMEs continue sending keypresses even during composition, so we 
just ignore them.
2023-12-26 13:16:33 +00:00
Guldoman 9612f20685 Improve `common.serialize` (#1640)
* Make `common.serialize` more locale-independent

* Handle inf/nan numbers in `common.serialize`
2023-12-26 13:16:33 +00:00
Guldoman 1669409610 Mark unsaved named files as dirty (#1598) 2023-12-26 13:16:33 +00:00
Takase 1196bf355c fix: dim rendering when antialiasing is turned off (#1641) 2023-12-26 13:16:33 +00:00
Takase 9017fadba6 docs: fix prebuilt install instructions (#1637)
* docs: fix prebuilt install instructions

Added missing documentation for Windows and macOS.
Also updated the Linux instruction for creating desktop entries.

* docs: more clarification and grammar fixes

* docs: clarify plugin and config load in portable mode

* docs: better phrasing

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

* docs: better phrasing

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-12-26 13:16:33 +00:00
Guldoman 17cb2e86ed Remove DPI detection for default `SCALE`
This often leads to `SCALE` values that are way off, and makes Lite XL 
unusable, so we now just default it to 1.
2023-12-26 13:16:33 +00:00
Takase 4f28f718a9 docs: update invite link on README 2023-12-26 13:16:33 +00:00
Guldoman febfcb5757 Make `linewrapping` consider the expanded `Scrollbar` size
This avoids reflowing the text when hovering the scrollbar.
2023-12-26 13:16:33 +00:00
Guldoman e5c17ed3ec Fix `Scrollbar.{expanded,contracted}_size` documentation 2023-12-26 13:16:33 +00:00
Robert Hildebrandt 351ef1ecea Fixed C++14 digit separators (#1593) 2023-12-26 13:16:33 +00:00
Takase 15e05aaf03 docs(core.config): add documentation for config options (#1512)
* docs(core.config): add documentation for config options

* docs(core.config): remove wrong newline

* docs(core.config): remove trailing whitespace

* docs(core.config): add missing whitespace

* docs(core.config): add disclaimer for core.file_size_limit

* docs(core.config): fix wrong description of the pattern

* docs(core.config): fix wrong description

* docs(core.config): fix wrong description for transitions

* docs(core.config): guide user to drawwhitespace plugin

* docs(core.config): clarify libdecor usage

* docs(core.config): clarify various things

* docs(core.config): clarify more about libdecor support

* docs(core.config): fix missing enum separator

* docs(core.config): remove wayland-specific advice on config.borderless
2023-12-26 13:16:33 +00:00
sammyette 1d5f7ae9b0 feat(statusview): make a separate item for doc position percent (#1579)
* feat(statusview): make a separate item for doc position percent

* chore: remove unused variable

* fix(statusview): remove command for percent doc item

* fix(statusview): change doc percent tooltip

* fix(statusview): change percent tooltip message
2023-12-26 13:16:33 +00:00
Jefferson González 27f24701c4 Autocomplete plugin improvements (#1519)
* Add icons support to autocomplete plugin

* Removed redundant flag check

* Added support for non syntax colors

* Assert if color name not in style.syntax

* Autocomplete plugin improvements

* Support suggestion symbols scoping
  - global: all open documents
  - local: current document
  - related: all open documents with same syntax
  - none: language syntax symbols only
* Register style.syntax[] entries as icons
* Other related fixes
2023-12-26 13:16:33 +00:00
Guldoman 25a0943087 Add `NaN` guard to `View:update_scrollbar` 2023-12-26 13:16:33 +00:00
Adam b0e1469a87 Adds super as a modkey. (#1590)
* Adds super as a modkey.

* Added in super designation for windows.
2023-12-26 13:16:33 +00:00
Guldoman 2ed17dd03f Normalize strokes in fixed order (#1572)
* Use normalized strokes when removing duplicates only when appropriate

* Use normalized stroke in `keymap.unbind`

* Normalize strokes by sorting the modifiers before the keys

This also sorts the modifiers in a fixed manner, decided by 
`modkeys.keys`.
We need to do this because we display the strokes in a few places like 
the command palette.
2023-12-26 13:16:33 +00:00
Jan 3993d689fb Use Lua wrap by default (#1481)
Debian and all its derivatives ship a broken Lua 5.4 that is missing some symbols.
To work around broken distros and make development and distribution easier use the wrap by default and add an option to use the system version.
2023-12-26 13:16:33 +00:00
Takase 3f3b4d52b4 docs(core.contextmenu): add documentation for contextmenu (#1567) 2023-12-26 13:16:33 +00:00
Guldoman 9bc44e2b45 Fix returned `percent` when clicking the `Scrollbar` `track` 2023-12-26 13:16:33 +00:00
Guldoman 50102fdc3a Fix `scrollbar` misinterpreting `percent` (#1587) 2023-12-26 13:16:33 +00:00
Takase dc14860166 fix(core): defer core:open-log until everything is loaded (#1585)
* fix(core): defer core:open-log until everything is loaded

* docs(core): document why core:open-log is opened in a thread
2023-12-26 13:16:33 +00:00
Adam eb306a2ff0 Windows Installer Path Modification (#1536)
* innosetup: installation path to environment task

Also set the uninstall icon shown on add/remove programs.

* Improved path description.

---------

Co-authored-by: jgmdev <jgmdev@gmail.com>
2023-12-26 13:16:33 +00:00
Velosofy f820b9301f Add "Open with Lite XL" to windows' context menu (#1333)
Closes #423
2023-12-26 13:15:53 +00:00
George Sokianos e4a2adf79b Added some information in the amiga readme 2023-12-26 11:40:44 +00:00
George Sokianos adc2919dfa Moved the release_files folder under resources/amiga 2023-12-26 10:58:41 +00:00
George Sokianos 0f7e075d6f Added the release_files folder 2023-12-18 17:53:40 +00:00
George Sokianos 1b00045146 Added codesets support for encoding switch 2023-12-18 17:19:13 +00:00
Guldoman 34cebe8fe4
Make license time-independent (#1655) 2023-12-01 15:41:25 +08:00
Takase 311651333a
feat(process): allow commands and envs on proces_start (#1477)
* feat(process): allow commands and envs on proces_start

* refactor(process): copy process arguments once whenever possible

Refactors the code to use an arglist type which is just lpCmdline on Windows
and a list in Linux.
The function automatically escapes the command when it is needed, avoiding
a second copy.

This also allows UTF-8 commands btw.

* fix(process): fix invalid dereference

* refactor(process): mark xstrdup as potentially unused

* feat(process): add parent process environment when launching process

* fix(process): fix operator precedence with array operators

* fix(process): fix segfault when freeing random memory

* fix(process): fix wrong check for setenv()

* fix(process): fix accidentally initializing an array by assignment

* fix(process): clear return value if success
2023-12-01 10:12:16 +08:00
Guldoman 8e2928aeb8
Improve font/color change detection in `language_md` (#1614)
* Delay setting font for custom `language_md` token types

* Improve font/color change detection in `language_md`
2023-11-30 11:45:58 +08:00
Guldoman bc935906d1
Use x offset to define render command rect in `rencache_draw_text` (#1618)
* Return x offset for the first character in `ren_font_group_get_width`

* Use x offset to define render command rect in `rencache_draw_text`
2023-11-30 11:45:25 +08:00
Guldoman 9be5a46a22
Fix patterns starting with `^` in `tokenizer` (#1645)
Previously the "dirty" version of the pattern was used, which could 
result in trying to match with multiple `^`, which failed valid matches.
2023-11-30 11:39:55 +08:00
Guldoman 885e6b3c50
Fix `language_js` regex constant detection (#1581)
* Fix `language_js` regex constant detection

* Simplify regex constant detection in `language_js`

* Add more possessive quantifiers in `language_js` regex constant detection

This avoids more catastrophic backtracking cases.

* Allow `.` after regex constant in `language_js`
2023-11-30 11:39:48 +08:00
Guldoman b1a647814f
Fix editing after undo not clearing the change id (#1574) 2023-11-30 11:39:41 +08:00
Guldoman 8eacca7ae1
Fix selecting newlines with `find-replace:select-add-{next,all}` (#1608)
* Avoid adding existing selections in `select_add_next`

* Use the first available selection as delimiter in `select_add_next`

* Fix returning searches with newlines in `search.find`

* Fix repeat search when the last result spanned multiple lines
2023-11-30 11:39:27 +08:00
Guldoman 19cef97bcd
Fix `core.redraw` when window is not focused (#1601)
* Execute at least one step when window has no focus

This way if `core.redraw` is set, it's respected.

* Fully run threads at least once when window has no focus

This allows threads that set `core.redraw` (like `projectsearch`) to
continue running even after the window loses focus.

"Fully" here means that `run_threads` has gone through *all* the "timed
out" coroutines at least once.
2023-11-30 11:39:16 +08:00
Guldoman c31a8ae0f6
Scale mouse coordinates by window scale (#1630)
* Update window scale on resize

* Scale mouse coordinates by window scale

* Avoid scaling mouse coordinates while using `LITE_USE_SDL_RENDERER`
2023-11-30 11:27:08 +08:00
takase1121 9272f5ef2d
chore(deps): update SDL2 2023-11-30 11:26:00 +08:00
takase1121 88dcb25396
chore(deps): update pcre2 2023-11-30 11:25:54 +08:00
takase1121 513432a784
chore(deps): update freetype 2023-11-30 11:25:46 +08:00
takase1121 43643a16c0
fix(ci,build.sh): un-hardcode lua subproject detection 2023-11-30 11:24:42 +08:00
Guldoman 5c5c77219b
Fix `dirmonitor` sorting issues (#1599)
* Use `PATHSEP` in path-related functions

* Don't stop on digits when getting the common part in `system.path_compare`

* Avoid sorting multiple times in `dirwatch.get_directory_files`

This also fixes the timeout detection in `recurse_pred`.
2023-11-30 09:20:34 +08:00
Guldoman 6cd1f96234
Fix running `core.step` when receiving an event while not waiting
When `time_to_wake` was <= 0, so when a coroutine needed to be executed 
as soon as possible, we didn't check for events, so we only performed a 
`core.step` with the blink timer.
This resulted in jerky reactions to input.
2023-11-29 09:41:55 +08:00
Adam 3bf3266ca5
Made coroutines make more sense, and fixed a bug. (#1381)
* Made coroutines make more sense, and fixed a bug.

* Fixed typo.

* Additional checking for off-cycles.

* Fixed issue with calling step too much.

* If we have no redraw, set next step time for next frame.

* Added in `now` variables to reduce calls.
2023-11-29 09:41:34 +08:00
Guldoman 4adfd44d9f
Limit `system.{sleep,wait_event}` to timeouts >= 0 (#1666)
Otherwise we might wait forever by mistake.
2023-11-29 09:13:12 +08:00
Daniel Margarido 35ef0a9484
Fixed issue with set_target_size passing the wrong value to plugins (#1657)
* Fixed issue with set_target_size passing the wrong value to plugins that are split on the right and activated from the settings UI.

* Added position awareness for the all resize_child_node calls.
2023-11-29 07:45:27 +08:00
Guldoman 01cab0611c
Fix deleting indentation with multiple cursors (#1670) 2023-11-29 07:41:27 +08:00
Guldoman fb7ed49a44
Avoid considering single spaces in `detectindent` (#1595) 2023-11-29 07:41:13 +08:00
Takase 34d163aa25
refactor(build): use dmgbuild to create dmgs (#1664)
* refactor(appdmg): make dmgs with dmgbuild

* fix(appdmg.sh): typo

* refactor(appdmg.sh): don't generate config on the fly

* fix(dmgbuild): icon file

* fix(gitignore): dmgbuild settings

* chore(resources): update readme with new files

* chore(resources/macos): add missing newline
2023-11-10 09:51:39 +08:00
Takase 63d99d4431
feat(package): ad-hoc sign macOS bundles (#1656)
* feat(package): ad-hoc sign macOS bundles

* fix(package.sh): syntax error

* docs(readme): add instructions for self-signed builds

* docs(readme): grammar

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-11-10 09:47:15 +08:00
Takase 5d53b13cf4
fix(dirmonitor): deadlock if error handler jumps somewhere else (#1647)
* fix: deadlock if error handler jumps somewhere else

* docs(dirmonitor): fix wrong data type in error callback

* docs(dirmonitor): clarify coroutines and deadlocks

* docs(dirmonitor): wording

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-10-24 20:16:52 +08:00
Guldoman 4005a46144
Save in the `workspace` unsaved named files and `crlf` status (#1597)
* Save in the `workspace` unsaved named files

* Save in the `workspace` the `crlf` status and restore it for "new" files
2023-10-18 13:14:28 +08:00
Guldoman 8c451928bf
Ignore keypresses during IME composition (#1573)
Some IMEs continue sending keypresses even during composition, so we 
just ignore them.
2023-10-18 13:14:23 +08:00
Guldoman a066190ee2
Improve `common.serialize` (#1640)
* Make `common.serialize` more locale-independent

* Handle inf/nan numbers in `common.serialize`
2023-10-18 13:14:14 +08:00
Guldoman 1ad3b70e9e
Mark unsaved named files as dirty (#1598) 2023-10-18 13:14:02 +08:00
Takase 82589526c0
fix: dim rendering when antialiasing is turned off (#1641) 2023-10-16 12:05:01 +08:00
Guldoman 316fbbe743
Remove DPI detection for default `SCALE`
This often leads to `SCALE` values that are way off, and makes Lite XL 
unusable, so we now just default it to 1.
2023-10-16 12:04:37 +08:00
Guldoman 784b911d41
Make `linewrapping` consider the expanded `Scrollbar` size
This avoids reflowing the text when hovering the scrollbar.
2023-09-13 08:05:35 +08:00
Robert Hildebrandt a9934c08d9
Fixed C++14 digit separators (#1593) 2023-09-13 08:04:05 +08:00
Guldoman 397b61e7c6
Fix returned `percent` when clicking the `Scrollbar` `track` 2023-08-26 08:56:35 +08:00
Takase 890e4882f3
fix(core): defer core:open-log until everything is loaded (#1585)
* fix(core): defer core:open-log until everything is loaded

* docs(core): document why core:open-log is opened in a thread
2023-08-26 08:53:05 +08:00
Takase 09dd111c61
docs(core.contextmenu): add documentation for contextmenu (#1567) 2023-08-26 08:52:29 +08:00
Jan d937693ddb
Use Lua wrap by default (#1481)
Debian and all its derivatives ship a broken Lua 5.4 that is missing some symbols.
To work around broken distros and make development and distribution easier use the wrap by default and add an option to use the system version.
2023-08-26 08:52:25 +08:00
Guldoman 3d93e16597
Normalize strokes in fixed order (#1572)
* Use normalized strokes when removing duplicates only when appropriate

* Use normalized stroke in `keymap.unbind`

* Normalize strokes by sorting the modifiers before the keys

This also sorts the modifiers in a fixed manner, decided by 
`modkeys.keys`.
We need to do this because we display the strokes in a few places like 
the command palette.
2023-08-26 08:52:10 +08:00
Takase eb27e543b4
ci(release): use lite-xl org (#1571) 2023-08-19 13:31:40 +08:00
Guldoman 86cfbe5f3b
Make `DocView` aware of scrollbars sizes (#1177)
* Make `DocView:scroll_to_make_visible` aware of vertical scrollbar width

* Make `DocView` aware of horizontal scrollbar size
2023-08-19 13:31:37 +08:00
Adam Harrison c279ef0034
Updated README.md as per PR comittee meeting #8. 2023-08-19 13:31:31 +08:00
Shreyas A S 9120fb0046
Updating the *Installing prebuild* section in README.md (#1548)
Sub sections that I've updated:
*To run lite-xl without installing:*
*To install lite-xl copy files over into appropriate directories:*

I think the directory structure of prebuilt packages has changed since when README.md was last updated. I've just updated it. Just that.

Co-authored-by: Shreyas A S <137637016+shreyasastech@users.noreply.github.com>
2023-08-19 13:31:27 +08:00
Delta-official e62a672d7e
Normalize stroke before adding keybind (#1334)
* Normalize stroke before adding keybind

* improve normalization algorithm and implement normalization in several functions

Signed-off-by: delta <darkussdelta@gmail.com>

---------

Signed-off-by: delta <darkussdelta@gmail.com>
2023-08-19 13:31:19 +08:00
Takase 07818934b6
feat(src/renderer): unify fontgroup baseline (#1560)
* feat(src/renderer): unify fontgroup baseline

* fix(src/renderer): use the first font's baseline for the text run
2023-08-19 13:31:18 +08:00
Takase 5d0bcc99fa
Documentation for core.command (#1564)
* docs(core.command): add documentation

* fix(core.common): fix warnings

* docs(core.command): add "core." prefix to all custom types

* docs(core.command); add name as parameter to command.perform

* docs(core.command): fix typo and wording

* docs(core.command): add disclaimer to command.generate_predicate

* docs(core.command): fix wording for predicate

* docs(core.command): document command name

* docs(core.command): document the always_true predicate
2023-08-19 13:30:45 +08:00
Takase b5b6682303
docs(system): fix missing parameter for system.path_compare (#1566)
* docs(system): fix missing parameter for system.path_compare

* docs(system): fix missing parameter in function prototype
2023-08-19 13:30:45 +08:00
Takase ad0d280ecc
Documentation for core.common (#1510)
* docs(core.common): add and improve documentation

* refactor(core.common): remove unused variable to get_height()

* docs(core.common): remove messy newlines

* docs(core.common): fix wording

* docs(core.common): use integer instead of number

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

* docs(core.common): update docs

the docs now follow the style in docs/ directory.
some of the changes suggested are also implemented.

* docs(core.common): fix typo

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

* docs(core.common): restyle annoatations

Extra whitespaces are removed and @overload is used whenever possible.

* docs(core.common): fix various documentation errors

* docs(core.common): simplify unicode description

* docs(core.common): fix return value

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

* docs(core.common): clarify common.bench for not being a benchmark

* docs(common): add disclaimer for numbers in common.serialize

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-08-19 13:30:45 +08:00
Takase 553251834b
feat(src/renderer): stream fonts with SDL_RWops on all platforms (#1555)
* feat(src/renderer): stream fonts with SDL_RWops on all platforms

This fixes #1529 where the font itself carries the font file, which gets copied around.
This commit streams the file, so the file is not entirely in memory.

* style(src/renderer): use standard C types

* refactor(src/renderer): implement FT_Stream.close

* fix(src/renderer): fix SDL_RWops double free
2023-08-19 13:30:18 +08:00
Takase 7f84ed311b
style(src/renderer): use FreeType header names (#1554) 2023-08-19 13:29:49 +08:00
Guldoman f7400c924e
Allow setting custom glyphset size (#1542)
* Properly set glyphset size

* Rename `MAX_GLYPHSET` to `GLYPHSET_SIZE`

* Use more appropriate types for font metrics
2023-08-19 13:29:48 +08:00
Guldoman e9678cc140
Skip checking `files` if no filename was provided to `syntax.get` 2023-08-19 13:29:48 +08:00
Luke aka SwissalpS b5617a3eef
Fix #1538 log scrolls automatically (the real PR) (#1546)
* fix #1538 log scrolls automatically

adds:
- when user scrolls, position is kept no matter how many new entries
arrive
- when user scrolls up to last entry, autoscroll is enabled again

does not add buttons to jump up/down
see #1538

* move scroll-test out of on_mouse_wheel

* determine diff_index with loop

* remove check at move_towards yoffset

* use while loop instead of repeat loop

* remove meaningless setter

* remove stray var
2023-08-19 13:29:48 +08:00
Luke aka SwissalpS bd53bc3718
comment typo in object.lua (#1541) 2023-08-19 13:29:48 +08:00
Jan 2af3082640
Attach command buffer to Renderer Window (#1472) 2023-08-19 13:29:05 +08:00
Guldoman 964b8fe29d
Increase number of loadable glyphsets (#1524)
This should be enough to load every unicode codepoint.
2023-08-19 13:28:37 +08:00
Guldoman f1f81c8851
Make `Doc:sanitize_position` return a more appropriate `col` (#1469)
If `line` is out of range, return the `col` "closest" to the original
values.
2023-08-19 13:28:14 +08:00
Guldoman 68e9c4670e
Revert "core syntax: strip the path from filename on syntax.get (#1168)" (#1322)
* Revert "core syntax: strip the path from filename on syntax.get (#1168)"

This reverts commit af6c4bc152.

The previous behavior was correct and allowed access to the full path for path-dependant syntaxes.

* Use `Doc.abs_filename` to obtain syntax when possible

This allows matching full paths in language syntaxes, but we lose the
possibility of matching the project root.
2023-08-19 13:28:14 +08:00
Guldoman ff884d7d4a
When logging don't use `core.status_view` if not yet initialized 2023-08-19 13:27:31 +08:00
Guldoman 3febcf454c
Mark `linewrapping` `open_files` table as weak
We weren't correctly garbage-collecting `Doc`s, so we had `Highlighter`s 
stay alive over their due time.
2023-08-19 13:27:28 +08:00
Jefferson González 6c17f6e2ee
Close lua state when exiting on a runtime error (#1487)
* Close lua state when exiting on a runtime error

* This change allows calling the garbage collector before exiting the
  application for a cleaner shutdown.
* Components like the shared memory object on #1486 will have a better
  chance at destroying no longer needed resources.

* Overriden os.exit to always close the state

* Allow setting close param on os.exit override

* Simplified the os.exit override a bit more

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-08-19 13:27:02 +08:00
takase1121 2a9b367e13
Move lineguide below blinking cursor, fixes #1488 (#1511)
* Move lineguide below blinking cursor, fixes #1488

* Added config_spec custom color
2023-08-19 13:26:01 +08:00
takase1121 64e5fd8ead
fix(renderer): fix memory leak when freeing glyphsets 2023-08-19 12:47:19 +08:00
takase1121 4c320a10c0
docs(system): make all parameters for set_window_hit_test optional 2023-08-19 12:45:48 +08:00
Guldoman bd36b3f615
Restore horizontal scroll position after scale change (#494)
* Restore horizontal scroll position after scale change

* Consider `View` horizontal size when restoring horizontal scroll

This is needed because `View:get_h_scrollable_size` includes the 
horizontal size, while `View.scroll.x` doesn't.
2023-08-19 12:45:16 +08:00
takase1121 c0b1fe348f
feat(bootstrap): return error string from C searcher 2023-08-19 12:45:15 +08:00
takase1121 6111b071ec
fix(rencache): fix compiler warning for printing size_t 2023-08-19 12:45:15 +08:00
Adam Harrison 793af14dca
Fixing linewrapping bug to do with wordwrapping. 2023-08-19 12:45:15 +08:00
takase1121 2517d34113
ci(build): update action dependencies 2023-08-19 12:44:19 +08:00
Takase 1952848caa
fix(process): check for HANDLE_INVALID (#1475) 2023-08-19 12:44:19 +08:00
Jan 760271d416
Make `system.path_compare` more digit-aware (#1474)
This allows a human friendly sorting filenames with numbers in them
So
- asd1
- asd10
- asd2
becomes
- asd1
- asd2
- asd10
2023-08-19 12:44:18 +08:00
Takase 2fe1f52a1f
Process API improvements (again) (#1370)
* feat(process): add push_error
* refactor(process): use push_error for better errors
* style(process): consistent error messages
* refactor(process): reimplement process.strerror() with push_error
* refactor(process): implement close_fd only once
* refactor(process): rename process_handle to process_handle_t
* fix(process): prevent errors from a NULL error message
* refactor(process): refactor push_error into 2 functions
* fix(process): fix wrong error message
* fix(process): check if push_error_string actually pushed something
* refactor(process): make error messages descriptive
* fix(process): check for empty table instead of aborting
* refactor(process): make error messages descriptive on Windows
* refactor(process): rename process_stream_handle to process_stream_t
* refactor(process): fix wrong usage of process_handle_t
* fix(process): fix wrong type name
* refactor(process): incoporate kill_list_thread into process_kill_list_t
* refactor(process): make kill_list per-state data
2023-08-19 12:43:57 +08:00
Takase 64a6d88618
Build releases with Ubuntu 18.04 container (#1460)
* ci(release): try using lite-xl-build-box

* ci(build): test with my own fork

* ci(build): do not install python via actions

* ci(build): disable package updates

* fix(scripts/appimage.sh): add workaround for non-FUSE environments

* ci(build): document why the actions are disabled

* ci(release): fix typo
2023-08-19 12:43:56 +08:00
Guldoman 97f3159415
Merge carets after `doc:move-to-{previous,next}-char` (#1462) 2023-08-19 12:43:56 +08:00
Takase 8e57b71118
refactor(plugin_api): move the header into include/ (#1440) 2023-08-19 12:43:09 +08:00
Takase a44a7eafe8
Cross compiling improvements + macOS universal binary (#1458)
* chore(resources): rename macos_arm64.txt to macos-arm64.txt
   This matches the platform-arch convention like many other parts of the project.
* chore(resources/cross): rename wasm.txt to unknown-wasm32.txt
* refactor(scripts/common.sh): use parameter expansion instead of if else
* feat(scripts/common.sh): support custom arch and platform for get_default_build_dir
* feat(scripts/build.sh): add --cross-platform, --cross-arch and --cross-file
* feat(scripts/package.sh): add --cross-platform and --cross-arch
* feat(build-packages.sh): add support for new options in build.sh and packages.sh
* ci(build): make arm64 binaries in CI
* ci(build): do not install external libraries
* ci(build): fix invalid artifact name
* ci(build): fix INSTALL_NAME
* ci(build): change name for macos artifacts
* ci(build): add script to build universal dmgs from individual dmgs
* ci(build): build universal dmgs
* fix(make-universal-binaries): fix wrong path for hdiutil
* ci(build): rename macos action
* fix(make-universal-binaries.sh): fix wrong pathname for ditto
* ci(release): build macos universal binaries
* ci(release): remove useless variables
* ci(release): fix wrong dependency
* ci(build): fix old ubuntu version
   This version will be restored once I complete some container-specific fixes.
* ci(build): make build_macos_universal depend on release
* ci(build): fix wrong dmg dir
* style(ci): capitalize 'universal' for CI name
* fix(make-universal-binaries.sh): fix truncated dmg name when it contains dots
* ci: styling changes
* ci(release): install appdmg only
2023-08-19 12:42:31 +08:00
Takase 1fe90da664
upgrade header files to Lua 5.4 (#1436)
* refactor(native_api_header): upgrade header files to Lua 5.4.

Almost all of the symbols in this file was from 5.2. This will obviously
not work because some function signatures have changed and some have
completely wrong return values, etc.
This commit updates the header files to Lua 5.4 based on the source code
and changes a few things.

* refactor(plugin_api): move the header into include/

* fix(lite_xl_plugin_api.h): include stdlib to avoid errors with exit

* refactor(lite_xl_plugin_api.h): do not return in SYMBOL_WRAP_CALL

* fix(lite_xl_plugin_api.h): fix wrong way of passing varargs

* fix(lite_xl_plugin_api.h): fix differing lua_rawlen definition

* fix(lite_xl_plugin_api.h): fix fallback function signature

* fix(lite_xl_plugin_api.h): fix conversion from void * to function pointer
2023-08-19 12:42:31 +08:00
Jefferson González 0d0f1b00d9
Fix for api_require wrong macro && conditions (#1465)
This mistake escaped my eyes when reviewing #1437 and causes
some symbols to not be exported, because the preprocessor macros
are expecting multiple LUA versions to evaluate as true at once.
The fix is to replace `&&` with `||`.
2023-08-19 12:42:31 +08:00
vqn f60228f610
#1393 followup (#1463)
* Fix incorrect check in doc:raw_remove

Restore caret position on command doc:cut

* merge cursors and fix new line in clipboard

* add new line to the last copied line
2023-08-19 12:41:57 +08:00
Guldoman d497402c30
Make `system.path_compare` more case-aware (#1457)
* Use Lua-provided string lengths for `system.path_compare`
* Make `system.path_compare` more case-aware
   Before, strings like `README.md` would be sorted before `changelog.md`, 
   because we only looked at the raw ascii values.
   Now the character case is considered as a secondary sorting key.
2023-08-19 12:41:56 +08:00
Takase fdd6ca3426
Update api_require to expose more symbols (#1437)
* feat(system): update api_require for more symbols
* fix(system): fix missing 5.1 symbols
* fix(system): add more missing symbols
* fix(system): add all symbols
   We got'em this time. I swear.
* fix(system): fix undefined symbols due to conditional compilation
   There is only pain and suffering.
   Turns out some of the symbols are only exported when the options are enabled.
   We need to preprocess the header.
2023-08-19 12:41:56 +08:00
Takase 84aeea61c2
Optimizing MSYS2 CI (#1435)
* feat(ci): install dependencies on setup
* fix(ci): don't update msys2 when setup
* fix(ci): download subprojects before patching
* doc(ci): document why meson subprojects download is called
2023-08-19 12:41:56 +08:00
Takase a0c8f01312
fix(CI): bump dependency versions (#1434)
* refactor(ci): use microsoft/setup-msbuild
* fix(ci): fix wrong option name for setup-msbuild
* fix(ci): bump setup-python version
* fix(lua-utf8-patch): enable support for windows vista and above
* fix(ci): use vs backend
* fix(ci): reconfigure project manually after patch
* fix(ci): add a separate build step
* fix(ci): use msvc-dev-cmd again
2023-08-19 12:41:56 +08:00
Takase 4e3d6824ff
fix: fix differing stacktrace on stdout and file (#1404)
* fix(c-bootstrap): produce identical stack traces
2023-08-19 12:41:55 +08:00
Takase bb31a1adf2
fix(windows-utf8-patch): fix os.getenv() not supporting UTF-8 output (#1397) 2023-08-19 12:40:43 +08:00
Takase a24432941c
Fix invalid EXEFILE and EXEDIR on Windows (#1396)
* fix(main): fix get_exe_filename returning invalid result on Windows
* fix(main): fix bootstrap not intepreting UTF-8 properly
2023-08-19 12:40:34 +08:00
vqn 12e0634f9c
fix cursors positions when deleting multiple selections (#1393)
* correctly handle overlapping selections merge cursors in Doc:raw_remove
2023-08-19 12:39:33 +08:00
Guldoman 6d217204f6
Allow `tokenizer` to pause and resume in the middle of a line (#1444) 2023-08-19 12:39:22 +08:00
Adam 6deca53303
Disable `trimwhitespace` and `drawwhitespace` via their configs (#1446)
Instead of completely disabling them, we now use their internal toggle.

Also moved `drawwhitespace` commands inside the plugin.

---

* Fixed bug where commands would show even when plugin was disbled. Also removed antiquated way of disabling.

* Fixed typos.

* Also moved trimwhitespace out of config, if it already has a default enabled value of false.

* Changed documentation.

* Clarified comments.
2023-08-19 12:39:22 +08:00
Guldoman bf35417f82
Don't calculate widths per-uft8-char when not needed (#1409) 2023-08-19 12:38:49 +08:00
Takase 84c7bb9de6
Asynchronous process reaping (#1412)
* refactor(process): introduce process_stream_handle separate from process_handle

* feat(process): introduce process_handle helper functions

* feat(process): add asynchronous process reaping

* feat(process): wait for shorter period if possible

* style(process): remove unecessary brackets

* style(process): fix parentheses

* refactor(process): remove useless setvbuf call

* style(process): remove unecessary value

* refactor(process): add size field into kill_list

* refactor(process): use SDL_Delay for sleeping

* style(process): remove trailing whitespace

* fix(main): destroy window before closing lua

* fix(process): check for timeout correctly

* refactor(process): remove unecessary if check

* refactor(process): remove size from the list

* fix(process): fix invalid delay calculation

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-08-19 12:38:49 +08:00
Guldoman 89864ee88c
Aggregate `SDL_Surface`s and their scale in `RenSurface` (#1429) 2023-08-19 12:38:49 +08:00
Guldoman a61531dbf0
Use clipping functions provided by SDL (#1426) 2023-08-19 12:38:35 +08:00
Guldoman 1d0725f904
Improve text width calculation precision (#1408)
In some extreme cases (~30000 chars) text width precision takes a hit.
Using double instead of float fixes that.
2023-08-19 12:38:35 +08:00
Takase 32860c111e
refactor(main): move SetProcessDPIAware to manifests (#1413) 2023-08-19 12:38:35 +08:00
Guldoman 8c47fad637
Split `Command` struct into different structs for each command type (#1407)
This reduces the space needed for each command.
2023-08-19 12:38:34 +08:00
Takase f685293417
Add manifest on Windows (#1405)
* fix(gitignore): add exclusion for manifest files

* feat(windows): add application manifest

* feat(build): use application manifest on windows

* refactor(build): use genrate_file to generate the manifest

* style(manifest): remove trailing whitespace
2023-08-19 12:38:34 +08:00
Guldoman 95611366bb
Use correct view for scrolling to `find-replace:repeat-find` results (#1400) 2023-08-19 12:38:34 +08:00
vqn 067271bc02
fix incorrect x_offset if opened docs have different tab sizes (#1383) 2023-08-19 12:38:13 +08:00
Adam c8f033ec8b
Fixed up some post 5.1/jit Symbols (#1385)
* Updated k functions to have appropriate method signatures for 5.3 and up.

* Fixed up some inconsistent signatures that I forgot.
2023-08-19 12:38:13 +08:00
Guldoman b0e524dd15
Improve `DocView:get_visible_line_range` precision (#1382) 2023-08-19 12:37:50 +08:00
Jefferson González 24491bc3fd
plugins scale: also rescale style.expanded_scrollbar_size (#1380) 2023-08-19 12:37:50 +08:00
Jefferson González 70ed171612
NagView: properly rescale on scale change (#1379)
* drop font option since style.font is always used
2023-08-19 12:37:49 +08:00
Jefferson González 6925c06599
Improved plugins config table handling (#1356)
* Warns user if trying to disable a plugin that is already
  enabled when doing `config.plugins.plugin_name = false` and also
  prevents replacing the current plugin config table with the false
  value to mitigate runtime issues.
* Uses a merge strategy by default when assigning a table to a plugin
  config to prevent a user from removing a plugin default config values
  as experienced and explained on this issue lite-xl-plugins#158
* This change is basically backwards compatible, but will require a
  change on the settings ui plugin on how it checks for already
  enabled plugins, since rawget will no longer be a working hack
  or workaround for this.
* As suggested by Adam dropped loaded key and switched to package.loaded
2023-08-19 12:37:29 +08:00
Takase b95fdfcf5f
fix: exec() error not returned to parent (#1363)
* fix: exec() error not returned to parent

* chore: remove accidental lua.h inclusion
2023-08-19 12:37:20 +08:00
takase1121 218ba3ebac
Context menu fixes and keyboard navigation (#1338)
* fix Doc contextmenu not registering commands if scale plugin is not found
* fix TreeView contextmenu commands not working if the mouse hovers DocView
* add keyboard navigation to TreeView contextmenu
* fix incorrect contextmenu predicate

Co-Authored-By: vqn <85911372+vqns@users.noreply.github.com>
2023-08-19 12:36:39 +08:00
Adam 017711b369
Getting rid of annoying forward slash on windows. (#1345) 2023-08-19 12:30:41 +08:00
jgmdev b8eb6865a6
gh workflow: fix path to macOS arm64 cross file 2023-08-19 12:30:41 +08:00
Jefferson González 03d11c869d
ci: fix msys build now requiring ca-certificates (#1348)
Thanks to Guldoman who discovered the cause for meson failing to
validate SSL certificates which turned out to be MSYS now requiring
ca-certificates package installed for the different architectures.
2023-08-19 12:30:41 +08:00
Jan a951c3cd39
pass RenWindow by argument (#1321)
* pass RenWindow to all renderer functions that need it

* pass RenWindow to all rencache functions that need it
2023-08-19 12:30:41 +08:00
Adam Harrison 5907118683
Added missing header declaration. 2023-08-19 12:29:18 +08:00
Jefferson González 95f18a1148
plugin api: allow usage on multiple source files (#1335)
As discussed with Adam on discord current Lite XL Lua Plugin API was not
working on native plugins with more than 1 source file since imported
symbols were not exposed to other unit files. The issue was tackled on #1332
but the solution introduced another issue when Lite XL was dynamically
linked to the system lua. So we opted to tackle this by using function
wrappers around the function pointers.
2023-08-19 12:29:18 +08:00
Guldoman bddb5e274d
Avoid drawing hidden text in `DocView:draw_line_text` (#1298)
* Stop drawing text past the `DocView` edge in `DocView:draw_line_text`

* Don't add draw commands if they fall outside the latest clip

The check was previously done with the window rect, so this will reduce 
a bit more the number of commands sent.
2023-08-19 12:28:48 +08:00
Jan d54a5d0672
remove static libgcc from meson (#1290) 2023-08-19 12:28:48 +08:00
Adam Harrison 4c18cf6744
Updated dummy method signature to match prototypes. 2023-08-19 12:28:48 +08:00
Guldoman a9d8f12cb7
Make empty groups in `regex.gmatch` return their offset (#1325)
This makes `regex.gmatch` behave like `string.gmatch`.
2023-08-19 12:28:47 +08:00
xwii 38fa9f976c
Use `table.move` to implement `common.splice` (#1324)
* Use `table.move` to implement `common.splice`

* Disallow negative `remove` in `common.splice`
2023-08-19 12:28:47 +08:00
adityaraj ae218bc005
Create Renderer Only When It Doesn't Exist (#1315) 2023-08-19 12:28:21 +08:00
Jan c8afe3d1bf
replace uses of SDL_Window with RenWindow (#1319)
Since Renwindow contains our instance of SDL_Window we can use this
to simplify future logic to create separate window instances
2023-08-19 12:28:21 +08:00
Takase 2d0ddc302f
Reorganize resources/ + wasm target (#1244)
* add README.md to resources directory
* add cross/ directory for meson cross files
* fix readme list syntax error
* fix reflink
* disable ASYNCIFY_ADVISE by default
* use executable names instead of hardcoding paths
2023-08-19 12:28:21 +08:00
jgmdev 291e7eab6f
packaging: use master branch for plugin addons 2023-08-19 12:28:21 +08:00
sammyette 1984573214
fix: center title and version in emptyview (#1311)
* fix: divide by amount of lines
2023-08-19 12:28:21 +08:00
Jan f5a224999a
defer lua error until after cleanup (#1310) 2023-08-19 12:28:20 +08:00
Jefferson González 4b6134a839
plugin api: added missing luaL_typeerror (#1313) 2023-08-19 12:28:20 +08:00
jgmdev 60f0e3f3da
Packaging Scripts: updated widgets install location 2023-08-19 12:28:20 +08:00
Guldoman f00f41b468
`linewrapping`: Disable horizontal scrolling when enabled (#1309) 2023-08-19 12:28:20 +08:00
Julien Voisin 1ab320bb9b
Handle readlink errors (#1292) 2023-08-19 12:28:19 +08:00
Guldoman 138cea45d5
Make `dirwatch` sorting compatible with what `file_bisect` expects (#1300)
The result of `a.filename < b.filename` is sometimes different from 
`system.path_compare(a.filename, a.type, b.filename, b.type)` which 
causes issues to `file_bisect`, as it expects the sorting to be done 
with `system.path_compare`.
2023-08-19 12:28:19 +08:00
Julien Voisin d86413cc30
Don't set a value twice (#1306) 2023-08-19 12:28:19 +08:00
Julien Voisin d06c9f401c
Fix a memory leak (#1305)
`font` was not freed upon error.
2023-08-19 12:28:19 +08:00
Julien Voisin d755fa6fba
Make api_require's nodes const (#1296) 2023-08-19 12:28:18 +08:00
Takase 69ce580970
do not allow users to create an empty font group (#1303) 2023-08-19 12:28:18 +08:00
Guldoman bd4e64cc7e
Allow command buffer to be expanded (#1297) 2023-08-19 12:28:18 +08:00
Dave 0fa0a59c8b
Minor typos in init text 2023-08-19 12:28:18 +08:00
Takase e0b5f56faa ci(release): use lite-xl org (#1571) 2023-08-07 15:26:50 +01:00
Guldoman 95c1805293 Make `DocView` aware of scrollbars sizes (#1177)
* Make `DocView:scroll_to_make_visible` aware of vertical scrollbar width

* Make `DocView` aware of horizontal scrollbar size
2023-08-07 15:26:49 +01:00
Adam Harrison e85a439656 Updated README.md as per PR comittee meeting #8. 2023-08-07 15:26:49 +01:00
Shreyas A S 09131e7ff6 Updating the *Installing prebuild* section in README.md (#1548)
Sub sections that I've updated:
*To run lite-xl without installing:*
*To install lite-xl copy files over into appropriate directories:*

I think the directory structure of prebuilt packages has changed since when README.md was last updated. I've just updated it. Just that.

Co-authored-by: Shreyas A S <137637016+shreyasastech@users.noreply.github.com>
2023-08-07 15:26:49 +01:00
Guldoman 4454fcc3a2 Use proper timeouts for coroutines that don't need to wait (#1467) 2023-08-07 15:26:49 +01:00
Delta-official a80414fb0b Normalize stroke before adding keybind (#1334)
* Normalize stroke before adding keybind

* improve normalization algorithm and implement normalization in several functions

Signed-off-by: delta <darkussdelta@gmail.com>

---------

Signed-off-by: delta <darkussdelta@gmail.com>
2023-08-07 15:26:49 +01:00
Takase 4e5c0ed1d4 feat(src/renderer): unify fontgroup baseline (#1560)
* feat(src/renderer): unify fontgroup baseline

* fix(src/renderer): use the first font's baseline for the text run
2023-08-07 15:26:49 +01:00
Adam 5b62eba35f Fixed things for when a thread requests a redraw. (#1570)
* Fixed things for when a thread requests a redraw.

* @guldoman's changes.

* Whoops.
2023-08-07 15:26:49 +01:00
Takase d1d4436691 Documentation for core.command (#1564)
* docs(core.command): add documentation

* fix(core.common): fix warnings

* docs(core.command): add "core." prefix to all custom types

* docs(core.command); add name as parameter to command.perform

* docs(core.command): fix typo and wording

* docs(core.command): add disclaimer to command.generate_predicate

* docs(core.command): fix wording for predicate

* docs(core.command): document command name

* docs(core.command): document the always_true predicate
2023-08-07 15:26:49 +01:00
Takase 3ab1d7f198 docs(system): fix missing parameter for system.path_compare (#1566)
* docs(system): fix missing parameter for system.path_compare

* docs(system): fix missing parameter in function prototype
2023-08-07 15:26:49 +01:00
Takase 691df348f0 Documentation for core.common (#1510)
* docs(core.common): add and improve documentation

* refactor(core.common): remove unused variable to get_height()

* docs(core.common): remove messy newlines

* docs(core.common): fix wording

* docs(core.common): use integer instead of number

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

* docs(core.common): update docs

the docs now follow the style in docs/ directory.
some of the changes suggested are also implemented.

* docs(core.common): fix typo

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

* docs(core.common): restyle annoatations

Extra whitespaces are removed and @overload is used whenever possible.

* docs(core.common): fix various documentation errors

* docs(core.common): simplify unicode description

* docs(core.common): fix return value

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

* docs(core.common): clarify common.bench for not being a benchmark

* docs(common): add disclaimer for numbers in common.serialize

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-08-07 15:26:49 +01:00
Adam fbdd8fa318 Updated extension for mac. (#1563) 2023-08-07 15:26:38 +01:00
Takase 60e71160b6 feat(src/renderer): stream fonts with SDL_RWops on all platforms (#1555)
* feat(src/renderer): stream fonts with SDL_RWops on all platforms

This fixes #1529 where the font itself carries the font file, which gets copied around.
This commit streams the file, so the file is not entirely in memory.

* style(src/renderer): use standard C types

* refactor(src/renderer): implement FT_Stream.close

* fix(src/renderer): fix SDL_RWops double free
2023-08-07 15:26:38 +01:00
Guldoman 523e62bdce Return state when tokenizing plaintext syntaxes 2023-08-07 15:26:38 +01:00
Takase fa694ae6f8 style(src/renderer): use FreeType header names (#1554) 2023-08-07 15:26:38 +01:00
Guldoman 608ad159cd Allow setting custom glyphset size (#1542)
* Properly set glyphset size

* Rename `MAX_GLYPHSET` to `GLYPHSET_SIZE`

* Use more appropriate types for font metrics
2023-08-07 15:26:38 +01:00
Guldoman 819bd81293 Skip checking `files` if no filename was provided to `syntax.get` 2023-08-07 15:26:38 +01:00
Luke aka SwissalpS d9aef2390c Fix #1538 log scrolls automatically (the real PR) (#1546)
* fix #1538 log scrolls automatically

adds:
- when user scrolls, position is kept no matter how many new entries
arrive
- when user scrolls up to last entry, autoscroll is enabled again

does not add buttons to jump up/down
see #1538

* move scroll-test out of on_mouse_wheel

* determine diff_index with loop

* remove check at move_towards yoffset

* use while loop instead of repeat loop

* remove meaningless setter

* remove stray var
2023-08-07 15:26:38 +01:00
Luke aka SwissalpS 526fc816c4 comment typo in object.lua (#1541) 2023-08-07 15:26:38 +01:00
Jan 0532ef1792 Attach command buffer to Renderer Window (#1472) 2023-08-07 15:26:38 +01:00
Guldoman 9328e7ae8f Increase number of loadable glyphsets (#1524)
This should be enough to load every unicode codepoint.
2023-08-07 15:26:38 +01:00
Jan dd479f8cd1 Add top tab margins (#1479)
adapted from #810 to allow styles to decide upon the top margin of the tab list
2023-08-07 15:26:38 +01:00
Guldoman 5758693f4f Show cursor at the start of the next line when selecting full lines (#761)
This was the previous behavior that regressed with the keymap clicks.

This also better shows that the selection extends to the next line.
2023-08-07 15:26:38 +01:00
Guldoman 9b61f1c597 Make `Doc:sanitize_position` return a more appropriate `col` (#1469)
If `line` is out of range, return the `col` "closest" to the original
values.
2023-08-07 15:26:38 +01:00
Guldoman d12a14869c Revert "core syntax: strip the path from filename on syntax.get (#1168)" (#1322)
* Revert "core syntax: strip the path from filename on syntax.get (#1168)"

This reverts commit af6c4bc152.

The previous behavior was correct and allowed access to the full path for path-dependant syntaxes.

* Use `Doc.abs_filename` to obtain syntax when possible

This allows matching full paths in language syntaxes, but we lose the
possibility of matching the project root.
2023-08-07 15:26:38 +01:00
Guldoman a01eba3fad Change AppID (#1187)
This ID reflects our domain (lite-xl.com).
2023-08-07 15:26:38 +01:00
Guldoman 48bcf66cc1 When logging don't use `core.status_view` if not yet initialized 2023-08-07 15:26:38 +01:00
Guldoman ffb8f5da0f Mark `linewrapping` `open_files` table as weak
We weren't correctly garbage-collecting `Doc`s, so we had `Highlighter`s 
stay alive over their due time.
2023-08-07 15:26:38 +01:00
Guldoman 43f9b8accc Add mouse grab (#1501)
* Add mouse grab

We now also send mouse movement events only to the interested view.

* Add deprecation messages handler

* Make various `View`s respect `on_mouse_left`

* `StatusView`
* `TitleView`
* `TreeView`
* `ToolbarView`

* Fix scrollbar in `TreeView` not updating

We were in some cases sending outdated mouse positions to the scrollbar, 
which made it think that the mouse was hovering it.

This also updates the hovered item more responsively during scroll.
2023-08-07 15:26:38 +01:00
Jefferson González 39182d49d6 Close lua state when exiting on a runtime error (#1487)
* Close lua state when exiting on a runtime error

* This change allows calling the garbage collector before exiting the
  application for a cleaner shutdown.
* Components like the shared memory object on #1486 will have a better
  chance at destroying no longer needed resources.

* Overriden os.exit to always close the state

* Allow setting close param on os.exit override

* Simplified the os.exit override a bit more

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-08-07 15:26:38 +01:00
Jefferson González 20fdcd668d Move lineguide below blinking cursor, fixes #1488 (#1511)
* Move lineguide below blinking cursor, fixes #1488

* Added config_spec custom color
2023-08-07 15:26:38 +01:00
takase1121 554a4d4f48 fix(renderer): fix memory leak when freeing glyphsets 2023-08-07 15:26:38 +01:00
takase1121 af2abe4c98 docs(system): make all parameters for set_window_hit_test optional 2023-08-07 15:26:38 +01:00
Jefferson González 3deeb762b4 detectindent: fix wrong detection reported by Adam (#1500)
* The comment patterns had to come before the string ones
* The smallest indentation size is now taken into consideration even if
  it only occurs once, we just make sure its size is more than 1 space.
2023-08-07 15:26:38 +01:00
Adam 3627bc01cf Allowed for overrides of toolbar items, so plugins can add things if they want to with different fonts. (#1157) 2023-08-07 15:26:38 +01:00
Guldoman f06580deee Restore horizontal scroll position after scale change (#494)
* Restore horizontal scroll position after scale change

* Consider `View` horizontal size when restoring horizontal scroll

This is needed because `View:get_h_scrollable_size` includes the 
horizontal size, while `View.scroll.x` doesn't.
2023-08-07 15:26:38 +01:00
takase1121 0766d804ba feat(bootstrap): return error string from C searcher 2023-08-07 15:26:38 +01:00
takase1121 1d37fa1be3 fix(rencache): fix compiler warning for printing size_t 2023-08-07 15:26:38 +01:00
Adam Harrison 116c14679d Fixing linewrapping bug to do with wordwrapping. 2023-08-07 15:26:38 +01:00
Adam 4f26fd1cf7 Added in double-clicking on emptyview and tab bar. (#1478)
* Added in double-clicking on emptyview and tab bar.

* Fixed issue with split tabs.

* Early exit if no overlapping node.

* Changed category of command to tabbar.

* Additional cleanup.

* Changed for whether we should show tabs.

* Fixed erroneous hover.
2023-08-07 15:26:38 +01:00
takase1121 c77b69a21c ci(build): update action dependencies 2023-08-07 15:26:38 +01:00
Takase c0c2e7222e fix(process): check for HANDLE_INVALID (#1475) 2023-08-07 15:26:38 +01:00
Jan bd93e5a4b6 Make `system.path_compare` more digit-aware (#1474)
This allows a human friendly sorting filenames with numbers in them
So
- asd1
- asd10
- asd2
becomes
- asd1
- asd2
- asd10
2023-08-07 15:26:38 +01:00
jgmdev 76a7fb9f79 StatusView compat fix with older Lua runtimes 2023-08-07 15:26:38 +01:00
Takase e667b16099 Process API improvements (again) (#1370)
* feat(process): add push_error
* refactor(process): use push_error for better errors
* style(process): consistent error messages
* refactor(process): reimplement process.strerror() with push_error
* refactor(process): implement close_fd only once
* refactor(process): rename process_handle to process_handle_t
* fix(process): prevent errors from a NULL error message
* refactor(process): refactor push_error into 2 functions
* fix(process): fix wrong error message
* fix(process): check if push_error_string actually pushed something
* refactor(process): make error messages descriptive
* fix(process): check for empty table instead of aborting
* refactor(process): make error messages descriptive on Windows
* refactor(process): rename process_stream_handle to process_stream_t
* refactor(process): fix wrong usage of process_handle_t
* fix(process): fix wrong type name
* refactor(process): incoporate kill_list_thread into process_kill_list_t
* refactor(process): make kill_list per-state data
2023-08-07 15:26:38 +01:00
Takase 60fae68a2e Build releases with Ubuntu 18.04 container (#1460)
* ci(release): try using lite-xl-build-box

* ci(build): test with my own fork

* ci(build): do not install python via actions

* ci(build): disable package updates

* fix(scripts/appimage.sh): add workaround for non-FUSE environments

* ci(build): document why the actions are disabled

* ci(release): fix typo
2023-08-07 15:26:38 +01:00
Guldoman eb5c42a6c9 Merge carets after `doc:move-to-{previous,next}-char` (#1462) 2023-08-07 15:26:38 +01:00
Takase 0dca16c462 refactor(plugin_api): move the header into include/ (#1440) 2023-08-07 15:26:38 +01:00
Guldoman 10bd794d8a Show error message in crash message box (#1461)
* Save to `error.txt` the same traceback shown on stdout
* Show error message in crash message box
2023-08-07 15:26:38 +01:00
Takase 3e36443c9d Cross compiling improvements + macOS universal binary (#1458)
* chore(resources): rename macos_arm64.txt to macos-arm64.txt
   This matches the platform-arch convention like many other parts of the project.
* chore(resources/cross): rename wasm.txt to unknown-wasm32.txt
* refactor(scripts/common.sh): use parameter expansion instead of if else
* feat(scripts/common.sh): support custom arch and platform for get_default_build_dir
* feat(scripts/build.sh): add --cross-platform, --cross-arch and --cross-file
* feat(scripts/package.sh): add --cross-platform and --cross-arch
* feat(build-packages.sh): add support for new options in build.sh and packages.sh
* ci(build): make arm64 binaries in CI
* ci(build): do not install external libraries
* ci(build): fix invalid artifact name
* ci(build): fix INSTALL_NAME
* ci(build): change name for macos artifacts
* ci(build): add script to build universal dmgs from individual dmgs
* ci(build): build universal dmgs
* fix(make-universal-binaries): fix wrong path for hdiutil
* ci(build): rename macos action
* fix(make-universal-binaries.sh): fix wrong pathname for ditto
* ci(release): build macos universal binaries
* ci(release): remove useless variables
* ci(release): fix wrong dependency
* ci(build): fix old ubuntu version
   This version will be restored once I complete some container-specific fixes.
* ci(build): make build_macos_universal depend on release
* ci(build): fix wrong dmg dir
* style(ci): capitalize 'universal' for CI name
* fix(make-universal-binaries.sh): fix truncated dmg name when it contains dots
* ci: styling changes
* ci(release): install appdmg only
2023-08-07 15:26:38 +01:00
Takase 3afcf84a09 upgrade header files to Lua 5.4 (#1436)
* refactor(native_api_header): upgrade header files to Lua 5.4.

Almost all of the symbols in this file was from 5.2. This will obviously
not work because some function signatures have changed and some have
completely wrong return values, etc.
This commit updates the header files to Lua 5.4 based on the source code
and changes a few things.

* refactor(plugin_api): move the header into include/

* fix(lite_xl_plugin_api.h): include stdlib to avoid errors with exit

* refactor(lite_xl_plugin_api.h): do not return in SYMBOL_WRAP_CALL

* fix(lite_xl_plugin_api.h): fix wrong way of passing varargs

* fix(lite_xl_plugin_api.h): fix differing lua_rawlen definition

* fix(lite_xl_plugin_api.h): fix fallback function signature

* fix(lite_xl_plugin_api.h): fix conversion from void * to function pointer
2023-08-07 15:26:38 +01:00
Jefferson González d16dce4fb6 Fix for api_require wrong macro && conditions (#1465)
This mistake escaped my eyes when reviewing #1437 and causes
some symbols to not be exported, because the preprocessor macros
are expecting multiple LUA versions to evaluate as true at once.
The fix is to replace `&&` with `||`.
2023-08-07 15:26:38 +01:00
sammyette 945914b276 feat: add statusview item to show selections (#1445) 2023-08-07 15:26:38 +01:00
vqn 577e99f519 #1393 followup (#1463)
* Fix incorrect check in doc:raw_remove

Restore caret position on command doc:cut

* merge cursors and fix new line in clipboard

* add new line to the last copied line
2023-08-07 15:26:38 +01:00
Guldoman 637064d351 Make `system.path_compare` more case-aware (#1457)
* Use Lua-provided string lengths for `system.path_compare`
* Make `system.path_compare` more case-aware
   Before, strings like `README.md` would be sorted before `changelog.md`, 
   because we only looked at the raw ascii values.
   Now the character case is considered as a secondary sorting key.
2023-08-07 15:26:38 +01:00
Takase 688bcaf707 Update api_require to expose more symbols (#1437)
* feat(system): update api_require for more symbols
* fix(system): fix missing 5.1 symbols
* fix(system): add more missing symbols
* fix(system): add all symbols
   We got'em this time. I swear.
* fix(system): fix undefined symbols due to conditional compilation
   There is only pain and suffering.
   Turns out some of the symbols are only exported when the options are enabled.
   We need to preprocess the header.
2023-08-07 15:26:38 +01:00
Takase f11cc18921 Optimizing MSYS2 CI (#1435)
* feat(ci): install dependencies on setup
* fix(ci): don't update msys2 when setup
* fix(ci): download subprojects before patching
* doc(ci): document why meson subprojects download is called
2023-08-07 15:26:38 +01:00
Takase 112fe7bddd fix(CI): bump dependency versions (#1434)
* refactor(ci): use microsoft/setup-msbuild
* fix(ci): fix wrong option name for setup-msbuild
* fix(ci): bump setup-python version
* fix(lua-utf8-patch): enable support for windows vista and above
* fix(ci): use vs backend
* fix(ci): reconfigure project manually after patch
* fix(ci): add a separate build step
* fix(ci): use msvc-dev-cmd again
2023-08-07 15:26:38 +01:00
Takase b623ad9b35 fix: fix differing stacktrace on stdout and file (#1404)
* fix(c-bootstrap): produce identical stack traces
2023-08-07 15:26:38 +01:00
Jan 9fb714236b Add View dragging (#1402) 2023-08-07 15:26:38 +01:00
Takase d4ff3cb094 fix(windows-utf8-patch): fix os.getenv() not supporting UTF-8 output (#1397) 2023-08-07 15:26:38 +01:00
Takase 612ebebb1f Fix invalid EXEFILE and EXEDIR on Windows (#1396)
* fix(main): fix get_exe_filename returning invalid result on Windows
* fix(main): fix bootstrap not intepreting UTF-8 properly
2023-08-07 15:26:38 +01:00
Adam 9634715618 Added in support for foreground and background events. (#1395) 2023-08-07 15:26:38 +01:00
vqn 7f9030bfb4 add autocompletion to multicursor (#1394)
* use Doc:remove
2023-08-07 15:26:38 +01:00
vqn 6de18442be fix cursors positions when deleting multiple selections (#1393)
* correctly handle overlapping selections merge cursors in Doc:raw_remove
2023-08-07 15:26:38 +01:00
Adam acbd8715f4 Added in explicit touchscreen keyboard support. (#1389) 2023-08-07 15:26:38 +01:00
Guldoman ca6fedd3f7 Allow `tokenizer` to pause and resume in the middle of a line (#1444) 2023-08-07 15:26:38 +01:00
Adam b30ea9e9ef Disable `trimwhitespace` and `drawwhitespace` via their configs (#1446)
Instead of completely disabling them, we now use their internal toggle.

Also moved `drawwhitespace` commands inside the plugin.

---

* Fixed bug where commands would show even when plugin was disbled. Also removed antiquated way of disabling.

* Fixed typos.

* Also moved trimwhitespace out of config, if it already has a default enabled value of false.

* Changed documentation.

* Clarified comments.
2023-08-07 15:26:38 +01:00
Guldoman c1adfb55d2 Limit `core.threads` without a timeout to run 30 times per second 2023-08-07 15:26:38 +01:00
Adam Harrison c8977ca62b Made things clearer, as per jgm's suggestion. 2023-08-07 15:26:38 +01:00
Adam b348acaa81 Made coroutines make more sense, and fixed a bug. (#1381)
* Made coroutines make more sense, and fixed a bug.

* Fixed typo.

* Additional checking for off-cycles.

* Fixed issue with calling step too much.

* If we have no redraw, set next step time for next frame.

* Added in `now` variables to reduce calls.
2023-08-07 15:26:38 +01:00
Guldoman d0ec3aa0fe Don't calculate widths per-uft8-char when not needed (#1409) 2023-08-07 15:26:38 +01:00
Takase ef70faa2fd Asynchronous process reaping (#1412)
* refactor(process): introduce process_stream_handle separate from process_handle

* feat(process): introduce process_handle helper functions

* feat(process): add asynchronous process reaping

* feat(process): wait for shorter period if possible

* style(process): remove unecessary brackets

* style(process): fix parentheses

* refactor(process): remove useless setvbuf call

* style(process): remove unecessary value

* refactor(process): add size field into kill_list

* refactor(process): use SDL_Delay for sleeping

* style(process): remove trailing whitespace

* fix(main): destroy window before closing lua

* fix(process): check for timeout correctly

* refactor(process): remove unecessary if check

* refactor(process): remove size from the list

* fix(process): fix invalid delay calculation

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-08-07 15:26:37 +01:00
Guldoman 71e4adbd6f Aggregate `SDL_Surface`s and their scale in `RenSurface` (#1429) 2023-08-07 15:26:06 +01:00
Guldoman e66174f9d8 Make `TreeView` follow the current tab (#1411)
* Make `TreeView` follow the current tab

* Use `TreeView:toggle_expand` in `TreeView:set_selection_to_path`

We can't use `item.expanded` directly because we need to update the 
cached tree structure.
2023-08-07 15:26:06 +01:00
Guldoman 94e2df991c Use clipping functions provided by SDL (#1426) 2023-08-07 15:26:06 +01:00
Guldoman ca29728e34 Improve text width calculation precision (#1408)
In some extreme cases (~30000 chars) text width precision takes a hit.
Using double instead of float fixes that.
2023-08-07 15:25:46 +01:00
Takase d31f128ef9 refactor(main): move SetProcessDPIAware to manifests (#1413) 2023-08-07 15:25:46 +01:00
Guldoman 703b14170b Split `Command` struct into different structs for each command type (#1407)
This reduces the space needed for each command.
2023-08-07 15:25:30 +01:00
Takase d27bd6b14d Add manifest on Windows (#1405)
* fix(gitignore): add exclusion for manifest files

* feat(windows): add application manifest

* feat(build): use application manifest on windows

* refactor(build): use genrate_file to generate the manifest

* style(manifest): remove trailing whitespace
2023-08-07 15:25:30 +01:00
Guldoman 4784a32eed Use correct view for scrolling to `find-replace:repeat-find` results (#1400) 2023-08-07 15:25:30 +01:00
Adam 9284e92291 Added in ability to specify prefix via env variable. (#1388) 2023-08-07 15:25:30 +01:00
vqn 9b45c9bdbd fix incorrect x_offset if opened docs have different tab sizes (#1383) 2023-08-07 15:25:06 +01:00
Adam 1f0cdc6831 Fixed up some post 5.1/jit Symbols (#1385)
* Updated k functions to have appropriate method signatures for 5.3 and up.

* Fixed up some inconsistent signatures that I forgot.
2023-08-07 15:25:06 +01:00
Guldoman cd8ec70d78 Make tab scrolling more flexible (#1384)
* Add `Object:{is_class_of,is_extended_by}` to check inverse relationships

* Make tab scrolling more flexible

This adds tab scrolling commands and connects them to mouse scroll
events.
This way scrolling behavior can be customized more easily.

For example an alternative behavior could be:
```lua
keymap.add({
  ["wheelup"] = "root:switch-to-hovered-previous-tab",
  ["wheeldown"] = "root:switch-to-hovered-next-tab"
})
```
2023-08-07 15:25:06 +01:00
jgmdev 1fe0796a30 Correct the inverted tabs scrolling 2023-08-07 15:25:06 +01:00
Eric Gaudet 5502f9e0a8 Make mouse scrollwheel hovering tabs scroll the tab bar (#1314) 2023-08-07 15:25:06 +01:00
Guldoman 5f24108772 Allow groups to be used in end delimiter patterns in tokenizer (#1317)
* Allow empty groups as first match in tokenizer
* Avoid pushing tokens with empty strings
* Allow groups to be used in end delimiter in tokenizer
* Use the first entry of the type table for the middle part of a subsyntax
This applies to delimited matches with a table for `type` and without a
`syntax` field.
* Match only once if using `at_start` in tokenizer `find_text`
* Check if match is escaped in the "close" case too
Also allow continuing matching if the match was escaped.
2023-08-07 15:25:06 +01:00
Guldoman a0c05791b1 Improve `DocView:get_visible_line_range` precision (#1382) 2023-08-07 15:02:19 +01:00
Jefferson González 0e3b5935e6 plugins scale: also rescale style.expanded_scrollbar_size (#1380) 2023-08-07 15:02:19 +01:00
Jefferson González 68108aeff2 NagView: properly rescale on scale change (#1379)
* drop font option since style.font is always used
2023-08-07 15:02:19 +01:00
Jefferson González 7f91514d6a Restore in-selection replace as discussed in #1331 (#1368) 2023-08-07 15:02:19 +01:00
Jefferson González 1fcc69d7aa Improved plugins config table handling (#1356)
* Warns user if trying to disable a plugin that is already
  enabled when doing `config.plugins.plugin_name = false` and also
  prevents replacing the current plugin config table with the false
  value to mitigate runtime issues.
* Uses a merge strategy by default when assigning a table to a plugin
  config to prevent a user from removing a plugin default config values
  as experienced and explained on this issue lite-xl-plugins#158
* This change is basically backwards compatible, but will require a
  change on the settings ui plugin on how it checks for already
  enabled plugins, since rawget will no longer be a working hack
  or workaround for this.
* As suggested by Adam dropped loaded key and switched to package.loaded
2023-08-07 15:02:19 +01:00
Guldoman c40e19d9fe Make mod-version follow semver (#1036)
* Make mod-version follow semver
  Now plugins can optionally specify the minor and patch version they
support.
  If no minor or patch version is specified, it's considered 0.
  Plugins are only loaded if they have the same major version and a
  smaller or equal minor+patch version.
* Add modversion to logging and plugin mismatch nagview

---------

Co-authored-by: Jefferson González <jgmdev@gmail.com>
2023-08-07 15:02:19 +01:00
Takase 0423611a99 fix: exec() error not returned to parent (#1363)
* fix: exec() error not returned to parent

* chore: remove accidental lua.h inclusion
2023-08-07 15:02:06 +01:00
vqn 6fc9aebae0 Context menu fixes and keyboard navigation (#1338)
* fix Doc contextmenu not registering commands if scale plugin is not found
* fix TreeView contextmenu commands not working if the mouse hovers DocView
* add keyboard navigation to TreeView contextmenu
* fix incorrect contextmenu predicate
2023-08-07 15:02:06 +01:00
Himura Kazuto 623419adfa Replace globally when replacing from selection (#1331) 2023-08-07 15:02:06 +01:00
sammyette 06cb09cb93 feat: add option to only draw whitespace if it is within selection (#1312)
* refactor: remove sort_positions usage
* refactor: move draw conditional to has_any_selection and other changes
  - snake case (sssss)
  - break after finding selection
* fix: typo of config plugins
* fix: do check for show selected only properly
* feat: only draw within selection per substitution
* `drawwhitespace`: Make `show_selected_only` work properly

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-08-07 15:02:06 +01:00
Adam 389449853a Abstracted open_doc out to allow for more easy overriding. (#1344) 2023-08-07 15:02:06 +01:00
Adam 1378c3d6a7 Getting rid of annoying forward slash on windows. (#1345) 2023-08-07 15:02:06 +01:00
jgmdev 8fb7a2d140 gh workflow: fix path to macOS arm64 cross file 2023-08-07 15:02:06 +01:00
Jefferson González 763d727874 ci: fix msys build now requiring ca-certificates (#1348)
Thanks to Guldoman who discovered the cause for meson failing to
validate SSL certificates which turned out to be MSYS now requiring
ca-certificates package installed for the different architectures.
2023-08-07 15:02:06 +01:00
Jan 66815b24b0 pass RenWindow by argument (#1321)
* pass RenWindow to all renderer functions that need it

* pass RenWindow to all rencache functions that need it
2023-08-07 15:02:06 +01:00
Adam Harrison 30c9c52426 Added missing header declaration. 2023-08-07 15:01:50 +01:00
Jefferson González b1e52bb9d4 plugin api: allow usage on multiple source files (#1335)
As discussed with Adam on discord current Lite XL Lua Plugin API was not
working on native plugins with more than 1 source file since imported
symbols were not exposed to other unit files. The issue was tackled on #1332
but the solution introduced another issue when Lite XL was dynamically
linked to the system lua. So we opted to tackle this by using function
wrappers around the function pointers.
2023-08-07 15:01:50 +01:00
Adam 9b0a348a91 Added in Config Postload (#1336)
* Added in an `onload` variable to configs which is called by the plugin loader.

* Used appropriate parameter.

* Fixed tabbing.
2023-08-07 15:01:50 +01:00
Guldoman 920d3ef1e3 Avoid drawing hidden text in `DocView:draw_line_text` (#1298)
* Stop drawing text past the `DocView` edge in `DocView:draw_line_text`

* Don't add draw commands if they fall outside the latest clip

The check was previously done with the window rect, so this will reduce 
a bit more the number of commands sent.
2023-08-07 15:01:50 +01:00
Jan d062c9e593 remove static libgcc from meson (#1290) 2023-08-07 15:01:50 +01:00
Adam Harrison b676314b1a Updated dummy method signature to match prototypes. 2023-08-07 15:01:50 +01:00
Guldoman 757b906ca2 Make empty groups in `regex.gmatch` return their offset (#1325)
This makes `regex.gmatch` behave like `string.gmatch`.
2023-08-07 15:01:50 +01:00
xwii 90a7882ed4 Use `table.move` to implement `common.splice` (#1324)
* Use `table.move` to implement `common.splice`

* Disallow negative `remove` in `common.splice`
2023-08-07 15:01:50 +01:00
Merlin Volkmer e52502b388 language_md: add nix code block highlighting (#1323) 2023-08-07 15:01:50 +01:00
adityaraj 9a831cb206 Create Renderer Only When It Doesn't Exist (#1315) 2023-08-07 15:01:50 +01:00
Jan 4d35dc4969 replace uses of SDL_Window with RenWindow (#1319)
Since Renwindow contains our instance of SDL_Window we can use this
to simplify future logic to create separate window instances
2023-08-07 15:01:31 +01:00
Takase 8bd6244add Reorganize resources/ + wasm target (#1244)
* add README.md to resources directory
* add cross/ directory for meson cross files
* fix readme list syntax error
* fix reflink
* disable ASYNCIFY_ADVISE by default
* use executable names instead of hardcoding paths
2023-08-07 15:01:06 +01:00
Takase 8c8635146e ci(release): use lite-xl org (#1571) 2023-08-07 14:51:14 +01:00
Guldoman 6d5c6051cd Make `DocView` aware of scrollbars sizes (#1177)
* Make `DocView:scroll_to_make_visible` aware of vertical scrollbar width

* Make `DocView` aware of horizontal scrollbar size
2023-08-07 14:51:14 +01:00
Adam Harrison 6bb9a89a8b Updated README.md as per PR comittee meeting #8. 2023-08-07 14:51:14 +01:00
Shreyas A S 02e421149b Updating the *Installing prebuild* section in README.md (#1548)
Sub sections that I've updated:
*To run lite-xl without installing:*
*To install lite-xl copy files over into appropriate directories:*

I think the directory structure of prebuilt packages has changed since when README.md was last updated. I've just updated it. Just that.

Co-authored-by: Shreyas A S <137637016+shreyasastech@users.noreply.github.com>
2023-08-07 14:51:14 +01:00
Guldoman 16bfa6d958 Use proper timeouts for coroutines that don't need to wait (#1467) 2023-08-07 14:51:14 +01:00
Delta-official 9c9f2dace0 Normalize stroke before adding keybind (#1334)
* Normalize stroke before adding keybind

* improve normalization algorithm and implement normalization in several functions

Signed-off-by: delta <darkussdelta@gmail.com>

---------

Signed-off-by: delta <darkussdelta@gmail.com>
2023-08-07 14:51:14 +01:00
Takase 8daf7dc926 feat(src/renderer): unify fontgroup baseline (#1560)
* feat(src/renderer): unify fontgroup baseline

* fix(src/renderer): use the first font's baseline for the text run
2023-08-07 14:51:14 +01:00
Adam 5145194f1f Fixed things for when a thread requests a redraw. (#1570)
* Fixed things for when a thread requests a redraw.

* @guldoman's changes.

* Whoops.
2023-08-07 14:51:14 +01:00
Takase 6ad67c18f0 Documentation for core.command (#1564)
* docs(core.command): add documentation

* fix(core.common): fix warnings

* docs(core.command): add "core." prefix to all custom types

* docs(core.command); add name as parameter to command.perform

* docs(core.command): fix typo and wording

* docs(core.command): add disclaimer to command.generate_predicate

* docs(core.command): fix wording for predicate

* docs(core.command): document command name

* docs(core.command): document the always_true predicate
2023-08-07 14:51:14 +01:00
Takase 7ba1d1ba8e docs(system): fix missing parameter for system.path_compare (#1566)
* docs(system): fix missing parameter for system.path_compare

* docs(system): fix missing parameter in function prototype
2023-08-07 14:51:14 +01:00
Takase ac3630c2ea Documentation for core.common (#1510)
* docs(core.common): add and improve documentation

* refactor(core.common): remove unused variable to get_height()

* docs(core.common): remove messy newlines

* docs(core.common): fix wording

* docs(core.common): use integer instead of number

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

* docs(core.common): update docs

the docs now follow the style in docs/ directory.
some of the changes suggested are also implemented.

* docs(core.common): fix typo

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

* docs(core.common): restyle annoatations

Extra whitespaces are removed and @overload is used whenever possible.

* docs(core.common): fix various documentation errors

* docs(core.common): simplify unicode description

* docs(core.common): fix return value

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

* docs(core.common): clarify common.bench for not being a benchmark

* docs(common): add disclaimer for numbers in common.serialize

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-08-07 14:51:14 +01:00
Adam c470683005 Updated extension for mac. (#1563) 2023-08-07 14:51:14 +01:00
Takase 8c9620aaed feat(src/renderer): stream fonts with SDL_RWops on all platforms (#1555)
* feat(src/renderer): stream fonts with SDL_RWops on all platforms

This fixes #1529 where the font itself carries the font file, which gets copied around.
This commit streams the file, so the file is not entirely in memory.

* style(src/renderer): use standard C types

* refactor(src/renderer): implement FT_Stream.close

* fix(src/renderer): fix SDL_RWops double free
2023-08-07 14:51:14 +01:00
Guldoman 0ebf3c0393 Return state when tokenizing plaintext syntaxes 2023-08-07 14:51:14 +01:00
Takase 74d9c15736 style(src/renderer): use FreeType header names (#1554) 2023-08-07 14:51:14 +01:00
Guldoman 3775032c78 Allow setting custom glyphset size (#1542)
* Properly set glyphset size

* Rename `MAX_GLYPHSET` to `GLYPHSET_SIZE`

* Use more appropriate types for font metrics
2023-08-07 14:51:14 +01:00
Guldoman ca3acd4ee9 Skip checking `files` if no filename was provided to `syntax.get` 2023-08-07 14:51:14 +01:00
Luke aka SwissalpS 0e5f35ff9f Fix #1538 log scrolls automatically (the real PR) (#1546)
* fix #1538 log scrolls automatically

adds:
- when user scrolls, position is kept no matter how many new entries
arrive
- when user scrolls up to last entry, autoscroll is enabled again

does not add buttons to jump up/down
see #1538

* move scroll-test out of on_mouse_wheel

* determine diff_index with loop

* remove check at move_towards yoffset

* use while loop instead of repeat loop

* remove meaningless setter

* remove stray var
2023-08-07 14:51:14 +01:00
Luke aka SwissalpS 39319e2ce9 comment typo in object.lua (#1541) 2023-08-07 14:51:14 +01:00
Jan 6e113cb15e Attach command buffer to Renderer Window (#1472) 2023-08-07 14:50:59 +01:00
Guldoman 9bb6589790 Increase number of loadable glyphsets (#1524)
This should be enough to load every unicode codepoint.
2023-08-07 14:50:59 +01:00
Jan e6c7001b5a Add top tab margins (#1479)
adapted from #810 to allow styles to decide upon the top margin of the tab list
2023-08-07 14:50:59 +01:00
Guldoman 94b4825754 Show cursor at the start of the next line when selecting full lines (#761)
This was the previous behavior that regressed with the keymap clicks.

This also better shows that the selection extends to the next line.
2023-08-07 14:50:59 +01:00
Guldoman 12a552931e Make `Doc:sanitize_position` return a more appropriate `col` (#1469)
If `line` is out of range, return the `col` "closest" to the original
values.
2023-08-07 14:50:59 +01:00
Guldoman ff38e449d1 Revert "core syntax: strip the path from filename on syntax.get (#1168)" (#1322)
* Revert "core syntax: strip the path from filename on syntax.get (#1168)"

This reverts commit af6c4bc152.

The previous behavior was correct and allowed access to the full path for path-dependant syntaxes.

* Use `Doc.abs_filename` to obtain syntax when possible

This allows matching full paths in language syntaxes, but we lose the
possibility of matching the project root.
2023-08-07 14:50:59 +01:00
Guldoman 122b72ed90 Change AppID (#1187)
This ID reflects our domain (lite-xl.com).
2023-08-07 14:50:59 +01:00
Guldoman f80a4563be When logging don't use `core.status_view` if not yet initialized 2023-08-07 14:50:59 +01:00
Guldoman 862aba0f91 Mark `linewrapping` `open_files` table as weak
We weren't correctly garbage-collecting `Doc`s, so we had `Highlighter`s 
stay alive over their due time.
2023-08-07 14:50:59 +01:00
Guldoman 528e5641fb Add mouse grab (#1501)
* Add mouse grab

We now also send mouse movement events only to the interested view.

* Add deprecation messages handler

* Make various `View`s respect `on_mouse_left`

* `StatusView`
* `TitleView`
* `TreeView`
* `ToolbarView`

* Fix scrollbar in `TreeView` not updating

We were in some cases sending outdated mouse positions to the scrollbar, 
which made it think that the mouse was hovering it.

This also updates the hovered item more responsively during scroll.
2023-08-07 14:50:59 +01:00
Jefferson González 35647067d8 Close lua state when exiting on a runtime error (#1487)
* Close lua state when exiting on a runtime error

* This change allows calling the garbage collector before exiting the
  application for a cleaner shutdown.
* Components like the shared memory object on #1486 will have a better
  chance at destroying no longer needed resources.

* Overriden os.exit to always close the state

* Allow setting close param on os.exit override

* Simplified the os.exit override a bit more

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-08-07 14:50:59 +01:00
Jefferson González ba753593f3 Move lineguide below blinking cursor, fixes #1488 (#1511)
* Move lineguide below blinking cursor, fixes #1488

* Added config_spec custom color
2023-08-07 14:50:59 +01:00
takase1121 363b102abc fix(renderer): fix memory leak when freeing glyphsets 2023-08-07 14:50:59 +01:00
takase1121 252bf87ead docs(system): make all parameters for set_window_hit_test optional 2023-08-07 14:50:59 +01:00
Jefferson González 431c8f4a36 detectindent: fix wrong detection reported by Adam (#1500)
* The comment patterns had to come before the string ones
* The smallest indentation size is now taken into consideration even if
  it only occurs once, we just make sure its size is more than 1 space.
2023-08-07 14:50:59 +01:00
Adam 21db8313c1 Allowed for overrides of toolbar items, so plugins can add things if they want to with different fonts. (#1157) 2023-08-07 14:50:59 +01:00
Guldoman e7168a1e00 Restore horizontal scroll position after scale change (#494)
* Restore horizontal scroll position after scale change

* Consider `View` horizontal size when restoring horizontal scroll

This is needed because `View:get_h_scrollable_size` includes the 
horizontal size, while `View.scroll.x` doesn't.
2023-08-07 14:50:59 +01:00
takase1121 c255e53d37 feat(bootstrap): return error string from C searcher 2023-08-07 14:50:59 +01:00
takase1121 a007a190ef fix(rencache): fix compiler warning for printing size_t 2023-08-07 14:50:59 +01:00
Adam Harrison 6714732222 Fixing linewrapping bug to do with wordwrapping. 2023-08-07 14:50:59 +01:00
Adam aa2ac0a4ce Added in double-clicking on emptyview and tab bar. (#1478)
* Added in double-clicking on emptyview and tab bar.

* Fixed issue with split tabs.

* Early exit if no overlapping node.

* Changed category of command to tabbar.

* Additional cleanup.

* Changed for whether we should show tabs.

* Fixed erroneous hover.
2023-08-07 14:50:59 +01:00
takase1121 2090afccca ci(build): update action dependencies 2023-08-07 14:50:59 +01:00
Takase 46260b8073 fix(process): check for HANDLE_INVALID (#1475) 2023-08-07 14:50:59 +01:00
Jan 0fae012c68 Make `system.path_compare` more digit-aware (#1474)
This allows a human friendly sorting filenames with numbers in them
So
- asd1
- asd10
- asd2
becomes
- asd1
- asd2
- asd10
2023-08-07 14:50:59 +01:00
jgmdev ff4364b0ff StatusView compat fix with older Lua runtimes 2023-08-07 14:50:59 +01:00
Takase e2a582d5fd Process API improvements (again) (#1370)
* feat(process): add push_error
* refactor(process): use push_error for better errors
* style(process): consistent error messages
* refactor(process): reimplement process.strerror() with push_error
* refactor(process): implement close_fd only once
* refactor(process): rename process_handle to process_handle_t
* fix(process): prevent errors from a NULL error message
* refactor(process): refactor push_error into 2 functions
* fix(process): fix wrong error message
* fix(process): check if push_error_string actually pushed something
* refactor(process): make error messages descriptive
* fix(process): check for empty table instead of aborting
* refactor(process): make error messages descriptive on Windows
* refactor(process): rename process_stream_handle to process_stream_t
* refactor(process): fix wrong usage of process_handle_t
* fix(process): fix wrong type name
* refactor(process): incoporate kill_list_thread into process_kill_list_t
* refactor(process): make kill_list per-state data
2023-08-07 14:50:59 +01:00
Takase 3c60c1c7f1 Build releases with Ubuntu 18.04 container (#1460)
* ci(release): try using lite-xl-build-box

* ci(build): test with my own fork

* ci(build): do not install python via actions

* ci(build): disable package updates

* fix(scripts/appimage.sh): add workaround for non-FUSE environments

* ci(build): document why the actions are disabled

* ci(release): fix typo
2023-08-07 14:50:58 +01:00
Guldoman 532d3a6572 Merge carets after `doc:move-to-{previous,next}-char` (#1462) 2023-08-07 14:50:58 +01:00
Takase 7f75619aa2 refactor(plugin_api): move the header into include/ (#1440) 2023-08-07 14:50:58 +01:00
Guldoman 0be18493a9 Show error message in crash message box (#1461)
* Save to `error.txt` the same traceback shown on stdout
* Show error message in crash message box
2023-08-07 14:50:58 +01:00
Takase 6ad288aa39 Cross compiling improvements + macOS universal binary (#1458)
* chore(resources): rename macos_arm64.txt to macos-arm64.txt
   This matches the platform-arch convention like many other parts of the project.
* chore(resources/cross): rename wasm.txt to unknown-wasm32.txt
* refactor(scripts/common.sh): use parameter expansion instead of if else
* feat(scripts/common.sh): support custom arch and platform for get_default_build_dir
* feat(scripts/build.sh): add --cross-platform, --cross-arch and --cross-file
* feat(scripts/package.sh): add --cross-platform and --cross-arch
* feat(build-packages.sh): add support for new options in build.sh and packages.sh
* ci(build): make arm64 binaries in CI
* ci(build): do not install external libraries
* ci(build): fix invalid artifact name
* ci(build): fix INSTALL_NAME
* ci(build): change name for macos artifacts
* ci(build): add script to build universal dmgs from individual dmgs
* ci(build): build universal dmgs
* fix(make-universal-binaries): fix wrong path for hdiutil
* ci(build): rename macos action
* fix(make-universal-binaries.sh): fix wrong pathname for ditto
* ci(release): build macos universal binaries
* ci(release): remove useless variables
* ci(release): fix wrong dependency
* ci(build): fix old ubuntu version
   This version will be restored once I complete some container-specific fixes.
* ci(build): make build_macos_universal depend on release
* ci(build): fix wrong dmg dir
* style(ci): capitalize 'universal' for CI name
* fix(make-universal-binaries.sh): fix truncated dmg name when it contains dots
* ci: styling changes
* ci(release): install appdmg only
2023-08-07 14:50:58 +01:00
Takase c2357721e5 upgrade header files to Lua 5.4 (#1436)
* refactor(native_api_header): upgrade header files to Lua 5.4.

Almost all of the symbols in this file was from 5.2. This will obviously
not work because some function signatures have changed and some have
completely wrong return values, etc.
This commit updates the header files to Lua 5.4 based on the source code
and changes a few things.

* refactor(plugin_api): move the header into include/

* fix(lite_xl_plugin_api.h): include stdlib to avoid errors with exit

* refactor(lite_xl_plugin_api.h): do not return in SYMBOL_WRAP_CALL

* fix(lite_xl_plugin_api.h): fix wrong way of passing varargs

* fix(lite_xl_plugin_api.h): fix differing lua_rawlen definition

* fix(lite_xl_plugin_api.h): fix fallback function signature

* fix(lite_xl_plugin_api.h): fix conversion from void * to function pointer
2023-08-07 14:50:58 +01:00
Jefferson González baa8f528f1 Fix for api_require wrong macro && conditions (#1465)
This mistake escaped my eyes when reviewing #1437 and causes
some symbols to not be exported, because the preprocessor macros
are expecting multiple LUA versions to evaluate as true at once.
The fix is to replace `&&` with `||`.
2023-08-07 14:50:58 +01:00
sammyette 2978037f51 feat: add statusview item to show selections (#1445) 2023-08-07 14:50:58 +01:00
vqn 7aa1217878 #1393 followup (#1463)
* Fix incorrect check in doc:raw_remove

Restore caret position on command doc:cut

* merge cursors and fix new line in clipboard

* add new line to the last copied line
2023-08-07 14:50:58 +01:00
Guldoman 0ee346014e Make `system.path_compare` more case-aware (#1457)
* Use Lua-provided string lengths for `system.path_compare`
* Make `system.path_compare` more case-aware
   Before, strings like `README.md` would be sorted before `changelog.md`, 
   because we only looked at the raw ascii values.
   Now the character case is considered as a secondary sorting key.
2023-08-07 14:50:58 +01:00
Takase 4e626bc320 Update api_require to expose more symbols (#1437)
* feat(system): update api_require for more symbols
* fix(system): fix missing 5.1 symbols
* fix(system): add more missing symbols
* fix(system): add all symbols
   We got'em this time. I swear.
* fix(system): fix undefined symbols due to conditional compilation
   There is only pain and suffering.
   Turns out some of the symbols are only exported when the options are enabled.
   We need to preprocess the header.
2023-08-07 14:50:58 +01:00
Takase c133c39e92 Optimizing MSYS2 CI (#1435)
* feat(ci): install dependencies on setup
* fix(ci): don't update msys2 when setup
* fix(ci): download subprojects before patching
* doc(ci): document why meson subprojects download is called
2023-08-07 14:50:58 +01:00
Takase ac9ca96698 fix(CI): bump dependency versions (#1434)
* refactor(ci): use microsoft/setup-msbuild
* fix(ci): fix wrong option name for setup-msbuild
* fix(ci): bump setup-python version
* fix(lua-utf8-patch): enable support for windows vista and above
* fix(ci): use vs backend
* fix(ci): reconfigure project manually after patch
* fix(ci): add a separate build step
* fix(ci): use msvc-dev-cmd again
2023-08-07 14:50:58 +01:00
Takase 26ff5e28a6 fix: fix differing stacktrace on stdout and file (#1404)
* fix(c-bootstrap): produce identical stack traces
2023-08-07 14:50:58 +01:00
Jan 662fde364b Add View dragging (#1402) 2023-08-07 14:50:58 +01:00
Takase bc2c433b00 fix(windows-utf8-patch): fix os.getenv() not supporting UTF-8 output (#1397) 2023-08-07 14:50:58 +01:00
Takase e935454992 Fix invalid EXEFILE and EXEDIR on Windows (#1396)
* fix(main): fix get_exe_filename returning invalid result on Windows
* fix(main): fix bootstrap not intepreting UTF-8 properly
2023-08-07 14:50:58 +01:00
Adam 7ca0ec18ca Added in support for foreground and background events. (#1395) 2023-08-07 14:50:58 +01:00
vqn 449f7d66c3 add autocompletion to multicursor (#1394)
* use Doc:remove
2023-08-07 14:50:58 +01:00
vqn 46ea86e28c fix cursors positions when deleting multiple selections (#1393)
* correctly handle overlapping selections merge cursors in Doc:raw_remove
2023-08-07 14:50:58 +01:00
Adam ee80b451c6 Added in explicit touchscreen keyboard support. (#1389) 2023-08-07 14:50:58 +01:00
Guldoman 1c8c569fae Allow `tokenizer` to pause and resume in the middle of a line (#1444) 2023-08-07 14:50:58 +01:00
Adam 1becf35508 Disable `trimwhitespace` and `drawwhitespace` via their configs (#1446)
Instead of completely disabling them, we now use their internal toggle.

Also moved `drawwhitespace` commands inside the plugin.

---

* Fixed bug where commands would show even when plugin was disbled. Also removed antiquated way of disabling.

* Fixed typos.

* Also moved trimwhitespace out of config, if it already has a default enabled value of false.

* Changed documentation.

* Clarified comments.
2023-08-07 14:50:58 +01:00
Guldoman b005454652 Limit `core.threads` without a timeout to run 30 times per second 2023-08-07 14:50:58 +01:00
Adam Harrison 73ff025552 Made things clearer, as per jgm's suggestion. 2023-08-07 14:50:58 +01:00
Adam 7f0651155d Made coroutines make more sense, and fixed a bug. (#1381)
* Made coroutines make more sense, and fixed a bug.

* Fixed typo.

* Additional checking for off-cycles.

* Fixed issue with calling step too much.

* If we have no redraw, set next step time for next frame.

* Added in `now` variables to reduce calls.
2023-08-07 14:50:58 +01:00
Guldoman 146dca9188 Don't calculate widths per-uft8-char when not needed (#1409) 2023-08-07 14:50:58 +01:00
Takase 350131dabc Asynchronous process reaping (#1412)
* refactor(process): introduce process_stream_handle separate from process_handle

* feat(process): introduce process_handle helper functions

* feat(process): add asynchronous process reaping

* feat(process): wait for shorter period if possible

* style(process): remove unecessary brackets

* style(process): fix parentheses

* refactor(process): remove useless setvbuf call

* style(process): remove unecessary value

* refactor(process): add size field into kill_list

* refactor(process): use SDL_Delay for sleeping

* style(process): remove trailing whitespace

* fix(main): destroy window before closing lua

* fix(process): check for timeout correctly

* refactor(process): remove unecessary if check

* refactor(process): remove size from the list

* fix(process): fix invalid delay calculation

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-08-07 14:50:58 +01:00
Guldoman 1d86665b6d Aggregate `SDL_Surface`s and their scale in `RenSurface` (#1429) 2023-08-07 14:50:58 +01:00
Guldoman 3739bf0186 Make `TreeView` follow the current tab (#1411)
* Make `TreeView` follow the current tab

* Use `TreeView:toggle_expand` in `TreeView:set_selection_to_path`

We can't use `item.expanded` directly because we need to update the 
cached tree structure.
2023-08-07 14:50:58 +01:00
Guldoman 4f1360a6c5 Use clipping functions provided by SDL (#1426) 2023-08-07 14:50:58 +01:00
Guldoman 7907fa785c Improve text width calculation precision (#1408)
In some extreme cases (~30000 chars) text width precision takes a hit.
Using double instead of float fixes that.
2023-08-07 14:50:58 +01:00
Takase 193871869d refactor(main): move SetProcessDPIAware to manifests (#1413) 2023-08-07 14:50:58 +01:00
Guldoman dbb9f30c81 Split `Command` struct into different structs for each command type (#1407)
This reduces the space needed for each command.
2023-08-07 14:50:58 +01:00
Takase 2d2d715fd9 Add manifest on Windows (#1405)
* fix(gitignore): add exclusion for manifest files

* feat(windows): add application manifest

* feat(build): use application manifest on windows

* refactor(build): use genrate_file to generate the manifest

* style(manifest): remove trailing whitespace
2023-08-07 14:50:58 +01:00
Guldoman e1f92683bc Use correct view for scrolling to `find-replace:repeat-find` results (#1400) 2023-08-07 14:50:58 +01:00
Adam 83c27cf9f4 Added in ability to specify prefix via env variable. (#1388) 2023-08-07 14:50:58 +01:00
vqn 9b4a86f763 fix incorrect x_offset if opened docs have different tab sizes (#1383) 2023-08-07 14:50:58 +01:00
Adam b9cc661a84 Fixed up some post 5.1/jit Symbols (#1385)
* Updated k functions to have appropriate method signatures for 5.3 and up.

* Fixed up some inconsistent signatures that I forgot.
2023-08-07 14:50:58 +01:00
Guldoman 7e0ddf2817 Make tab scrolling more flexible (#1384)
* Add `Object:{is_class_of,is_extended_by}` to check inverse relationships

* Make tab scrolling more flexible

This adds tab scrolling commands and connects them to mouse scroll
events.
This way scrolling behavior can be customized more easily.

For example an alternative behavior could be:
```lua
keymap.add({
  ["wheelup"] = "root:switch-to-hovered-previous-tab",
  ["wheeldown"] = "root:switch-to-hovered-next-tab"
})
```
2023-08-07 14:50:58 +01:00
jgmdev 51ab72f715 Correct the inverted tabs scrolling 2023-08-07 14:50:58 +01:00
Eric Gaudet 911eb325cc Make mouse scrollwheel hovering tabs scroll the tab bar (#1314) 2023-08-07 14:50:58 +01:00
Guldoman d9925b7d44 Allow groups to be used in end delimiter patterns in tokenizer (#1317)
* Allow empty groups as first match in tokenizer
* Avoid pushing tokens with empty strings
* Allow groups to be used in end delimiter in tokenizer
* Use the first entry of the type table for the middle part of a subsyntax
This applies to delimited matches with a table for `type` and without a 
`syntax` field.
* Match only once if using `at_start` in tokenizer `find_text`
* Check if match is escaped in the "close" case too
Also allow continuing matching if the match was escaped.
2023-08-07 14:50:58 +01:00
Guldoman be5d23557d Improve `DocView:get_visible_line_range` precision (#1382) 2023-08-07 14:50:58 +01:00
Jefferson González ce9d540e92 plugins scale: also rescale style.expanded_scrollbar_size (#1380) 2023-08-07 14:50:58 +01:00
Jefferson González 84039331a5 NagView: properly rescale on scale change (#1379)
* drop font option since style.font is always used
2023-08-07 14:50:58 +01:00
Jefferson González 1c2571bad7 Restore in-selection replace as discussed in #1331 (#1368) 2023-08-07 14:50:58 +01:00
Jefferson González d68583b688 Improved plugins config table handling (#1356)
* Warns user if trying to disable a plugin that is already
  enabled when doing `config.plugins.plugin_name = false` and also
  prevents replacing the current plugin config table with the false
  value to mitigate runtime issues.
* Uses a merge strategy by default when assigning a table to a plugin
  config to prevent a user from removing a plugin default config values
  as experienced and explained on this issue lite-xl-plugins#158
* This change is basically backwards compatible, but will require a
  change on the settings ui plugin on how it checks for already
  enabled plugins, since rawget will no longer be a working hack
  or workaround for this.
* As suggested by Adam dropped loaded key and switched to package.loaded
2023-08-07 14:50:58 +01:00
Guldoman 57cd4e2949 Make mod-version follow semver (#1036)
* Make mod-version follow semver
  Now plugins can optionally specify the minor and patch version they 
support.
  If no minor or patch version is specified, it's considered 0.
  Plugins are only loaded if they have the same major version and a 
  smaller or equal minor+patch version.
* Add modversion to logging and plugin mismatch nagview

---------

Co-authored-by: Jefferson González <jgmdev@gmail.com>
2023-08-07 14:50:58 +01:00
Takase ab3d6004a1 fix: exec() error not returned to parent (#1363)
* fix: exec() error not returned to parent

* chore: remove accidental lua.h inclusion
2023-08-07 14:50:58 +01:00
vqn b634b61866 Context menu fixes and keyboard navigation (#1338)
* fix Doc contextmenu not registering commands if scale plugin is not found
* fix TreeView contextmenu commands not working if the mouse hovers DocView
* add keyboard navigation to TreeView contextmenu
* fix incorrect contextmenu predicate
2023-08-07 14:50:58 +01:00
Himura Kazuto aef400bc90 Replace globally when replacing from selection (#1331) 2023-08-07 14:50:58 +01:00
sammyette 3f917dcb45 feat: add option to only draw whitespace if it is within selection (#1312)
* refactor: remove sort_positions usage
* refactor: move draw conditional to has_any_selection and other changes
  - snake case (sssss)
  - break after finding selection
* fix: typo of config plugins
* fix: do check for show selected only properly
* feat: only draw within selection per substitution
* `drawwhitespace`: Make `show_selected_only` work properly

---------

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2023-08-07 14:50:58 +01:00
Adam 4ef4b99c7a Abstracted open_doc out to allow for more easy overriding. (#1344) 2023-08-07 14:50:58 +01:00
Adam f74716b436 Getting rid of annoying forward slash on windows. (#1345) 2023-08-07 14:50:58 +01:00
jgmdev 8fbc843260 gh workflow: fix path to macOS arm64 cross file 2023-08-07 14:50:58 +01:00
Jefferson González ccceb2a65c ci: fix msys build now requiring ca-certificates (#1348)
Thanks to Guldoman who discovered the cause for meson failing to
validate SSL certificates which turned out to be MSYS now requiring
ca-certificates package installed for the different architectures.
2023-08-07 14:50:58 +01:00
Jan 16182d01d8 pass RenWindow by argument (#1321)
* pass RenWindow to all renderer functions that need it

* pass RenWindow to all rencache functions that need it
2023-08-07 14:50:58 +01:00
Adam Harrison de06dcc5bb Added missing header declaration. 2023-08-07 14:50:43 +01:00
Jefferson González 9dcdf1f7c9 plugin api: allow usage on multiple source files (#1335)
As discussed with Adam on discord current Lite XL Lua Plugin API was not
working on native plugins with more than 1 source file since imported
symbols were not exposed to other unit files. The issue was tackled on #1332
but the solution introduced another issue when Lite XL was dynamically
linked to the system lua. So we opted to tackle this by using function
wrappers around the function pointers.
2023-08-07 14:50:43 +01:00
Adam 4682092b8d Added in Config Postload (#1336)
* Added in an `onload` variable to configs which is called by the plugin loader.

* Used appropriate parameter.

* Fixed tabbing.
2023-08-07 14:50:43 +01:00
Guldoman 12bae1ec95 Avoid drawing hidden text in `DocView:draw_line_text` (#1298)
* Stop drawing text past the `DocView` edge in `DocView:draw_line_text`

* Don't add draw commands if they fall outside the latest clip

The check was previously done with the window rect, so this will reduce 
a bit more the number of commands sent.
2023-08-07 14:50:43 +01:00
Jan 64065b98ca remove static libgcc from meson (#1290) 2023-08-07 14:50:43 +01:00
Adam Harrison bdd87298d6 Updated dummy method signature to match prototypes. 2023-08-07 14:50:43 +01:00
Guldoman b58ba3fede Make empty groups in `regex.gmatch` return their offset (#1325)
This makes `regex.gmatch` behave like `string.gmatch`.
2023-08-07 14:50:43 +01:00
xwii e4c5fceaf9 Use `table.move` to implement `common.splice` (#1324)
* Use `table.move` to implement `common.splice`

* Disallow negative `remove` in `common.splice`
2023-08-07 14:50:43 +01:00
Merlin Volkmer 75b6173dc9 language_md: add nix code block highlighting (#1323) 2023-08-07 14:50:43 +01:00
adityaraj 8c4f093c41 Create Renderer Only When It Doesn't Exist (#1315) 2023-08-07 14:50:43 +01:00
Jan c44a3cd291 replace uses of SDL_Window with RenWindow (#1319)
Since Renwindow contains our instance of SDL_Window we can use this
to simplify future logic to create separate window instances
2023-08-07 14:50:43 +01:00
Takase 3edd53a835 Reorganize resources/ + wasm target (#1244)
* add README.md to resources directory
* add cross/ directory for meson cross files
* fix readme list syntax error
* fix reflink
* disable ASYNCIFY_ADVISE by default
* use executable names instead of hardcoding paths
2023-08-07 14:50:43 +01:00
George Sokianos 9134c115fe Prepare 2.1.1r2 release 2023-05-14 11:03:41 +01:00
194 changed files with 14531 additions and 2130 deletions

View File

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

View File

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

3
.gitignore vendored
View File

@ -22,9 +22,10 @@ LiteXL*
lite lite
.config/ .config/
*.lha *.lha
release_files
*.o *.o
*.snalyzerinfo *.snalyzerinfo
!resources/windows/*.diff !resources/windows/*.diff
!resources/windows/*.exe.manifest.in
!resources/macos/*.py

View File

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

View File

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

View File

@ -9,21 +9,19 @@ LiteXL_OBJ := \
src/api/api.o src/api/dirmonitor.o \ src/api/api.o src/api/dirmonitor.o \
src/api/regex.o src/api/renderer.o src/api/system.o \ src/api/regex.o src/api/renderer.o src/api/system.o \
src/api/utf8.o src/platform/amigaos4.o \ src/api/utf8.o src/platform/amigaos4.o \
src/api/dirmonitor/os4.o src/api/dirmonitor/os4.o src/platform/codesets.o
outfile := lite-xl outfile := lite-xl
compiler := gcc-11 compiler := gcc
cxxcompiler := g++-11 cxxcompiler := g++
INCPATH := -Isrc -I/sdk/local/newlib/include/SDL2 \ INCPATH := -Isrc -I/sdk/local/newlib/include/SDL2 \
-I/sdk/local/common/include/lua54 -I/sdk/local/common/include/freetype2 -I/sdk/local/common/include/lua54 -I/sdk/local/common/include/freetype2
DFLAGS += -D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR DFLAGS += -D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR
CFLAGS ?= -Werror -Wwrite-strings -O3 -std=gnu11 -fno-strict-aliasing
CFLAGS += -Werror -Wwrite-strings -O3 -std=gnu11 -fno-strict-aliasing LFLAGS ?= -mcrt=newlib -lpcre2-8 -lSDL2 -llua54 -lfreetype -lpng -lz \
-lpthread -athread=native
LFLAGS += -mcrt=newlib -lauto \
-lpcre2 -lSDL2 -llua54 -lfreetype -lz -lm -lpthread -athread=native
ifeq ($(DEBUG),1) ifeq ($(DEBUG),1)
CFLAGS += -g -gstabs CFLAGS += -g -gstabs
@ -56,7 +54,7 @@ LiteXL: $(LiteXL_OBJ)
src/main.o: src/main.c src/api/api.h src/rencache.h \ src/main.o: src/main.c src/api/api.h src/rencache.h \
src/renderer.h src/platform/amigaos4.h src/renderer.h src/platform/amigaos4.h src/platform/codesets.h
src/rencache.o: src/rencache.c src/rencache.o: src/rencache.c
@ -70,13 +68,15 @@ src/api/regex.o: src/api/regex.c
src/api/renderer.o: src/api/renderer.c src/api/renderer.o: src/api/renderer.c
src/api/system.o: src/api/system.c src/api/system.o: src/api/system.c src/platform/amigaos4.h
src/platform/amigaos4.o: src/platform/amigaos4.c src/platform/amigaos4.o: src/platform/amigaos4.c
src/platform/codesets.o: src/platform/codesets.c
src/api/dirmonitor.o: src/api/dirmonitor.c src/api/dirmonitor/os4.c src/api/dirmonitor.o: src/api/dirmonitor.c src/api/dirmonitor/os4.c
src/api/utf8.o: src/api/utf8.c src/api/utf8.o: src/api/utf8.c src/platform/amigaos4.h
src/api/dirmonitor/os4.o: src/api/dirmonitor/os4.c src/api/dirmonitor/os4.o: src/api/dirmonitor/os4.c
@ -85,8 +85,9 @@ src/api/process.o: src/api/process.c
release: clean LiteXL release: clean LiteXL
@echo "Creating release files..." @echo "Creating release files..."
@mkdir -p release/LiteXL2 @mkdir -p release/LiteXL2
@cp -r release_files/* release/LiteXL2/ @cp -r resources/amiga/* release/LiteXL2/
@mv release/LiteXL2/LiteXL2.info release/ @mv release/LiteXL2/LiteXL2.info release/
@mv release/LiteXL2/AutoInstall release/
@cp -r data release/LiteXL2/ @cp -r data release/LiteXL2/
@cp changelog.md release/LiteXL2/ @cp changelog.md release/LiteXL2/
@cp $(outfile) release/LiteXL2/ @cp $(outfile) release/LiteXL2/
@ -94,6 +95,7 @@ release: clean LiteXL
@cp README.md release/LiteXL2/ @cp README.md release/LiteXL2/
@cp README_Amiga.md release/LiteXL2/ @cp README_Amiga.md release/LiteXL2/
@cp LICENSE release/LiteXL2/ @cp LICENSE release/LiteXL2/
@cp -r licenses release/LiteXL2/
@echo "Creating release archive..." @echo "Creating release archive..."
@lha -aeqr3 a LiteXL2_OS4.lha release/ @lha -aeqr3 a LiteXL2_OS4.lha release/
@echo "Clean release files..." @echo "Clean release files..."

View File

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

View File

@ -87,6 +87,15 @@ Shows the current time and date in a view with large text
**bracketmatch** **bracketmatch**
Underlines matching pair for bracket under the caret Underlines matching pair for bracket under the caret
**codesets**
This plugin uses the codesets.library on AmigaOS 4 and the
charsets.library on MorphOS to translate ISO encoded files to unicode
and vice-versa. When this is enabled new menu items are added to
load/save the code with a different encoding. Also there is a new
section at the status bar that show the file encoding.
This plugin is **EXPERIMENTAL** and heavily inspired from the encoding
plugin at https://github.com/jgmdev/lite-xl-encoding
**colorpreview** **colorpreview**
Underlays color values (eg. `#ff00ff` or `rgb(255, 0, 255)`) with their Underlays color values (eg. `#ff00ff` or `rgb(255, 0, 255)`) with their
resultant color. resultant color.
@ -107,7 +116,10 @@ Marks tabs as non-preview on any change or tab double clicking.
**ghmarkdown** **ghmarkdown**
Opens a preview of the current markdown file in a browser window. Opens a preview of the current markdown file in a browser window.
On AmigaOS 4 it uses *urlopen* and on MorphOS it uses *openurl* to load On AmigaOS 4 it uses *urlopen* and on MorphOS it uses *openurl* to load
the generated html in the browser. the generated html in the browser. It requires a GitHub application token
because it uses its Rest API. Add it at the init.lua file in your config
folder like below:
`config.plugins.ghmarkdown.github_token = "<token here>"`
**indentguide** **indentguide**
Adds indent guides Adds indent guides
@ -166,6 +178,9 @@ Highlights regions of code that match the current selection
**smallclock** **smallclock**
It adds a small clock at the bottom right corner. It adds a small clock at the bottom right corner.
**tetris**
Play Tetris inside Lite XL.
## Tips and tricks ## Tips and tricks
### Transitions ### Transitions
@ -215,6 +230,64 @@ https://git.walkero.gr/walkero/lite-xl/issues
# Changelog # Changelog
## [2.1.4r1] - future
### Added
- Added the ability to open files and folders by drag 'n drop them on the
LiteXL icon when this is on the AmiDock
### Updated
- Updated the code to the upstream 2.1.4 release
### Fixed
- Fix opening files from the root of a device
## [2.1.3r1] - 2024-03-09
### Added
- Added AmiUpdate support
- Added the Tetris plugin
### Updated
- Updated the code to the upstream 2.1.3 release
- Compiled with SDL 2.30.0 that supports editing with ISO encodings, other
than English. Now the editor should be able to support any language
and in conjuction with the codesets plugin be able to make text
encodings conversions
- Now the codesets plugin supports MorphOS 3.18 and above
### Changed
- Changed the way the "Open in system" option executes the WBRun command
in AmigaOS 4, with a more secure way
- Did a lot of code cleanup in sync with the upstream, and parts of code
that were left over
- Compiled with pcre2 10.42 (MorphOS version only)
### Fixed
- I did a lot of changes on path manipulation and usage, fixing scanning
the root of a partition or an assign path
- Fixed an error with the codesets plugin, where an empty file could
not be opened
- Improved the folder suggestions when opening projects or changing paths.
Now even the root folders of a partition are presented
- Fixed ghmarkdown plugin, but now requires a GitHub token to be provided
## [2.1.2r1] - 2023-12-19
### Added
- Added the new experimental codesets plugin (AmigaOS4 version only).
MorphOS version is in WIP
### Changed
- Synced with the latest upstream v2.1.2 code
- Compiled with gcc 11.3.0
- Compiled with SDL 2.28.4
- Compiled with libfreetype 2.13.x
- Compiled with lua 5.4.6
- Compiled with linpng 1.6.40 (AmigaOS4 version only)
- Compiled with libz 1.2.13 (AmigaOS4 version only)
## [2.1.1r2] - 2022-05-14
### Changed
- Compiled with latest SDL v2.26.5-rc2
## [2.1.1r1] - 2022-01-29 ## [2.1.1r1] - 2022-01-29
### Changed ### Changed
- Binary name changed to lite-xl - Binary name changed to lite-xl

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -37,17 +37,38 @@ local modkey_map = modkeys_os.map
local modkeys = modkeys_os.keys local modkeys = modkeys_os.keys
---Normalizes a stroke sequence to follow the modkeys table
---@param stroke string
---@return string
local function normalize_stroke(stroke)
local stroke_table = {}
for key in stroke:gmatch("[^+]+") do
table.insert(stroke_table, key)
end
table.sort(stroke_table, function(a, b)
if a == b then return false end
for _, mod in ipairs(modkeys) do
if a == mod or b == mod then
return a == mod
end
end
return a < b
end)
return table.concat(stroke_table, "+")
end
---Generates a stroke sequence including currently pressed mod keys. ---Generates a stroke sequence including currently pressed mod keys.
---@param key string ---@param key string
---@return string ---@return string
local function key_to_stroke(key) local function key_to_stroke(key)
local stroke = "" local keys = { key }
for _, mk in ipairs(modkeys) do for _, mk in ipairs(modkeys) do
if keymap.modkeys[mk] then if keymap.modkeys[mk] then
stroke = stroke .. mk .. "+" table.insert(keys, mk)
end end
end end
return stroke .. key return normalize_stroke(table.concat(keys, "+"))
end end
@ -76,11 +97,12 @@ end
---@param map keymap.map ---@param map keymap.map
local function remove_duplicates(map) local function remove_duplicates(map)
for stroke, commands in pairs(map) do for stroke, commands in pairs(map) do
local normalized_stroke = normalize_stroke(stroke)
if type(commands) == "string" or type(commands) == "function" then if type(commands) == "string" or type(commands) == "function" then
commands = { commands } commands = { commands }
end end
if keymap.map[stroke] then if keymap.map[normalized_stroke] then
for _, registered_cmd in ipairs(keymap.map[stroke]) do for _, registered_cmd in ipairs(keymap.map[normalized_stroke]) do
local j = 0 local j = 0
for i=1, #commands do for i=1, #commands do
while commands[i + j] == registered_cmd do while commands[i + j] == registered_cmd do
@ -98,11 +120,12 @@ local function remove_duplicates(map)
end end
end end
---Add bindings by replacing commands that were previously assigned to a shortcut. ---Add bindings by replacing commands that were previously assigned to a shortcut.
---@param map keymap.map ---@param map keymap.map
function keymap.add_direct(map) function keymap.add_direct(map)
for stroke, commands in pairs(map) do for stroke, commands in pairs(map) do
stroke = normalize_stroke(stroke)
if type(commands) == "string" or type(commands) == "function" then if type(commands) == "string" or type(commands) == "function" then
commands = { commands } commands = { commands }
end end
@ -130,6 +153,7 @@ function keymap.add(map, overwrite)
if macos then if macos then
stroke = stroke:gsub("%f[%a]ctrl%f[%A]", "cmd") stroke = stroke:gsub("%f[%a]ctrl%f[%A]", "cmd")
end end
stroke = normalize_stroke(stroke)
if overwrite then if overwrite then
if keymap.map[stroke] then if keymap.map[stroke] then
for _, cmd in ipairs(keymap.map[stroke]) do for _, cmd in ipairs(keymap.map[stroke]) do
@ -155,6 +179,7 @@ end
---@param shortcut string ---@param shortcut string
---@param cmd string ---@param cmd string
function keymap.unbind(shortcut, cmd) function keymap.unbind(shortcut, cmd)
shortcut = normalize_stroke(shortcut)
remove_only(keymap.map, shortcut, cmd) remove_only(keymap.map, shortcut, cmd)
remove_only(keymap.reverse_map, cmd, shortcut) remove_only(keymap.reverse_map, cmd, shortcut)
end end
@ -180,10 +205,6 @@ 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
@ -390,4 +411,3 @@ keymap.add_direct {
} }
return keymap return keymap

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,15 @@
-- this file is used by lite-xl to setup the Lua environment when starting -- this file is used by lite-xl to setup the Lua environment when starting
VERSION = "2.1.1r1" VERSION = "2.1.4r1"
MOD_VERSION = "3" MOD_VERSION = "3"
SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or SCALE SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or 1
PATHSEP = package.config:sub(1, 1) PATHSEP = package.config:sub(1, 1)
EXEDIR = EXEFILE:match("^(.+)[/\\][^/\\]+$") EXEDIR = EXEFILE:match("^(.+)[/\\][^/\\]+$")
if MACOS_RESOURCES then if MACOS_RESOURCES then
DATADIR = MACOS_RESOURCES DATADIR = MACOS_RESOURCES
else else
local prefix = EXEDIR:match("^(.+)[/\\]bin$") local prefix = os.getenv('LITE_PREFIX') or EXEDIR:match("^(.+)[/\\]bin$")
DATADIR = prefix and (prefix .. PATHSEP .. 'share' .. PATHSEP .. 'lite-xl') or (EXEDIR .. PATHSEP .. 'data') DATADIR = prefix and (prefix .. PATHSEP .. 'share' .. PATHSEP .. 'lite-xl') or (EXEDIR .. PATHSEP .. 'data')
end end
USERDIR = (system.get_file_info(EXEDIR .. PATHSEP .. 'user') and (EXEDIR .. PATHSEP .. 'user')) USERDIR = (system.get_file_info(EXEDIR .. PATHSEP .. 'user') and (EXEDIR .. PATHSEP .. 'user'))
@ -35,8 +35,8 @@ package.cpath =
package.native_plugins = {} package.native_plugins = {}
package.searchers = { package.searchers[1], package.searchers[2], function(modname) package.searchers = { package.searchers[1], package.searchers[2], function(modname)
local path = package.searchpath(modname, package.cpath) local path, err = package.searchpath(modname, package.cpath)
if not path then return nil end if not path then return err end
return system.load_native_plugin, path return system.load_native_plugin, path
end } end }
@ -55,4 +55,3 @@ local appimage_owd = os.getenv("OWD")
if os.getenv("APPIMAGE") and appimage_owd then if os.getenv("APPIMAGE") and appimage_owd then
system.chdir(appimage_owd) system.chdir(appimage_owd)
end end

View File

@ -40,4 +40,3 @@ style.syntax_fonts = {}
style.log = {} style.log = {}
return style return style

View File

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

View File

@ -1,14 +1,16 @@
local core = require "core" local core = require "core"
local syntax = require "core.syntax" local syntax = require "core.syntax"
local config = require "core.config"
local tokenizer = {} local tokenizer = {}
local bad_patterns = {} local bad_patterns = {}
local function push_token(t, type, text) local function push_token(t, type, text)
if not text or #text == 0 then return end
type = type or "normal" type = type or "normal"
local prev_type = t[#t-1] local prev_type = t[#t-1]
local prev_text = t[#t] local prev_text = t[#t]
if prev_type and (prev_type == type or prev_text:ufind("^%s*$")) then if prev_type and (prev_type == type or (prev_text:ufind("^%s*$") and type ~= "incomplete")) then
t[#t-1] = type t[#t-1] = type
t[#t] = prev_text .. text t[#t] = prev_text .. text
else else
@ -121,15 +123,17 @@ local function report_bad_pattern(log_fn, syntax, pattern_idx, msg, ...)
end end
if bad_patterns[syntax][pattern_idx] then return end if bad_patterns[syntax][pattern_idx] then return end
bad_patterns[syntax][pattern_idx] = true bad_patterns[syntax][pattern_idx] = true
log_fn("Malformed pattern #%d in %s language plugin. " .. msg, log_fn("Malformed pattern #%d <%s> in %s language plugin.\n" .. msg,
pattern_idx, syntax.name or "unnamed", ...) pattern_idx,
syntax.patterns[pattern_idx].pattern or syntax.patterns[pattern_idx].regex,
syntax.name or "unnamed", ...)
end end
---@param incoming_syntax table ---@param incoming_syntax table
---@param text string ---@param text string
---@param state string ---@param state string
function tokenizer.tokenize(incoming_syntax, text, state) function tokenizer.tokenize(incoming_syntax, text, state, resume)
local res = {} local res
local i = 1 local i = 1
if #incoming_syntax.patterns == 0 then if #incoming_syntax.patterns == 0 then
@ -137,6 +141,20 @@ function tokenizer.tokenize(incoming_syntax, text, state)
end end
state = state or string.char(0) state = state or string.char(0)
if resume then
res = resume.res
-- Remove "incomplete" tokens
while res[#res-1] == "incomplete" do
table.remove(res, #res)
table.remove(res, #res)
end
i = resume.i
state = resume.state
end
res = res or {}
-- incoming_syntax : the parent syntax of the file. -- incoming_syntax : the parent syntax of the file.
-- state : a string of bytes representing syntax state (see above) -- state : a string of bytes representing syntax state (see above)
@ -195,9 +213,11 @@ function tokenizer.tokenize(incoming_syntax, text, state)
-- Remove '^' from the beginning of the pattern -- Remove '^' from the beginning of the pattern
if type(target) == "table" then if type(target) == "table" then
target[p_idx] = code:usub(2) target[p_idx] = code:usub(2)
code = target[p_idx]
else else
p.pattern = p.pattern and code:usub(2) p.pattern = p.pattern and code:usub(2)
p.regex = p.regex and code:usub(2) p.regex = p.regex and code:usub(2)
code = p.pattern or p.regex
end end
end end
end end
@ -245,97 +265,122 @@ function tokenizer.tokenize(incoming_syntax, text, state)
end end
local text_len = text:ulen() local text_len = text:ulen()
if text_len ~= nil then local start_time = system.get_time()
while i <= text_len do local starting_i = i
-- continue trying to match the end pattern of a pair if we have a state set while text_len ~= nil and i <= text_len do
if current_pattern_idx > 0 then -- Every 200 chars, check if we're out of time
local p = current_syntax.patterns[current_pattern_idx] if i - starting_i > 200 then
local s, e = find_text(text, p, i, false, true) starting_i = i
if system.get_time() - start_time > 0.5 / config.fps then
local cont = true -- We're out of time
-- If we're in subsyntax mode, always check to see if we end our syntax push_token(res, "incomplete", string.usub(text, i))
-- first, before the found delimeter, as ending the subsyntax takes return res, string.char(0), {
-- precedence over ending the delimiter in the subsyntax. res = res,
if subsyntax_info then i = i,
local ss, se = find_text(text, subsyntax_info, i, false, true) state = state
-- If we find that we end the subsyntax before the }
-- delimiter, push the token, and signal we shouldn't
-- treat the bit after as a token to be normally parsed
-- (as it's the syntax delimiter).
if ss and (s == nil or ss < s) then
push_token(res, p.type, text:usub(i, ss - 1))
i = ss
cont = false
end
end
-- If we don't have any concerns about syntax delimiters,
-- continue on as normal.
if cont then
if s then
push_token(res, p.type, text:usub(i, e))
set_subsyntax_pattern_idx(0)
i = e + 1
else
push_token(res, p.type, text:usub(i))
break
end
end
end end
-- General end of syntax check. Applies in the case where end
-- we're ending early in the middle of a delimiter, or -- continue trying to match the end pattern of a pair if we have a state set
-- just normally, upon finding a token. if current_pattern_idx > 0 then
local p = current_syntax.patterns[current_pattern_idx]
local s, e = find_text(text, p, i, false, true)
local cont = true
-- If we're in subsyntax mode, always check to see if we end our syntax
-- first, before the found delimeter, as ending the subsyntax takes
-- precedence over ending the delimiter in the subsyntax.
if subsyntax_info then if subsyntax_info then
local s, e = find_text(text, subsyntax_info, i, true, true) local ss, se = find_text(text, subsyntax_info, i, false, true)
if s then -- If we find that we end the subsyntax before the
push_token(res, subsyntax_info.type, text:usub(i, e)) -- delimiter, push the token, and signal we shouldn't
-- On finding unescaped delimiter, pop it. -- treat the bit after as a token to be normally parsed
pop_subsyntax() -- (as it's the syntax delimiter).
i = e + 1 if ss and (s == nil or ss < s) then
push_token(res, p.type, text:usub(i, ss - 1))
i = ss
cont = false
end end
end end
-- If we don't have any concerns about syntax delimiters,
-- find matching pattern -- continue on as normal.
local matched = false if cont then
for n, p in ipairs(current_syntax.patterns) do if s then
local find_results = { find_text(text, p, i, true, false) } push_token(res, p.type, text:usub(i, e))
if find_results[1] then set_subsyntax_pattern_idx(0)
local type_is_table = type(p.type) == "table" i = e + 1
local n_types = type_is_table and #p.type or 1 else
if #find_results == 2 and type_is_table then push_token(res, p.type, text:usub(i))
report_bad_pattern(core.warn, current_syntax, n,
"Token type is a table, but a string was expected.")
p.type = p.type[1]
elseif #find_results - 1 > n_types then
report_bad_pattern(core.error, current_syntax, n,
"Not enough token types: got %d needed %d.", n_types, #find_results - 1)
elseif #find_results - 1 < n_types then
report_bad_pattern(core.warn, current_syntax, n,
"Too many token types: got %d needed %d.", n_types, #find_results - 1)
end
-- matched pattern; make and add tokens
push_tokens(res, current_syntax, p, text, find_results)
-- update state if this was a start|end pattern pair
if type(p.pattern or p.regex) == "table" then
-- If we have a subsyntax, push that onto the subsyntax stack.
if p.syntax then
push_subsyntax(p, n)
else
set_subsyntax_pattern_idx(n)
end
end
-- move cursor past this token
i = find_results[2] + 1
matched = true
break break
end end
end end
end
-- consume character if we didn't match -- General end of syntax check. Applies in the case where
if not matched then -- we're ending early in the middle of a delimiter, or
push_token(res, "normal", text:usub(i, i)) -- just normally, upon finding a token.
i = i + 1 while subsyntax_info do
local s, e = find_text(text, subsyntax_info, i, true, true)
if s then
push_token(res, subsyntax_info.type, text:usub(i, e))
-- On finding unescaped delimiter, pop it.
pop_subsyntax()
i = e + 1
else
break
end end
end end
-- find matching pattern
local matched = false
for n, p in ipairs(current_syntax.patterns) do
local find_results = { find_text(text, p, i, true, false) }
if find_results[1] then
-- Check for patterns successfully matching nothing
if find_results[1] > find_results[2] then
report_bad_pattern(core.warn, current_syntax, n,
"Pattern successfully matched, but nothing was captured.")
goto continue
end
-- Check for patterns with mismatching number of `types`
local type_is_table = type(p.type) == "table"
local n_types = type_is_table and #p.type or 1
if #find_results == 2 and type_is_table then
report_bad_pattern(core.warn, current_syntax, n,
"Token type is a table, but a string was expected.")
p.type = p.type[1]
elseif #find_results - 1 > n_types then
report_bad_pattern(core.error, current_syntax, n,
"Not enough token types: got %d needed %d.", n_types, #find_results - 1)
elseif #find_results - 1 < n_types then
report_bad_pattern(core.warn, current_syntax, n,
"Too many token types: got %d needed %d.", n_types, #find_results - 1)
end
-- matched pattern; make and add tokens
push_tokens(res, current_syntax, p, text, find_results)
-- update state if this was a start|end pattern pair
if type(p.pattern or p.regex) == "table" then
-- If we have a subsyntax, push that onto the subsyntax stack.
if p.syntax then
push_subsyntax(p, n)
else
set_subsyntax_pattern_idx(n)
end
end
-- move cursor past this token
i = find_results[2] + 1
matched = true
break
::continue::
end
end
-- consume character if we didn't match
if not matched then
push_token(res, "normal", text:usub(i, i))
i = i + 1
end
end end
return res, state return res, state

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

24
resources/README.md Normal file
View File

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

View File

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

Binary file not shown.

BIN
resources/amiga/addons.info Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,370 @@
--mod-version:3 --priority:5
--[[
This code is responsible for the encoding change
using codesets library. It requires LiteXL 2.1.1r3
and above to work.
Heavily inspired from the encoding plugin
https://github.com/jgmdev/lite-xl-encoding
Configuration:
useSystemEncoding
By default the system encoding is used to open
a file. If you want to disable that you may add
the following line in you config file
config.plugins.codesets.useSystemEncoding = false
]]
local core = require "core"
local common = require "core.common"
local command = require "core.command"
local config = require "core.config"
local style = require "core.style"
local Doc = require "core.doc"
local DocView = require "core.docview"
local CommandView = require "core.commandview"
local StatusView = require "core.statusview"
---@type encoding
local encoding = require "codesetsextra"
config.plugins.codesets = common.merge({
useSystemEncoding = true
}, config.plugins.codesets)
-- Reference to plugin config
local conf = config.plugins.codesets
local encodings = {}
---@class encodings.encoding
---@field charset string
---@field name string
---List of encoding regions.
---@type table<integer,string>
encodings.groups = {
"West European",
"East European",
"East Asian",
"SE & SW Asian",
"Middle Eastern",
"Unicode"
}
---Supported iconv encodings grouped by region.
---@type table<integer,encodings.encoding[]>
encodings.list = {
-- West European
{
{ charset = "ISO-8859-14", name = "Celtic" },
{ charset = "ISO-8859-7", name = "Greek" },
{ charset = "WINDOWS-1253", name = "Greek" },
{ charset = "ISO-8859-10", name = "Nordic" },
{ charset = "ISO-8859-3", name = "South European" },
{ charset = "IBM850", name = "Western" },
{ charset = "ISO-8859-1", name = "Western" },
{ charset = "ISO-8859-15", name = "Western" },
{ charset = "WINDOWS-1252", name = "Western" }
},
-- East European
{
{ charset = "ISO-8859-4", name = "Baltic" },
{ charset = "ISO-8859-13", name = "Baltic" },
{ charset = "WINDOWS-1257", name = "Baltic" },
{ charset = "IBM852", name = "Central European" },
{ charset = "ISO-8859-2", name = "Central European" },
{ charset = "WINDOWS-1250", name = "Central European" },
{ charset = "IBM855", name = "Cyrillic" },
{ charset = "ISO-8859-5", name = "Cyrillic" },
{ charset = "ISO-IR-111", name = "Cyrillic" },
{ charset = "KOI8-R", name = "Cyrillic" },
{ charset = "WINDOWS-1251", name = "Cyrillic" },
{ charset = "CP866", name = "Cyrillic/Russian" },
{ charset = "KOI8-U", name = "Cyrillic/Ukrainian" },
{ charset = "ISO-8859-16", name = "Romanian" }
},
-- East Asian
{
{ charset = "GB18030", name = "Chinese Simplified" },
{ charset = "GB2312", name = "Chinese Simplified" },
{ charset = "GBK", name = "Chinese Simplified" },
{ charset = "HZ", name = "Chinese Simplified" },
{ charset = "BIG5", name = "Chinese Traditional" },
{ charset = "BIG5-HKSCS", name = "Chinese Traditional" },
{ charset = "EUC-TW", name = "Chinese Traditional" },
{ charset = "EUC-JP", name = "Japanese" },
{ charset = "ISO-2022-JP", name = "Japanese" },
{ charset = "SHIFT_JIS", name = "Japanese" },
{ charset = "CP932", name = "Japanese" },
{ charset = "EUC-KR", name = "Korean" },
{ charset = "ISO-2022-KR", name = "Korean" },
{ charset = "JOHAB", name = "Korean" },
{ charset = "UHC", name = "Korean" }
},
-- SE & SW Asian
{
{ charset = "ARMSCII-8", name = "Armenian" },
{ charset = "GEORGIAN-ACADEMY", name = "Georgian" },
{ charset = "TIS-620", name = "Thai" },
{ charset = "IBM857", name = "Turkish" },
{ charset = "WINDOWS-1254", name = "Turkish" },
{ charset = "ISO-8859-9", name = "Turkish" },
{ charset = "TCVN", name = "Vietnamese" },
{ charset = "VISCII", name = "Vietnamese" },
{ charset = "WINDOWS-1258", name = "Vietnamese" }
},
-- Middle Eastern
{
{ charset = "IBM864", name = "Arabic" },
{ charset = "ISO-8859-6", name = "Arabic" },
{ charset = "WINDOWS-1256", name = "Arabic" },
{ charset = "IBM862", name = "Hebrew" },
{ charset = "ISO-8859-8-I", name = "Hebrew" },
{ charset = "WINDOWS-1255", name = "Hebrew" },
{ charset = "ISO-8859-8", name = "Hebrew Visual" }
},
-- Unicode
{
{ charset = "UTF-7", name = "Unicode" },
{ charset = "UTF-8", name = "Unicode" },
{ charset = "UTF-16LE", name = "Unicode" },
{ charset = "UTF-16BE", name = "Unicode" },
{ charset = "UCS-2LE", name = "Unicode" },
{ charset = "UCS-2BE", name = "Unicode" },
{ charset = "UTF-32LE", name = "Unicode" },
{ charset = "UTF-32BE", name = "Unicode" }
}
};
---Get the list of encodings associated to a region.
---@param label string
---@return encodings.encoding[] | nil
function encodings.get_group(label)
for idx, name in ipairs(encodings.groups) do
if name == label then
return encodings.list[idx]
end
end
end
---Get the list of encodings associated to a region.
---@return encodings.encoding[] | nil
function encodings.get_all()
local all = {}
for idx, _ in ipairs(encodings.groups) do
for _, item in ipairs(encodings.list[idx]) do
table.insert(all, item)
end
end
return all
end
---Open a commandview to select a charset and executes the given callback,
---@param title_label string Title displayed on the commandview
---@param callback fun(charset: string)
function encodings.select_encoding(title_label, callback)
core.command_view:enter(title_label, {
submit = function(_, item)
callback(item.charset)
end,
suggest = function(text)
local charsets = encodings.get_all()
local list_labels = {}
local list_charset = {}
for _, element in ipairs(charsets) do
local label = element.name .. " (" .. element.charset .. ")"
table.insert(list_labels, label)
list_charset[label] = element.charset
end
local res = common.fuzzy_match(list_labels, text)
for i, name in ipairs(res) do
res[i] = {
text = name,
charset = list_charset[name]
}
end
return res
end
})
end
--------------------------------------------------------------------------------
-- Overwrite Doc methods to properly add encoding detection and conversion.
--------------------------------------------------------------------------------
function Doc:new(filename, abs_filename, new_file)
self.new_file = new_file
self.encoding = nil
self.convert = false
self:reset()
if filename then
self:set_filename(filename, abs_filename)
if not new_file then
self:load(filename)
end
end
end
function Doc:load(filename)
if not self.encoding then
local errmsg
if conf.useSystemEncoding then
self.encoding, errmsg = encoding.systemCodeset();
else
self.encoding, errmsg = encoding.detect(filename);
end
if not self.encoding then core.error("%s", errmsg) error(errmsg) end
end
self.convert = false
if self.encoding ~= "UTF-8" and self.encoding ~= "ASCII"
and self.encoding ~= "US-ASCII" and self.encoding ~= "ISO-8859-1"
then
self.convert = true
end
local fp = assert( io.open(filename, "rb") )
self:reset()
self.lines = {}
local i = 1
if self.convert then
local content = fp:read("*a");
content = assert(encoding.convert("UTF-8", self.encoding, content, {
strict = false,
handle_from_bom = true
}))
for line in content:gmatch("([^\n]*)\n?") do
if line:byte(-1) == 13 then
line = line:sub(1, -2)
self.crlf = true
end
table.insert(self.lines, line .. "\n")
self.highlighter.lines[i] = false
i = i + 1
end
content = nil
else
for line in fp:lines() do
if (i == 1) then line = encoding.strip_bom(line, "UTF-8") end
if line:byte(-1) == 13 then
line = line:sub(1, -2)
self.crlf = true
end
table.insert(self.lines, line .. "\n")
self.highlighter.lines[i] = false
i = i + 1
end
end
if #self.lines == 0 then
table.insert(self.lines, "\n")
end
fp:close()
self:reset_syntax()
end
function Doc:save(filename, abs_filename)
if not filename then
assert(self.filename, "no filename set to default to")
filename = self.filename
abs_filename = self.abs_filename
else
assert(self.filename or abs_filename, "calling save on unnamed doc without absolute path")
end
local fp
local output = ""
if not self.convert then
fp = assert( io.open(filename, "wb") )
for _, line in ipairs(self.lines) do
if self.crlf then line = line:gsub("\n", "\r\n") end
fp:write(line)
end
else
output = table.concat(self.lines);
if self.crlf then output = output:gsub("\n", "\r\n") end
end
local conversion_error = false
if self.convert then
local errmsg
output, errmsg = encoding.convert(self.encoding, "UTF-8", output, {
strict = true,
handle_to_bom = true
})
if output then
fp = assert( io.open(filename, "wb") )
fp:write(encoding.get_charset_bom(self.encoding) .. output)
fp:close()
else
conversion_error = true
core.error("%s", errmsg)
end
else
fp:close()
end
self:set_filename(filename, abs_filename)
if not conversion_error then
self.new_file = false
else
self.new_file = true
end
self:clean()
end
--------------------------------------------------------------------------------
-- Register command to change current document encoding.
--------------------------------------------------------------------------------
command.add("core.docview", {
["doc:change-encoding"] = function(dv)
encodings.select_encoding("Select Output Encoding", function(charset)
dv.doc.encoding = charset
if charset ~= "UTF-8" and charset ~= "ASCII"
and charset ~= "US-ASCII" and charset ~= "ISO-8859-1"
then
dv.doc.convert = true
else
dv.doc.convert = false
end
dv.doc:save()
end)
end,
["doc:reload-with-encoding"] = function(dv)
encodings.select_encoding("Reload With Encoding", function(charset)
dv.doc.encoding = charset
if charset ~= "UTF-8" and charset ~= "ASCII"
and charset ~= "US-ASCII" and charset ~= "ISO-8859-1"
then
dv.doc.convert = true
else
dv.doc.convert = false
end
dv.doc:reload()
end)
end
})
--------------------------------------------------------------------------------
-- Register a statusbar item to view change current doc encoding.
--------------------------------------------------------------------------------
core.status_view:add_item({
predicate = function()
return core.active_view:is(DocView)
and not core.active_view:is(CommandView)
end,
name = "doc:encoding",
alignment = StatusView.Item.RIGHT,
get_item = function()
local dv = core.active_view
return {
style.text, dv.doc.encoding or "none"
}
end,
command = function(button)
if button == "left" then
command.perform "doc:change-encoding"
elseif button == "right" then
command.perform "doc:reload-with-encoding"
end
end,
tooltip = "encoding"
})
return encodings;

View File

@ -0,0 +1,100 @@
-- mod-version:3
local config = require "core.config"
local common = require "core.common"
local DocView = require "core.docview"
config.plugins.colorpreview = common.merge({
enabled = true,
-- The config specification used by the settings gui
config_spec = {
name = "Color Preview",
{
label = "Enable",
description = "Enable or disable the color preview feature.",
path = "enabled",
type = "toggle",
default = true
}
}
}, config.plugins.colorpreview)
local white = { common.color "#ffffff" }
local black = { common.color "#000000" }
local tmp = {}
-- Workaround for bug in Lite XL 2.1
-- Remove this when b029f5993edb7dee5ccd2ba55faac1ec22e24609 is in a release
local function get_selection(doc, sort)
local line1, col1, line2, col2 = doc:get_selection_idx(doc.last_selection)
if line1 then
return doc:get_selection_idx(doc.last_selection, sort)
else
return doc:get_selection_idx(1, sort)
end
end
local function draw_color_previews(self, line, x, y, ptn, base, nibbles)
local text = self.doc.lines[line]
local s, e = 0, 0
while true do
s, e = text:find(ptn, e + 1)
if not s then break end
local str = text:sub(s, e)
local r, g, b, a = str:match(ptn)
r, g, b = tonumber(r, base), tonumber(g, base), tonumber(b, base)
a = tonumber(a or "", base)
if a ~= nil then
if base ~= 16 then
a = a * 0xff
end
else
a = 0xff
end
-- #123 becomes #112233
if nibbles then
r = r * 16
g = g * 16
b = b * 16
end
local x1 = x + self:get_col_x_offset(line, s)
local x2 = x + self:get_col_x_offset(line, e + 1)
local oy = self:get_line_text_y_offset()
local text_color = math.max(r, g, b) < 128 and white or black
tmp[1], tmp[2], tmp[3], tmp[4] = r, g, b, a
local l1, _, l2, _ = get_selection(self.doc, true)
if not (self.doc:has_selection() and line >= l1 and line <= l2) then
renderer.draw_rect(x1, y, x2 - x1, self:get_line_height(), tmp)
renderer.draw_text(self:get_font(), str, x1, y + oy, text_color)
end
end
end
local draw_line_text = DocView.draw_line_text
function DocView:draw_line_text(line, x, y)
local lh = draw_line_text(self, line, x, y)
if config.plugins.colorpreview.enabled then
draw_color_previews(self, line, x, y,
"#(%x%x)(%x%x)(%x%x)(%x?%x?)%f[%W]",
16
)
-- support #fff css format
draw_color_previews(self, line, x, y, "#(%x)(%x)(%x)%f[%W]", 16, true)
draw_color_previews(self, line, x, y,
"rgba?%((%d+)%D+(%d+)%D+(%d+)[%s,]-([%.%d]-)%s-%)",
nil
)
end
return lh
end

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