Compare commits

...

162 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
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
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
74 changed files with 1847 additions and 1646 deletions

View File

@ -9,13 +9,15 @@ on:
inputs:
version:
description: Release Version
default: v2.1.1
default: v2.1.4
required: true
jobs:
release:
name: Create Release
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
version: ${{ steps.tag.outputs.version }}

View File

@ -1,4 +1,6 @@
Copyright (c) 2020-present 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
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/regex.o src/api/renderer.o src/api/system.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
compiler := gcc
cxxcompiler := g++
compiler := ppc-morphos-gcc-11
cxxcompiler := ppc-morphos-g++-11
INCPATH := -Isrc -Ilib/dmon -I/sdk/gg/usr/local/include/SDL2 -I/sdk/gg/usr/include/freetype -I/sdk/gg/usr/include/lua5.4
DFLAGS := -D__USE_INLINE__
CFLAGS := -Wall -Wwrite-strings -O2 -noixemul -g -std=gnu11 -fno-strict-aliasing
LFLAGS := -noixemul -lpcre2 -lSDL2 -llua54 -lagg -lfreetype -lm -lc -L/usr/local/lib
INCPATH := -Isrc -Ilib/dmon -I/sdk/gg/usr/local/include/SDL2 \
-I/sdk/gg/usr/include/freetype -I/sdk/gg/usr/include/lua5.4
DFLAGS ?= -D__USE_INLINE__
CFLAGS ?= -Wall -Wwrite-strings -O2 -noixemul -g -std=gnu11 -fno-strict-aliasing
LFLAGS ?= -noixemul -lpcre2-8 -lSDL2 -llua54 -lagg -lfreetype -lm -lc -L/usr/local/lib
ifeq ($(DEBUG),1)
CFLAGS += -g -gstabs
LFLAGS += -gstabs
endif
.PHONY: LiteXL clean release
@ -32,11 +36,11 @@ clean:
LiteXL: $(LiteXL_OBJ)
@echo "Linking LiteXL"
@$(cxxcompiler) -o $(outfile) $(LiteXL_OBJ) $(LFLAGS)
$(compiler) -o $(outfile) $(LiteXL_OBJ) $(LFLAGS)
.c.o:
@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/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/platform/codesets.o: src/platform/codesets.c
release: clean LiteXL
@echo "Creating release files..."
@mkdir -p release/LiteXL2
@cp resources/amiga/* release/LiteXL2/ -r
@cp -r resources/amiga/* release/LiteXL2/
@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 $(outfile) release/LiteXL2/
@strip release/LiteXL2/$(outfile)
@cp README.md release/LiteXL2/
@cp README_Amiga.md release/LiteXL2/
@cp LICENSE release/LiteXL2/
@cp -r licenses release/LiteXL2/
@echo "Creating release archive..."
@lha -aeqr3 a LiteXL2_MOS.lha release/
@echo "Clean release files..."

View File

@ -12,18 +12,16 @@ LiteXL_OBJ := \
src/api/dirmonitor/os4.o src/platform/codesets.o
outfile := lite-xl
compiler := gcc-11
cxxcompiler := g++-11
compiler := gcc
cxxcompiler := g++
INCPATH := -Isrc -I/sdk/local/newlib/include/SDL2 \
-I/sdk/local/common/include/lua54 -I/sdk/local/common/include/freetype2
DFLAGS += -D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR
CFLAGS += -Werror -Wwrite-strings -O3 -std=gnu11 -fno-strict-aliasing
LFLAGS += -mcrt=newlib \
-lpcre2 -lSDL2 -llua54 -lfreetype -lpng -lz -lpthread -athread=native
CFLAGS ?= -Werror -Wwrite-strings -O3 -std=gnu11 -fno-strict-aliasing
LFLAGS ?= -mcrt=newlib -lpcre2-8 -lSDL2 -llua54 -lfreetype -lpng -lz \
-lpthread -athread=native
ifeq ($(DEBUG),1)
CFLAGS += -g -gstabs
@ -89,6 +87,7 @@ release: clean LiteXL
@mkdir -p release/LiteXL2
@cp -r resources/amiga/* release/LiteXL2/
@mv release/LiteXL2/LiteXL2.info release/
@mv release/LiteXL2/AutoInstall release/
@cp -r data release/LiteXL2/
@cp changelog.md release/LiteXL2/
@cp $(outfile) release/LiteXL2/

View File

@ -1,7 +1,7 @@
# Lite XL
[![CI]](https://github.com/lite-xl/lite-xl/actions/workflows/build.yml)
[![Discord Badge Image]](https://discord.gg/UQKnzBhY5H)
[![Discord Badge Image]](https://discord.gg/RWzqC3nx7K)
![screenshot-dark]
@ -124,7 +124,6 @@ cd lite-xl
```
To run lite-xl without installing:
```sh
./lite-xl
```
@ -137,68 +136,31 @@ 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/
```
#### Add Lite XL to PATH
To run Lite XL from the command line, you must add it to PATH.
If `$HOME/.local/bin` is not in PATH:
```sh
echo -e 'export PATH=$PATH:$HOME/.local/bin' >> $HOME/.bashrc
```
Alternatively on recent versions of GNOME and KDE Plasma,
you can add `$HOME/.local/bin` to PATH via `~/.config/environment.d/envvars.conf`:
```ini
PATH=$HOME/.local/bin:$PATH
```
> **Note**
> Some systems might not load `.bashrc` when logging in.
> This can cause problems with launching applications from the desktop / menu.
#### Add Lite XL to application launchers
To get the icon to show up in app launcher, you need to create a desktop
entry and put it into `/usr/share/applications` or `~/.local/share/applications`.
Here is an example for a desktop entry in `~/.local/share/applications/com.lite_xl.LiteXL.desktop`,
assuming Lite XL is in PATH:
```ini
[Desktop Entry]
Type=Application
Name=Lite XL
Comment=A lightweight text editor written in Lua
Exec=lite-xl %F
Icon=lite-xl
Terminal=false
StartupWMClass=lite-xl
Categories=Development;IDE;
MimeType=text/plain;inode/directory;
```
To get the icon to show up in app launcher immediately, run:
To get the icon to show up in app launcher:
```sh
xdg-desktop-menu forceupdate
```
Alternatively, you may log out and log in again.
You may need to logout and login again to see icon in app launcher.
#### Uninstall
To uninstall Lite XL, run:
To uninstall just run:
```sh
rm -f $HOME/.local/bin/lite-xl
rm -rf $HOME/.local/share/icons/hicolor/scalable/apps/lite-xl.svg \
$HOME/.local/share/applications/com.lite_xl.LiteXL.desktop \
$HOME/.local/share/metainfo/com.lite_xl.LiteXL.appdata.xml \
$HOME/.local/share/applications/org.lite_xl.lite_xl.desktop \
$HOME/.local/share/metainfo/org.lite_xl.lite_xl.appdata.xml \
$HOME/.local/share/lite-xl
```
## Contributing
Any additional functionality that can be added through a plugin should be done

View File

@ -88,10 +88,11 @@ Shows the current time and date in a view with large text
Underlines matching pair for bracket under the caret
**codesets**
This plugin is exclusive for AmigaOS 4 and uses the codesets.library to
translate ISO encoded files to unicode. 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 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
@ -115,7 +116,10 @@ Marks tabs as non-preview on any change or tab double clicking.
**ghmarkdown**
Opens a preview of the current markdown file in a browser window.
On AmigaOS 4 it uses *urlopen* and on MorphOS it uses *openurl* to load
the generated html in the browser.
the generated html in the browser. It requires a GitHub application token
because it uses its Rest API. Add it at the init.lua file in your config
folder like below:
`config.plugins.ghmarkdown.github_token = "<token here>"`
**indentguide**
Adds indent guides
@ -174,6 +178,9 @@ Highlights regions of code that match the current selection
**smallclock**
It adds a small clock at the bottom right corner.
**tetris**
Play Tetris inside Lite XL.
## Tips and tricks
### Transitions
@ -223,6 +230,46 @@ https://git.walkero.gr/walkero/lite-xl/issues
# 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).

View File

@ -1,5 +1,505 @@
# 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
### 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
[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.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

View File

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

View File

@ -44,8 +44,8 @@ local function save(filename)
else
core.error(err)
core.nag_view:show("Saving failed", string.format("Couldn't save file \"%s\". Do you want to save to another location?", doc().filename), {
{ text = "No", default_no = true },
{ text = "Yes", default_yes = true }
{ text = "Yes", default_yes = true },
{ text = "No", default_no = true }
}, function(item)
if item.text == "Yes" then
core.add_thread(function()
@ -93,11 +93,14 @@ local function cut_or_copy(delete)
system.set_clipboard(full_text)
end
local function split_cursor(direction)
local function split_cursor(dv, direction)
local new_cursors = {}
for _, line1, col1 in doc():get_selections() do
if line1 + direction >= 1 and line1 + direction <= #doc().lines then
table.insert(new_cursors, { line1 + direction, col1 })
local dv_translate = direction < 0
and DocView.translate.previous_line
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
-- add selections in the order that will leave the "last" added one as doc.last_selection
@ -107,7 +110,7 @@ local function split_cursor(direction)
end
for i = start, stop, direction do
local v = new_cursors[i]
doc():add_selection(v[1], v[2])
dv.doc:add_selection(v[1], v[2])
end
core.blink_reset()
end
@ -360,7 +363,7 @@ local commands = {
["doc:select-lines"] = function(dv)
for idx, line1, _, line2 in dv.doc:get_selections(true) do
append_line_if_last_line(line2)
dv.doc:set_selections(idx, line2 + 1, 1, line1, 1)
dv.doc:set_selections(idx, line1, 1, line2 + 1, 1)
end
end,
@ -545,11 +548,6 @@ local commands = {
dv.doc.crlf = not dv.doc.crlf
end,
["doc:toggle-overwrite"] = function(dv)
dv.doc.overwrite = not dv.doc.overwrite
core.blink_reset() -- to show the cursor has changed edit modes
end,
["doc:save-as"] = function(dv)
local last_doc = core.last_active_view and core.last_active_view.doc
local text
@ -626,12 +624,12 @@ local commands = {
end,
["doc:create-cursor-previous-line"] = function(dv)
split_cursor(-1)
split_cursor(dv, -1)
dv.doc:merge_cursors()
end,
["doc:create-cursor-next-line"] = function(dv)
split_cursor(1)
split_cursor(dv, 1)
dv.doc:merge_cursors()
end

View File

@ -196,23 +196,6 @@ local function select_next(reverse)
if l2 then doc():set_selection(l2, c2, l1, c1) end
end
---@param in_selection? boolean whether to replace in the selections only, or in the whole file.
local function find_replace(in_selection)
local l1, c1, l2, c2 = doc():get_selection()
local selected_text = ""
if not in_selection then
selected_text = doc():get_text(l1, c1, l2, c2)
doc():set_selection(l2, c2, l2, c2)
end
replace("Text", l1 == l2 and selected_text or "", function(text, old, new)
if not find_regex then
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
end
local result, matches = regex.gsub(regex.compile(old, "m"), text, new)
return result, matches
end)
end
command.add(has_unique_selection, {
["find-replace:select-next"] = select_next,
["find-replace:select-previous"] = function() select_next(true) end,
@ -229,11 +212,15 @@ command.add("core.docview!", {
end,
["find-replace:replace"] = function()
find_replace()
end,
["find-replace:replace-in-selection"] = function()
find_replace(true)
local l1, c1, l2, c2 = doc():get_selection()
local selected_text = doc():get_text(l1, c1, l2, c2)
replace("Text", l1 == l2 and selected_text or "", function(text, old, new)
if not find_regex then
return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil)
end
local result, matches = regex.gsub(regex.compile(old, "m"), text, new)
return result, matches
end)
end,
["find-replace:replace-symbol"] = function()

View File

@ -4,7 +4,6 @@ local DocView = require "core.docview"
local command = require "core.command"
local common = require "core.common"
local config = require "core.config"
local Node = require "core.node"
local t = {
@ -30,6 +29,20 @@ local t = {
core.confirm_close_docs(docs, core.root_view.close_all_docviews, core.root_view, true)
end,
["root:switch-to-previous-tab"] = function(node)
local idx = node:get_view_idx(core.active_view)
idx = idx - 1
if idx < 1 then idx = #node.views end
node:set_active_view(node.views[idx])
end,
["root:switch-to-next-tab"] = function(node)
local idx = node:get_view_idx(core.active_view)
idx = idx + 1
if idx > #node.views then idx = 1 end
node:set_active_view(node.views[idx])
end,
["root:move-tab-left"] = function(node)
local idx = node:get_view_idx(core.active_view)
if idx > 1 then
@ -104,7 +117,7 @@ end, t)
command.add(nil, {
["root:scroll"] = function(delta)
local view = core.root_view.overlapping_view or core.active_view
local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view
if view and view.scrollable then
view.scroll.to.y = view.scroll.to.y + delta * -config.mouse_wheel_scroll
return true
@ -112,7 +125,7 @@ command.add(nil, {
return false
end,
["root:horizontal-scroll"] = function(delta)
local view = core.root_view.overlapping_view or core.active_view
local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view
if view and view.scrollable then
view.scroll.to.x = view.scroll.to.x + delta * -config.mouse_wheel_scroll
return true
@ -120,74 +133,3 @@ command.add(nil, {
return false
end
})
command.add(function(node)
if not Node:is_extended_by(node) then node = nil end
-- No node was specified, use the active one
node = node or core.root_view:get_active_node()
if not node then return false end
return true, node
end,
{
["root:switch-to-previous-tab"] = function(node)
local idx = node:get_view_idx(node.active_view)
idx = idx - 1
if idx < 1 then idx = #node.views end
node:set_active_view(node.views[idx])
end,
["root:switch-to-next-tab"] = function(node)
local idx = node:get_view_idx(node.active_view)
idx = idx + 1
if idx > #node.views then idx = 1 end
node:set_active_view(node.views[idx])
end,
["root:scroll-tabs-backward"] = function(node)
node:scroll_tabs(1)
end,
["root:scroll-tabs-forward"] = function(node)
node:scroll_tabs(2)
end
}
)
command.add(function()
local node = core.root_view.root_node:get_child_overlapping_point(core.root_view.mouse.x, core.root_view.mouse.y)
if not node then return false end
return (node.hovered_tab or node.hovered_scroll_button > 0) and true, node
end,
{
["root:switch-to-hovered-previous-tab"] = function(node)
command.perform("root:switch-to-previous-tab", node)
end,
["root:switch-to-hovered-next-tab"] = function(node)
command.perform("root:switch-to-next-tab", node)
end,
["root:scroll-hovered-tabs-backward"] = function(node)
command.perform("root:scroll-tabs-backward", node)
end,
["root:scroll-hovered-tabs-forward"] = function(node)
command.perform("root:scroll-tabs-forward", node)
end
}
)
-- double clicking the tab bar, or on the emptyview should open a new doc
command.add(function(x, y)
local node = x and y and core.root_view.root_node:get_child_overlapping_point(x, y)
return node and node:is_in_tab_area(x, y)
end, {
["tabbar:new-doc"] = function()
command.perform("core:new-doc")
end
})
command.add("core.emptyview", {
["emptyview:new-doc"] = function()
command.perform("core:new-doc")
end
})

View File

@ -50,6 +50,7 @@ local default_state = {
function CommandView:new()
CommandView.super.new(self, SingleLineDoc())
self.suggestion_idx = 1
self.suggestions_offset = 1
self.suggestions = {}
self.suggestions_height = 0
self.last_change_id = 0
@ -84,11 +85,6 @@ function CommandView:get_line_screen_position(line, col)
end
function CommandView:supports_text_input()
return true
end
function CommandView:get_scrollable_size()
return 0
end
@ -128,6 +124,24 @@ function CommandView:move_suggestion_idx(dir)
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
local n = self.suggestion_idx + dir
self.suggestion_idx = overflow_suggestion_idx(n, #self.suggestions)
@ -151,6 +165,8 @@ function CommandView:move_suggestion_idx(dir)
self.last_change_id = self.doc:get_change_id()
self.state.suggest(self:get_text())
end
self.suggestions_offset = get_suggestions_offset()
end
@ -261,6 +277,7 @@ function CommandView:update_suggestions()
end
self.suggestions = res
self.suggestion_idx = 1
self.suggestions_offset = 1
end
@ -304,7 +321,7 @@ function CommandView:update()
self:move_towards("suggestions_height", dest, nil, "commandview")
-- 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")
-- update size based on whether this is the active_view
@ -340,6 +357,7 @@ local function draw_suggestions_box(self)
local h = math.ceil(self.suggestions_height)
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
if #self.suggestions > 0 then
renderer.draw_rect(rx, ry, rw, rh, style.background3)
@ -349,14 +367,12 @@ local function draw_suggestions_box(self)
end
-- draw suggestion text
local offset = math.max(self.suggestion_idx - max_suggestions, 0)
local last = math.min(offset + max_suggestions, #self.suggestions)
core.push_clip_rect(rx, ry, rw, rh)
local first = 1 + offset
local first = math.max(self.suggestions_offset, 1)
local last = math.min(self.suggestions_offset + max_suggestions, #self.suggestions)
for i=first, last do
local item = self.suggestions[i]
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)
if item.info then

View File

@ -226,7 +226,13 @@ function common.path_suggest(text, root)
if root and root:sub(-1) ~= PATHSEP then
root = root .. PATHSEP
end
local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
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
-- ignore root if path is absolute
local is_absolute = common.is_absolute_path(text)
@ -279,7 +285,12 @@ end
---@param text string The input path.
---@return string[]
function common.dir_path_suggest(text)
local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
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 res = {}
for _, file in ipairs(files) do
@ -298,7 +309,13 @@ end
---@param dir_list string[] A list of paths to filter.
---@return string[]
function common.dir_list_suggest(text, dir_list)
local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
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 = {}
for _, dir_path in ipairs(dir_list) do
if dir_path:lower():find(text:lower(), nil, true) == 1 then
@ -465,11 +482,35 @@ function common.basename(path)
end
---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)
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
@ -519,6 +560,13 @@ local function split_on_slash(s, sep_pattern)
if s:match("^["..PATHSEP.."]") then
t[#t + 1] = ""
end
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
end
@ -541,6 +589,12 @@ function common.normalize_volume(filename)
return drive:upper() .. rem
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
end
@ -566,6 +620,11 @@ function common.normalize_path(filename)
volume, filename = drive, rem
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
local relpath = filename:match('^/(.+)')
if relpath then
@ -596,7 +655,7 @@ end
---@param path string
---@return boolean
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
@ -605,7 +664,7 @@ end
---@param path string The parent path.
---@return boolean
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
@ -615,6 +674,9 @@ end
---@return boolean
function common.relative_path(ref_dir, dir)
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)
if drive and ref_drive and drive ~= ref_drive then
-- Windows, different drives, system.absolute_path fails for C:\..\D:\
@ -657,7 +719,7 @@ function common.mkdirp(path)
path = updir
end
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
return false, "cannot create directory", path
end
@ -721,4 +783,3 @@ end
return common

View File

@ -2,71 +2,15 @@ local common = require "core.common"
local config = {}
---The frame rate of Lite XL.
---Note that setting this value to the screen's refresh rate
---does not eliminate screen tearing.
---
---Defaults to 60.
---@type number
config.fps = 60
---Maximum number of log items that will be stored.
---When the number of log items exceed this value, old items will be discarded.
---
---Defaults to 800.
---@type number
config.max_log_items = 800
---The timeout, in seconds, before a message dissapears from StatusView.
---
---Defaults to 5.
---@type number
config.message_timeout = 5
---The number of pixels scrolled per-step.
---
---Defaults to 50 * SCALE.
---@type number
config.mouse_wheel_scroll = 50 * SCALE
---Enables/disables transitions when scrolling with the scrollbar.
---When enabled, the scrollbar will have inertia and slowly move towards the cursor.
---Otherwise, the scrollbar will immediately follow the cursor.
---
---Defaults to false.
---@type boolean
config.animate_drag_scroll = false
---Enables/disables scrolling past the end of a document.
---
---Defaults to true.
---@type boolean
config.scroll_past_end = true
---@alias config.scrollbartype
---| "expanded" # A thicker scrollbar is shown at all times.
---| "contracted" # A thinner scrollbar is shown at all times.
---| false # The scrollbar expands when the cursor hovers over it.
---Controls whether the DocView scrollbar is always shown or hidden.
---This option does not affect other View's scrollbars.
---
---Defaults to false.
---@type config.scrollbartype
---@type "expanded" | "contracted" | false @Force the scrollbar status of the DocView
config.force_scrollbar_status = false
---The file size limit, in megabytes.
---Files larger than this size will not be shown in the file picker.
---
---Defaults to 10.
---@type number
config.file_size_limit = 10
---A list of files and directories to ignore.
---Each element is a Lua pattern, where patterns ending with a forward slash
---are recognized as directories while patterns ending with an anchor ("$") are
---recognized as files.
---@type string[]
config.ignore_files = {
-- folders
"^%.svn/", "^%.git/", "^%.hg/", "^CVS/", "^%.Trash/", "^%.Trash%-.*/",
@ -77,194 +21,46 @@ config.ignore_files = {
"%.suo$", "%.pdb$", "%.idb$", "%.class$", "%.psd$", "%.db$",
"^desktop%.ini$", "^%.DS_Store$", "^%.directory$",
}
---Lua pattern used to find symbols when advanced syntax highlighting
---is not available.
---This pattern is also used for navigation, e.g. move to next word.
---
---The default pattern matches all letters, followed by any number
---of letters and digits.
---@type string
config.symbol_pattern = "[%a_][%w_]*"
---A list of characters that delimits a word.
---
---The default is ``" \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"``
---@type string
config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
---The timeout, in seconds, before several consecutive actions
---are merged as a single undo step.
---
---The default is 0.3 seconds.
---@type number
config.undo_merge_timeout = 0.3
---The maximum number of undo steps per-document.
---
---The default is 10000.
---@type number
config.max_undos = 10000
---The maximum number of tabs shown at a time.
---
---The default is 8.
---@type number
config.max_tabs = 8
---Shows/hides the tab bar when there is only one tab open.
---
---The tab bar is always shown by default.
---@type boolean
config.always_show_tabs = true
---@alias config.highlightlinetype
---| true # Always highlight the current line.
---| false # Never highlight the current line.
---| "no_selection" # Highlight the current line if no text is selected.
---Highlights the current line.
---
---The default is true.
---@type config.highlightlinetype
-- Possible values: false, true, "no_selection"
config.highlight_current_line = true
---The spacing between each line of text.
---
---The default is 120% of the height of the text (1.2).
---@type number
config.line_height = 1.2
---The number of spaces each level of indentation represents.
---
---The default is 2.
---@type number
config.indent_size = 2
---The type of indentation.
---
---The default is "soft" (spaces).
---@type "soft" | "hard"
config.tab_type = "soft"
---Do not remove whitespaces when advancing to the next line.
---
---Defaults to false.
---@type boolean
config.keep_newline_whitespace = false
---Default line endings for new files.
---
---Defaults to `crlf` (`\r\n`) on Windows and `lf` (`\n`) on everything else.
---@type "crlf" | "lf"
config.line_endings = PLATFORM == "Windows" and "crlf" or "lf"
---Maximum number of characters per-line for the line guide.
---
---Defaults to 80.
---@type number
config.line_limit = 80
---Maximum number of project files to keep track of.
---If the number of files in the project exceeds this number,
---Lite XL will not be able to keep track of them.
---They will be not be searched when searching for files or text.
---
---Defaults to 2000.
---@type number
config.max_project_files = 2000
---Enables/disables all transitions.
---
---Defaults to true.
---@type boolean
config.transitions = true
---Enable/disable individual transitions.
---These values are overriden by `config.transitions`.
config.disabled_transitions = {
---Disables scrolling transitions.
scroll = false,
---Disables transitions for CommandView's suggestions list.
commandview = false,
---Disables transitions for showing/hiding the context menu.
contextmenu = false,
---Disables transitions when clicking on log items in LogView.
logview = false,
---Disables transitions for showing/hiding the Nagbar.
nagbar = false,
---Disables transitions when scrolling the tab bar.
tabs = false,
---Disables transitions when a tab is being dragged.
tab_drag = false,
---Disables transitions when a notification is shown.
statusbar = false,
}
---The rate of all transitions.
---
---Defaults to 1.
---@type number
config.animation_rate = 1.0
---The caret's blinking period, in seconds.
---
---Defaults to 0.8.
---@type number
config.blink_period = 0.8
---Disables caret blinking.
---
---Defaults to false.
---@type boolean
config.disable_blink = false
---Draws whitespaces as dots.
---This option is deprecated.
---Please use the drawwhitespace plugin instead.
---@deprecated
config.draw_whitespace = false
---Disables system-drawn window borders.
---
---When set to true, Lite XL draws its own window decorations,
---which can be useful for certain setups.
---
---Defaults to false.
---@type boolean
config.borderless = false
---Shows/hides the close buttons on tabs.
---When hidden, users can close tabs via keyboard shortcuts or commands.
---
---Defaults to true.
---@type boolean
config.tab_close_button = true
---Maximum number of clicks recognized by Lite XL.
---
---Defaults to 3.
---@type number
config.max_clicks = 3
---Disables plugin version checking.
---Do not change this unless you know what you are doing.
---
---Defaults to false.
---@type boolean
-- set as true to be able to test non supported plugins
config.skip_plugins_version = false
-- holds the plugins real config table
local plugins_config = {}
---A table containing configuration for all the plugins.
---
---This is a metatable that automaticaly creates a minimal
---configuration when a plugin is initially configured.
---Each plugins will then call `common.merge()` to get the finalized
---plugin config.
---Do not use raw operations on this table.
---@type table
-- virtual representation of plugins config table
config.plugins = {}
-- allows virtual access to the plugins config table

View File

@ -177,7 +177,8 @@ end
-- compute a file's info entry completed with "filename" to be used
-- in project scan or falsy if it shouldn't appear in the list.
local function get_project_file_info(root, file, 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
-- a directory, for example for /dev/* entries on linux.
if info and info.type then
@ -200,7 +201,8 @@ function dirwatch.get_directory_files(dir, root, path, entries_count, recurse_pr
local t0 = system.get_time()
local ignore_compiled = compile_ignore_files()
local all = system.list_dir(root .. PATHSEP .. path)
local all = system.list_dir(common.basepath(root) .. path)
if not all then return nil end
local entries = { }
for _, file in ipairs(all) do

View File

@ -48,7 +48,7 @@ function Highlighter:start()
self:update_notify(retokenized_from, max - retokenized_from)
end
core.redraw = true
coroutine.yield(0)
coroutine.yield()
end
self.max_wanted_line = 0
self.running = false

View File

@ -1,6 +1,5 @@
local Object = require "core.object"
local Highlighter = require "core.doc.highlighter"
local translate = require "core.doc.translate"
local core = require "core"
local syntax = require "core.syntax"
local config = require "core.config"
@ -28,11 +27,9 @@ function Doc:new(filename, abs_filename, new_file)
self:load(filename)
end
end
if new_file then
self.crlf = config.line_endings == "crlf"
end
end
function Doc:reset()
self.lines = { "\n" }
self.selections = { 1, 1, 1, 1 }
@ -41,15 +38,15 @@ function Doc:reset()
self.redo_stack = { idx = 1 }
self.clean_change_id = 1
self.highlighter = Highlighter(self)
self.overwrite = false
self:reset_syntax()
end
function Doc:reset_syntax()
local header = self:get_text(1, 1, self:position_offset(1, 1, 128))
local path = self.abs_filename
if not path and self.filename then
path = core.project_dir .. PATHSEP .. self.filename
path = common.basepath(core.project_dir) .. self.filename
end
if path then path = common.normalize_path(path) end
local syn = syntax.get(path, header)
@ -59,14 +56,16 @@ function Doc:reset_syntax()
end
end
function Doc:set_filename(filename, abs_filename)
self.filename = filename
self.abs_filename = abs_filename
self:reset_syntax()
end
function Doc:load(filename)
local fp = assert(io.open(filename, "rb"))
local fp = assert( io.open(filename, "rb") )
self:reset()
self.lines = {}
local i = 1
@ -86,6 +85,7 @@ function Doc:load(filename)
self:reset_syntax()
end
function Doc:reload()
if self.filename then
local sel = { self:get_selection() }
@ -95,6 +95,7 @@ function Doc:reload()
end
end
function Doc:save(filename, abs_filename)
if not filename then
assert(self.filename, "no filename set to default to")
@ -103,7 +104,7 @@ function Doc:save(filename, abs_filename)
else
assert(self.filename or abs_filename, "calling save on unnamed doc without absolute path")
end
local fp = assert(io.open(filename, "wb"))
local 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)
@ -114,10 +115,12 @@ function Doc:save(filename, abs_filename)
self:clean()
end
function Doc:get_name()
return self.filename or "unsaved"
end
function Doc:is_dirty()
if self.new_file then
if self.filename then return true end
@ -127,17 +130,20 @@ function Doc:is_dirty()
end
end
function Doc:clean()
self.clean_change_id = self:get_change_id()
end
function Doc:get_indent_info()
if not self.indent_info then return config.tab_type, config.indent_size, false end
return self.indent_info.type or config.tab_type,
self.indent_info.size or config.indent_size,
self.indent_info.confirmed
self.indent_info.size or config.indent_size,
self.indent_info.confirmed
end
function Doc:get_change_id()
return self.undo_stack.idx
end
@ -161,14 +167,13 @@ function Doc:get_selection(sort)
return line1, col1, line2, col2, swap
end
---Get the selection specified by `idx`
---@param idx integer @the index of the selection to retrieve
---@param sort? boolean @whether to sort the selection returned
---@return integer,integer,integer,integer,boolean? @line1, col1, line2, col2, was the selection sorted
function Doc:get_selection_idx(idx, sort)
local line1, col1, line2, col2 = self.selections[idx * 4 - 3], self.selections[idx * 4 - 2],
self.selections[idx * 4 - 1],
self.selections[idx * 4]
local line1, col1, line2, col2 = self.selections[idx*4-3], self.selections[idx*4-2], self.selections[idx*4-1], self.selections[idx*4]
if line1 and sort then
return sort_positions(line1, col1, line2, col2)
else
@ -212,7 +217,7 @@ function Doc:set_selections(idx, line1, col1, line2, col2, swap, rm)
if swap then line1, col1, line2, col2 = line2, col2, line1, col1 end
line1, col1 = self:sanitize_position(line1, col1)
line2, col2 = self:sanitize_position(line2 or line1, col2 or col1)
common.splice(self.selections, (idx - 1) * 4 + 1, rm == nil and 4 or rm, { line1, col1, line2, col2 })
common.splice(self.selections, (idx - 1)*4 + 1, rm == nil and 4 or rm, { line1, col1, line2, col2 })
end
function Doc:add_selection(line1, col1, line2, col2, swap)
@ -228,6 +233,7 @@ function Doc:add_selection(line1, col1, line2, col2, swap)
self.last_selection = target
end
function Doc:remove_selection(idx)
if self.last_selection >= idx then
self.last_selection = self.last_selection - 1
@ -235,6 +241,7 @@ function Doc:remove_selection(idx)
common.splice(self.selections, (idx - 1) * 4 + 1, 4)
end
function Doc:set_selection(line1, col1, line2, col2, swap)
self.selections = {}
self:set_selections(1, line1, col1, line2, col2, swap)
@ -245,24 +252,24 @@ function Doc:merge_cursors(idx)
for i = (idx or (#self.selections - 3)), (idx or 5), -4 do
for j = 1, i - 4, 4 do
if self.selections[i] == self.selections[j] and
self.selections[i + 1] == self.selections[j + 1] then
common.splice(self.selections, i, 4)
if self.last_selection >= (i + 3) / 4 then
self.last_selection = self.last_selection - 1
end
break
self.selections[i+1] == self.selections[j+1] then
common.splice(self.selections, i, 4)
if self.last_selection >= (i+3)/4 then
self.last_selection = self.last_selection - 1
end
break
end
end
end
end
local function selection_iterator(invariant, idx)
local target = invariant[3] and (idx * 4 - 7) or (idx * 4 + 1)
local target = invariant[3] and (idx*4 - 7) or (idx*4 + 1)
if target > #invariant[1] or target <= 0 or (type(invariant[3]) == "number" and invariant[3] ~= idx - 1) then return end
if invariant[2] then
return idx + (invariant[3] and -1 or 1), sort_positions(table.unpack(invariant[1], target, target + 4))
return idx+(invariant[3] and -1 or 1), sort_positions(table.unpack(invariant[1], target, target+4))
else
return idx + (invariant[3] and -1 or 1), table.unpack(invariant[1], target, target + 4)
return idx+(invariant[3] and -1 or 1), table.unpack(invariant[1], target, target+4)
end
end
@ -270,9 +277,8 @@ end
-- If a number, runs for exactly that iteration.
function Doc:get_selections(sort_intra, idx_reverse)
return selection_iterator, { self.selections, sort_intra, idx_reverse },
idx_reverse == true and ((#self.selections / 4) + 1) or ((idx_reverse or -1) + 1)
idx_reverse == true and ((#self.selections / 4) + 1) or ((idx_reverse or -1)+1)
end
-- End of cursor seciton.
function Doc:sanitize_position(line, col)
@ -285,6 +291,7 @@ function Doc:sanitize_position(line, col)
return line, common.clamp(col, 1, #self.lines[line])
end
local function position_offset_func(self, line, col, fn, ...)
line, col = self:sanitize_position(line, col)
return fn(self, line, col, ...)
@ -323,6 +330,7 @@ function Doc:position_offset(line, col, ...)
end
end
function Doc:get_text(line1, col1, line2, col2)
line1, col1 = self:sanitize_position(line1, col1)
line2, col2 = self:sanitize_position(line2, col2)
@ -338,11 +346,13 @@ function Doc:get_text(line1, col1, line2, col2)
return table.concat(lines)
end
function Doc:get_char(line, col)
line, col = self:sanitize_position(line, col)
return self.lines[line]:sub(col, col)
end
local function push_undo(undo_stack, time, type, ...)
undo_stack[undo_stack.idx] = { type = type, time = time, ... }
undo_stack[undo_stack.idx - config.max_undos] = nil
@ -403,8 +413,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
if cline1 < line then break end
local line_addition = (line < cline1 or col < ccol1) and #lines - 1 or 0
local column_addition = line == cline1 and ccol1 > col and len or 0
self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition,
ccol2 + column_addition)
self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition, ccol2 + column_addition)
end
-- push undo
@ -417,6 +426,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
self:sanitize_selection()
end
function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
-- push undo
local text = self:get_text(line1, col1, line2, col2)
@ -475,6 +485,7 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
self:sanitize_selection()
end
function Doc:insert(line, col, text)
self.redo_stack = { idx = 1 }
-- Reset the clean id when we're pushing something new before it
@ -486,6 +497,7 @@ function Doc:insert(line, col, text)
self:on_text_change("insert")
end
function Doc:remove(line1, col1, line2, col2)
self.redo_stack = { idx = 1 }
line1, col1 = self:sanitize_position(line1, col1)
@ -495,34 +507,28 @@ function Doc:remove(line1, col1, line2, col2)
self:on_text_change("remove")
end
function Doc:undo()
pop_undo(self, self.undo_stack, self.redo_stack, false)
end
function Doc:redo()
pop_undo(self, self.redo_stack, self.undo_stack, false)
end
function Doc:text_input(text, idx)
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
local had_selection = false
if line1 ~= line2 or col1 ~= col2 then
self:delete_to_cursor(sidx)
had_selection = true
end
if self.overwrite
and not had_selection
and col1 < #self.lines[line1]
and text:ulen() == 1 then
self:remove(line1, col1, translate.next_char(self, line1, col1))
end
self:insert(line1, col1, text)
self:move_to_cursor(sidx, #text)
end
end
function Doc:ime_text_editing(text, start, length, idx)
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
if line1 ~= line2 or col1 ~= col2 then
@ -533,6 +539,7 @@ function Doc:ime_text_editing(text, start, length, idx)
end
end
function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
local old_text = self:get_text(line1, col1, line2, col2)
local new_text, res = fn(old_text)
@ -548,7 +555,7 @@ function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
end
function Doc:replace(fn)
local has_selection, results = false, {}
local has_selection, results = false, { }
for idx, line1, col1, line2, col2 in self:get_selections(true) do
if line1 ~= line2 or col1 ~= col2 then
results[idx] = self:replace_cursor(idx, line1, col1, line2, col2, fn)
@ -562,6 +569,7 @@ function Doc:replace(fn)
return results
end
function Doc:delete_to_cursor(idx, ...)
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do
if line1 ~= line2 or col1 ~= col2 then
@ -575,7 +583,6 @@ function Doc:delete_to_cursor(idx, ...)
end
self:merge_cursors(idx)
end
function Doc:delete_to(...) return self:delete_to_cursor(nil, ...) end
function Doc:move_to_cursor(idx, ...)
@ -584,9 +591,9 @@ function Doc:move_to_cursor(idx, ...)
end
self:merge_cursors(idx)
end
function Doc:move_to(...) return self:move_to_cursor(nil, ...) end
function Doc:select_to_cursor(idx, ...)
for sidx, line, col, line2, col2 in self:get_selections(false, idx) do
line, col = self:position_offset(line, col, ...)
@ -594,9 +601,9 @@ function Doc:select_to_cursor(idx, ...)
end
self:merge_cursors(idx)
end
function Doc:select_to(...) return self:select_to_cursor(nil, ...) end
function Doc:get_indent_string()
local indent_type, indent_size = self:get_indent_info()
if indent_type == "hard" then
@ -618,7 +625,7 @@ function Doc:get_line_indent(line, rnd_up)
local indent = e and line:sub(1, e):gsub("\t", soft_tab) or ""
local number = #indent / #soft_tab
return e, indent:sub(1,
(rnd_up and math.ceil(number) or math.floor(number)) * #soft_tab)
(rnd_up and math.ceil(number) or math.floor(number))*#soft_tab)
end
end
@ -667,4 +674,5 @@ function Doc:on_close()
core.log_quiet("Closed doc \"%s\"", self:get_name())
end
return Doc

View File

@ -254,11 +254,6 @@ function DocView:scroll_to_line(line, ignore_if_visible, instant)
end
function DocView:supports_text_input()
return true
end
function DocView:scroll_to_make_visible(line, col)
local _, oy = self:get_content_offset()
local _, ly = self:get_line_screen_position(line, col)
@ -322,7 +317,7 @@ function DocView:mouse_selection(doc, snap_type, line1, col1, line2, col2)
line1, col1 = translate.start_of_word(doc, line1, col1)
line2, col2 = translate.end_of_word(doc, line2, col2)
elseif snap_type == "lines" then
col1, col2, line2 = 1, 1, line2 + 1
col1, col2 = 1, math.huge
end
if swap then
return line2, col2, line1, col1
@ -460,16 +455,9 @@ function DocView:draw_line_text(line, x, y)
return self:get_line_height()
end
function DocView:draw_overwrite_caret(x, y, width)
local lh = self:get_line_height()
renderer.draw_rect(x, y + lh - style.caret_width, width, style.caret_width, style.caret)
end
function DocView:draw_caret(x, y)
local lh = self:get_line_height()
renderer.draw_rect(x, y, style.caret_width, lh, style.caret)
local lh = self:get_line_height()
renderer.draw_rect(x, y, style.caret_width, lh, style.caret)
end
function DocView:draw_line_body(line, x, y)
@ -566,12 +554,7 @@ function DocView:draw_overlay()
else
if config.disable_blink
or (core.blink_timer - core.blink_start) % T < T / 2 then
local x, y = self:get_line_screen_position(line1, col1)
if self.doc.overwrite then
self:draw_overwrite_caret(x, y, self:get_font():get_width(self.doc:get_char(line1, col1)))
else
self:draw_caret(x, y)
end
self:draw_caret(self:get_line_screen_position(line1, col1))
end
end
end

View File

@ -18,13 +18,13 @@ local Doc
local core = {}
local function load_session()
local ok, t = pcall(dofile, USERDIR .. PATHSEP .. "session.lua")
local ok, t = pcall(dofile, common.basepath(USERDIR) .. "session.lua")
return ok and t or {}
end
local function save_session()
local fp = io.open(USERDIR .. PATHSEP .. "session.lua", "w")
local fp = io.open(common.basepath(USERDIR) .. "session.lua", "w")
if fp then
fp:write("return {recents=", common.serialize(core.recent_projects),
", window=", common.serialize(table.pack(system.get_window_size())),
@ -188,7 +188,7 @@ local function refresh_directory(topdir, target)
-- If this file doesn't exist, we should be calling this on our parent directory, assume we'll do that.
-- Unwatch just in case.
if files == nil then
topdir.watch:unwatch(topdir.name .. PATHSEP .. (target or ""))
topdir.watch:unwatch(common.basepath(topdir.name) .. (target or ""))
return true
end
@ -212,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.
table.remove(topdir.files, old_idx)
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
directory_end_idx = directory_end_idx - 1
end
@ -223,7 +223,7 @@ local function refresh_directory(topdir, target)
end
end
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
refresh_directory(topdir, v.filename)
end
@ -272,7 +272,7 @@ function core.add_project_directory(path)
refresh_directory(topdir)
else
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
topdir.watch:watch(topdir.name)
@ -286,7 +286,7 @@ function core.add_project_directory(path)
local changed = topdir.watch:check(function(target)
if target == topdir.name then return refresh_directory(topdir) end
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
-- 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"})
@ -373,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")
dir.shown_subdir[filename] = expanded
if expanded then
dir.watch:watch(dir.name .. PATHSEP .. filename)
dir.watch:watch(common.basepath(dir.name) .. filename)
else
dir.watch:unwatch(dir.name .. PATHSEP .. filename)
dir.watch:unwatch(common.basepath(dir.name) .. filename)
end
return refresh_directory(dir, filename)
end
@ -387,7 +387,7 @@ end
local function find_files_rec(root, path)
local all = system.list_dir(root .. path) or {}
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)
if info then
info.filename = strip_leading_path(file)
@ -462,7 +462,7 @@ local function create_user_directory()
error("cannot create directory \"" .. USERDIR .. "\": " .. err)
end
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
error("cannot create directory: \"" .. subdirname .. "\"")
end
@ -572,22 +572,22 @@ local config = require "core.config"
--
-- 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
-- of any directory entry before checking if it matches.
--
-- "^%.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
-- is immediately followed by a '$' then the pattern will be applied to the full
-- 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
-- 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.
--
-- "^/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"
-- "^/subprojects/.+/" match any directory inside a top-level folder named "subprojects".
-- "^/build.*/" will match any top level directory whose name begins with "build".
-- "^/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.
-- config.plugins.trimwitespace = true
@ -602,7 +602,7 @@ function core.load_user_directory()
if not stat_dir then
create_user_directory()
end
local init_filename = USERDIR .. PATHSEP .. "init.lua"
local init_filename = common.basepath(USERDIR) .. "init.lua"
local stat_file = system.get_file_info(init_filename)
if not stat_file then
write_user_init_file(init_filename)
@ -635,7 +635,7 @@ end
local function add_config_files_hooks()
-- auto-realod style when user's module is saved by overriding 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)
local module_filename = system.absolute_path(".lite_project.lua")
doc_save(self, filename, abs_filename)
@ -660,17 +660,14 @@ function core.project_absolute_path(filename)
return common.normalize_path(filename)
elseif not core.project_dir then
local cwd = system.absolute_path(".")
return cwd .. PATHSEP .. common.normalize_path(filename)
return common.basepath(cwd) .. common.normalize_path(filename)
else
return core.project_dir .. PATHSEP .. filename
return common.basepath(core.project_dir) .. filename
end
end
function core.init()
core.log_items = {}
core.log_quiet("Lite XL version %s - mod-version %s", VERSION, MOD_VERSION_STRING)
command = require "core.command"
keymap = require "core.keymap"
dirwatch = require "core.dirwatch"
@ -716,7 +713,7 @@ function core.init()
local file_abs = core.project_absolute_path(arg_filename)
if file_abs then
table.insert(files, file_abs)
project_dir = file_abs:match("^(.+)[/\\].+$")
project_dir = file_abs:match("^(.+)[:/\\].+$")
end
end
end
@ -724,6 +721,7 @@ function core.init()
core.frame_start = 0
core.clip_rect_stack = {{ 0,0,0,0 }}
core.log_items = {}
core.docs = {}
core.cursor_clipboard = {}
core.cursor_clipboard_whole_line = {}
@ -825,19 +823,15 @@ function core.init()
local msg = {}
for _, entry in pairs(plugins_refuse_list) do
if #entry.plugins > 0 then
local msg_list = {}
for _, p in pairs(entry.plugins) do
table.insert(msg_list, string.format("%s[%s]", p.file, p.version_string))
end
msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(msg_list, "\n"))
msg[#msg + 1] = string.format("Plugins from directory \"%s\":\n%s", common.home_encode(entry.dir), table.concat(entry.plugins, "\n"))
end
end
core.nag_view:show(
"Refused Plugins",
string.format(
"Some plugins are not loaded due to version mismatch. Expected version %s.\n\n%s.\n\n" ..
"Some plugins are not loaded due to version mismatch.\n\n%s.\n\n" ..
"Please download a recent version from https://github.com/lite-xl/lite-xl-plugins.",
MOD_VERSION_STRING, table.concat(msg, ".\n\n")),
table.concat(msg, ".\n\n")),
opt, function(item)
if item.text == "Exit" then os.exit(1) end
end)
@ -884,7 +878,7 @@ function core.delete_temp_files(dir)
dir = type(dir) == "string" and common.normalize_path(dir) or USERDIR
for _, filename in ipairs(system.list_dir(dir) or {}) do
if filename:find(temp_file_prefix, 1, true) == 1 then
os.remove(dir .. PATHSEP .. filename)
os.remove(common.basepath(dir) .. filename)
end
end
end
@ -892,7 +886,7 @@ end
function core.temp_filename(ext, dir)
dir = type(dir) == "string" and common.normalize_path(dir) or USERDIR
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 "")
end
@ -926,8 +920,6 @@ function core.restart()
end
local mod_version_regex =
regex.compile([[--.*mod-version:(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:$|\s)]])
local function get_plugin_details(filename)
local info = system.get_file_info(filename)
if info ~= nil and info.type == "dir" then
@ -939,32 +931,17 @@ local function get_plugin_details(filename)
if not f then return false end
local priority = false
local version_match = false
local major, minor, patch
for line in f:lines() do
if not version_match then
local _major, _minor, _patch = mod_version_regex:match(line)
if _major then
_major = tonumber(_major) or 0
_minor = tonumber(_minor) or 0
_patch = tonumber(_patch) or 0
major, minor, patch = _major, _minor, _patch
version_match = major == MOD_VERSION_MAJOR
if version_match then
version_match = minor <= MOD_VERSION_MINOR
end
if version_match then
version_match = patch <= MOD_VERSION_PATCH
end
local mod_version = line:match('%-%-.*%f[%a]mod%-version%s*:%s*(%d+)')
if mod_version then
version_match = (mod_version == MOD_VERSION)
end
end
if not priority then
priority = line:match('%-%-.*%f[%a]priority%s*:%s*(%d+)')
if priority then priority = tonumber(priority) end
end
if version_match then
break
end
@ -972,7 +949,6 @@ local function get_plugin_details(filename)
f:close()
return true, {
version_match = version_match,
version = major and {major, minor, patch} or {},
priority = priority or 100
}
end
@ -986,7 +962,7 @@ function core.load_plugins()
}
local files, ordered = {}, {}
for _, root_dir in ipairs {DATADIR, USERDIR} do
local plugin_dir = root_dir .. PATHSEP .. "plugins"
local plugin_dir = common.basepath(root_dir) .. "plugins"
for _, filename in ipairs(system.list_dir(plugin_dir) or {}) do
if not files[filename] then
table.insert(
@ -1001,15 +977,13 @@ function core.load_plugins()
for _, plugin in ipairs(ordered) do
local dir = files[plugin.file]
local name = plugin.file:match("(.-)%.lua$") or plugin.file
local is_lua_file, details = get_plugin_details(dir .. PATHSEP .. plugin.file)
local is_lua_file, details = get_plugin_details(common.basepath(dir) .. plugin.file)
plugin.valid = is_lua_file
plugin.name = name
plugin.dir = dir
plugin.priority = details and details.priority or 100
plugin.version_match = details and details.version_match or false
plugin.version = details and details.version or {}
plugin.version_string = #plugin.version > 0 and table.concat(plugin.version, ".") or "unknown"
end
-- sort by priority or name for plugins that have same priority
@ -1025,35 +999,27 @@ function core.load_plugins()
if plugin.valid then
if not config.skip_plugins_version and not plugin.version_match then
core.log_quiet(
"Version mismatch for plugin %q[%s] from %s",
"Version mismatch for plugin %q from %s",
plugin.name,
plugin.version_string,
plugin.dir
)
local rlist = plugin.dir:find(USERDIR, 1, true) == 1
and 'userdir' or 'datadir'
local list = refused_list[rlist].plugins
table.insert(list, plugin)
table.insert(list, plugin.file)
elseif config.plugins[plugin.name] ~= false then
local start = system.get_time()
local ok, loaded_plugin = core.try(require, "plugins." .. plugin.name)
local ok = core.try(require, "plugins." .. plugin.name)
if ok then
local plugin_version = ""
if plugin.version_string ~= MOD_VERSION_STRING then
plugin_version = "["..plugin.version_string.."]"
end
core.log_quiet(
"Loaded plugin %q%s from %s in %.1fms",
"Loaded plugin %q from %s in %.1fms",
plugin.name,
plugin_version,
plugin.dir,
(system.get_time() - start) * 1000
)
end
if not ok then
no_errors = false
elseif config.plugins[plugin.name].onload then
core.try(config.plugins[plugin.name].onload, loaded_plugin)
end
end
end
@ -1107,7 +1073,6 @@ function core.set_active_view(view)
-- Reset the IME even if the focus didn't change
ime.stop()
if view ~= core.active_view then
system.text_input(view:supports_text_input())
if core.active_view and core.active_view.force_focus then
core.next_active_view = view
return
@ -1296,12 +1261,6 @@ function core.on_event(type, ...)
if not core.root_view:on_mouse_wheel(...) then
did_keymap = keymap.on_mouse_wheel(...)
end
elseif type == "touchpressed" then
core.root_view:on_touch_pressed(...)
elseif type == "touchreleased" then
core.root_view:on_touch_released(...)
elseif type == "touchmoved" then
core.root_view:on_touch_moved(...)
elseif type == "resized" then
core.window_mode = system.get_window_mode()
elseif type == "minimized" or type == "maximized" or type == "restored" then
@ -1351,11 +1310,6 @@ function core.step()
did_keymap = false
elseif type == "mousemoved" then
core.try(core.on_event, type, a, b, c, d)
elseif type == "enteringforeground" then
-- to break our frame refresh in two if we get entering/entered at the same time.
-- required to avoid flashing and refresh issues on mobile
core.redraw = true
break
else
local _, res = core.try(core.on_event, type, a, b, c, d)
did_keymap = res or did_keymap
@ -1412,10 +1366,11 @@ local run_threads = coroutine.wrap(function()
else
core.threads[k] = nil
end
else
wait = wait or (1/30)
elseif wait then
thread.wake = system.get_time() + wait
minimal_time_to_wake = math.min(minimal_time_to_wake, wait)
else
minimal_time_to_wake = 0
end
else
minimal_time_to_wake = math.min(minimal_time_to_wake, thread.wake - system.get_time())
@ -1496,7 +1451,7 @@ end
function core.on_error(err)
-- write error to file
local fp = io.open(USERDIR .. PATHSEP .. "error.txt", "wb")
local fp = io.open(common.basepath(USERDIR) .. "error.txt", "wb")
fp:write("Error: " .. tostring(err) .. "\n")
fp:write(debug.traceback("", 4) .. "\n")
fp:close()
@ -1509,16 +1464,4 @@ function core.on_error(err)
end
local alerted_deprecations = {}
---Show deprecation notice once per `kind`.
---
---@param kind string
function core.deprecation_log(kind)
if alerted_deprecations[kind] then return end
alerted_deprecations[kind] = true
core.warn("Used deprecated functionality [%s]. Check if your plugins are up to date.", kind)
end
return core

View File

@ -36,8 +36,6 @@ local function keymap_macos(keymap)
["wheel"] = "root:scroll",
["hwheel"] = "root:horizontal-scroll",
["shift+hwheel"] = "root:horizontal-scroll",
["wheelup"] = "root:scroll-hovered-tabs-backward",
["wheeldown"] = "root:scroll-hovered-tabs-forward",
["cmd+f"] = "find-replace:find",
["cmd+r"] = "find-replace:replace",

View File

@ -71,6 +71,7 @@ local function key_to_stroke(key)
return normalize_stroke(table.concat(keys, "+"))
end
---Remove the given value from an array associated to a key in a table.
---@param tbl table<string, string> The table containing the key
---@param k string The key containing the array
@ -124,6 +125,7 @@ end
function keymap.add_direct(map)
for stroke, commands in pairs(map) do
stroke = normalize_stroke(stroke)
if type(commands) == "string" or type(commands) == "function" then
commands = { commands }
end
@ -323,8 +325,6 @@ keymap.add_direct {
["wheel"] = "root:scroll",
["hwheel"] = "root:horizontal-scroll",
["shift+wheel"] = "root:horizontal-scroll",
["wheelup"] = "root:scroll-hovered-tabs-backward",
["wheeldown"] = "root:scroll-hovered-tabs-forward",
["ctrl+f"] = "find-replace:find",
["ctrl+r"] = "find-replace:replace",
@ -341,7 +341,6 @@ keymap.add_direct {
["ctrl+x"] = "doc:cut",
["ctrl+c"] = "doc:copy",
["ctrl+v"] = "doc:paste",
["insert"] = "doc:toggle-overwrite",
["ctrl+insert"] = "doc:copy",
["shift+insert"] = "doc:paste",
["escape"] = { "command:escape", "doc:select-none", "dialog:select-no" },
@ -391,7 +390,7 @@ keymap.add_direct {
["shift+1lclick"] = "doc:select-to-cursor",
["ctrl+1lclick"] = "doc:split-cursor",
["1lclick"] = "doc:set-cursor",
["2lclick"] = { "doc:set-cursor-word", "emptyview:new-doc", "tabbar:new-doc" },
["2lclick"] = "doc:set-cursor-word",
["3lclick"] = "doc:set-cursor-line",
["shift+left"] = "doc:select-to-previous-char",
["shift+right"] = "doc:select-to-next-char",
@ -412,4 +411,3 @@ keymap.add_direct {
}
return keymap

View File

@ -7,12 +7,8 @@ modkeys.map = {
["right shift"] = "shift",
["left alt"] = "alt",
["right alt"] = "altgr",
["left gui"] = "super",
["left windows"] = "super",
["right gui"] = "super",
["right windows"] = "super"
}
modkeys.keys = { "ctrl", "shift", "alt", "altgr", "super" }
modkeys.keys = { "ctrl", "shift", "alt", "altgr" }
return modkeys

View File

@ -24,7 +24,6 @@ function NagView:new()
self.scrollable = true
self.target_height = 0
self.on_mouse_pressed_root = nil
self.dim_alpha = 0
end
function NagView:get_title()
@ -69,9 +68,7 @@ function NagView:dim_window_content()
oy = oy + self.show_height
local w, h = core.root_view.size.x, core.root_view.size.y - oy
core.root_view:defer_draw(function()
local dim_color = { table.unpack(style.nagbar_dim) }
dim_color[4] = style.nagbar_dim[4] * self.dim_alpha
renderer.draw_rect(ox, oy, w, h, dim_color)
renderer.draw_rect(ox, oy, w, h, style.nagbar_dim)
end)
end
@ -175,13 +172,10 @@ function NagView:update()
NagView.super.update(self)
if self.visible and core.active_view == self and self.title then
local target_height = self:get_target_height()
self:move_towards(self, "show_height", target_height, nil, "nagbar")
self:move_towards(self, "show_height", self:get_target_height(), nil, "nagbar")
self:move_towards(self, "underline_progress", 1, nil, "nagbar")
self:move_towards(self, "dim_alpha", self.show_height / target_height, nil, "nagbar")
else
self:move_towards(self, "show_height", 0, nil, "nagbar")
self:move_towards(self, "dim_alpha", 0, nil, "nagbar")
if self.show_height <= 0 then
self.title = nil
self.message = nil

View File

@ -18,6 +18,7 @@ function Node:new(type)
if self.type == "leaf" then
self:add_view(EmptyView())
end
self.hovered = {x = -1, y = -1 }
self.hovered_close = 0
self.tab_shift = 0
self.tab_offset = 1
@ -32,10 +33,9 @@ function Node:propagate(fn, ...)
end
---@deprecated
function Node:on_mouse_moved(x, y, ...)
core.deprecation_log("Node:on_mouse_moved")
if self.type == "leaf" then
self.hovered.x, self.hovered.y = x, y
self.active_view:on_mouse_moved(x, y, ...)
else
self:propagate("on_mouse_moved", x, y, ...)
@ -43,9 +43,7 @@ function Node:on_mouse_moved(x, y, ...)
end
---@deprecated
function Node:on_mouse_released(...)
core.deprecation_log("Node:on_mouse_released")
if self.type == "leaf" then
self.active_view:on_mouse_released(...)
else
@ -54,9 +52,7 @@ function Node:on_mouse_released(...)
end
---@deprecated
function Node:on_mouse_left()
core.deprecation_log("Node:on_mouse_left")
if self.type == "leaf" then
self.active_view:on_mouse_left()
else
@ -65,17 +61,6 @@ function Node:on_mouse_left()
end
---@deprecated
function Node:on_touch_moved(...)
core.deprecation_log("Node:on_touch_moved")
if self.type == "leaf" then
self.active_view:on_touch_moved(...)
else
self:propagate("on_touch_moved", ...)
end
end
function Node:consume(node)
for k, _ in pairs(self) do self[k] = nil end
for k, v in pairs(node) do self[k] = v end
@ -177,12 +162,8 @@ function Node:add_view(view, idx)
assert(not self.locked, "Tried to add view to locked node")
if self.views[1] and self.views[1]:is(EmptyView) then
table.remove(self.views)
if idx and idx > 1 then
idx = idx - 1
end
end
idx = common.clamp(idx or (#self.views + 1), 1, (#self.views + 1))
table.insert(self.views, idx, view)
table.insert(self.views, idx or (#self.views + 1), view)
self:set_active_view(view)
end
@ -320,7 +301,7 @@ function Node:tab_hovered_update(px, py)
if px >= cx and px < cx + cw and py >= y and py < y + h and config.tab_close_button then
self.hovered_close = tab_index
end
elseif #self.views > self:get_visible_tabs_number() then
else
self.hovered_scroll_button = self:get_scroll_button_index(px, py) or 0
end
end
@ -338,17 +319,10 @@ function Node:get_child_overlapping_point(x, y)
return child:get_child_overlapping_point(x, y)
end
-- returns: total height, text padding, top margin
local function get_tab_y_sizes()
local height = style.font:get_height()
local padding = style.padding.y
local margin = style.margin.tab.top
return height + (padding * 2) + margin, padding, margin
end
function Node:get_scroll_button_rect(index)
local w, pad = get_scroll_button_width()
local h = get_tab_y_sizes()
local h = style.font:get_height() + style.padding.y * 2
local x = self.position.x + (index == 1 and self.size.x - w * 2 or self.size.x - w)
return x, self.position.y, w, h, pad
end
@ -359,8 +333,8 @@ function Node:get_tab_rect(idx)
local x0 = self.position.x
local x1 = x0 + common.clamp(self.tab_width * (idx - 1) - self.tab_shift, 0, maxw)
local x2 = x0 + common.clamp(self.tab_width * idx - self.tab_shift, 0, maxw)
local h, pad_y, margin_y = get_tab_y_sizes()
return x1, self.position.y, x2 - x1, h, margin_y
local h = style.font:get_height() + style.padding.y * 2
return x1, self.position.y, x2 - x1, h
end
@ -508,7 +482,7 @@ function Node:update()
for _, view in ipairs(self.views) do
view:update()
end
self:tab_hovered_update(core.root_view.mouse.x, core.root_view.mouse.y)
self:tab_hovered_update(self.hovered.x, self.hovered.y)
local tab_width = self:target_tab_width()
self:move_towards("tab_shift", tab_width * (self.tab_offset - 1), nil, "tabs")
self:move_towards("tab_width", tab_width, nil, "tabs")
@ -551,7 +525,6 @@ function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalo
if is_active then
color = style.text
renderer.draw_rect(x, y, w, h, style.background)
renderer.draw_rect(x, y, w, ds, style.divider)
renderer.draw_rect(x + w, y, ds, h, style.divider)
renderer.draw_rect(x - ds, y, ds, h, style.divider)
end
@ -559,8 +532,7 @@ function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalo
end
function Node:draw_tab(view, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
local _, padding_y, margin_y = get_tab_y_sizes()
x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y + margin_y, w, h - margin_y, standalone)
x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalone)
-- Close button
local cx, cw, cpad = close_button_location(x, w)
local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button)
@ -636,13 +608,6 @@ function Node:is_empty()
end
function Node:is_in_tab_area(x, y)
if not self:should_show_tabs() then return false end
local _, ty, _, th = self:get_scroll_button_rect(1)
return y >= ty and y < ty + th
end
function Node:close_all_docviews(keep_active)
local node_active_view = self.active_view
local lost_active_view = false
@ -781,7 +746,7 @@ function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
tab_index = self:get_visible_tabs_number() + (self.tab_offset - 1 or 0)
end
end
local tab_x, tab_y, tab_w, tab_h, margin_y = self:get_tab_rect(tab_index)
local tab_x, tab_y, tab_w, tab_h = self:get_tab_rect(tab_index)
if x > tab_x + tab_w / 2 and tab_index <= #self.views then
-- use next tab
tab_x = tab_x + tab_w
@ -792,7 +757,7 @@ function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index)
tab_index = tab_index - 1
tab_x = tab_x - tab_w
end
return tab_index, tab_x, tab_y + margin_y, tab_w, tab_h - margin_y
return tab_index, tab_x, tab_y, tab_w, tab_h
end
return Node

View File

@ -27,13 +27,6 @@ function Object:is(T)
return getmetatable(self) == T
end
---Check if the parameter is strictly of the object type.
---@param T any
---@return boolean
function Object:is_class_of(T)
return getmetatable(T) == self
end
---Check if the object inherits from the given type.
---@param T any
---@return boolean
@ -48,22 +41,6 @@ function Object:extends(T)
return false
end
---Check if the parameter inherits from the object.
---@param T any
---@return boolean
function Object:is_extended_by(T)
local mt = getmetatable(T)
while mt do
if mt == self then
return true
end
local _mt = getmetatable(T)
if mt == _mt then break end
mt = _mt
end
return false
end
---Metamethod to get a string representation of an object.
---@return string
function Object:__tostring()

View File

@ -24,9 +24,6 @@ function RootView:new()
base_color = style.drag_overlay_tab,
color = { table.unpack(style.drag_overlay_tab) } }
self.drag_overlay_tab.to = { x = 0, y = 0, w = 0, h = 0 }
self.grab = nil -- = {view = nil, button = nil}
self.overlapping_view = nil
self.touched_view = nil
end
@ -119,31 +116,6 @@ function RootView:close_all_docviews(keep_active)
end
---Obtain mouse grab.
---
---This means that mouse movements will be sent to the specified view, even when
---those occur outside of it.
---There can't be multiple mouse grabs, even for different buttons.
---@see RootView:ungrab_mouse
---@param button core.view.mousebutton
---@param view core.view
function RootView:grab_mouse(button, view)
assert(self.grab == nil)
self.grab = {view = view, button = button}
end
---Release mouse grab.
---
---The specified button *must* be the last button that grabbed the mouse.
---@see RootView:grab_mouse
---@param button core.view.mousebutton
function RootView:ungrab_mouse(button)
assert(self.grab and self.grab.button == button)
self.grab = nil
end
---Function to intercept mouse pressed events on the active view.
---Do nothing by default.
---@param button core.view.mousebutton
@ -160,10 +132,6 @@ end
---@param clicks integer
---@return boolean
function RootView:on_mouse_pressed(button, x, y, clicks)
-- If there is a grab, release it first
if self.grab then
self:on_mouse_released(self.grab.button, x, y)
end
local div = self.root_node:get_divider_overlapping_point(x, y)
local node = self.root_node:get_child_overlapping_point(x, y)
if div and (node and not node.active_view:scrollbar_overlaps_point(x, y)) then
@ -188,7 +156,6 @@ function RootView:on_mouse_pressed(button, x, y, clicks)
end
elseif not self.dragged_node then -- avoid sending on_mouse_pressed events when dragging tabs
core.set_active_view(node.active_view)
self:grab_mouse(button, node.active_view)
return self.on_view_mouse_pressed(button, x, y, clicks) or node.active_view:on_mouse_pressed(button, x, y, clicks)
end
end
@ -221,21 +188,6 @@ end
---@param x number
---@param y number
function RootView:on_mouse_released(button, x, y, ...)
if self.grab then
if self.grab.button == button then
local grabbed_view = self.grab.view
grabbed_view:on_mouse_released(button, x, y, ...)
self:ungrab_mouse(button)
-- If the mouse was released over a different view, send it the mouse position
local hovered_view = self.root_node:get_child_overlapping_point(x, y)
if grabbed_view ~= hovered_view then
self:on_mouse_moved(x, y, 0, 0)
end
end
return
end
if self.dragged_divider then
self.dragged_divider = nil
end
@ -276,6 +228,8 @@ function RootView:on_mouse_released(button, x, y, ...)
end
self.dragged_node = nil
end
else -- avoid sending on_mouse_released events when dragging tabs
self.root_node:on_mouse_released(button, x, y, ...)
end
end
@ -296,14 +250,6 @@ end
---@param dx number
---@param dy number
function RootView:on_mouse_moved(x, y, dx, dy)
self.mouse.x, self.mouse.y = x, y
if self.grab then
self.grab.view:on_mouse_moved(x, y, dx, dy)
core.request_cursor(self.grab.view.cursor)
return
end
if core.active_view == core.nag_view then
core.request_cursor("arrow")
core.active_view:on_mouse_moved(x, y, dx, dy)
@ -323,6 +269,8 @@ function RootView:on_mouse_moved(x, y, dx, dy)
return
end
self.mouse.x, self.mouse.y = x, y
local dn = self.dragged_node
if dn and not dn.dragging then
-- start dragging only after enough movement
@ -335,33 +283,32 @@ function RootView:on_mouse_moved(x, y, dx, dy)
-- avoid sending on_mouse_moved events when dragging tabs
if dn then return end
local last_overlapping_view = self.overlapping_view
local overlapping_node = self.root_node:get_child_overlapping_point(x, y)
self.overlapping_view = overlapping_node and overlapping_node.active_view
self.root_node:on_mouse_moved(x, y, dx, dy)
if last_overlapping_view and last_overlapping_view ~= self.overlapping_view then
last_overlapping_view:on_mouse_left()
local last_overlapping_node = self.overlapping_node
self.overlapping_node = self.root_node:get_child_overlapping_point(x, y)
if last_overlapping_node and last_overlapping_node ~= self.overlapping_node then
last_overlapping_node:on_mouse_left()
end
if not self.overlapping_view then return end
self.overlapping_view:on_mouse_moved(x, y, dx, dy)
core.request_cursor(self.overlapping_view.cursor)
if not overlapping_node then return end
local div = self.root_node:get_divider_overlapping_point(x, y)
if overlapping_node:get_scroll_button_index(x, y) or overlapping_node:is_in_tab_area(x, y) then
local tab_index = self.overlapping_node and self.overlapping_node:get_tab_overlapping_point(x, y)
if self.overlapping_node and self.overlapping_node:get_scroll_button_index(x, y) then
core.request_cursor("arrow")
elseif div and not self.overlapping_view:scrollbar_overlaps_point(x, y) then
elseif div and (self.overlapping_node and not self.overlapping_node.active_view:scrollbar_overlaps_point(x, y)) then
core.request_cursor(div.type == "hsplit" and "sizeh" or "sizev")
elseif tab_index then
core.request_cursor("arrow")
elseif self.overlapping_node then
core.request_cursor(self.overlapping_node.active_view.cursor)
end
end
function RootView:on_mouse_left()
if self.overlapping_view then
self.overlapping_view:on_mouse_left()
if self.overlapping_node then
self.overlapping_node:on_mouse_left()
end
end
@ -387,50 +334,6 @@ function RootView:on_text_input(...)
core.active_view:on_text_input(...)
end
function RootView:on_touch_pressed(x, y, ...)
local touched_node = self.root_node:get_child_overlapping_point(x, y)
self.touched_view = touched_node and touched_node.active_view
end
function RootView:on_touch_released(x, y, ...)
self.touched_view = nil
end
function RootView:on_touch_moved(x, y, dx, dy, ...)
if not self.touched_view then return end
if core.active_view == core.nag_view then
core.active_view:on_touch_moved(x, y, dx, dy, ...)
return
end
if self.dragged_divider then
local node = self.dragged_divider
if node.type == "hsplit" then
x = common.clamp(x - node.position.x, 0, self.root_node.size.x * 0.95)
resize_child_node(node, "x", x, dx)
elseif node.type == "vsplit" then
y = common.clamp(y - node.position.y, 0, self.root_node.size.y * 0.95)
resize_child_node(node, "y", y, dy)
end
node.divider = common.clamp(node.divider, 0.01, 0.99)
return
end
local dn = self.dragged_node
if dn and not dn.dragging then
-- start dragging only after enough movement
dn.dragging = common.distance(x, y, dn.drag_start_x, dn.drag_start_y) > style.tab_width * .05
if dn.dragging then
core.request_cursor("hand")
end
end
-- avoid sending on_touch_moved events when dragging tabs
if dn then return end
self.touched_view:on_touch_moved(x, y, dx, dy, ...)
end
function RootView:on_ime_text_editing(...)
core.active_view:on_ime_text_editing(...)
end

View File

@ -58,9 +58,9 @@ function Scrollbar:new(options)
---@type "expanded" | "contracted" | false @Force the scrollbar status
self.force_status = options.force_status
self:set_forced_status(options.force_status)
---@type number? @Override the default value specified by `style.scrollbar_size`
self.contracted_size = options.contracted_size
---@type number? @Override the default value specified by `style.expanded_scrollbar_size`
self.contracted_size = options.contracted_size
---@type number? @Override the default value specified by `style.scrollbar_size`
self.expanded_size = options.expanded_size
end
@ -121,7 +121,7 @@ function Scrollbar:_get_thumb_rect_normal()
across_size = across_size + (expanded_scrollbar_size - scrollbar_size) * self.expand_percent
return
nr.across + nr.across_size - across_size,
nr.along + self.percent * (nr.along_size - along_size),
nr.along + self.percent * nr.scrollable * (nr.along_size - along_size) / (sz - nr.along_size),
across_size,
along_size
end
@ -238,8 +238,7 @@ end
function Scrollbar:_on_mouse_moved_normal(x, y, dx, dy)
if self.dragging then
local nr = self.normal_rect
local _, _, _, along_size = self:_get_thumb_rect_normal()
return common.clamp((y - nr.along + self.drag_start_offset) / (nr.along_size - along_size), 0, 1)
return common.clamp((y - nr.along + self.drag_start_offset) / nr.along_size, 0, 1)
end
return self:_update_hover_status_normal(x, y)
end
@ -282,7 +281,7 @@ function Scrollbar:set_size(x, y, w, h, scrollable)
end
---Updates the scrollbar location
---@param percent number @number between 0 and 1 where 0 means thumb at the top and 1 at the bottom
---@param percent number @number between 0 and 1 representing the position of the middle part of the thumb
function Scrollbar:set_percent(percent)
self.percent = percent
end

View File

@ -1,9 +1,6 @@
-- this file is used by lite-xl to setup the Lua environment when starting
VERSION = "2.1.2r1"
MOD_VERSION_MAJOR = 3
MOD_VERSION_MINOR = 0
MOD_VERSION_PATCH = 0
MOD_VERSION_STRING = string.format("%d.%d.%d", MOD_VERSION_MAJOR, MOD_VERSION_MINOR, MOD_VERSION_PATCH)
VERSION = "2.1.4r1"
MOD_VERSION = "3"
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)
@ -25,7 +22,7 @@ package.path = DATADIR .. '/?/init.lua;' .. package.path
package.path = USERDIR .. '/?.lua;' .. package.path
package.path = USERDIR .. '/?/init.lua;' .. package.path
local suffix = PLATFORM == "Windows" and 'dll' or 'so'
local suffix = PLATFORM == "Mac OS X" and 'lib' or (PLATFORM == "Windows" and 'dll' or 'so')
package.cpath =
USERDIR .. '/?.' .. ARCH .. "." .. suffix .. ";" ..
USERDIR .. '/?/init.' .. ARCH .. "." .. suffix .. ";" ..

View File

@ -232,42 +232,15 @@ function StatusView:register_docview_items()
return {
style.text, line, ":",
col > config.line_limit and style.accent or style.text, col,
style.text
style.text,
self.separator,
string.format("%.f%%", line / #dv.doc.lines * 100)
}
end,
command = "doc:go-to-line",
tooltip = "line : column"
})
self:add_item({
predicate = predicate_docview,
name = "doc:position-percent",
alignment = StatusView.Item.LEFT,
get_item = function()
local dv = core.active_view
local line = dv.doc:get_selection()
return {
string.format("%.f%%", line / #dv.doc.lines * 100)
}
end,
tooltip = "caret position"
})
self:add_item({
predicate = predicate_docview,
name = "doc:selections",
alignment = StatusView.Item.LEFT,
get_item = function()
local dv = core.active_view
local nsel = math.floor(#dv.doc.selections / 4)
if nsel > 1 then
return { style.text, nsel, " selections" }
end
return {}
end
})
self:add_item({
predicate = predicate_docview,
name = "doc:indentation",
@ -319,19 +292,6 @@ function StatusView:register_docview_items()
end,
command = "doc:toggle-line-ending"
})
self:add_item {
predicate = predicate_docview,
name = "doc:overwrite-mode",
alignment = StatusView.Item.RIGHT,
get_item = function()
return {
style.text, core.active_view.doc.overwrite and "OVR" or "INS"
}
end,
command = "doc:toggle-overwrite",
separator = StatusView.separator2
}
end
@ -1014,12 +974,6 @@ function StatusView:on_mouse_pressed(button, x, y, clicks)
end
function StatusView:on_mouse_left()
StatusView.super.on_mouse_left(self)
self.hovered_item = {}
end
function StatusView:on_mouse_moved(x, y, dx, dy)
if not self.visible then return end
StatusView.super.on_mouse_moved(self, x, y, dx, dy)

View File

@ -1,23 +1,13 @@
local common = require "core.common"
local style = {}
style.padding = { x = common.round(14 * SCALE), y = common.round(7 * SCALE) }
style.divider_size = common.round(1 * SCALE)
style.scrollbar_size = common.round(4 * SCALE)
style.expanded_scrollbar_size = common.round(12 * SCALE)
style.caret_width = common.round(2 * SCALE)
style.tab_width = common.round(170 * SCALE)
style.padding = {
x = common.round(14 * SCALE),
y = common.round(7 * SCALE),
}
style.margin = {
tab = {
top = common.round(-style.divider_size * SCALE)
}
}
-- The function renderer.font.load can accept an option table as a second optional argument.
-- It shoud be like the following:
--
@ -50,4 +40,3 @@ style.syntax_fonts = {}
style.log = {}
return style

View File

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

View File

@ -112,12 +112,6 @@ function TitleView:on_mouse_pressed(button, x, y, clicks)
end
function TitleView:on_mouse_left()
TitleView.super.on_mouse_left(self)
self.hovered_item = nil
end
function TitleView:on_mouse_moved(px, py, ...)
if self.size.y == 0 then return end
TitleView.super.on_mouse_moved(self, px, py, ...)

View File

@ -28,8 +28,11 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
-- Each position spans characters from i_n to ((i_n+1) - 1), to form
-- consecutive spans of text.
--
-- Insert the start index at i_1 to make iterating easier
table.insert(find_results, 3, find_results[1])
-- If i_1 is not equal to start, start is automatically inserted at
-- that index.
if find_results[3] ~= find_results[1] then
table.insert(find_results, 3, find_results[1])
end
-- Copy the ending index to the end of the table, so that an ending index
-- always follows a starting index after position 3 in the table.
table.insert(find_results, find_results[2] + 1)
@ -39,10 +42,8 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
local fin = find_results[i + 1] - 1
local type = pattern.type[i - 2]
-- ↑ (i - 2) to convert from [3; n] to [1; n]
if fin >= start then
local text = full_text:usub(start, fin)
push_token(t, syn.symbols[text] or type, text)
end
local text = full_text:usub(start, fin)
push_token(t, syn.symbols[text] or type, text)
end
else
local start, fin = find_results[1], find_results[2]
@ -122,8 +123,10 @@ local function report_bad_pattern(log_fn, syntax, pattern_idx, msg, ...)
end
if bad_patterns[syntax][pattern_idx] then return end
bad_patterns[syntax][pattern_idx] = true
log_fn("Malformed pattern #%d in %s language plugin. " .. msg,
pattern_idx, syntax.name or "unnamed", ...)
log_fn("Malformed pattern #%d <%s> in %s language plugin.\n" .. msg,
pattern_idx,
syntax.patterns[pattern_idx].pattern or syntax.patterns[pattern_idx].regex,
syntax.name or "unnamed", ...)
end
---@param incoming_syntax table
@ -133,12 +136,12 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
local res
local i = 1
state = state or string.char(0)
if #incoming_syntax.patterns == 0 then
return { "normal", text }, state
return { "normal", text }
end
state = state or string.char(0)
if resume then
res = resume.res
-- Remove "incomplete" tokens
@ -241,7 +244,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
res[1] = char_pos_1
res[2] = char_pos_2
end
if not res[1] then return end
if res[1] and target[3] then
-- Check to see if the escaped character is there,
-- and if it is not itself escaped.
@ -253,19 +255,18 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
if count % 2 == 0 then
-- The match is not escaped, so confirm it
break
else
-- The match is escaped, so avoid it
res[1] = false
elseif not close then
-- The *open* match is escaped, so avoid it
return
end
end
until at_start or not close or not target[3]
until not res[1] or not close or not target[3]
return table.unpack(res)
end
local text_len = text:ulen()
local start_time = system.get_time()
local starting_i = i
while text_len ~= nil and i <= text_len do
-- Every 200 chars, check if we're out of time
if i - starting_i > 200 then
@ -284,9 +285,6 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
if current_pattern_idx > 0 then
local p = current_syntax.patterns[current_pattern_idx]
local s, e = find_text(text, p, i, false, true)
-- Use the first token type specified in the type table for the "middle"
-- part of the subsyntax.
local token_type = type(p.type) == "table" and p.type[1] or p.type
local cont = true
-- If we're in subsyntax mode, always check to see if we end our syntax
@ -299,7 +297,7 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
-- 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, token_type, text:usub(i, ss - 1))
push_token(res, p.type, text:usub(i, ss - 1))
i = ss
cont = false
end
@ -308,11 +306,11 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
-- continue on as normal.
if cont then
if s then
push_token(res, token_type, text:usub(i, e))
push_token(res, p.type, text:usub(i, e))
set_subsyntax_pattern_idx(0)
i = e + 1
else
push_token(res, token_type, text:usub(i))
push_token(res, p.type, text:usub(i))
break
end
end
@ -321,10 +319,9 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
-- we're ending early in the middle of a delimiter, or
-- just normally, upon finding a token.
while subsyntax_info do
local find_results = { find_text(text, subsyntax_info, i, true, true) }
local s, e = find_results[1], find_results[2]
local s, e = find_text(text, subsyntax_info, i, true, true)
if s then
push_tokens(res, current_syntax, subsyntax_info, text, find_results)
push_token(res, subsyntax_info.type, text:usub(i, e))
-- On finding unescaped delimiter, pop it.
pop_subsyntax()
i = e + 1
@ -338,6 +335,14 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
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
@ -351,6 +356,7 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
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
@ -366,6 +372,7 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
i = find_results[2] + 1
matched = true
break
::continue::
end
end

View File

@ -108,10 +108,6 @@ function View:get_h_scrollable_size()
end
function View:supports_text_input()
return false
end
---@param x number
---@param y number
---@return boolean
@ -142,14 +138,14 @@ function View:on_mouse_pressed(button, x, y, clicks)
local result = self.v_scrollbar:on_mouse_pressed(button, x, y, clicks)
if result then
if result ~= true then
self.scroll.to.y = result * (self:get_scrollable_size() - self.size.y)
self.scroll.to.y = result * self:get_scrollable_size()
end
return true
end
result = self.h_scrollbar:on_mouse_pressed(button, x, y, clicks)
if result then
if result ~= true then
self.scroll.to.x = result * (self:get_h_scrollable_size() - self.size.x)
self.scroll.to.x = result * self:get_h_scrollable_size()
end
return true
end
@ -177,7 +173,7 @@ function View:on_mouse_moved(x, y, dx, dy)
result = self.v_scrollbar:on_mouse_moved(x, y, dx, dy)
if result then
if result ~= true then
self.scroll.to.y = result * (self:get_scrollable_size() - self.size.y)
self.scroll.to.y = result * self:get_scrollable_size()
if not config.animate_drag_scroll then
self:clamp_scroll_position()
self.scroll.y = self.scroll.to.y
@ -191,7 +187,7 @@ function View:on_mouse_moved(x, y, dx, dy)
result = self.h_scrollbar:on_mouse_moved(x, y, dx, dy)
if result then
if result ~= true then
self.scroll.to.x = result * (self:get_h_scrollable_size() - self.size.x)
self.scroll.to.x = result * self:get_h_scrollable_size()
if not config.animate_drag_scroll then
self:clamp_scroll_position()
self.scroll.x = self.scroll.to.x
@ -248,23 +244,6 @@ function View:get_content_bounds()
return x, y, x + self.size.x, y + self.size.y
end
---@param x number
---@param y number
---@param dx number
---@param dy number
---@param i number
function View:on_touch_moved(x, y, dx, dy, i)
if not self.scrollable then return end
if self.dragging_scrollbar then
local delta = self:get_scrollable_size() / self.size.y * dy
self.scroll.to.y = self.scroll.to.y + delta
end
self.hovered_scrollbar = self:scrollbar_overlaps_point(x, y)
self.scroll.to.y = self.scroll.to.y + -dy
self.scroll.to.x = self.scroll.to.x + -dx
end
---@return number x
---@return number y
@ -287,16 +266,12 @@ end
function View:update_scrollbar()
local v_scrollable = self:get_scrollable_size()
self.v_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, v_scrollable)
local v_percent = self.scroll.y/(v_scrollable - self.size.y)
-- Avoid setting nan percent
self.v_scrollbar:set_percent(v_percent == v_percent and v_percent or 0)
self.v_scrollbar:set_percent(self.scroll.y/v_scrollable)
self.v_scrollbar:update()
local h_scrollable = self:get_h_scrollable_size()
self.h_scrollbar:set_size(self.position.x, self.position.y, self.size.x, self.size.y, h_scrollable)
local h_percent = self.scroll.x/(h_scrollable - self.size.x)
-- Avoid setting nan percent
self.h_scrollbar:set_percent(h_percent == h_percent and h_percent or 0)
self.h_scrollbar:set_percent(self.scroll.x/h_scrollable)
self.h_scrollbar:update()
end

View File

@ -10,10 +10,6 @@ local RootView = require "core.rootview"
local DocView = require "core.docview"
local Doc = require "core.doc"
---Symbols cache of all open documents
---@type table<core.doc, table>
local cache = setmetatable({}, { __mode = "k" })
config.plugins.autocomplete = common.merge({
-- Amount of characters that need to be written for autocomplete
min_len = 3,
@ -23,16 +19,8 @@ config.plugins.autocomplete = common.merge({
max_suggestions = 100,
-- Maximum amount of symbols to cache per document
max_symbols = 4000,
-- Which symbols to show on the suggestions list: global, local, related, none
suggestions_scope = "global",
-- Font size of the description box
desc_font_size = 12,
-- Do not show the icons associated to the suggestions
hide_icons = false,
-- Position where icons will be displayed on the suggestions list
icon_position = "left",
-- Do not show the additional information related to a suggestion
hide_info = false,
-- The config specification used by gui generators
config_spec = {
name = "Autocomplete",
@ -72,26 +60,6 @@ config.plugins.autocomplete = common.merge({
min = 1000,
max = 10000
},
{
label = "Suggestions Scope",
description = "Which symbols to show on the suggestions list.",
path = "suggestions_scope",
type = "selection",
default = "global",
values = {
{"All Documents", "global"},
{"Current Document", "local"},
{"Related Documents", "related"},
{"Known Symbols", "none"}
},
on_apply = function(value)
if value == "global" then
for _, doc in ipairs(core.docs) do
if cache[doc] then cache[doc] = nil end
end
end
end
},
{
label = "Description Font Size",
description = "Font size of the description box.",
@ -99,31 +67,6 @@ config.plugins.autocomplete = common.merge({
type = "number",
default = 12,
min = 8
},
{
label = "Hide Icons",
description = "Do not show icons on the suggestions list.",
path = "hide_icons",
type = "toggle",
default = false
},
{
label = "Icons Position",
description = "Position to display icons on the suggestions list.",
path = "icon_position",
type = "selection",
default = "left",
values = {
{"Left", "left"},
{"Right", "Right"}
}
},
{
label = "Hide Items Info",
description = "Do not show the additional info related to each suggestion.",
path = "hide_info",
type = "toggle",
default = false
}
}
}, config.plugins.autocomplete)
@ -133,7 +76,6 @@ local autocomplete = {}
autocomplete.map = {}
autocomplete.map_manually = {}
autocomplete.on_close = nil
autocomplete.icons = {}
-- Flag that indicates if the autocomplete box was manually triggered
-- with the autocomplete.complete() function to prevent the suggestions
@ -153,7 +95,6 @@ function autocomplete.add(t, manually_triggered)
{
text = text,
info = info.info,
icon = info.icon, -- Name of icon to show
desc = info.desc, -- Description shown on item selected
onhover = info.onhover, -- A callback called once when item is hovered
onselect = info.onselect, -- A callback called when item is selected
@ -178,35 +119,28 @@ end
--
-- Thread that scans open document symbols and cache them
--
local global_symbols = {}
local max_symbols = config.plugins.autocomplete.max_symbols
core.add_thread(function()
local function load_syntax_symbols(doc)
if doc.syntax and not autocomplete.map["language_"..doc.syntax.name] then
local symbols = {
name = "language_"..doc.syntax.name,
files = doc.syntax.files,
items = {}
}
for name, type in pairs(doc.syntax.symbols) do
symbols.items[name] = type
local cache = setmetatable({}, { __mode = "k" })
local function get_syntax_symbols(symbols, doc)
if doc.syntax then
for sym in pairs(doc.syntax.symbols) do
symbols[sym] = true
end
autocomplete.add(symbols)
return symbols.items
end
return {}
end
local function get_symbols(doc)
local s = {}
local syntax_symbols = load_syntax_symbols(doc)
local max_symbols = config.plugins.autocomplete.max_symbols
get_syntax_symbols(s, doc)
if doc.disable_symbols then return s end
local i = 1
local symbols_count = 0
while i <= #doc.lines do
for sym in doc.lines[i]:gmatch(config.symbol_pattern) do
if not s[sym] and not syntax_symbols[sym] then
if not s[sym] then
symbols_count = symbols_count + 1
if symbols_count > max_symbols then
s = nil
@ -252,18 +186,14 @@ core.add_thread(function()
}
end
-- update symbol set with doc's symbol set
if config.plugins.autocomplete.suggestions_scope == "global" then
for sym in pairs(cache[doc].symbols) do
symbols[sym] = true
end
for sym in pairs(cache[doc].symbols) do
symbols[sym] = true
end
coroutine.yield()
end
-- update global symbols list
if config.plugins.autocomplete.suggestions_scope == "global" then
global_symbols = symbols
end
-- update symbols list
autocomplete.add { name = "open-docs", items = symbols }
-- wait for next scan
local valid = true
@ -282,12 +212,14 @@ end)
local partial = ""
local suggestions_offset = 1
local suggestions_idx = 1
local suggestions = {}
local last_line, last_col
local function reset_suggestions()
suggestions_offset = 1
suggestions_idx = 1
suggestions = {}
@ -310,50 +242,12 @@ local function update_suggestions()
map = autocomplete.map_manually
end
local assigned_sym = {}
-- get all relevant suggestions for given filename
local items = {}
for _, v in pairs(map) do
if common.match_pattern(filename, v.files) then
for _, item in pairs(v.items) do
table.insert(items, item)
assigned_sym[item.text] = true
end
end
end
-- Append the global, local or related text symbols if applicable
local scope = config.plugins.autocomplete.suggestions_scope
if not triggered_manually then
local text_symbols = nil
if scope == "global" then
text_symbols = global_symbols
elseif scope == "local" and cache[doc] and cache[doc].symbols then
text_symbols = cache[doc].symbols
elseif scope == "related" then
for _, d in ipairs(core.docs) do
if doc.syntax == d.syntax then
if cache[d].symbols then
for name in pairs(cache[d].symbols) do
if not assigned_sym[name] then
table.insert(items, setmetatable(
{text = name, info = "normal"}, mt
))
end
end
end
end
end
end
if text_symbols then
for name in pairs(text_symbols) do
if not assigned_sym[name] then
table.insert(items, setmetatable({text = name, info = "normal"}, mt))
end
end
end
end
@ -369,6 +263,7 @@ local function update_suggestions()
end
end
suggestions_idx = 1
suggestions_offset = 1
end
local function get_partial_symbol()
@ -384,8 +279,10 @@ local function get_active_view()
end
end
local last_max_width = 0
local function get_suggestions_rect(av)
if #suggestions == 0 then
last_max_width = 0
return 0, 0, 0, 0
end
@ -394,51 +291,49 @@ local function get_suggestions_rect(av)
y = y + av:get_line_height() + style.padding.y
local font = av:get_font()
local th = font:get_height()
local has_icons = false
local hide_info = config.plugins.autocomplete.hide_info
local hide_icons = config.plugins.autocomplete.hide_icons
local max_width = 0
for _, s in ipairs(suggestions) do
local w = font:get_width(s.text)
if s.info and not hide_info then
w = w + style.font:get_width(s.info) + style.padding.x
end
local icon = s.icon or s.info
if not hide_icons and icon and autocomplete.icons[icon] then
w = w + autocomplete.icons[icon].font:get_width(
autocomplete.icons[icon].char
) + (style.padding.x / 2)
has_icons = true
end
max_width = math.max(max_width, w)
end
local ah = config.plugins.autocomplete.max_height
local max_items = #suggestions
if max_items > ah then
max_items = ah
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
for i = start_index, start_index + show_count - 1 do
local s = suggestions[i]
local w = font:get_width(s.text)
if s.info then
w = w + style.font:get_width(s.info) + style.padding.x
end
max_width = math.max(max_width, w)
end
max_width = math.max(last_max_width, max_width)
last_max_width = max_width
max_width = max_width + style.padding.x * 2
x = x - style.padding.x
-- additional line to display total items
max_items = max_items + 1
if max_width < 150 then
max_width = 150
if max_width > core.root_view.size.x then
max_width = core.root_view.size.x
end
if max_width < 150 * SCALE then
max_width = 150 * SCALE
end
-- if portion not visiable to right, reposition to DocView right margin
if (x - av.position.x) + max_width > av.size.x then
x = (av.size.x + av.position.x) - max_width - (style.padding.x * 2)
if x + max_width > core.root_view.size.x then
x = (av.size.x + av.position.x) - max_width
end
return
x - style.padding.x,
x,
y - style.padding.y,
max_width + style.padding.x * 2,
max_items * (th + style.padding.y) + style.padding.y,
has_icons
max_width,
max_items * (th + style.padding.y) + style.padding.y
end
local function wrap_line(line, max_chars)
@ -558,16 +453,15 @@ local function draw_suggestions_box(av)
local ah = config.plugins.autocomplete.max_height
-- draw background rect
local rx, ry, rw, rh, has_icons = get_suggestions_rect(av)
local rx, ry, rw, rh = get_suggestions_rect(av)
renderer.draw_rect(rx, ry, rw, rh, style.background3)
-- draw text
local font = av:get_font()
local lh = font:get_height() + style.padding.y
local y = ry + style.padding.y / 2
local show_count = #suggestions <= ah and #suggestions or ah
local start_index = suggestions_idx > ah and (suggestions_idx-(ah-1)) or 1
local hide_info = config.plugins.autocomplete.hide_info
local show_count = math.min(#suggestions, ah)
local start_index = suggestions_offset
for i=start_index, start_index+show_count-1, 1 do
if not suggestions[i] then
@ -575,44 +469,23 @@ local function draw_suggestions_box(av)
end
local s = suggestions[i]
local icon_l_padding, icon_r_padding = 0, 0
if has_icons then
local icon = s.icon or s.info
if icon and autocomplete.icons[icon] then
local ifont = autocomplete.icons[icon].font
local itext = autocomplete.icons[icon].char
local icolor = autocomplete.icons[icon].color
if i == suggestions_idx then
icolor = style.accent
elseif type(icolor) == "string" then
icolor = style.syntax[icolor]
end
if config.plugins.autocomplete.icon_position == "left" then
common.draw_text(
ifont, icolor, itext, "left", rx + style.padding.x, y, rw, lh
)
icon_l_padding = ifont:get_width(itext) + (style.padding.x / 2)
else
common.draw_text(
ifont, icolor, itext, "right", rx, y, rw - style.padding.x, lh
)
icon_r_padding = ifont:get_width(itext) + (style.padding.x / 2)
end
end
end
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
common.draw_text(
font, color, s.text, "left",
rx + icon_l_padding + style.padding.x, y, rw, lh
)
if s.info and not hide_info then
-- 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
color = (i == suggestions_idx) and style.text or style.dim
common.draw_text(
style.font, color, s.info, "right",
rx, y, rw - icon_r_padding - style.padding.x, lh
)
common.draw_text(style.font, color, s.info, "right", rx, y, rw - style.padding.x, lh)
end
y = y + lh
if suggestions_idx == i then
@ -773,31 +646,6 @@ function autocomplete.can_complete()
return false
end
---Register a font icon that can be assigned to completion items.
---@param name string
---@param character string
---@param font? renderer.font
---@param color? string | renderer.color A style.syntax[] name or specific color
function autocomplete.add_icon(name, character, font, color)
local color_type = type(color)
assert(
not color or color_type == "table"
or (color_type == "string" and style.syntax[color]),
"invalid icon color given"
)
autocomplete.icons[name] = {
char = character,
font = font or style.code_font,
color = color or "keyword"
}
end
--
-- Register built-in syntax symbol types icon
--
for name, _ in pairs(style.syntax) do
autocomplete.add_icon(name, "M", style.icon_font, name)
end
--
-- Commands
@ -811,6 +659,7 @@ command.add(predicate, {
["autocomplete:complete"] = function(dv)
local doc = dv.doc
local item = suggestions[suggestions_idx]
local text = item.text
local inserted = false
if item.onselect then
inserted = item.onselect(suggestions_idx, item)
@ -819,7 +668,7 @@ command.add(predicate, {
local current_partial = get_partial_symbol()
local sz = #current_partial
for idx, line1, col1, line2, col2 in doc:get_selections(true) do
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
@ -840,10 +689,24 @@ command.add(predicate, {
["autocomplete:previous"] = function()
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,
["autocomplete:next"] = function()
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,
["autocomplete:cycle"] = function()

View File

@ -69,7 +69,7 @@ function dirwatch:check(change_callback, ...)
for _, doc in ipairs(core.docs) do
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 "")
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
reload_doc(doc)
else

View File

@ -37,11 +37,7 @@ local function optimal_indent_from_stat(stat)
elseif
indent > stat[y]
and
(
indent_occurrences_more_than_once(stat, y)
or
(y == count and stat[y] > 1)
)
indent_occurrences_more_than_once(stat, y)
then
score = 0
break
@ -122,10 +118,10 @@ local function get_comment_patterns(syntax, _loop)
end
if type(pattern.regex) == "table" then
table.insert(comments, {
"r", regex.compile(startp), regex.compile(pattern.regex[2]), r=startp
"r", regex.compile(startp), regex.compile(pattern.regex[2])
})
elseif not_is_string then
table.insert(comments, {"r", regex.compile(startp), r=startp})
table.insert(comments, {"r", regex.compile(startp)})
end
end
elseif pattern.syntax then
@ -156,25 +152,6 @@ local function get_comment_patterns(syntax, _loop)
table.insert(comments, {"p", "^%s*" .. block_comment[1], block_comment[2]})
end
end
-- Put comments first and strings last
table.sort(comments, function(c1, c2)
local comment1, comment2 = false, false
if
(c1[1] == "p" and string.find(c1[2], "^%s*", 1, true))
or
(c1[1] == "r" and string.find(c1["r"], "^\\s*", 1, true))
then
comment1 = true
end
if
(c2[1] == "p" and string.find(c2[2], "^%s*", 1, true))
or
(c2[1] == "r" and string.find(c2["r"], "^\\s*", 1, true))
then
comment2 = true
end
return comment1 and not comment2
end)
comments_cache[syntax] = comments
if #comments > 0 then
return comments

View File

@ -1,6 +1,5 @@
-- mod-version:3
local core = require "core"
local style = require "core.style"
local DocView = require "core.docview"
local common = require "core.common"
@ -13,7 +12,6 @@ config.plugins.drawwhitespace = common.merge({
show_leading = true,
show_trailing = true,
show_middle = true,
show_selected_only = false,
show_middle_min = 1,
@ -67,13 +65,6 @@ config.plugins.drawwhitespace = common.merge({
type = "toggle",
default = true,
},
{
label = "Show Selected Only",
description = "Only draw whitespaces if it is within a selection.",
path = "show_selected_only",
type = "toggle",
default = false,
},
{
label = "Show Trailing as Error",
description = "Uses an error square to spot them easily, requires 'Show Trailing' enabled.",
@ -302,41 +293,11 @@ function DocView:draw_line_text(idx, x, y)
for i=1,#cache,4 do
local tx = cache[i + 1] + x
local tw = cache[i + 2]
local sub = cache[i]
local color = cache[i + 3]
local partials = {}
if config.plugins.drawwhitespace.show_selected_only and self.doc:has_any_selection() then
for _, l1, c1, l2, c2 in self.doc:get_selections(true) do
if idx > l1 and idx < l2 then
-- Between selection lines, so everything is selected
table.insert(partials, false)
elseif idx == l1 and idx == l2 then
-- Both ends of the selection are on the same line
local _x1 = math.max(cache[i + 1], self:get_col_x_offset(idx, c1))
local _x2 = math.min((cache[i + 1] + tw), self:get_col_x_offset(idx, c2))
if _x1 < _x2 then
table.insert(partials, {_x1 + x, 0, _x2 - _x1, math.huge})
end
elseif idx >= l1 and idx <= l2 then
-- On one of the selection ends
if idx == l1 then -- Start of the selection
local _x = math.max(cache[i + 1], self:get_col_x_offset(idx, c1))
table.insert(partials, {_x + x, 0, math.huge, math.huge})
else -- End of the selection
local _x = math.min((cache[i + 1] + tw), self:get_col_x_offset(idx, c2))
table.insert(partials, {0, 0, _x + x, math.huge})
end
end
end
end
if #partials == 0 and not config.plugins.drawwhitespace.show_selected_only then
renderer.draw_text(font, sub, tx, ty, color)
else
for _, p in pairs(partials) do
if p then core.push_clip_rect(table.unpack(p)) end
renderer.draw_text(font, sub, tx, ty, color)
if p then core.pop_clip_rect() end
if tx <= x2 then
local sub = cache[i]
local color = cache[i + 3]
if tx + tw >= x1 then
tx = renderer.draw_text(font, sub, tx, ty, color)
end
end
end

View File

@ -5,7 +5,8 @@ syntax.add {
name = "C++",
files = {
"%.h$", "%.inl$", "%.cpp$", "%.cc$", "%.C$", "%.cxx$",
"%.c++$", "%.hh$", "%.H$", "%.hxx$", "%.hpp$", "%.h++$"
"%.c++$", "%.hh$", "%.H$", "%.hxx$", "%.hpp$", "%.h++$",
"%.ino$"
},
comment = "//",
block_comment = { "/*", "*/" },

View File

@ -20,10 +20,10 @@ local syntax = require "core.syntax"
-- 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/R0w8Qw/1
-- 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]*\s*(?:[\n,;\)\]\}\.]|\/[\/*]))()]=],
"/()[gmiyuvsd]*", "\\"
}
@ -57,18 +57,19 @@ syntax.add {
comment = "//",
block_comment = { "/*", "*/" },
patterns = {
{ pattern = "//.*", type = "comment" },
{ pattern = { "/%*", "%*/" }, type = "comment" },
{ regex = regex_pattern, syntax = inner_regex_syntax, type = {"string", "string"} },
{ pattern = { '"', '"', '\\' }, type = "string" },
{ pattern = { "'", "'", '\\' }, type = "string" },
{ pattern = { "`", "`", '\\' }, type = "string" },
{ pattern = "0x[%da-fA-F_]+n?()%s*()/?", type = {"number", "normal", "operator"} },
{ pattern = "-?%d+[%d%.eE_n]*()%s*()/?", type = {"number", "normal", "operator"} },
{ pattern = "-?%.?%d+()%s*()/?", type = {"number", "normal", "operator"} },
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
{ pattern = "[%a_][%w_]*%f[(]", type = "function" },
{ pattern = "[%a_][%w_]*()%s*()/?", type = {"symbol", "normal", "operator"} },
{ pattern = "//.*", type = "comment" },
{ pattern = { "/%*", "%*/" }, type = "comment" },
{ regex = regex_pattern, syntax = inner_regex_syntax, type = {"string", "string"} },
{ pattern = { '"', '"', '\\' }, type = "string" },
{ pattern = { "'", "'", '\\' }, type = "string" },
{ pattern = { "`", "`", '\\' }, type = "string" },
-- Use (?:\/(?!\/|\*))? to avoid that a regex can start after a number, while also allowing // and /* comments
{ regex = [[-?0[xXbBoO][\da-fA-F_]+n?()\s*()(?:\/(?!\/|\*))?]], type = {"number", "normal", "operator"} },
{ regex = [[-?\d+[0-9.eE_n]*()\s*()(?:\/(?!\/|\*))?]], type = {"number", "normal", "operator"} },
{ regex = [[-?\.?\d+()\s*()(?:\/(?!\/|\*))?]], type = {"number", "normal", "operator"} },
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
{ pattern = "[%a_][%w_]*%f[(]", type = "function" },
{ pattern = "[%a_][%w_]*", type = "symbol" },
},
symbols = {
["async"] = "keyword",
@ -92,6 +93,7 @@ syntax.add {
["get"] = "keyword",
["if"] = "keyword",
["import"] = "keyword",
["from"] = "keyword",
["in"] = "keyword",
["of"] = "keyword",
["instanceof"] = "keyword",

View File

@ -128,7 +128,6 @@ syntax.add {
{ pattern = { "```go", "```" }, type = "string", syntax = ".go" },
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
{ pattern = { "```nix", "```" }, type = "string", syntax = ".nix" },
{ pattern = { "```", "```" }, type = "string" },
{ pattern = { "``", "``" }, type = "string" },
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" },
@ -267,4 +266,3 @@ core.add_thread(function()
coroutine.yield(1)
end
end)

View File

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

View File

@ -15,8 +15,6 @@ config.plugins.lineguide = common.merge({
-- 120,
config.line_limit
},
use_custom_color = false,
custom_color = style.selection,
-- The config specification used by gui generators
config_spec = {
name = "Line Guide",
@ -65,21 +63,7 @@ config.plugins.lineguide = common.merge({
end
return new_rulers
end
},
{
label = "Use Custom Color",
description = "Enable the utilization of a custom line color.",
path = "use_custom_color",
type = "toggle",
default = false
},
{
label = "Custom Color",
description = "Applied when the above toggle is enabled.",
path = "custom_color",
type = "color",
default = style.selection
},
}
}
}, config.plugins.lineguide)
@ -102,12 +86,10 @@ function DocView:draw_overlay(...)
and
self:is(DocView)
then
local conf = config.plugins.lineguide
local line_x = self:get_line_screen_position(1)
local character_width = self:get_font():get_width("n")
local ruler_width = config.plugins.lineguide.width
local ruler_color = conf.use_custom_color and conf.custom_color
or (style.guide or style.selection)
local ruler_color = style.guide or style.selection
for k,v in ipairs(config.plugins.lineguide.rulers) do
local ruler = get_ruler(v)

View File

@ -37,7 +37,7 @@ local function find_all_matches_in_file(t, filename, fn)
table.insert(t, { file = filename, text = (start_index > 1 and "..." or "") .. line:sub(start_index, 256 + start_index), line = n, col = s })
core.redraw = true
end
if n % 100 == 0 then coroutine.yield(0) end
if n % 100 == 0 then coroutine.yield() end
n = n + 1
core.redraw = true
end

View File

@ -48,7 +48,7 @@ end
function ToolbarView:get_icon_width()
local max_width = 0
for i,v in ipairs(self.toolbar_commands) do max_width = math.max(max_width, (v.font or self.toolbar_font):get_width(v.symbol)) end
for i,v in ipairs(self.toolbar_commands) do max_width = math.max(max_width, self.toolbar_font:get_width(v.symbol)) end
return max_width
end
@ -83,7 +83,7 @@ function ToolbarView:draw()
for item, x, y, w, h in self:each_item() do
local color = item == self.hovered_item and command.is_valid(item.command) and style.text or style.dim
common.draw_text(item.font or self.toolbar_font, color, item.symbol, nil, x, y, 0, h)
common.draw_text(self.toolbar_font, color, item.symbol, nil, x, y, 0, h)
end
end
@ -100,16 +100,6 @@ function ToolbarView:on_mouse_pressed(button, x, y, clicks)
end
function ToolbarView:on_mouse_left()
ToolbarView.super.on_mouse_left(self)
if self.tooltip then
core.status_view:remove_tooltip()
self.tooltip = false
end
self.hovered_item = nil
end
function ToolbarView:on_mouse_moved(px, py, ...)
if not self.visible then return end
ToolbarView.super.on_mouse_moved(self, px, py, ...)

View File

@ -9,15 +9,10 @@ local View = require "core.view"
local ContextMenu = require "core.contextmenu"
local RootView = require "core.rootview"
local CommandView = require "core.commandview"
local DocView = require "core.docview"
config.plugins.treeview = common.merge({
-- Default treeview width
size = 200 * SCALE,
highlight_focused_file = true,
expand_dirs_to_focused_file = false,
scroll_to_focused_file = false,
animate_scroll_to_focused_file = true
size = 200 * SCALE
}, config.plugins.treeview)
local tooltip_offset = style.font:get_height()
@ -51,7 +46,7 @@ function TreeView:new()
self.target_size = config.plugins.treeview.size
self.cache = {}
self.tooltip = { x = 0, y = 0, begin = 0, alpha = 0 }
self.last_scroll_y = 0
self.cursor_pos = { x = 0, y = 0 }
self.item_icon_width = 0
self.item_text_spacing = 0
@ -88,7 +83,7 @@ function TreeView:get_cached(dir, item, dirname)
else
t.filename = item.filename
t.depth = get_depth(item.filename)
t.abs_filename = dirname .. PATHSEP .. item.filename
t.abs_filename = common.basepath(dirname) .. item.filename
end
t.name = basename
t.type = item.type
@ -174,73 +169,20 @@ function TreeView:each_item()
end
function TreeView:set_selection(selection, selection_y, center, instant)
function TreeView:set_selection(selection, selection_y)
self.selected_item = selection
if selection and selection_y
and (selection_y <= 0 or selection_y >= self.size.y) then
local lh = self:get_item_height()
if not center and selection_y >= self.size.y - lh then
if selection_y >= self.size.y - lh then
selection_y = selection_y - self.size.y + lh
end
if center then
selection_y = selection_y - (self.size.y - lh) / 2
end
local _, y = self:get_content_offset()
self.scroll.to.y = selection_y - y
self.scroll.to.y = common.clamp(self.scroll.to.y, 0, self:get_scrollable_size() - self.size.y)
if instant then
self.scroll.y = self.scroll.to.y
end
self.scroll.to.y = selection and (selection_y - y)
end
end
---Sets the selection to the file with the specified path.
---
---@param path string #Absolute path of item to select
---@param expand boolean #Expand dirs leading to the item
---@param scroll_to boolean #Scroll to make the item visible
---@param instant boolean #Don't animate the scroll
---@return table? #The selected item
function TreeView:set_selection_to_path(path, expand, scroll_to, instant)
local to_select, to_select_y
local let_it_finish, done
::restart::
for item, x,y,w,h in self:each_item() do
if not done then
if item.type == "dir" then
local _, to = string.find(path, item.abs_filename..PATHSEP, 1, true)
if to and to == #item.abs_filename + #PATHSEP then
to_select, to_select_y = item, y
if expand and not item.expanded then
-- Use TreeView:toggle_expand to update the directory structure.
-- Directly using item.expanded doesn't update the cached tree.
self:toggle_expand(true, item)
-- Because we altered the size of the TreeView
-- and because TreeView:get_scrollable_size uses self.count_lines
-- which gets updated only when TreeView:each_item finishes,
-- we can't stop here or we risk that the scroll
-- gets clamped by View:clamp_scroll_position.
let_it_finish = true
-- We need to restart the process because if TreeView:toggle_expand
-- altered the cache, TreeView:each_item risks looping indefinitely.
goto restart
end
end
else
if item.abs_filename == path then
to_select, to_select_y = item, y
done = true
if not let_it_finish then break end
end
end
end
end
if to_select then
self:set_selection(to_select, scroll_to and to_select_y, true, instant)
end
return to_select
end
function TreeView:get_text_bounding_box(item, x, y, w, h)
local icon_width = style.icon_font:get_width("D")
@ -251,9 +193,10 @@ function TreeView:get_text_bounding_box(item, x, y, w, h)
end
function TreeView:on_mouse_moved(px, py, ...)
if not self.visible then return end
self.cursor_pos.x = px
self.cursor_pos.y = py
if TreeView.super.on_mouse_moved(self, px, py, ...) then
-- mouse movement handled by the View (scrollbar)
self.hovered_item = nil
@ -280,12 +223,6 @@ function TreeView:on_mouse_moved(px, py, ...)
end
function TreeView:on_mouse_left()
TreeView.super.on_mouse_left(self)
self.hovered_item = nil
end
function TreeView:update()
-- update width
local dest = self.visible and self.target_size or 0
@ -296,7 +233,7 @@ function TreeView:update()
self:move_towards(self.size, "x", dest, nil, "treeview")
end
if self.size.x == 0 or self.size.y == 0 or not self.visible then return end
if not self.visible then return end
local duration = system.get_time() - self.tooltip.begin
if self.hovered_item and self.tooltip.x and duration > tooltip_delay then
@ -309,30 +246,10 @@ function TreeView:update()
self.item_text_spacing = style.icon_font:get_width("f") / 2
-- this will make sure hovered_item is updated
local dy = math.abs(self.last_scroll_y - self.scroll.y)
if dy > 0 then
self:on_mouse_moved(core.root_view.mouse.x, core.root_view.mouse.y, 0, 0)
self.last_scroll_y = self.scroll.y
end
local config = config.plugins.treeview
if config.highlight_focused_file then
-- Try to only highlight when we actually change tabs
local current_node = core.root_view:get_active_node()
local current_active_view = core.active_view
if current_node and not current_node.locked
and current_active_view ~= self and current_active_view ~= self.last_active_view then
self.selected_item = nil
self.last_active_view = current_active_view
if DocView:is_extended_by(current_active_view) then
local abs_filename = current_active_view.doc
and current_active_view.doc.abs_filename or ""
self:set_selection_to_path(abs_filename,
config.expand_dirs_to_focused_file,
config.scroll_to_focused_file,
not config.animate_scroll_to_focused_file)
end
end
-- we don't want events when the thing is scrolling fast
local dy = math.abs(self.scroll.to.y - self.scroll.y)
if self.scroll.to.y ~= 0 and dy < self:get_item_height() then
self:on_mouse_moved(self.cursor_pos.x, self.cursor_pos.y, 0, 0)
end
TreeView.super.update(self)
@ -505,8 +422,8 @@ function TreeView:get_previous(item)
end
function TreeView:toggle_expand(toggle, item)
item = item or self.selected_item
function TreeView:toggle_expand(toggle)
local item = self.selected_item
if not item then return end
@ -524,11 +441,6 @@ function TreeView:toggle_expand(toggle, item)
end
function TreeView:open_doc(filename)
core.root_view:open_doc(core.open_doc(filename))
end
-- init
local view = TreeView()
local node = core.root_view:get_active_node()
@ -707,7 +619,8 @@ command.add(
if core.last_active_view and core.active_view == view then
core.set_active_view(core.last_active_view)
end
view:open_doc(core.normalize_to_project_dir(item.abs_filename))
local doc_filename = core.normalize_to_project_dir(item.abs_filename)
core.root_view:open_doc(core.open_doc(doc_filename))
end)
end
end,
@ -753,26 +666,6 @@ command.add(
view:toggle_expand(true)
end
end,
["treeview-context:show"] = function()
if view.hovered_item then
menu:show(core.root_view.mouse.x, core.root_view.mouse.y)
return
end
local item = view.selected_item
if not item then return end
local x, y
for _i, _x, _y, _w, _h in view:each_item() do
if _i == item then
x = _x + _w / 2
y = _y + _h / 2
break
end
end
menu:show(x, y)
end
})
@ -786,7 +679,7 @@ command.add(
local relfilename = item.filename
if item.dir_name ~= core.project_dir then
-- 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
local file_info = system.get_file_info(filename)
local file_type = file_info.type == "dir" and "Directory" or "File"
@ -831,7 +724,7 @@ command.add(
submit = function(filename)
local abs_filename = filename
if not common.is_absolute_path(filename) then
abs_filename = item.dir_name .. PATHSEP .. filename
abs_filename = common.basepath(item.dir_name) .. filename
end
local res, err = os.rename(old_abs_filename, abs_filename)
if res then -- successfully renamed
@ -861,12 +754,12 @@ command.add(
core.command_view:enter("Filename", {
text = text,
submit = function(filename)
local doc_filename = item.dir_name .. PATHSEP .. filename
local doc_filename = common.basepath(item.dir_name) .. filename
core.log(doc_filename)
local file = io.open(doc_filename, "a+")
file:write("")
file:close()
view:open_doc(doc_filename)
core.root_view:open_doc(core.open_doc(doc_filename))
core.log("Created %s", doc_filename)
end,
suggest = function(text)
@ -883,7 +776,7 @@ command.add(
core.command_view:enter("Folder Name", {
text = text,
submit = function(filename)
local dir_path = item.dir_name .. PATHSEP .. filename
local dir_path = common.basepath(item.dir_name) .. filename
common.mkdirp(dir_path)
core.log("Created %s", dir_path)
end,
@ -935,25 +828,6 @@ command.add(function()
})
command.add(
function()
return menu.show_context_menu == true and core.active_view:is(TreeView)
end, {
["treeview-context:focus-previous"] = function()
menu:focus_previous()
end,
["treeview-context:focus-next"] = function()
menu:focus_next()
end,
["treeview-context:hide"] = function()
menu:hide()
end,
["treeview-context:on-selected"] = function()
menu:call_selected_item()
end,
})
keymap.add {
["ctrl+\\"] = "treeview:toggle",
["up"] = "treeview:previous",
@ -969,15 +843,6 @@ keymap.add {
["ctrl+lclick"] = "treeview:new-folder"
}
keymap.add {
["menu"] = "treeview-context:show",
["return"] = "treeview-context:on-selected",
["up"] = "treeview-context:focus-previous",
["down"] = "treeview-context:focus-next",
["escape"] = "treeview-context:hide"
}
-- The config specification used by gui generators
config.plugins.treeview.config_spec = {
name = "Treeview",

View File

@ -7,7 +7,7 @@ local LogView = require "core.logview"
local function workspace_files_for(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)
if not info_wsdir then
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
local id = tonumber(file:sub(n + 1):match("^-(%d+)$"))
if id then
coroutine.yield(workspace_dir .. PATHSEP .. file, id)
coroutine.yield(common.basepath(workspace_dir) .. file, id)
end
end
end
@ -52,7 +52,7 @@ local function get_workspace_filename(project_dir)
id = id + 1
end
local basename = common.basename(project_dir)
return USERDIR .. PATHSEP .. "ws" .. PATHSEP .. basename .. "-" .. tostring(id)
return common.basepath(USERDIR) .. "ws" .. PATHSEP .. basename .. "-" .. tostring(id)
end
@ -166,7 +166,7 @@ local function load_node(node, t)
active_view = view
end
if not view:is(DocView) then
view.scroll = v.scroll
view.scroll = v.scroll
end
end
end

View File

@ -1,6 +1,6 @@
project('lite-xl',
['c'],
version : '2.1.1',
version : '2.1.4',
license : 'MIT',
meson_version : '>= 0.56',
default_options : [
@ -101,7 +101,7 @@ if not get_option('source-only')
endforeach
else
lua_dep = dependency('', fallback: ['lua', 'lua_dep'], required : true,
default_options: default_fallback_options + ['default_library=static', 'line_editing=disabled', 'interpreter=false']
default_options: default_fallback_options + ['default_library=static', 'line_editing=false', 'interpreter=false']
)
endif
@ -208,10 +208,10 @@ else
install_data('resources/icons/lite-xl.svg',
install_dir : 'share/icons/hicolor/scalable/apps'
)
install_data('resources/linux/com.lite_xl.LiteXL.desktop',
install_data('resources/linux/org.lite_xl.lite_xl.desktop',
install_dir : 'share/applications'
)
install_data('resources/linux/com.lite_xl.LiteXL.appdata.xml',
install_data('resources/linux/org.lite_xl.lite_xl.appdata.xml',
install_dir : 'share/metainfo'
)
endif

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

View File

@ -1,8 +1,25 @@
-- mod-version:3
local core = require "core"
local command = require "core.command"
local common = require "core.common"
local config = require "core.config"
local keymap = require "core.keymap"
config.plugins.ghmarkdown = common.merge({
-- Find information on how to generate your own token at
-- https://docs.github.com/en/rest/markdown/markdown?apiVersion=2022-11-28#render-a-markdown-document-in-raw-mode
github_token = "",
config_spec = {
name = "GHMarkdown",
{
label = "GitHub token",
description = "Enter your personal GitHub token",
path = "github_token",
type = "string",
default = ""
}
}
}, config.plugins.ghmarkdown)
local html = [[
<html>
@ -31,7 +48,9 @@ local html = [[
<script>
var xhr = new XMLHttpRequest;
xhr.open("POST", "https://api.github.com/markdown/raw");
xhr.setRequestHeader("Content-Type", "text/plain");
xhr.setRequestHeader("content-type", "text/plain");
xhr.setRequestHeader("authorization", "Bearer ${token}");
xhr.setRequestHeader("x-github-api-version", "2022-11-28");
xhr.onload = function() { document.body.innerHTML = xhr.responseText; };
xhr.send("${content}");
</script>
@ -42,11 +61,17 @@ local html = [[
command.add("core.docview!", {
["ghmarkdown:show-preview"] = function(dv)
if config.plugins.ghmarkdown.github_token == "" then
core.error "You need to provide your own GitHub token"
return
end
local content = dv.doc:get_text(1, 1, math.huge, math.huge)
local esc = { ['"'] = '\\"', ["\n"] = '\\n' }
local text = html:gsub("${(.-)}", {
title = dv:get_name(),
content = content:gsub(".", esc)
content = content:gsub(".", esc),
token = config.plugins.ghmarkdown.github_token
})
local htmlfile = core.temp_filename(".html")

View File

@ -0,0 +1,414 @@
-- mod-version:3
local core = require "core"
local config = require "core.config"
local style = require "core.style"
local common = require "core.common"
local View = require "core.view"
local command = require "core.command"
local keymap = require "core.keymap"
local TetrisView = View:extend()
config.plugins.tetris = common.merge({
tick = 0.5, -- The amount of time in seconds it takes for a piece to fall one line at 0 score.
height = 30, -- The amount of cells of height.
width = 10, -- The amount of cells of width.
cell_size = 18, -- The size in pixels of each cell.
cell_padding = 2, -- pixels between each cell
drop_shadow = true, -- Should cast a drop shadow.
lock_delay = 3, -- the multiplier for lock delay over a normal tick. set to 0 to disable
down_amount = 1 -- the amount we move a tetronimo down when you hit the down key; change to math.huge for instant.
}, config.plugins.tetris)
function TetrisView:new(options)
TetrisView.super.new(self)
self.cell_size = options.cell_size
self.cell_padding = options.cell_padding
self.grid = { x = options.width, y = options.height }
self.size.x = self.grid.x * (self.cell_size + self.cell_padding) + style.padding.x * 2
self.cells = { }
self.score = 0
self.paused = false
self.initial_tick = options.tick
self.lock_delay = options.lock_delay
self.drop_shadow = options.drop_shadow
self.tick = self:calculate_tick(self.score)
self.finished = false
self.thread = core.add_thread(function()
while not self.finished do
self:step()
core.redraw = true
coroutine.yield(self.tick)
end
end)
-- easier to specify rotations than to start doing matrix multiplication
self.tetronimos = {
{
color = { common.color "#ff0000" },
shape = { {
0,1,1,0,
0,1,1,0,
0,0,0,0,
0,0,0,0
} }
},
{
color = { common.color "#00ff00" },
shape = { {
1,1,0,0,
1,0,0,0,
1,0,0,0,
0,0,0,0
}, {
1,1,1,0,
0,0,1,0,
0,0,0,0,
0,0,0,0
}, {
0,0,1,0,
0,0,1,0,
0,1,1,0,
0,0,0,0
}, {
0,0,0,0,
1,0,0,0,
1,1,1,0,
0,0,0,0
} }
},
{
color = { common.color "#0000ff" },
shape = { {
1,0,0,0,
1,0,0,0,
1,1,0,0,
0,0,0,0
}, {
1,1,1,0,
1,0,0,0,
0,0,0,0,
0,0,0,0
}, {
0,1,1,0,
0,0,1,0,
0,0,1,0,
0,0,0,0
}, {
0,0,0,0,
0,0,1,0,
1,1,1,0,
0,0,0,0
} }
},
{
color = { common.color "#00ffff" },
shape = { {
1,1,0,0,
0,1,1,0,
0,0,0,0,
0,0,0,0
}, {
0,0,1,0,
0,1,1,0,
0,1,0,0,
0,0,0,0
}, {
0, 0,0,0,
1,1,0,0,
0,1,1,0,
0,0,0,0
}, {
0,1,0,0,
1,1,0,0,
1,0,0,0,
0,0,0,0
} }
},
{
color = { common.color "#ffff00" },
shape = { {
0,1,1,0,
1,1,0,0,
0,0,0,0,
0,0,0,0
}, {
0,1,0,0,
0,1,1,0,
0,0,1,0,
0,0,0,0
}, {
0,0,0,0,
0,1,1,0,
1,1,0,0,
0,0,0,0
}, {
1,0,0,0,
1,1,0,0,
0,1,0,0,
0,0,0,0
} }
},
{
color = { common.color "#ff00ff" },
shape = { {
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,1,0,0
}, {
0,0,0,0,
1,1,1,1,
0,0,0,0,
0,0,0,0
}, {
0,0,1,0,
0,0,1,0,
0,0,1,0,
0,0,1,0
}, {
0,0,0,0,
0,0,0,0,
1,1,1,1,
0,0,0,0
} }
},
{
color = { common.color "#ffffff" },
shape = { {
1,1,1,0,
0,1,0,0,
0,0,0,0,
0,0,0,0
}, {
0,0,1,0,
0,1,1,0,
0,0,1,0,
0,0,0,0
}, {
0,0,0,0,
0,1,0,0,
1,1,1,0,
0,0,0,0
}, {
1,0,0,0,
1,1,0,0,
1,0,0,0,
0,0,0,0
} }
}
}
self.live_piece = nil
self.hold_piece = nil
end
function TetrisView:calculate_tick(score)
return self.initial_tick / (math.floor(score / 10) + 1)
end
function TetrisView:does_collide(x, y, tetronimo, rot)
local shape = tetronimo.shape[rot]
for i = 0, 3 do
for j = 0, 3 do
local ny = y + i
local nx = x + j
if (nx >= self.grid.x or ny >= self.grid.y or nx < 0 or ny < 0 or self.cells[self.grid.x * ny + nx + 1]) and shape[i * 4 + j + 1] == 1 then
return true
end
end
end
return false
end
function TetrisView:finalize_live_piece()
assert(self.live_piece)
local shape = self.live_piece.tetronimo.shape[self.live_piece.rot]
for i = 0, 3 do
for j = 0, 3 do
local ny = self.live_piece.y + i
local nx = self.live_piece.x + j
if shape[(i * 4 + j) + 1] == 1 then
self.cells[ny * self.grid.x + nx + 1] = self.live_piece.idx
end
end
end
for y = self.live_piece.y, math.min(self.live_piece.y + 4, self.grid.y - 1) do
local all_present = true
for x = 0, self.grid.x - 1 do
if not self.cells[y * self.grid.x + x + 1] then all_present = false end
end
if all_present then
self.score = self.score + 1
self.tick = self:calculate_tick(self.score)
for ny = y, 2, -1 do
for nx = 0, self.grid.x - 1 do
self.cells[ny * self.grid.x + nx + 1] = self.cells[(ny - 1) * self.grid.x + nx + 1]
end
end
end
end
self.live_piece = nil
end
function TetrisView:step()
if not self.finished and not self.paused then
if not self.live_piece then
local idx = self.next_piece or math.floor(math.random() * #self.tetronimos) + 1
self.live_piece = { tetronimo = self.tetronimos[idx], idx = idx, x = math.floor(self.grid.x / 2), y = 0, rot = 1 }
self.next_piece = math.floor(math.random() * #self.tetronimos) + 1
if (self:does_collide(self.live_piece.x, self.live_piece.y + 1, self.live_piece.tetronimo, self.live_piece.rot)) then
self:finalize_live_piece()
self.finished = true
end
else
if (self:does_collide(self.live_piece.x, self.live_piece.y + 1, self.live_piece.tetronimo, self.live_piece.rot)) then
self.live_piece.countup = (self.live_piece.countup or 0) + 1
if self.live_piece.countup > self.lock_delay then
self:finalize_live_piece()
end
else
self.live_piece.y = self.live_piece.y + 1
self.live_piece.countup = 0
end
end
end
end
function TetrisView:draw_tetronimo(posx, posy, tetronimo, rot, color)
local shape = tetronimo.shape[rot]
for y = 0, 3 do
for x = 0, 3 do
if shape[y * 4 + x + 1] == 1 then
renderer.draw_rect(posx + x * (self.cell_size + self.cell_padding), posy + y * (self.cell_size + self.cell_padding), self.cell_size, self.cell_size, color or tetronimo.color)
end
end
end
end
function TetrisView:draw()
self:draw_background(style.background3)
local lh = style.font:get_height()
local tx = self.position.x + style.padding.x
local ty = self.position.y + style.padding.y
renderer.draw_text(style.font, "Score: " .. self.score, tx, self.position.y + style.padding.y, style.normal)
local w = renderer.draw_text(style.font, "Next Piece", tx, self.position.y + style.padding.y + lh, style.normal)
if self.next_piece then
self:draw_tetronimo(tx, self.position.y + style.padding.y + lh * 2, self.tetronimos[self.next_piece], 1)
end
if self.held_piece then
self:draw_tetronimo(w + style.padding.x, self.position.y + style.padding.y + lh * 2, self.tetronimos[self.held_piece], 1)
end
renderer.draw_text(style.font, "Held Piece", w + style.padding.x, self.position.y + style.padding.y + lh, style.normal)
ty = ty + lh * 2 + (self.cell_size + self.cell_padding) * 4 + style.padding.y
renderer.draw_rect(tx, ty, (self.cell_size + self.cell_padding) * self.grid.x, (self.cell_size + self.cell_padding) * self.grid.y, style.background)
for y = 0, self.grid.y - 1 do
for x = 0, self.grid.x - 1 do
if self.cells[y * self.grid.x + x + 1] then
local color = self.tetronimos[self.cells[y * self.grid.x + x + 1]].color
renderer.draw_rect(tx + x * (self.cell_size + self.cell_padding), ty + y * (self.cell_size + self.cell_padding), self.cell_size, self.cell_size, color)
end
end
end
if self.live_piece then
self:draw_tetronimo(tx + self.live_piece.x * (self.cell_size + self.cell_padding), ty + self.live_piece.y * (self.cell_size + self.cell_padding), self.live_piece.tetronimo, self.live_piece.rot)
if self.drop_shadow then
local y = self:get_max_drop(math.huge)
if y ~= self.live_piece.y then
self:draw_tetronimo(tx + self.live_piece.x * (self.cell_size + self.cell_padding), ty + y * (self.cell_size + self.cell_padding), self.live_piece.tetronimo, self.live_piece.rot, { self.live_piece.tetronimo.color[1], self.live_piece.tetronimo.color[2], self.live_piece.tetronimo.color[3], 50 })
end
end
end
if self.finished or self.paused then renderer.draw_rect(tx, ty, self.grid.x * (self.cell_size + self.cell_padding), self.grid.y * (self.cell_size + self.cell_padding), { common.color "rgba(255, 255, 255, 0.5)" }) end
if self.finished then common.draw_text(style.font, style.error, "GAME OVER", "center", tx, ty, self.grid.x * (self.cell_size + self.cell_padding), self.grid.y * (self.cell_size + self.cell_padding)) end
if self.paused then common.draw_text(style.font, style.warn, "PAUSED", "center", tx, ty, self.grid.x * (self.cell_size + self.cell_padding), self.grid.y * (self.cell_size + self.cell_padding)) end
end
function TetrisView:rotate()
if self.live_piece and not self.paused then
local new_rot = (self.live_piece.rot % #self.live_piece.tetronimo.shape) + 1
if not self:does_collide(self.live_piece.x, self.live_piece.y, self.live_piece.tetronimo, new_rot) then
self.live_piece.rot = new_rot
end
end
end
function TetrisView:hold()
if self.live_piece and not self.paused then
if self.held_piece then
if not self:does_collide(self.live_piece.x, self.live_piece.y, self.tetronimos[self.held_piece], 1) then
local live_piece = self.live_piece.idx
self.live_piece = { x = self.live_piece.x, y = self.live_piece.y, rot = 1, idx = self.held_piece, tetronimo = self.tetronimos[self.held_piece] }
self.held_piece = live_piece
end
else
self.held_piece = self.live_piece.idx
self.live_piece = nil
end
end
end
function TetrisView:get_max_drop(amount)
if self.live_piece then
for y = self.live_piece.y, math.min(self.grid.y, self.live_piece.y + amount) do
if self:does_collide(self.live_piece.x, y + 1, self.live_piece.tetronimo, self.live_piece.rot) then
return y, true
end
end
end
return self.live_piece.y + amount, false
end
function TetrisView:drop(amount)
if self.live_piece and not self.paused then
local y, collides = self:get_max_drop(amount)
self.live_piece.y = y
if collides then
self:finalize_live_piece()
end
end
end
function TetrisView:shift(delta)
if self.live_piece and not self.paused and not self:does_collide(self.live_piece.x + delta, self.live_piece.y, self.live_piece.tetronimo, self.live_piece.rot) then
self.live_piece.x = self.live_piece.x + delta
end
end
command.add(TetrisView, {
["tetris:rotate"] = function() core.active_view:rotate() end,
["tetris:shift-left"] = function() core.active_view:shift(-1) end,
["tetris:shift-right"] = function() core.active_view:shift(1) end,
["tetris:drop"] = function() core.active_view:drop(config.plugins.tetris.down_amount) end,
["tetris:hard-drop"] = function() core.active_view:drop(math.huge) end,
["tetris:hold"] = function() core.active_view:hold() end,
["tetris:toggle-pause"] = function() core.active_view.paused = not core.active_view.paused end,
["tetris:quit"] = function()
core.active_view.finished = true
core.active_view.node:close_view(core.root_view.root_node, core.active_view)
end
})
command.add(nil, {
["tetris:start"] = function()
local view = TetrisView(config.plugins.tetris)
local node = core.root_view:get_active_node()
view.node = node:split("right", view, { x = true }, false)
core.set_active_view(view)
end
})
keymap.add {
["up"] = "tetris:rotate",
["left"] = "tetris:shift-left",
["right"] = "tetris:shift-right",
["down"] = "tetris:drop",
["space"] = "tetris:hard-drop",
["tab"] = "tetris:hold",
["escape"] = "tetris:quit",
["ctrl+e"] = { "tetris:quit", "tetris:start" },
["p"] = "tetris:toggle-pause"
}
return { view = TetrisView }

View File

@ -2,13 +2,13 @@
* lite_xl_plugin_api.h
* API for writing C extension modules loaded by Lite XL.
* This file is licensed under MIT.
*
*
* The Lite XL plugin API is quite simple.
* You would write a lua C extension and replace any references to lua.h, lauxlib.h
* and lualib.h with lite_xl_plugin_api.h.
* In your main file (where your entrypoint resides), define LITE_XL_PLUGIN_ENTRYPOINT.
* If you have multiple entrypoints, define LITE_XL_PLUGIN_ENTRYPOINT in one of them.
*
*
* After that, you need to create a Lite XL entrypoint, which is formatted as
* luaopen_lite_xl_xxxxx instead of luaopen_xxxxx.
* This entrypoint accepts a lua_State and an extra parameter of type void *.
@ -16,9 +16,9 @@
* If you have multiple entrypoints, you must call lite_xl_plugin_init() in
* each of them.
* This function is not thread safe, so don't try to do anything stupid.
*
*
* An example:
*
*
* #define LITE_XL_PLUGIN_ENTRYPOINT
* #include "lite_xl_plugin_api.h"
* int luaopen_lite_xl_xxxxx(lua_State* L, void* XL) {
@ -26,19 +26,14 @@
* ...
* return 1;
* }
*
*
* You can compile the library just like any Lua library without linking to Lua.
* An example command would be: gcc -shared -o xxxxx.so xxxxx.c
* You must not link to ANY lua library to avoid symbol collision.
*
* This file contains stock configuration for a typical installation of Lua 5.4.6.
*
* This file contains stock configuration for a typical installation of Lua 5.4.
* DO NOT MODIFY ANYTHING. MODIFYING STUFFS IN HERE WILL BREAK
* COMPATIBILITY WITH LITE XL AND CAUSE UNDEBUGGABLE BUGS.
*
* For reference, here are a list of permalinks to previous version of this file that targets an older version of Lua.
* If you don't need functionalities offered by the new version, use the OLDEST FILE for backwards compatibility.
*
* - Lua 5.4.4: https://github.com/lite-xl/lite-xl/blob/397973067f14420b26e3b20a238a50016c0b75e2/resources/include/lite_xl_plugin_api.h
**/
#ifndef LITE_XL_PLUGIN_API
#define LITE_XL_PLUGIN_API
@ -65,8 +60,8 @@
#define FE_7(what, x, ...) what x,FE_6(what, __VA_ARGS__)
#define FE_8(what, x, ...) what x,FE_7(what, __VA_ARGS__)
#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__)
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__)
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0
#define FOR_EACH_(N, what, ...) CONCAT(FE_, N)(what, __VA_ARGS__)
#define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__)
@ -1033,7 +1028,6 @@ extern const char lua_ident[];
SYMBOL_DECLARE(lua_State *, lua_newstate, lua_Alloc f, void *ud)
SYMBOL_DECLARE(void, lua_close, lua_State *L)
SYMBOL_DECLARE(lua_State *, lua_newthread, lua_State *L)
SYMBOL_DECLARE(int, lua_closethread, lua_State *L, lua_State *from)
SYMBOL_DECLARE(int, lua_resetthread, lua_State *L)
SYMBOL_DECLARE(lua_CFunction, lua_atpanic, lua_State *L, lua_CFunction panicf)
@ -1745,9 +1739,6 @@ SYMBOL_WRAP_DECL(void, lua_close, lua_State *L) {
SYMBOL_WRAP_DECL(lua_State *, lua_newthread, lua_State *L) {
return SYMBOL_WRAP_CALL(lua_newthread, L);
}
SYMBOL_WRAP_DECL(int, lua_closethread, lua_State *L, lua_State *from) {
return SYMBOL_WRAP_CALL(lua_closethread, L, from);
}
SYMBOL_WRAP_DECL(int, lua_resetthread, lua_State *L) {
return SYMBOL_WRAP_CALL(lua_resetthread, L);
}
@ -2360,7 +2351,6 @@ void lite_xl_plugin_init(void *XL) {
IMPORT_SYMBOL(lua_newstate, lua_State *, lua_Alloc f, void *ud);
IMPORT_SYMBOL(lua_close, void, lua_State *L);
IMPORT_SYMBOL(lua_newthread, lua_State *, lua_State *L);
IMPORT_SYMBOL(lua_closethread, int, lua_State *L, lua_State *from);
IMPORT_SYMBOL(lua_resetthread, int, lua_State *L);
IMPORT_SYMBOL(lua_atpanic, lua_CFunction, lua_State *L, lua_CFunction panicf);
IMPORT_SYMBOL(lua_version, lua_Number, lua_State *L);
@ -2570,4 +2560,4 @@ void lite_xl_plugin_init(void *XL);
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
******************************************************************************/

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>com.lite_xl.LiteXL</id>
<id>org.lite_xl.lite_xl</id>
<metadata_license>MIT</metadata_license>
<project_license>MIT</project_license>
<name>Lite XL</name>
<summary>A lightweight text editor written in Lua</summary>
<content_rating type="oars-1.0" />
<launchable type="desktop-id">com.lite_xl.LiteXL.desktop</launchable>
<launchable type="desktop-id">org.lite_xl.lite_xl.desktop</launchable>
<description>
<p>
@ -29,6 +29,6 @@
</provides>
<releases>
<release version="2.1.1" date="2022-12-29" />
<release version="2.1.4" date="2024-04-16" />
</releases>
</component>

View File

@ -1,6 +1,6 @@
diff -ruN lua-5.4.4/meson.build lua-5.4.4-patched/meson.build
--- lua-5.4.4/meson.build Wed Feb 22 18:16:56 2023
+++ lua-5.4.4-patched/meson.build Wed Feb 22 04:10:01 2023
diff -ruN lua-5.4.4\meson.build lua-5.4.4-patched\meson.build
--- lua-5.4.4\meson.build Wed Feb 22 18:16:56 2023
+++ lua-5.4.4-patched\meson.build Wed Feb 22 04:10:01 2023
@@ -85,6 +85,7 @@
'src/lutf8lib.c',
'src/lvm.c',
@ -9,13 +9,13 @@ diff -ruN lua-5.4.4/meson.build lua-5.4.4-patched/meson.build
dependencies: lua_lib_deps,
version: meson.project_version(),
soversion: lua_versions[0] + '.' + lua_versions[1],
diff -ruN lua-5.4.4/src/luaconf.h lua-5.4.4-patched/src/luaconf.h
--- lua-5.4.4/src/luaconf.h Thu Jan 13 19:24:43 2022
+++ lua-5.4.4-patched/src/luaconf.h Wed Feb 22 04:10:02 2023
diff -ruN lua-5.4.4\src\luaconf.h lua-5.4.4-patched\src\luaconf.h
--- lua-5.4.4\src\luaconf.h Thu Jan 13 19:24:43 2022
+++ lua-5.4.4-patched\src\luaconf.h Wed Feb 22 04:10:02 2023
@@ -782,5 +782,15 @@
+#if defined(lua_c) || defined(luac_c) || (defined(LUA_LIB) && \
+ (defined(lauxlib_c) || defined(liolib_c) || \
+ defined(loadlib_c) || defined(loslib_c)))
@ -27,22 +27,22 @@ diff -ruN lua-5.4.4/src/luaconf.h lua-5.4.4-patched/src/luaconf.h
+
+
#endif
diff -ruN lua-5.4.4/src/Makefile lua-5.4.4-patched/src/Makefile
--- lua-5.4.4/src/Makefile Thu Jul 15 22:01:52 2021
+++ lua-5.4.4-patched/src/Makefile Wed Feb 22 04:10:02 2023
diff -ruN lua-5.4.4\src\Makefile lua-5.4.4-patched\src\Makefile
--- lua-5.4.4\src\Makefile Thu Jul 15 22:01:52 2021
+++ lua-5.4.4-patched\src\Makefile Wed Feb 22 04:10:02 2023
@@ -33,7 +33,7 @@
PLATS= guess aix bsd c89 freebsd generic linux linux-readline macosx mingw posix solaris
LUA_A= liblua.a
-CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o
+CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o utf8_wrappers.o
LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)
diff -ruN lua-5.4.4/src/utf8_wrappers.c lua-5.4.4-patched/src/utf8_wrappers.c
--- lua-5.4.4/src/utf8_wrappers.c Thu Jan 01 08:00:00 1970
+++ lua-5.4.4-patched/src/utf8_wrappers.c Wed Feb 22 18:13:45 2023
diff -ruN lua-5.4.4\src\utf8_wrappers.c lua-5.4.4-patched\src\utf8_wrappers.c
--- lua-5.4.4\src\utf8_wrappers.c Thu Jan 01 08:00:00 1970
+++ lua-5.4.4-patched\src\utf8_wrappers.c Wed Feb 22 18:13:45 2023
@@ -0,0 +1,129 @@
+/**
+ * Wrappers to provide Unicode (UTF-8) support on Windows.
@ -173,9 +173,9 @@ diff -ruN lua-5.4.4/src/utf8_wrappers.c lua-5.4.4-patched/src/utf8_wrappers.c
+ return env_value;
+}
+#endif
diff -ruN lua-5.4.4/src/utf8_wrappers.h lua-5.4.4-patched/src/utf8_wrappers.h
--- lua-5.4.4/src/utf8_wrappers.h Thu Jan 01 08:00:00 1970
+++ lua-5.4.4-patched/src/utf8_wrappers.h Wed Feb 22 18:09:48 2023
diff -ruN lua-5.4.4\src\utf8_wrappers.h lua-5.4.4-patched\src\utf8_wrappers.h
--- lua-5.4.4\src\utf8_wrappers.h Thu Jan 01 08:00:00 1970
+++ lua-5.4.4-patched\src\utf8_wrappers.h Wed Feb 22 18:09:48 2023
@@ -0,0 +1,46 @@
+/**
+ * Wrappers to provide Unicode (UTF-8) support on Windows.

View File

@ -138,7 +138,7 @@ generate_appimage() {
mv AppRun LiteXL.AppDir/
# These could be symlinks but it seems they doesn't work with AppimageLauncher
cp resources/icons/lite-xl.svg LiteXL.AppDir/
cp resources/linux/com.lite_xl.LiteXL.desktop LiteXL.AppDir/
cp resources/linux/org.lite_xl.lite_xl.desktop LiteXL.AppDir/
if [[ $ADDONS == true ]]; then
addons_download "${BUILD_DIR}"

View File

@ -77,11 +77,17 @@ get_platform_arch() {
platform=$(get_platform_name)
arch=${CROSS_ARCH:-$(uname -m)}
if [[ $MSYSTEM != "" ]]; then
if [[ $MSYSTEM == "MINGW64" ]]; then
case "$MSYSTEM" in
MINGW64|UCRT64|CLANG64)
arch=x86_64
else
;;
MINGW32|CLANG32)
arch=i686
fi
;;
CLANGARM64)
arch=aarch64
;;
esac
fi
echo "$arch"
}

View File

@ -1,7 +1,7 @@
#define MyAppName "Lite XL"
#define MyAppVersion "@PROJECT_VERSION@"
#define MyAppPublisher "Lite XL Team"
#define MyAppURL "https://lite-xl.github.io"
#define MyAppURL "https://lite-xl.com"
#define MyAppExeName "lite-xl.exe"
#define BuildDir "@PROJECT_BUILD_DIR@"
#define SourceDir "@PROJECT_SOURCE_DIR@"
@ -26,12 +26,16 @@ AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
#if Arch=="x64"
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
#define ArchInternal "x86_64"
#else
#if Arch=="x86"
#define ArchInternal "i686"
#else
ArchitecturesAllowed={#Arch}
ArchitecturesInstallIn64BitMode={#Arch}
#if Arch=="x64"
#define ArchInternal "x86_64"
#elif Arch=="arm64"
#define ArchInternal "aarch64"
#endif
#endif
AllowNoIcons=yes
@ -69,9 +73,7 @@ Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescrip
Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked
[Files]
Source: "{#BuildDir}/src/lite-xl.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#BuildDir}/mingwLibs{#Arch}/*"; DestDir: "{app}"; Flags: ignoreversion ; Check: DirExists(ExpandConstant('{#BuildDir}/mingwLibs{#Arch}'))
Source: "{#SourceDir}/data/*"; DestDir: "{app}/data"; Flags: ignoreversion recursesubdirs
Source: "{#SourceDir}/lite-xl/*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]

View File

@ -29,13 +29,24 @@ main() {
local version
local output
if [[ $MSYSTEM == "MINGW64" ]]; then
case "$MSYSTEM" in
MINGW64|UCRT64|CLANG64)
arch=x64
arch_file=x86_64
else
arch=i686;
;;
MINGW32|CLANG32)
arch=x86
arch_file=i686
fi
;;
CLANGARM64)
arch=arm64
arch_file=aarch64
;;
*)
echo "error: unsupported MSYSTEM type: $MSYSTEM"
exit 1
;;
esac
initial_arg_count=$#

View File

@ -213,14 +213,18 @@ main() {
if [[ $platform == "windows" ]]; then
exe_file="${exe_file}.exe"
stripcmd="strip --strip-all"
# Copy MinGW libraries dependencies.
# MSYS2 ldd command seems to be only 64bit, so use ntldd
# see https://github.com/msys2/MINGW-packages/issues/4164
ntldd -R "${exe_file}" \
| grep mingw \
| awk '{print $3}' \
| sed 's#\\#/#g' \
| xargs -I '{}' cp -v '{}' "$(pwd)/${dest_dir}/"
if command -v ntldd >/dev/null 2>&1; then
# Copy MinGW libraries dependencies.
# MSYS2 ldd command seems to be only 64bit, so use ntldd
# see https://github.com/msys2/MINGW-packages/issues/4164
ntldd -R "${exe_file}" \
| grep mingw \
| awk '{print $3}' \
| sed 's#\\#/#g' \
| xargs -I '{}' cp -v '{}' "$(pwd)/${dest_dir}/"
else
echo "WARNING: ntldd not found; assuming program is static"
fi
else
# Windows archive is always portable
package_name+="-portable"

View File

@ -7,7 +7,7 @@ int luaopen_regex(lua_State *L);
int luaopen_dirmonitor(lua_State* L);
int luaopen_utf8extra(lua_State* L);
#if defined(__amigaos4__)
#if defined(__amigaos4__) || defined(__morphos__)
int luaopen_codesets(lua_State* L);
#endif
@ -18,7 +18,7 @@ static const luaL_Reg libs[] = {
// { "process", luaopen_process },
{ "dirmonitor", luaopen_dirmonitor },
{ "utf8extra", luaopen_utf8extra },
#if defined(__amigaos4__)
#if defined(__amigaos4__) || defined(__morphos__)
{ "codesetsextra", luaopen_codesets },
#endif
{ NULL, NULL }
@ -29,3 +29,4 @@ void api_load_libs(lua_State *L) {
for (int i = 0; libs[i].name; i++)
luaL_requiref(L, libs[i].name, libs[i].func, 1);
}

View File

@ -39,6 +39,7 @@ typedef wchar_t *process_env_t;
#define HANDLE_INVALID (INVALID_HANDLE_VALUE)
#define PROCESS_GET_HANDLE(P) ((P)->process_information.hProcess)
#define PROCESS_ARGLIST_INITIALIZER { 0 }
static volatile long PipeSerialNumber;
@ -52,6 +53,7 @@ typedef char **process_env_t;
#define HANDLE_INVALID (0)
#define PROCESS_GET_HANDLE(P) ((P)->pid)
#define PROCESS_ARGLIST_INITIALIZER NULL
#endif
@ -440,6 +442,7 @@ static int process_arglist_add(process_arglist_t *list, size_t *list_len, const
static void process_arglist_free(process_arglist_t *list) {
if (!*list) return;
#ifndef _WIN32
char **cmd = *list;
for (int i = 0; cmd[i]; i++)
@ -563,14 +566,13 @@ static int process_env_add(process_env_t *env_list, size_t *env_len, const char
}
static void process_env_free(process_env_t *list) {
static void process_env_free(process_env_t *list, size_t list_len) {
if (!*list) return;
#ifdef _WIN32
free(*list);
#else
for (size_t i = 0; (*list)[i]; i++) free((*list)[i]);
free(*list);
#ifndef _WIN32
for (size_t i = 0; i < list_len; i++)
free((*list)[i]);
#endif
free(*list);
*list = NULL;
}
@ -578,7 +580,8 @@ static void process_env_free(process_env_t *list) {
static int process_start(lua_State* L) {
int r, retval = 1;
size_t env_len = 0, cmd_len = 0, arglist_len = 0, env_vars_len = 0;
process_arglist_t arglist;
process_t *self = NULL;
process_arglist_t arglist = PROCESS_ARGLIST_INITIALIZER;
process_env_t env_vars = NULL;
const char *cwd = NULL;
bool detach = false, escape = true;
@ -668,7 +671,7 @@ static int process_start(lua_State* L) {
lua_pop(L, 1);
}
process_t* self = lua_newuserdata(L, sizeof(process_t));
self = lua_newuserdata(L, sizeof(process_t));
memset(self, 0, sizeof(process_t));
luaL_setmetatable(L, API_TYPE_PROCESS);
self->deadline = deadline;
@ -826,14 +829,16 @@ static int process_start(lua_State* L) {
if (control_pipe[0]) close(control_pipe[0]);
if (control_pipe[1]) close(control_pipe[1]);
#endif
for (int stream = 0; stream < 3; ++stream) {
process_stream_t* pipe = &self->child_pipes[stream][stream == STDIN_FD ? 0 : 1];
if (*pipe) {
close_fd(pipe);
if (self) {
for (int stream = 0; stream < 3; ++stream) {
process_stream_t* pipe = &self->child_pipes[stream][stream == STDIN_FD ? 0 : 1];
if (*pipe) {
close_fd(pipe);
}
}
}
process_arglist_free(&arglist);
process_env_free(&env_vars);
process_env_free(&env_vars, env_vars_len);
if (retval == -1)
return lua_error(L);
@ -849,7 +854,7 @@ static int g_read(lua_State* L, int stream, unsigned long read_size) {
return luaL_error(L, "error: redirect to handles, FILE* and paths are not supported");
#if _WIN32
int writable_stream_idx = stream - 1;
if (self->reading[writable_stream_idx] || !ReadFile(self->child_pipes[stream][0], self->buffer[writable_stream_idx], READ_BUF_SIZE, NULL, &self->overlapped[writable_stream_idx])) {
if (self->reading[writable_stream_idx] || !ReadFile(self->child_pipes[stream][0], self->buffer[writable_stream_idx], read_size > READ_BUF_SIZE ? READ_BUF_SIZE : read_size, NULL, &self->overlapped[writable_stream_idx])) {
if (self->reading[writable_stream_idx] || GetLastError() == ERROR_IO_PENDING) {
self->reading[writable_stream_idx] = true;
DWORD bytesTransferred = 0;
@ -975,10 +980,7 @@ static int self_signal(lua_State* L, signal_e sig) {
static int f_terminate(lua_State* L) { return self_signal(L, SIGNAL_TERM); }
static int f_kill(lua_State* L) { return self_signal(L, SIGNAL_KILL); }
static int f_interrupt(lua_State* L) { return self_signal(L, SIGNAL_INTERRUPT); }
<<<<<<< HEAD
=======
>>>>>>> master
static int f_gc(lua_State* L) {
process_kill_list_t *list = NULL;
process_kill_t *p = NULL;

View File

@ -90,7 +90,7 @@ static int f_font_load(lua_State *L) {
return ret_code;
RenFont** font = lua_newuserdata(L, sizeof(RenFont*));
*font = ren_font_load(window_renderer, filename, size, antialiasing, hinting, style);
*font = ren_font_load(&window_renderer, filename, size, antialiasing, hinting, style);
if (!*font)
return luaL_error(L, "failed to load font");
luaL_setmetatable(L, API_TYPE_FONT);
@ -130,7 +130,7 @@ static int f_font_copy(lua_State *L) {
}
for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) {
RenFont** font = lua_newuserdata(L, sizeof(RenFont*));
*font = ren_font_copy(window_renderer, fonts[i], size, antialiasing, hinting, style);
*font = ren_font_copy(&window_renderer, fonts[i], size, antialiasing, hinting, style);
if (!*font)
return luaL_error(L, "failed to copy font");
luaL_setmetatable(L, API_TYPE_FONT);
@ -198,7 +198,7 @@ static int f_font_get_width(lua_State *L) {
size_t len;
const char *text = luaL_checklstring(L, 2, &len);
lua_pushnumber(L, ren_font_group_get_width(window_renderer, fonts, text, len, NULL));
lua_pushnumber(L, ren_font_group_get_width(&window_renderer, fonts, text, len, NULL));
return 1;
}
@ -217,7 +217,7 @@ static int f_font_get_size(lua_State *L) {
static int f_font_set_size(lua_State *L) {
RenFont* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1);
float size = luaL_checknumber(L, 2);
ren_font_group_set_size(window_renderer, fonts, size);
ren_font_group_set_size(&window_renderer, fonts, size);
return 0;
}
@ -276,7 +276,7 @@ static int f_show_debug(lua_State *L) {
static int f_get_size(lua_State *L) {
int w, h;
ren_get_size(window_renderer, &w, &h);
ren_get_size(&window_renderer, &w, &h);
lua_pushnumber(L, w);
lua_pushnumber(L, h);
return 2;
@ -284,13 +284,13 @@ static int f_get_size(lua_State *L) {
static int f_begin_frame(UNUSED lua_State *L) {
rencache_begin_frame(window_renderer);
rencache_begin_frame(&window_renderer);
return 0;
}
static int f_end_frame(UNUSED lua_State *L) {
rencache_end_frame(window_renderer);
rencache_end_frame(&window_renderer);
// clear the font reference table
lua_newtable(L);
lua_rawseti(L, LUA_REGISTRYINDEX, RENDERER_FONT_REF);
@ -311,7 +311,7 @@ static int f_set_clip_rect(lua_State *L) {
lua_Number w = luaL_checknumber(L, 3);
lua_Number h = luaL_checknumber(L, 4);
RenRect rect = rect_to_grid(x, y, w, h);
rencache_set_clip_rect(window_renderer, rect);
rencache_set_clip_rect(&window_renderer, rect);
return 0;
}
@ -323,7 +323,7 @@ static int f_draw_rect(lua_State *L) {
lua_Number h = luaL_checknumber(L, 4);
RenRect rect = rect_to_grid(x, y, w, h);
RenColor color = checkcolor(L, 5, 255);
rencache_draw_rect(window_renderer, rect, color);
rencache_draw_rect(&window_renderer, rect, color);
return 0;
}
@ -348,7 +348,7 @@ static int f_draw_text(lua_State *L) {
double x = luaL_checknumber(L, 3);
int y = luaL_checknumber(L, 4);
RenColor color = checkcolor(L, 5, 255);
x = rencache_draw_text(window_renderer, fonts, text, len, x, y, color);
x = rencache_draw_text(&window_renderer, fonts, text, len, x, y, color);
lua_pushnumber(L, x);
return 1;
}
@ -391,4 +391,3 @@ int luaopen_renderer(lua_State *L) {
lua_setfield(L, -2, "font");
return 1;
}

View File

@ -78,7 +78,7 @@ static SDL_HitTestResult SDLCALL hit_test(SDL_Window *window, const SDL_Point *p
const int controls_width = hit_info->controls_width;
int w, h;
SDL_GetWindowSize(window_renderer->window, &w, &h);
SDL_GetWindowSize(window_renderer.window, &w, &h);
if (pt->y < hit_info->title_height &&
#if RESIZE_FROM_TOP
@ -197,7 +197,7 @@ top:
case SDL_WINDOWEVENT:
if (e.window.event == SDL_WINDOWEVENT_RESIZED) {
ren_resize_window(window_renderer);
ren_resize_window(&window_renderer);
lua_pushstring(L, "resized");
/* The size below will be in points. */
lua_pushinteger(L, e.window.data1);
@ -236,8 +236,8 @@ top:
SDL_GetMouseState(&mx, &my);
lua_pushstring(L, "filedropped");
lua_pushstring(L, e.drop.file);
lua_pushinteger(L, mx * window_renderer->scale_x);
lua_pushinteger(L, my * window_renderer->scale_y);
lua_pushinteger(L, mx * window_renderer.scale_x);
lua_pushinteger(L, my * window_renderer.scale_y);
SDL_free(e.drop.file);
return 4;
@ -294,8 +294,8 @@ top:
if (e.button.button == 1) { SDL_CaptureMouse(1); }
lua_pushstring(L, "mousepressed");
lua_pushstring(L, button_name(e.button.button));
lua_pushinteger(L, e.button.x * window_renderer->scale_x);
lua_pushinteger(L, e.button.y * window_renderer->scale_y);
lua_pushinteger(L, e.button.x * window_renderer.scale_x);
lua_pushinteger(L, e.button.y * window_renderer.scale_y);
lua_pushinteger(L, e.button.clicks);
return 5;
@ -303,8 +303,8 @@ top:
if (e.button.button == 1) { SDL_CaptureMouse(0); }
lua_pushstring(L, "mousereleased");
lua_pushstring(L, button_name(e.button.button));
lua_pushinteger(L, e.button.x * window_renderer->scale_x);
lua_pushinteger(L, e.button.y * window_renderer->scale_y);
lua_pushinteger(L, e.button.x * window_renderer.scale_x);
lua_pushinteger(L, e.button.y * window_renderer.scale_y);
return 4;
case SDL_MOUSEMOTION:
@ -316,10 +316,10 @@ top:
e.motion.yrel += event_plus.motion.yrel;
}
lua_pushstring(L, "mousemoved");
lua_pushinteger(L, e.motion.x * window_renderer->scale_x);
lua_pushinteger(L, e.motion.y * window_renderer->scale_y);
lua_pushinteger(L, e.motion.xrel * window_renderer->scale_x);
lua_pushinteger(L, e.motion.yrel * window_renderer->scale_y);
lua_pushinteger(L, e.motion.x * window_renderer.scale_x);
lua_pushinteger(L, e.motion.y * window_renderer.scale_y);
lua_pushinteger(L, e.motion.xrel * window_renderer.scale_x);
lua_pushinteger(L, e.motion.yrel * window_renderer.scale_y);
return 5;
case SDL_MOUSEWHEEL:
@ -335,7 +335,7 @@ top:
return 3;
case SDL_FINGERDOWN:
SDL_GetWindowSize(window_renderer->window, &w, &h);
SDL_GetWindowSize(window_renderer.window, &w, &h);
lua_pushstring(L, "touchpressed");
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
@ -344,7 +344,7 @@ top:
return 4;
case SDL_FINGERUP:
SDL_GetWindowSize(window_renderer->window, &w, &h);
SDL_GetWindowSize(window_renderer.window, &w, &h);
lua_pushstring(L, "touchreleased");
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
@ -360,7 +360,7 @@ top:
e.tfinger.dx += event_plus.tfinger.dx;
e.tfinger.dy += event_plus.tfinger.dy;
}
SDL_GetWindowSize(window_renderer->window, &w, &h);
SDL_GetWindowSize(window_renderer.window, &w, &h);
lua_pushstring(L, "touchmoved");
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
@ -369,21 +369,6 @@ top:
lua_pushinteger(L, (lua_Integer)(e.tfinger.dy * h));
lua_pushinteger(L, e.tfinger.fingerId);
return 6;
case SDL_APP_WILLENTERFOREGROUND:
case SDL_APP_DIDENTERFOREGROUND:
#ifdef LITE_USE_SDL_RENDERER
rencache_invalidate();
#else
SDL_UpdateWindowSurface(window_renderer->window);
#endif
lua_pushstring(L, e.type == SDL_APP_WILLENTERFOREGROUND ? "enteringforeground" : "enteredforeground");
return 1;
case SDL_APP_WILLENTERBACKGROUND:
lua_pushstring(L, "enteringbackground");
return 1;
case SDL_APP_DIDENTERBACKGROUND:
lua_pushstring(L, "enteredbackground");
return 1;
default:
goto top;
@ -440,7 +425,7 @@ static int f_set_cursor(lua_State *L) {
static int f_set_window_title(lua_State *L) {
const char *title = luaL_checkstring(L, 1);
SDL_SetWindowTitle(window_renderer->window, title);
SDL_SetWindowTitle(window_renderer.window, title);
return 0;
}
@ -450,39 +435,39 @@ enum { WIN_NORMAL, WIN_MINIMIZED, WIN_MAXIMIZED, WIN_FULLSCREEN };
static int f_set_window_mode(lua_State *L) {
int n = luaL_checkoption(L, 1, "normal", window_opts);
SDL_SetWindowFullscreen(window_renderer->window,
SDL_SetWindowFullscreen(window_renderer.window,
n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
if (n == WIN_NORMAL) { SDL_RestoreWindow(window_renderer->window); }
if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window_renderer->window); }
if (n == WIN_MINIMIZED) { SDL_MinimizeWindow(window_renderer->window); }
if (n == WIN_NORMAL) { SDL_RestoreWindow(window_renderer.window); }
if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window_renderer.window); }
if (n == WIN_MINIMIZED) { SDL_MinimizeWindow(window_renderer.window); }
return 0;
}
static int f_set_window_bordered(lua_State *L) {
int bordered = lua_toboolean(L, 1);
SDL_SetWindowBordered(window_renderer->window, bordered);
SDL_SetWindowBordered(window_renderer.window, bordered);
return 0;
}
static int f_set_window_hit_test(lua_State *L) {
if (lua_gettop(L) == 0) {
SDL_SetWindowHitTest(window_renderer->window, NULL, NULL);
SDL_SetWindowHitTest(window_renderer.window, NULL, NULL);
return 0;
}
window_hit_info->title_height = luaL_checknumber(L, 1);
window_hit_info->controls_width = luaL_checknumber(L, 2);
window_hit_info->resize_border = luaL_checknumber(L, 3);
SDL_SetWindowHitTest(window_renderer->window, hit_test, window_hit_info);
SDL_SetWindowHitTest(window_renderer.window, hit_test, window_hit_info);
return 0;
}
static int f_get_window_size(lua_State *L) {
int x, y, w, h;
SDL_GetWindowSize(window_renderer->window, &w, &h);
SDL_GetWindowPosition(window_renderer->window, &x, &y);
SDL_GetWindowSize(window_renderer.window, &w, &h);
SDL_GetWindowPosition(window_renderer.window, &x, &y);
lua_pushinteger(L, w);
lua_pushinteger(L, h);
lua_pushinteger(L, x);
@ -496,22 +481,22 @@ static int f_set_window_size(lua_State *L) {
double h = luaL_checknumber(L, 2);
double x = luaL_checknumber(L, 3);
double y = luaL_checknumber(L, 4);
SDL_SetWindowSize(window_renderer->window, w, h);
SDL_SetWindowPosition(window_renderer->window, x, y);
ren_resize_window(window_renderer);
SDL_SetWindowSize(window_renderer.window, w, h);
SDL_SetWindowPosition(window_renderer.window, x, y);
ren_resize_window(&window_renderer);
return 0;
}
static int f_window_has_focus(lua_State *L) {
unsigned flags = SDL_GetWindowFlags(window_renderer->window);
unsigned flags = SDL_GetWindowFlags(window_renderer.window);
lua_pushboolean(L, flags & SDL_WINDOW_INPUT_FOCUS);
return 1;
}
static int f_get_window_mode(lua_State *L) {
unsigned flags = SDL_GetWindowFlags(window_renderer->window);
unsigned flags = SDL_GetWindowFlags(window_renderer.window);
if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) {
lua_pushstring(L, "fullscreen");
} else if (flags & SDL_WINDOW_MINIMIZED) {
@ -549,8 +534,8 @@ static int f_raise_window(lua_State *L) {
to allow the window to be focused. Also on wayland the raise window event
may not always be obeyed.
*/
SDL_SetWindowInputFocus(window_renderer->window);
SDL_RaiseWindow(window_renderer->window);
SDL_SetWindowInputFocus(window_renderer.window);
SDL_RaiseWindow(window_renderer.window);
return 0;
}
@ -891,6 +876,13 @@ static int f_exec(lua_State *L) {
#if _WIN32
sprintf(buf, "cmd /c \"%s\"", cmd);
WinExec(buf, SW_HIDE);
#elif defined(__amigaos4__)
SystemTags( cmd,
SYS_Input, ZERO,
SYS_Output, NULL,
SYS_Error, ZERO,
SYS_Asynch, TRUE,
TAG_DONE);
#else
sprintf(buf, "%s &", cmd);
int res = system(buf);
@ -931,7 +923,7 @@ static int f_fuzzy_match(lua_State *L) {
static int f_set_window_opacity(lua_State *L) {
double n = luaL_checknumber(L, 1);
int r = SDL_SetWindowOpacity(window_renderer->window, n);
int r = SDL_SetWindowOpacity(window_renderer.window, n);
lua_pushboolean(L, r > -1);
return 1;
}
@ -1087,7 +1079,6 @@ static int f_path_compare(lua_State *L) {
size_t offset = 0, i, j;
for (i = 0; i < len1 && i < len2; i++) {
if (path1[i] != path2[i]) break;
if (isdigit(path1[i])) break;
if (path1[i] == PATHSEP) {
offset = i + 1;
}
@ -1158,15 +1149,6 @@ static int f_path_compare(lua_State *L) {
}
static int f_text_input(lua_State* L) {
if (lua_toboolean(L, 1))
SDL_StartTextInput();
else
SDL_StopTextInput();
return 0;
}
static const luaL_Reg lib[] = {
{ "poll_event", f_poll_event },
{ "wait_event", f_wait_event },
@ -1200,7 +1182,6 @@ static const luaL_Reg lib[] = {
{ "load_native_plugin", f_load_native_plugin },
{ "path_compare", f_path_compare },
{ "get_fs_type", f_get_fs_type },
{ "text_input", f_text_input },
{ NULL, NULL }
};
@ -1212,4 +1193,3 @@ int luaopen_system(lua_State *L) {
luaL_newlib(L, lib);
return 1;
}

View File

@ -8,7 +8,7 @@
#include <signal.h>
#if defined(__amigaos4__) || defined(__morphos__)
#define VSTRING "Lite XL 2.1.2r1 (28.12.2023)"
#define VSTRING "Lite XL 2.1.3r2 (18.03.2024)"
#define VERSTAG "\0$VER: " VSTRING
#endif
@ -20,11 +20,13 @@
#include <mach-o/dyld.h>
#elif defined(__amigaos4__)
#include <locale.h>
#include <workbench/startup.h>
#include "platform/codesets.h"
#include "platform/amigaos4.h"
static CONST_STRPTR stack USED = "$STACK:102400";
static CONST_STRPTR version USED = VERSTAG;
#elif defined(__morphos__)
#include "platform/codesets.h"
#include "platform/morphos.h"
unsigned long __stack = 1000000;
UBYTE VString[] = VERSTAG;
@ -111,8 +113,6 @@ void set_macos_bundle_resources(lua_State *L);
// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-140
#if defined(__x86_64__) || defined(_M_AMD64) || defined(__MINGW64__)
#define ARCH_PROCESSOR "x86_64"
#elif __arm__
#define ARCH_PROCESSOR "arm"
#elif defined(__amigaos4__) || defined(__morphos__)
#define ARCH_PROCESSOR "ppc"
#elif defined(__i386__) || defined(_M_IX86) || defined(__MINGW32__)
@ -148,16 +148,17 @@ int main(int argc, char **argv) {
#if defined(__amigaos4__) || defined(__morphos__)
setlocale(LC_ALL, "C");
#endif
#if defined(__amigaos4__)
OpenLibs();
int libsOpened = OpenLibs();
if (libsOpened != RETURN_OK)
{
return libsOpened;
}
#endif
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
fprintf(stderr, "Error initializing sdl: %s", SDL_GetError());
exit(1);
@ -209,7 +210,7 @@ int main(int argc, char **argv) {
fprintf(stderr, "Error creating lite-xl window: %s", SDL_GetError());
exit(1);
}
window_renderer = ren_init(window);
ren_init(window);
lua_State *L;
init_lua:
@ -223,6 +224,27 @@ init_lua:
lua_pushstring(L, argv[i]);
lua_rawseti(L, -2, i + 1);
}
#if defined(__amigaos4__)
if (!argc)
{
struct WBStartup *wbs = (struct WBStartup *)argv;
if (wbs->sm_NumArgs > 1)
{
char result[MAX_DOS_PATH];
for (int i = 0; i < wbs->sm_NumArgs; i++) {
getFullPathFromLock(wbs->sm_ArgList[i].wa_Lock, result);
if (result[strlen(result) -1] == ':')
strcat(result, wbs->sm_ArgList[i].wa_Name);
else
sprintf(result, "%s/%s", result, wbs->sm_ArgList[i].wa_Name);
lua_pushstring(L, result);
lua_rawseti(L, -2, i + 1);
}
}
}
#endif
lua_setglobal(L, "ARGS");
lua_pushstring(L, SDL_GetPlatform());
@ -269,23 +291,22 @@ init_lua:
" core.init()\n"
" core.run()\n"
"end, function(err)\n"
" local error_path = 'error.txt'\n"
" local error_dir\n"
" io.stdout:write('Error: '..tostring(err)..'\\n')\n"
" io.stdout:write(debug.traceback(nil, 2)..'\\n')\n"
" if core and core.on_error then\n"
" error_path = USERDIR .. PATHSEP .. error_path\n"
" error_dir=USERDIR\n"
" pcall(core.on_error, err)\n"
" else\n"
" local fp = io.open(error_path, 'wb')\n"
" error_dir=system.absolute_path(error_dir)\n"
" local fp = io.open('error.txt', 'wb')\n"
" fp:write('Error: ' .. tostring(err) .. '\\n')\n"
" fp:write(debug.traceback(nil, 2)..'\\n')\n"
" fp:write(debug.traceback(nil, 4)..'\\n')\n"
" fp:close()\n"
" error_path = system.absolute_path(error_path)\n"
" end\n"
" system.show_fatal_error('Lite XL internal error',\n"
" 'An internal error occurred in a critical part of the application.\\n\\n'..\n"
" 'Error: '..tostring(err)..'\\n\\n'..\n"
" 'Details can be found in \\\"'..error_path..'\\\"')\n"
" 'Please verify the file \\\"error.txt\\\" in the directory '..error_dir)\n"
" os.exit(1)\n"
"end)\n"
"return core and core.restart_request\n";
@ -303,11 +324,11 @@ init_lua:
// This allows the window to be destroyed before lite-xl is done with
// reaping child processes
ren_free(window_renderer);
ren_free_window_resources(&window_renderer);
lua_close(L);
#if defined(__amigaos4__)
CleanExit("JustExit");
#if defined(__amigaos4__) || defined(__morphos__)
CleanExit("FineExit");
#endif
return EXIT_SUCCESS;
}

View File

@ -70,3 +70,12 @@ char *_fullpath(const char *path)
return NULL;
}
void getFullPathFromLock(BPTR lock, char *result)
{
if (lock)
{
DevNameFromLock(lock, result, MAX_DOS_PATH, DN_FULLPATH);
return;
}
}

View File

@ -6,6 +6,6 @@
char *_fullpath(const char *);
void getFullPathFromLock(BPTR, char *);
#endif

View File

@ -5,7 +5,7 @@
* Heavily inspired from the encoding plugin
* https://github.com/jgmdev/lite-xl-encoding
*/
#include <SDL2/SDL_stdinc.h>
#include <SDL_stdinc.h>
#include <stdbool.h>
#include <lua.h>
@ -14,8 +14,14 @@
#include "codesets.h"
#if defined(__amigaos4__)
struct Library *CodesetsBase = NULL;
struct CodesetsIFace *ICodesets = NULL;
#endif
#if defined(__morphos__)
struct Library *CharsetsBase = NULL;
#endif
typedef struct {
const char* charset;
@ -87,6 +93,7 @@ static const char* encoding_charset_from_bom(
// Lua methods for codesets
int Lcodesets_detect(lua_State *L) {
#if defined(__amigaos4__)
const char* filename = luaL_checkstring(L, 1);
BPTR fileHandle = FOpen(filename, MODE_OLDFILE, 0);
@ -134,12 +141,25 @@ int Lcodesets_detect(lua_State *L) {
}
return 1;
#endif
#if defined(__morphos__)
lua_pushstring(L, "could not detect the file encoding");
return 2;
#endif
}
int Lcodesets_systemCodeset(lua_State *L) {
#if defined(__amigaos4__)
struct codeset *systemCodeset;
systemCodeset = CodesetsFindA(NULL, NULL);
lua_pushstring(L, systemCodeset->name);
#endif
#if defined(__morphos__)
char buf[16];
GetSystemCharset(buf, 16);
lua_pushstring(L, buf);
#endif
return 1;
}
@ -148,7 +168,13 @@ int Lcodesets_convert(lua_State *L) {
const char* to = luaL_checkstring(L, 1);
const char* from = luaL_checkstring(L, 2);
size_t text_len = 0;
#if defined(__amigaos4__)
const char* text = luaL_checklstring(L, 3, &text_len);
#endif
#if defined(__morphos__)
APTR text = luaL_checklstring(L, 3, &text_len);
#endif
/* conversion options */
bool strict = false;
bool handle_to_bom = false;
@ -156,6 +182,12 @@ int Lcodesets_convert(lua_State *L) {
const unsigned char* bom;
size_t bom_len = 0;
if (text_len == 0)
{
lua_pushlstring(L, text, text_len);
return 1;
}
if (lua_gettop(L) > 3 && lua_istable(L, 4)) {
lua_getfield(L, 4, "handle_to_bom");
if (lua_isboolean(L, -1)) {
@ -176,12 +208,13 @@ int Lcodesets_convert(lua_State *L) {
encoding_charset_from_bom(text, text_len, &bom_len);
}
#if defined(__amigaos4__)
char *output;
ULONG output_len;
struct codeset *srcCodeset;
struct codeset *destCodeset;
ULONG errNum = 0;
struct codeset *srcCodeset;
struct codeset *destCodeset;
srcCodeset = CodesetsFind(from, CSA_FallbackToDefault, FALSE, TAG_DONE);
// srcCodeset = CodesetsFindBest(CSA_Source, text,
// CSA_ErrPtr, &errNum,
@ -206,6 +239,39 @@ int Lcodesets_convert(lua_State *L) {
CSA_Source, text,
CSA_DestLenPtr, &output_len,
TAG_DONE);
#endif
#if defined(__morphos__)
// LONG output_len = 0;
ULONG fromMib = GetCharsetNumber(from, CSF_IANA_MIMENAME);
if (fromMib == 0)
fromMib = GetCharsetNumber(from, CSF_IANA_NAME);
if (fromMib == 0)
fromMib = GetCharsetNumber(from, CSF_IANA_ALIAS);
ULONG toMib = GetCharsetNumber(to, CSF_IANA_MIMENAME);
if (toMib == 0)
toMib = GetCharsetNumber(to, CSF_IANA_NAME);
if (toMib == 0)
toMib = GetCharsetNumber(to, CSF_IANA_ALIAS);
LONG output_len = GetByteSize((APTR)text, text_len, fromMib, toMib);
char *output = calloc(output_len, sizeof(char) + 1);
LONG dstEnc = 0;
struct TagItem tags[] = { { CST_DoNotTerminate, FALSE }, { CST_GetDestEncoding, &dstEnc }, { TAG_DONE, 0 } };
LONG result = ConvertTagList((APTR)text, text_len, (APTR)output, output_len, fromMib, toMib, tags);
if (result <= 0)
{
lua_pushfstring(L, "failed converting from '%s' to '%s'", from, to);
free(output);
return 2;
}
#endif
// if (!output)
// {
// lua_pushnil(L);
@ -245,12 +311,22 @@ int Lcodesets_convert(lua_State *L) {
} else if (!output) {
lua_pushnil(L);
lua_pushfstring(L, "failed converting from '%s' to '%s'", from, to);
#if defined(__amigaos4__)
CodesetsFreeA(output, NULL);
#endif
#if defined(__morphos__)
free(output);
#endif
return 2;
}
lua_pushlstring(L, output, output_len);
#if defined(__amigaos4__)
CodesetsFreeA(output, NULL);
#endif
#if defined(__morphos__)
free(output);
#endif
return 1;
}
@ -354,21 +430,32 @@ int luaopen_codesets (lua_State *L) {
int OpenLibs(void)
{
#if defined(__amigaos4__)
if ((CodesetsBase = OpenLibrary( "codesets.library", 6 )))
{
ICodesets = (struct CodesetsIFace *)GetInterface( CodesetsBase, "main", 1L, NULL );
if(!ICodesets) return CleanExit("Can't open codesets.library Interface");
}
else return CleanExit("Can't open codesets.library version 6");
#endif
#if defined(__morphos__)
if ((CharsetsBase = OpenLibrary( "charsets.library", 53 )) == NULL)
return CleanExit("Can't open charsets.library version 53");
#endif
return RETURN_OK;
}
int CleanExit(const char *str)
{
#if defined(__amigaos4__)
if(ICodesets) DropInterface((struct Interface *) ICodesets);
if(CodesetsBase) CloseLibrary(CodesetsBase);
#endif
#if defined(__morphos__)
if(CharsetsBase) CloseLibrary(CharsetsBase);
#endif
if(strcmp(str, "JustExit"))
if(strcmp(str, "FineExit"))
{
printf("Error::%s\n", str);
return RETURN_ERROR;

View File

@ -3,10 +3,14 @@
#include <proto/dos.h>
#include <proto/exec.h>
#if defined(__amigaos4__)
#include <proto/codesets.h>
#endif
#if defined(__morphos__)
#include <proto/charsets.h>
#endif
int OpenLibs(void);
int CleanExit(const char *);
#endif

View File

@ -14,4 +14,3 @@ void rencache_begin_frame(RenWindow *window_renderer);
void rencache_end_frame(RenWindow *window_renderer);
#endif

View File

@ -22,7 +22,7 @@
#define MAX_LOADABLE_GLYPHSETS (MAX_UNICODE / GLYPHSET_SIZE)
#define SUBPIXEL_BITMAPS_CACHED 3
RenWindow* window_renderer = NULL;
RenWindow window_renderer = {0};
static FT_Library library;
// draw_rect_surface is used as a 1x1 surface to simplify ren_draw_rect with blending
@ -236,7 +236,7 @@ static void font_file_close(FT_Stream stream) {
RenFont* ren_font_load(RenWindow *window_renderer, const char* path, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style) {
RenFont *font = NULL;
FT_Face face = NULL;
SDL_RWops *file = SDL_RWFromFile(path, "rb");
if (!file)
goto rwops_failure;
@ -501,35 +501,31 @@ void ren_draw_rect(RenSurface *rs, RenRect rect, RenColor color) {
}
/*************** Window Management ****************/
RenWindow* ren_init(SDL_Window *win) {
assert(win);
int error = FT_Init_FreeType( &library );
if ( error ) {
fprintf(stderr, "internal font error when starting the application\n");
return NULL;
}
RenWindow* window_renderer = malloc(sizeof(RenWindow));
window_renderer->window = win;
renwin_init_surface(window_renderer);
renwin_init_command_buf(window_renderer);
renwin_clip_to_surface(window_renderer);
draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32,
0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
return window_renderer;
}
void ren_free(RenWindow* window_renderer) {
assert(window_renderer);
void ren_free_window_resources(RenWindow *window_renderer) {
renwin_free(window_renderer);
SDL_FreeSurface(draw_rect_surface);
free(window_renderer->command_buf);
window_renderer->command_buf = NULL;
window_renderer->command_buf_size = 0;
free(window_renderer);
}
// TODO remove global and return RenWindow*
void ren_init(SDL_Window *win) {
assert(win);
int error = FT_Init_FreeType( &library );
if ( error ) {
fprintf(stderr, "internal font error when starting the application\n");
return;
}
window_renderer.window = win;
renwin_init_surface(&window_renderer);
renwin_init_command_buf(&window_renderer);
renwin_clip_to_surface(&window_renderer);
draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32,
0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
}
void ren_resize_window(RenWindow *window_renderer) {
renwin_resize_surface(window_renderer);
renwin_update_scale(window_renderer);

View File

@ -23,7 +23,7 @@ typedef struct { SDL_Surface *surface; int scale; } RenSurface;
struct RenWindow;
typedef struct RenWindow RenWindow;
extern RenWindow* window_renderer;
extern RenWindow window_renderer;
RenFont* ren_font_load(RenWindow *window_renderer, const char *filename, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style);
RenFont* ren_font_copy(RenWindow *window_renderer, RenFont* font, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, int style);
@ -39,12 +39,12 @@ double ren_draw_text(RenSurface *rs, RenFont **font, const char *text, size_t le
void ren_draw_rect(RenSurface *rs, RenRect rect, RenColor color);
RenWindow* ren_init(SDL_Window *win);
void ren_free(RenWindow* window_renderer);
void ren_init(SDL_Window *win);
void ren_resize_window(RenWindow *window_renderer);
void ren_update_rects(RenWindow *window_renderer, RenRect *rects, int count);
void ren_set_clip_rect(RenWindow *window_renderer, RenRect rect);
void ren_get_size(RenWindow *window_renderer, int *x, int *y); /* Reports the size in points. */
void ren_free_window_resources(RenWindow *window_renderer);
#endif

View File

@ -149,4 +149,3 @@ void renwin_free(RenWindow *ren) {
SDL_FreeSurface(ren->rensurface.surface);
#endif
}

View File

@ -1,14 +1,12 @@
[wrap-file]
directory = lua-5.4.6
source_url = https://www.lua.org/ftp/lua-5.4.6.tar.gz
source_filename = lua-5.4.6.tar.gz
source_hash = 7d5ea1b9cb6aa0b59ca3dde1c6adcb57ef83a1ba8e5432c0ecd06bf439b3ad88
patch_filename = lua_5.4.6-3_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.6-3/get_patch
patch_hash = 9b72a95422fd47f79f969d9abdb589ee95712d5512a5246f94e7e4f63d2cb7b7
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/lua_5.4.6-3/lua-5.4.6.tar.gz
wrapdb_version = 5.4.6-3
directory = lua-5.4.4
source_url = https://www.lua.org/ftp/lua-5.4.4.tar.gz
source_filename = lua-5.4.4.tar.gz
source_hash = 164c7849653b80ae67bec4b7473b884bf5cc8d2dca05653475ec2ed27b9ebf61
patch_filename = lua_5.4.4-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.4-1/get_patch
patch_hash = e61cd965c629d6543176f41a9f1cb9050edfd1566cf00ce768ff211086e40bdc
[provide]
lua-5.4 = lua_dep
lua = lua_dep