Compare commits

...

177 Commits

Author SHA1 Message Date
George Sokianos 5c983f10b5 Merge branch 'master' into amiga2.1 2023-12-26 13:36:13 +00:00
Jan 7b67a5d81b turn window_renderer into managed pointer (#1683)
* turn window_renderer into managed pointer
this will make it easier to move it into userdata in the future

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

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

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

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

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

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

This also allows UTF-8 commands btw.

* fix(process): fix invalid dereference

* refactor(process): mark xstrdup as potentially unused

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

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

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

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

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

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

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

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

* Implemented discord discussion.

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

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

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

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

* multiline

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

* move and rename some stuff

* remove unneeded extra check

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

* overwrite disregards pasting text

* disregard overwrite on selections; doc only removes selection

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

---------

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

* Simplify regex constant detection in `language_js`

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

This avoids more catastrophic backtracking cases.

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

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

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

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

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

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

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

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

* Scale mouse coordinates by window scale

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

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

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

* feat(subprojects): update freetype version

* feat(subprojects): update pcre2 to latest version

* feat(subprojects): update lua to latest version

* feat(lite_xl_plugin_api): add lua_closethread to symbols list

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

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

* feat(subprojects): update wraps again

* ci(build): fix lua subproject not found

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

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

* Avoid sorting multiple times in `dirwatch.get_directory_files`

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

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

* fix(appdmg.sh): typo

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

* fix(dmgbuild): icon file

* fix(gitignore): dmgbuild settings

* chore(resources): update readme with new files

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

* fix(package.sh): syntax error

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

* docs(readme): grammar

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

---------

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

* tiny changes

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

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

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

* docs(dirmonitor): clarify coroutines and deadlocks

* docs(dirmonitor): wording

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

---------

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

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

* ci(build): fix step wording

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

---------

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

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

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

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

* docs: more clarification and grammar fixes

* docs: clarify plugin and config load in portable mode

* docs: better phrasing

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

* docs: better phrasing

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

---------

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

* docs(core.config): remove wrong newline

* docs(core.config): remove trailing whitespace

* docs(core.config): add missing whitespace

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

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

* docs(core.config): fix wrong description

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

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

* docs(core.config): clarify libdecor usage

* docs(core.config): clarify various things

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

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

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

* chore: remove unused variable

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

* fix(statusview): change doc percent tooltip

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

* Removed redundant flag check

* Added support for non syntax colors

* Assert if color name not in style.syntax

* Autocomplete plugin improvements

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

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

* Use normalized stroke in `keymap.unbind`

* Normalize strokes by sorting the modifiers before the keys

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

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

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

* Improved path description.

---------

Co-authored-by: jgmdev <jgmdev@gmail.com>
2023-12-26 13:16:33 +00:00
Velosofy f820b9301f Add "Open with Lite XL" to windows' context menu (#1333)
Closes #423
2023-12-26 13:15:53 +00:00
Takase 8c8635146e ci(release): use lite-xl org (#1571) 2023-08-07 14:51:14 +01:00
Guldoman 6d5c6051cd Make `DocView` aware of scrollbars sizes (#1177)
* Make `DocView:scroll_to_make_visible` aware of vertical scrollbar width

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

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

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

* improve normalization algorithm and implement normalization in several functions

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

---------

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

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

* @guldoman's changes.

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

* fix(core.common): fix warnings

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

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

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

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

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

* docs(core.command): document command name

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

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

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

* docs(core.common): remove messy newlines

* docs(core.common): fix wording

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

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

* docs(core.common): update docs

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

* docs(core.common): fix typo

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

* docs(core.common): restyle annoatations

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

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

* docs(core.common): simplify unicode description

* docs(core.common): fix return value

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

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

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

---------

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

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

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

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

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

* Rename `MAX_GLYPHSET` to `GLYPHSET_SIZE`

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

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

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

* move scroll-test out of on_mouse_wheel

* determine diff_index with loop

* remove check at move_towards yoffset

* use while loop instead of repeat loop

* remove meaningless setter

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

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

This reverts commit af6c4bc152.

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

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

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

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

* Add deprecation messages handler

* Make various `View`s respect `on_mouse_left`

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

* Fix scrollbar in `TreeView` not updating

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

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

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

* Overriden os.exit to always close the state

* Allow setting close param on os.exit override

* Simplified the os.exit override a bit more

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

---------

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

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

* Consider `View` horizontal size when restoring horizontal scroll

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

* Fixed issue with split tabs.

* Early exit if no overlapping node.

* Changed category of command to tabbar.

* Additional cleanup.

* Changed for whether we should show tabs.

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

* ci(build): test with my own fork

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

* ci(build): disable package updates

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

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

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

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

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

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

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

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

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

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

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

Restore caret position on command doc:cut

* merge cursors and fix new line in clipboard

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

Also moved `drawwhitespace` commands inside the plugin.

---

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

* Fixed typos.

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

* Changed documentation.

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

* Fixed typo.

* Additional checking for off-cycles.

* Fixed issue with calling step too much.

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

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

* feat(process): introduce process_handle helper functions

* feat(process): add asynchronous process reaping

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

* style(process): remove unecessary brackets

* style(process): fix parentheses

* refactor(process): remove useless setvbuf call

* style(process): remove unecessary value

* refactor(process): add size field into kill_list

* refactor(process): use SDL_Delay for sleeping

* style(process): remove trailing whitespace

* fix(main): destroy window before closing lua

* fix(process): check for timeout correctly

* refactor(process): remove unecessary if check

* refactor(process): remove size from the list

* fix(process): fix invalid delay calculation

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

---------

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

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

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

* feat(windows): add application manifest

* feat(build): use application manifest on windows

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

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

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

* Make tab scrolling more flexible

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

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

---------

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

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

---------

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

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

* Used appropriate parameter.

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

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

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

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

View File

@ -110,6 +110,12 @@ jobs:
run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-macos-universal" >> "$GITHUB_ENV"
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dmgbuild
run: pip install dmgbuild
- uses: actions/checkout@v3
- name: Download artifacts
uses: actions/download-artifact@v3
@ -117,8 +123,6 @@ jobs:
with:
name: macOS DMG Images
path: dmgs-original
- name: Install appdmg
run: cd ~; npm i appdmg; cd -
- name: Make universal bundles
run: |
bash --version
@ -200,13 +204,14 @@ jobs:
run: |
"INSTALL_NAME=lite-xl-$($env:GITHUB_REF -replace ".*/")-windows-msvc-${{ matrix.arch.name }}" >> $env:GITHUB_ENV
"INSTALL_REF=$($env:GITHUB_REF -replace ".*/")" >> $env:GITHUB_ENV
"LUA_SUBPROJECT_PATH=subprojects/lua-5.4.4" >> $env:GITHUB_ENV
"LUA_SUBPROJECT_PATH=subprojects/$(awk -F ' *= *' '/directory/ { printf $2 }' subprojects/lua.wrap)" >> $env:GITHUB_ENV
- name: Download and patch subprojects
shell: bash
run: |
meson subprojects download
cat resources/windows/001-lua-unicode.diff | patch -Np1 -d "$LUA_SUBPROJECT_PATH"
- name: Configure
run: |
# Download the subprojects first so we can patch it before configuring.
# This avoids reconfiguring the subprojects when compiling.
meson subprojects download
Get-Content -Path resources/windows/001-lua-unicode.diff -Raw | patch -d $env:LUA_SUBPROJECT_PATH -p1 --forward
meson setup --wrap-mode=forcefallback build
- name: Build
run: |

View File

@ -185,8 +185,8 @@ jobs:
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install appdmg
run: cd ~; npm i appdmg; cd -
- name: Install dmgbuild
run: pip install dmgbuild
- name: Prepare DMG Images
run: |
mkdir -p dmgs-addons dmgs-normal

1
.gitignore vendored
View File

@ -28,3 +28,4 @@ lite
!resources/windows/*.diff
!resources/windows/*.exe.manifest.in
!resources/macos/*.py

View File

@ -1,4 +1,4 @@
Copyright (c) 2020-2021 Lite XL Team
Copyright (c) 2020-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

@ -1,7 +1,7 @@
# Lite XL
[![CI]](https://github.com/lite-xl/lite-xl/actions/workflows/build.yml)
[![Discord Badge Image]](https://discord.gg/RWzqC3nx7K)
[![Discord Badge Image]](https://discord.gg/UQKnzBhY5H)
![screenshot-dark]
@ -81,6 +81,39 @@ affects only the place where the application is actually installed.
Head over to [releases](https://github.com/lite-xl/lite-xl/releases) and download the version for your operating system.
### Windows
Lite XL comes with installers on Windows for typical installations.
Alternatively, we provide ZIP archives that you can download and extract anywhere and run directly.
To make Lite XL portable (e.g. running Lite XL from a thumb drive),
simply create a `user` folder where `lite-xl.exe` is located.
Lite XL will load and store all your configurations and plugins in the folder.
### macOS
We provide DMG files for macOS. Simply drag the program into your Applications folder.
> **Important**
> Newer versions of Lite XL are signed with a self-signed certificate,
> so you'll have to follow these steps when running Lite XL for the first time.
>
> 1. Find Lite XL in Finder (do not open it in Launchpad).
> 2. Control-click Lite XL, then choose `Open` from the shortcut menu.
> 3. Click `Open` in the popup menu.
>
> The correct steps may vary between macOS versions, so you should refer to
> the [macOS User Guide](https://support.apple.com/en-my/guide/mac-help/mh40616/mac).
>
> On an older version of Lite XL, you will need to run these commands instead:
>
> ```sh
> # clears attributes from the directory
> xattr -cr /Applications/Lite\ XL.app
> ```
>
> Otherwise, macOS will display a **very misleading error** saying that the application is damaged.
### Linux
Unzip the file and `cd` into the `lite-xl` directory:
@ -91,6 +124,7 @@ cd lite-xl
```
To run lite-xl without installing:
```sh
./lite-xl
```
@ -103,21 +137,59 @@ 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
```
To get the icon to show up in app launcher:
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:
```sh
xdg-desktop-menu forceupdate
```
You may need to logout and login again to see icon in app launcher.
Alternatively, you may log out and log in again.
To uninstall just run:
#### Uninstall
To uninstall Lite XL, run:
```sh
rm -f $HOME/.local/bin/lite-xl
@ -127,7 +199,6 @@ rm -rf $HOME/.local/share/icons/hicolor/scalable/apps/lite-xl.svg \
$HOME/.local/share/lite-xl
```
## Contributing
Any additional functionality that can be added through a plugin should be done

View File

@ -38,7 +38,7 @@ show_help() {
echo "-v --version VERSION Sets the version on the package name."
echo "-A --appimage Create an AppImage (Linux only)."
echo "-D --dmg Create a DMG disk image (macOS only)."
echo " Requires NPM and AppDMG."
echo " Requires dmgbuild."
echo "-I --innosetup Create an InnoSetup installer (Windows only)."
echo "-r --release Compile in release mode."
echo "-S --source Create a source code package,"

View File

@ -43,7 +43,7 @@ local function save(filename)
core.log("Saved \"%s\"", saved_filename)
else
core.error(err)
core.nag_view:show("Saving failed", string.format("Could not save \"%s\" do you want to save to another location?", doc().filename), {
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 }
}, function(item)
@ -340,10 +340,11 @@ local commands = {
local text = dv.doc:get_text(line1, 1, line1, col1)
if #text >= indent_size and text:find("^ *$") then
dv.doc:delete_to_cursor(idx, 0, -indent_size)
return
goto continue
end
end
dv.doc:delete_to_cursor(idx, translate.previous_char)
::continue::
end
end,
@ -544,6 +545,11 @@ 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

View File

@ -164,13 +164,16 @@ local function is_in_any_selection(line, col)
end
local function select_add_next(all)
local il1, ic1 = doc():get_selection(true)
for idx, l1, c1, l2, c2 in doc():get_selections(true, true) do
local il1, ic1
for _, l1, c1, l2, c2 in doc():get_selections(true, true) do
if not il1 then
il1, ic1 = l1, c1
end
local text = doc():get_text(l1, c1, l2, c2)
repeat
l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true })
if l1 == il1 and c1 == ic1 then break end
if l2 and (all or not is_in_any_selection(l2, c2)) then
if l2 and not is_in_any_selection(l2, c2) then
doc():add_selection(l2, c2, l1, c1)
if not all then
core.active_view:scroll_to_make_visible(l2, c2)
@ -266,7 +269,7 @@ command.add(valid_for_finding, {
core.error("No find to continue from")
else
local sl1, sc1, sl2, sc2 = dv.doc:get_selection(true)
local line1, col1, line2, col2 = last_fn(dv.doc, sl1, sc2, last_text, case_sensitive, find_regex, false)
local line1, col1, line2, col2 = last_fn(dv.doc, sl2, sc2, last_text, case_sensitive, find_regex, false)
if line1 then
dv.doc:set_selection(line2, col2, line1, col1)
dv:scroll_to_line(line2, true)

View File

@ -226,7 +226,7 @@ function common.path_suggest(text, root)
if root and root:sub(-1) ~= PATHSEP then
root = root .. PATHSEP
end
local path, name = text:match("^(.-)([^:/\\]*)$")
local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
local clean_dotslash = false
-- ignore root if path is absolute
local is_absolute = common.is_absolute_path(text)
@ -279,7 +279,7 @@ end
---@param text string The input path.
---@return string[]
function common.dir_path_suggest(text)
local path, name = text:match("^(.-)([^:/\\]*)$")
local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
local files = system.list_dir(path == "" and "." or path) or {}
local res = {}
for _, file in ipairs(files) do
@ -298,7 +298,7 @@ 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("^(.-)([^:/\\]*)$")
local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
local res = {}
for _, dir_path in ipairs(dir_list) do
if dir_path:lower():find(text:lower(), nil, true) == 1 then
@ -378,12 +378,15 @@ function common.bench(name, fn, ...)
return res
end
-- From gvx/Ser
local oddvals = {[tostring(1/0)] = "1/0", [tostring(-1/0)] = "-1/0", [tostring(-(0/0))] = "-(0/0)", [tostring(0/0)] = "0/0"}
local function serialize(val, pretty, indent_str, escape, sort, limit, level)
local space = pretty and " " or ""
local indent = pretty and string.rep(indent_str, level) or ""
local newline = pretty and "\n" or ""
if type(val) == "string" then
local ty = type(val)
if ty == "string" then
local out = string.format("%q", val)
if escape then
out = string.gsub(out, "\\\n", "\\n")
@ -395,7 +398,7 @@ local function serialize(val, pretty, indent_str, escape, sort, limit, level)
out = string.gsub(out, "\\13", "\\r")
end
return out
elseif type(val) == "table" then
elseif ty == "table" then
-- early exit
if level >= limit then return tostring(val) end
local next_indent = pretty and (indent .. indent_str) or ""
@ -410,6 +413,12 @@ local function serialize(val, pretty, indent_str, escape, sort, limit, level)
if sort then table.sort(t) end
return "{" .. newline .. table.concat(t, "," .. newline) .. newline .. indent .. "}"
end
if ty == "number" then
-- tostring is locale-dependent, so we need to replace an eventual `,` with `.`
local res, _ = tostring(val):gsub(",", ".")
-- handle inf/nan
return oddvals[res] or res
end
return tostring(val)
end
@ -452,7 +461,7 @@ end
function common.basename(path)
-- a path should never end by / or \ except if it is '/' (unix root) or
-- 'X:\' (windows drive)
return path:match("[^\\/]+$") or path
return path:match("[^"..PATHSEP.."]+$") or path
end
@ -461,7 +470,7 @@ end
---@param path string
---@return string|nil
function common.dirname(path)
return path:match("(.+)[:\\/][^\\/]+$")
return path:match("(.+)["..PATHSEP.."][^"..PATHSEP.."]+$")
end
@ -507,10 +516,10 @@ end
local function split_on_slash(s, sep_pattern)
local t = {}
if s:match("^[/\\]") then
if s:match("^["..PATHSEP.."]") then
t[#t + 1] = ""
end
for fragment in string.gmatch(s, "([^/\\]+)") do
for fragment in string.gmatch(s, "([^"..PATHSEP.."]+)") do
t[#t + 1] = fragment
end
return t
@ -643,7 +652,7 @@ function common.mkdirp(path)
while path and path ~= "" do
local success_mkdir = system.mkdir(path)
if success_mkdir then break end
local updir, basedir = path:match("(.*)[/\\](.+)$")
local updir, basedir = path:match("(.*)["..PATHSEP.."](.+)$")
table.insert(subdirs, 1, basedir or path)
path = updir
end

View File

@ -2,15 +2,71 @@ 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
---@type "expanded" | "contracted" | false @Force the scrollbar status of the DocView
---@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
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%-.*/",
@ -21,46 +77,194 @@ 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
-- Possible values: false, true, "no_selection"
---@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
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
-- set as true to be able to test non supported plugins
---Disables plugin version checking.
---Do not change this unless you know what you are doing.
---
---Defaults to false.
---@type boolean
config.skip_plugins_version = false
-- holds the plugins real config table
local plugins_config = {}
-- virtual representation of plugins config table
---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
config.plugins = {}
-- allows virtual access to the plugins config table

View File

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

View File

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

View File

@ -1,5 +1,6 @@
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"
@ -27,9 +28,11 @@ 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 }
@ -38,10 +41,10 @@ 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
@ -56,16 +59,14 @@ 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
@ -85,7 +86,6 @@ function Doc:load(filename)
self:reset_syntax()
end
function Doc:reload()
if self.filename then
local sel = { self:get_selection() }
@ -95,7 +95,6 @@ function Doc:reload()
end
end
function Doc:save(filename, abs_filename)
if not filename then
assert(self.filename, "no filename set to default to")
@ -104,7 +103,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)
@ -115,34 +114,30 @@ 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
return #self.lines > 1 or #self.lines[1] > 1
else
return self.clean_change_id ~= self:get_change_id()
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
@ -166,13 +161,14 @@ 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
@ -216,7 +212,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)
@ -232,7 +228,6 @@ 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
@ -240,7 +235,6 @@ 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)
@ -251,24 +245,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
@ -276,8 +270,9 @@ 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)
@ -290,7 +285,6 @@ 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, ...)
@ -329,7 +323,6 @@ 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)
@ -345,13 +338,11 @@ 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
@ -412,7 +403,8 @@ 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
@ -425,7 +417,6 @@ 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)
@ -484,15 +475,17 @@ 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
if self:get_change_id() < self.clean_change_id then
self.clean_change_id = -1
end
line, col = self:sanitize_position(line, col)
self:raw_insert(line, col, text, self.undo_stack, system.get_time())
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)
@ -502,28 +495,34 @@ 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
@ -534,7 +533,6 @@ 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)
@ -550,7 +548,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)
@ -564,7 +562,6 @@ 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
@ -578,6 +575,7 @@ 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, ...)
@ -586,8 +584,8 @@ 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: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
@ -596,8 +594,8 @@ 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:select_to(...) return self:select_to_cursor(nil, ...) end
function Doc:get_indent_string()
local indent_type, indent_size = self:get_indent_info()
@ -620,7 +618,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
@ -669,5 +667,4 @@ function Doc:on_close()
core.log_quiet("Closed doc \"%s\"", self:get_name())
end
return Doc

View File

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

View File

@ -460,9 +460,16 @@ 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)
@ -559,7 +566,12 @@ function DocView:draw_overlay()
else
if config.disable_blink
or (core.blink_timer - core.blink_start) % T < T / 2 then
self:draw_caret(self:get_line_screen_position(line1, col1))
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
end
end
end

View File

@ -102,7 +102,7 @@ local function strip_leading_path(filename)
end
local function strip_trailing_slash(filename)
if filename:match("[^:][/\\]$") then
if filename:match("[^:]["..PATHSEP.."]$") then
return filename:sub(1, -2)
end
return filename
@ -120,9 +120,7 @@ local function show_max_files_warning(dir)
"Too many files in project directory: stopped reading at "..
config.max_project_files.." files. For more information see "..
"usage.md at https://github.com/lite-xl/lite-xl."
if core.status_view then
core.status_view:show_message("!", style.accent, message)
end
core.warn(message)
end
@ -184,7 +182,7 @@ local function refresh_directory(topdir, target)
directory_start_idx = directory_start_idx + 1
end
local files = dirwatch.get_directory_files(topdir, topdir.name, (target or ""), {}, 0, function() return false end)
local files = dirwatch.get_directory_files(topdir, topdir.name, (target or ""), 0, function() return false end)
local change = false
-- If this file doesn't exist, we should be calling this on our parent directory, assume we'll do that.
@ -265,7 +263,7 @@ function core.add_project_directory(path)
local fstype = PLATFORM == "Linux" and system.get_fs_type(topdir.name) or "unknown"
topdir.force_scans = (fstype == "nfs" or fstype == "fuse")
local t, complete, entries_count = dirwatch.get_directory_files(topdir, topdir.name, "", {}, 0, timed_max_files_pred)
local t, complete, entries_count = dirwatch.get_directory_files(topdir, topdir.name, "", 0, timed_max_files_pred)
topdir.files = t
if not complete then
topdir.slow_filesystem = not complete and (entries_count <= config.max_project_files)
@ -810,7 +808,11 @@ function core.init()
end
if not plugins_success or got_user_error or got_project_error then
command.perform("core:open-log")
-- defer LogView to after everything is initialized,
-- so that EmptyView won't be added after LogView.
core.add_thread(function()
command.perform("core:open-log")
end)
end
core.configure_borderless_window()
@ -1274,6 +1276,9 @@ function core.on_event(type, ...)
elseif type == "textediting" then
ime.on_text_editing(...)
elseif type == "keypressed" then
-- In some cases during IME composition input is still sent to us
-- so we just ignore it.
if ime.editing then return false end
did_keymap = keymap.on_key_pressed(...)
elseif type == "keyreleased" then
keymap.on_key_released(...)
@ -1418,11 +1423,11 @@ local run_threads = coroutine.wrap(function()
-- stop running threads if we're about to hit the end of frame
if system.get_time() - core.frame_start > max_time then
coroutine.yield(0)
coroutine.yield(0, false)
end
end
coroutine.yield(minimal_time_to_wake)
coroutine.yield(minimal_time_to_wake, true)
end
end)
@ -1430,10 +1435,15 @@ end)
function core.run()
local next_step
local last_frame_time
local run_threads_full = 0
while true do
core.frame_start = system.get_time()
local time_to_wake = run_threads()
local time_to_wake, threads_done = run_threads()
if threads_done then
run_threads_full = run_threads_full + 1
end
local did_redraw = false
local did_step = false
local force_draw = core.redraw and last_frame_time and core.frame_start - last_frame_time > (1 / config.fps)
if force_draw or not next_step or system.get_time() >= next_step then
if core.step() then
@ -1441,11 +1451,12 @@ function core.run()
last_frame_time = core.frame_start
end
next_step = nil
did_step = true
end
if core.restart_request or core.quit_request then break end
if not did_redraw then
if system.window_has_focus() then
if system.window_has_focus() or not did_step or run_threads_full < 2 then
local now = system.get_time()
if not next_step then -- compute the time until the next blink
local t = now - core.blink_start
@ -1454,7 +1465,7 @@ function core.run()
local cursor_time_to_wake = dt + 1 / config.fps
next_step = now + cursor_time_to_wake
end
if time_to_wake > 0 and system.wait_event(math.min(next_step - now, time_to_wake)) then
if system.wait_event(math.min(next_step - now, time_to_wake)) then
next_step = nil -- if we've recevied an event, perform a step
end
else
@ -1462,6 +1473,7 @@ function core.run()
next_step = nil -- perform a step when we're not in focus if get we an event
end
else -- if we redrew, then make sure we only draw at most FPS/sec
run_threads_full = 0
local now = system.get_time()
local elapsed = now - core.frame_start
local next_frame = math.max(0, 1 / config.fps - elapsed)

View File

@ -42,15 +42,19 @@ local modkeys = modkeys_os.keys
---@return string
local function normalize_stroke(stroke)
local stroke_table = {}
for modkey in stroke:gmatch("(%w+)%+") do
table.insert(stroke_table, modkey)
for key in stroke:gmatch("[^+]+") do
table.insert(stroke_table, key)
end
if not next(stroke_table) then
return stroke
end
table.sort(stroke_table)
local new_stroke = table.concat(stroke_table, "+") .. "+"
return new_stroke .. stroke:sub(new_stroke:len() + 1)
table.sort(stroke_table, function(a, b)
if a == b then return false end
for _, mod in ipairs(modkeys) do
if a == mod or b == mod then
return a == mod
end
end
return a < b
end)
return table.concat(stroke_table, "+")
end
@ -58,13 +62,13 @@ end
---@param key string
---@return string
local function key_to_stroke(key)
local stroke = ""
local keys = { key }
for _, mk in ipairs(modkeys) do
if keymap.modkeys[mk] then
stroke = stroke .. mk .. "+"
table.insert(keys, mk)
end
end
return normalize_stroke(stroke) .. key
return normalize_stroke(table.concat(keys, "+"))
end
---Remove the given value from an array associated to a key in a table.
@ -92,12 +96,12 @@ end
---@param map keymap.map
local function remove_duplicates(map)
for stroke, commands in pairs(map) do
stroke = normalize_stroke(stroke)
local normalized_stroke = normalize_stroke(stroke)
if type(commands) == "string" or type(commands) == "function" then
commands = { commands }
end
if keymap.map[stroke] then
for _, registered_cmd in ipairs(keymap.map[stroke]) do
if keymap.map[normalized_stroke] then
for _, registered_cmd in ipairs(keymap.map[normalized_stroke]) do
local j = 0
for i=1, #commands do
while commands[i + j] == registered_cmd do
@ -120,7 +124,6 @@ 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
@ -174,7 +177,8 @@ end
---@param shortcut string
---@param cmd string
function keymap.unbind(shortcut, cmd)
remove_only(keymap.map, normalize_stroke(shortcut), cmd)
shortcut = normalize_stroke(shortcut)
remove_only(keymap.map, shortcut, cmd)
remove_only(keymap.reverse_map, cmd, shortcut)
end
@ -199,10 +203,6 @@ end
-- Events listening
--------------------------------------------------------------------------------
function keymap.on_key_pressed(k, ...)
-- In MacOS and Windows during IME composition input is still sent to us
-- so we just ignore it
if PLATFORM ~= "Linux" and ime.editing then return false end
local mk = modkey_map[k]
if mk then
keymap.modkeys[mk] = true
@ -341,6 +341,7 @@ 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" },

View File

@ -7,8 +7,12 @@ 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", "alt", "altgr", "shift" }
modkeys.keys = { "ctrl", "shift", "alt", "altgr", "super" }
return modkeys

View File

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

View File

@ -24,6 +24,7 @@ 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()
@ -68,7 +69,9 @@ 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()
renderer.draw_rect(ox, oy, w, h, style.nagbar_dim)
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)
end)
end
@ -172,10 +175,13 @@ function NagView:update()
NagView.super.update(self)
if self.visible and core.active_view == self and self.title then
self:move_towards(self, "show_height", self:get_target_height(), nil, "nagbar")
local target_height = self:get_target_height()
self:move_towards(self, "show_height", 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

@ -177,8 +177,12 @@ 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
table.insert(self.views, idx or (#self.views + 1), view)
idx = common.clamp(idx or (#self.views + 1), 1, (#self.views + 1))
table.insert(self.views, idx, view)
self:set_active_view(view)
end

View File

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

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.expanded_scrollbar_size`
self.contracted_size = options.contracted_size
---@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.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.scrollable * (nr.along_size - along_size) / (sz - nr.along_size),
nr.along + self.percent * (nr.along_size - along_size),
across_size,
along_size
end
@ -189,8 +189,9 @@ function Scrollbar:_on_mouse_pressed_normal(button, x, y, clicks)
self.drag_start_offset = along - y
return true
elseif overlaps == "track" then
local nr = self.normal_rect
self.drag_start_offset = - along_size / 2
return (y - self.normal_rect.along - along_size / 2) / self.normal_rect.along_size
return common.clamp((y - nr.along - along_size / 2) / (nr.along_size - along_size), 0, 1)
end
end
end
@ -237,7 +238,8 @@ end
function Scrollbar:_on_mouse_moved_normal(x, y, dx, dy)
if self.dragging then
local nr = self.normal_rect
return common.clamp((y - nr.along + self.drag_start_offset) / nr.along_size, 0, 1)
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)
end
return self:_update_hover_status_normal(x, y)
end
@ -280,7 +282,7 @@ function Scrollbar:set_size(x, y, w, h, scrollable)
end
---Updates the scrollbar location
---@param percent number @number between 0 and 1 representing the position of the middle part of the thumb
---@param percent number @number between 0 and 1 where 0 means thumb at the top and 1 at the bottom
function Scrollbar:set_percent(percent)
self.percent = percent
end

View File

@ -1,11 +1,11 @@
-- this file is used by lite-xl to setup the Lua environment when starting
VERSION = "2.1.1r3"
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)
SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or SCALE
SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or 1
PATHSEP = package.config:sub(1, 1)
EXEDIR = EXEFILE:match("^(.+)[/\\][^/\\]+$")

View File

@ -232,15 +232,27 @@ function StatusView:register_docview_items()
return {
style.text, line, ":",
col > config.line_limit and style.accent or style.text, col,
style.text,
self.separator,
string.format("%.f%%", line / #dv.doc.lines * 100)
style.text
}
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",
@ -307,6 +319,19 @@ 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

View File

@ -3,7 +3,7 @@ local common = require "core.common"
local syntax = {}
syntax.items = {}
local plain_text_syntax = { name = "Plain Text", patterns = {}, symbols = {} }
syntax.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 plain_text_syntax
or syntax.plain_text_syntax
end

View File

@ -210,9 +210,11 @@ function tokenizer.tokenize(incoming_syntax, text, state, resume)
-- Remove '^' from the beginning of the pattern
if type(target) == "table" then
target[p_idx] = code:usub(2)
code = target[p_idx]
else
p.pattern = p.pattern and code:usub(2)
p.regex = p.regex and code:usub(2)
code = p.pattern or p.regex
end
end
end

View File

@ -142,14 +142,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.scroll.to.y = result * (self:get_scrollable_size() - self.size.y)
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.scroll.to.x = result * (self:get_h_scrollable_size() - self.size.x)
end
return true
end
@ -177,7 +177,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.scroll.to.y = result * (self:get_scrollable_size() - self.size.y)
if not config.animate_drag_scroll then
self:clamp_scroll_position()
self.scroll.y = self.scroll.to.y
@ -191,7 +191,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.scroll.to.x = result * (self:get_h_scrollable_size() - self.size.x)
if not config.animate_drag_scroll then
self:clamp_scroll_position()
self.scroll.x = self.scroll.to.x
@ -287,12 +287,16 @@ 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)
self.v_scrollbar:set_percent(self.scroll.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: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)
self.h_scrollbar:set_percent(self.scroll.x/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:update()
end

View File

@ -10,6 +10,10 @@ 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,
@ -19,8 +23,16 @@ 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",
@ -60,6 +72,26 @@ 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.",
@ -67,6 +99,31 @@ 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)
@ -76,6 +133,7 @@ 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
@ -95,6 +153,7 @@ 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
@ -119,28 +178,35 @@ end
--
-- Thread that scans open document symbols and cache them
--
local max_symbols = config.plugins.autocomplete.max_symbols
local global_symbols = {}
core.add_thread(function()
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
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
end
autocomplete.add(symbols)
return symbols.items
end
return {}
end
local function get_symbols(doc)
local s = {}
get_syntax_symbols(s, doc)
local syntax_symbols = load_syntax_symbols(doc)
local max_symbols = config.plugins.autocomplete.max_symbols
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] then
if not s[sym] and not syntax_symbols[sym] then
symbols_count = symbols_count + 1
if symbols_count > max_symbols then
s = nil
@ -186,14 +252,18 @@ core.add_thread(function()
}
end
-- update symbol set with doc's symbol set
for sym in pairs(cache[doc].symbols) do
symbols[sym] = true
if config.plugins.autocomplete.suggestions_scope == "global" then
for sym in pairs(cache[doc].symbols) do
symbols[sym] = true
end
end
coroutine.yield()
end
-- update symbols list
autocomplete.add { name = "open-docs", items = symbols }
-- update global symbols list
if config.plugins.autocomplete.suggestions_scope == "global" then
global_symbols = symbols
end
-- wait for next scan
local valid = true
@ -240,12 +310,50 @@ 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
@ -286,13 +394,23 @@ 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 then
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
@ -319,7 +437,8 @@ local function get_suggestions_rect(av)
x - style.padding.x,
y - style.padding.y,
max_width + style.padding.x * 2,
max_items * (th + style.padding.y) + style.padding.y
max_items * (th + style.padding.y) + style.padding.y,
has_icons
end
local function wrap_line(line, max_chars)
@ -439,7 +558,7 @@ local function draw_suggestions_box(av)
local ah = config.plugins.autocomplete.max_height
-- draw background rect
local rx, ry, rw, rh = get_suggestions_rect(av)
local rx, ry, rw, rh, has_icons = get_suggestions_rect(av)
renderer.draw_rect(rx, ry, rw, rh, style.background3)
-- draw text
@ -448,17 +567,52 @@ local function draw_suggestions_box(av)
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
for i=start_index, start_index+show_count-1, 1 do
if not suggestions[i] then
break
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 color = (i == suggestions_idx) and style.accent or style.text
common.draw_text(font, color, s.text, "left", rx + style.padding.x, y, rw, lh)
if s.info then
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
color = (i == suggestions_idx) and style.text or style.dim
common.draw_text(style.font, color, s.info, "right", rx, y, rw - style.padding.x, lh)
common.draw_text(
style.font, color, s.info, "right",
rx, y, rw - icon_r_padding - style.padding.x, lh
)
end
y = y + lh
if suggestions_idx == i then
@ -619,6 +773,31 @@ 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
@ -632,7 +811,6 @@ 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)

View File

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

View File

@ -347,7 +347,7 @@ end
command.add(nil, {
["draw-whitespace:toggle"] = function()
config.plugins.drawwhitespace.enabled = not config.drawwhitespace.enabled
config.plugins.drawwhitespace.enabled = not config.plugins.drawwhitespace.enabled
end,
["draw-whitespace:disable"] = function()

View File

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

View File

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

View File

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

View File

@ -219,7 +219,7 @@ function LineWrapping.draw_guide(docview)
end
function LineWrapping.update_docview_breaks(docview)
local x,y,w,h = docview.v_scrollbar:get_thumb_rect()
local w = docview.v_scrollbar.expanded_size or style.expanded_scrollbar_size
local width = (type(config.plugins.linewrapping.width_override) == "function" and config.plugins.linewrapping.width_override(docview))
or config.plugins.linewrapping.width_override or (docview.size.x - docview:get_gutter_width() - w)
if (not docview.wrapped_settings or docview.wrapped_settings.width == nil or width ~= docview.wrapped_settings.width) then

View File

@ -29,7 +29,7 @@ local tooltip_alpha_rate = 1
local function get_depth(filename)
local n = 1
for sep in filename:gmatch("[\\/]") do
for _ in filename:gmatch(PATHSEP) do
n = n + 1
end
return n

View File

@ -83,7 +83,8 @@ local function save_view(view)
filename = view.doc.filename,
selection = { view.doc:get_selection() },
scroll = { x = view.scroll.to.x, y = view.scroll.to.y },
text = not view.doc.filename and view.doc:get_text(1, 1, math.huge, math.huge)
crlf = view.doc.crlf,
text = view.doc.new_file and view.doc:get_text(1, 1, math.huge, math.huge)
}
end
if mt == LogView then return end
@ -106,7 +107,6 @@ local function load_view(t)
if not t.filename then
-- document not associated to a file
dv = DocView(core.open_doc())
if t.text then dv.doc:insert(1, 1, t.text) end
else
-- we have a filename, try to read the file
local ok, doc = pcall(core.open_doc, t.filename)
@ -114,9 +114,11 @@ local function load_view(t)
dv = DocView(doc)
end
end
-- doc view "dv" can be nil here if the filename associated to the document
-- cannot be read.
if dv and dv.doc then
if dv.doc.new_file and t.text then
dv.doc:insert(1, 1, t.text)
dv.doc.crlf = t.crlf
end
dv.doc:set_selection(table.unpack(t.selection))
dv.last_line1, dv.last_col1, dv.last_line2, dv.last_col2 = dv.doc:get_selection()
dv.scroll.x, dv.scroll.to.x = t.scroll.x, t.scroll.x

View File

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

View File

@ -61,10 +61,10 @@ function system.poll_event() end
---
---Wait until an event is triggered.
---
---@param timeout number Amount of seconds, also supports fractions
---of a second, eg: 0.01
---@param timeout? number Amount of seconds, also supports fractions
---of a second, eg: 0.01. If not provided, waits forever.
---
---@return boolean status True on success or false if there was an error.
---@return boolean status True on success or false if there was an error or if no event was received.
function system.wait_event(timeout) end
---

View File

@ -4,8 +4,7 @@ project('lite-xl',
license : 'MIT',
meson_version : '>= 0.56',
default_options : [
'c_std=gnu11',
'wrap_mode=nofallback'
'c_std=gnu11'
]
)
@ -84,23 +83,27 @@ if not get_option('source-only')
'lua', # Fedora
]
foreach lua : lua_names
last_lua = (lua == lua_names[-1] or get_option('wrap_mode') == 'forcefallback')
lua_dep = dependency(lua, fallback: last_lua ? ['lua', 'lua_dep'] : [], required : false,
version: '>= 5.4',
default_options: default_fallback_options + ['default_library=static', 'line_editing=false', 'interpreter=false']
)
if lua_dep.found()
break
endif
if get_option('use_system_lua')
foreach lua : lua_names
last_lua = (lua == lua_names[-1] or get_option('wrap_mode') == 'forcefallback')
lua_dep = dependency(lua, required : false,
)
if lua_dep.found()
break
endif
if last_lua
# If we could not find lua on the system and fallbacks are disabled
# try the compiler as a last ditch effort, since Lua has no official
# pkg-config support.
lua_dep = cc.find_library('lua', required : true)
endif
endforeach
if last_lua
# If we could not find lua on the system and fallbacks are disabled
# try the compiler as a last ditch effort, since Lua has no official
# pkg-config support.
lua_dep = cc.find_library('lua', required : true)
endif
endforeach
else
lua_dep = dependency('', fallback: ['lua', 'lua_dep'], required : true,
default_options: default_fallback_options + ['default_library=static', 'line_editing=disabled', 'interpreter=false']
)
endif
pcre2_dep = dependency('libpcre2-8', fallback: ['pcre2', 'libpcre2_8'],
default_options: default_fallback_options + ['default_library=static', 'grep=false', 'test=false']
@ -120,6 +123,7 @@ if not get_option('source-only')
sdl_options += 'use_atomic=enabled'
sdl_options += 'use_threads=enabled'
sdl_options += 'use_timers=enabled'
sdl_options += 'with_main=true'
# investigate if this is truly needed
# Do not remove before https://github.com/libsdl-org/SDL/issues/5413 is released
sdl_options += 'use_events=enabled'
@ -152,12 +156,24 @@ if not get_option('source-only')
sdl_options += 'use_video_vulkan=disabled'
sdl_options += 'use_video_offscreen=disabled'
sdl_options += 'use_power=disabled'
sdl_options += 'system_iconv=disabled'
sdl_dep = dependency('sdl2', fallback: ['sdl2', 'sdl2_dep'],
default_options: default_fallback_options + sdl_options
)
lite_deps = [lua_dep, sdl_dep, freetype_dep, pcre2_dep, libm, libdl]
if host_machine.system() == 'windows'
if sdl_dep.type_name() == 'internal'
sdlmain_dep = dependency('sdl2main', fallback: ['sdl2main_dep'])
else
sdlmain_dep = cc.find_library('SDL2main')
endif
else
sdlmain_dep = dependency('', required: false)
assert(not sdlmain_dep.found(), 'checking if fake dependency has been found')
endif
lite_deps = [lua_dep, sdl_dep, sdlmain_dep, freetype_dep, pcre2_dep, libm, libdl]
endif
#===============================================================================
# Install Configuration

View File

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

View File

@ -11,8 +11,9 @@ This folder contains resources that is used for building or packaging the projec
- `icons/icon.{icns,ico,inl,rc,svg}`: lite-xl icon in various formats.
- `linux/com.lite_xl.LiteXL.appdata.xml`: AppStream metadata.
- `linux/com.lite_xl.LiteXL.desktop`: Desktop file for Linux desktops.
- `macos/appdmg.png`: Background image for packaging MacOS DMGs.
- `macos/Info.plist.in`: Template for generating `info.plist` on MacOS. See `macos/macos-retina-display.md` for details.
- `macos/dmg-cover.png`: Background image for packaging macOS DMGs.
- `macos/Info.plist.in`: Template for generating `info.plist` on macOS. See `macos/macos-retina-display.md` for details.
- `macos/lite-xl-dmg.py`: Configuration options for dmgbuild for packaging macOS DMGs.
- `windows/001-lua-unicode.diff`: Patch for allowing Lua to load files with UTF-8 filenames on Windows.
### Development

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,14 +26,19 @@
* ...
* 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.
*
* This file contains stock configuration for a typical installation of Lua 5.4.6.
* 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
@ -60,8 +65,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__)
@ -1028,6 +1033,7 @@ 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)
@ -1739,6 +1745,9 @@ 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);
}
@ -2351,6 +2360,7 @@ 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);
@ -2560,4 +2570,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

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -0,0 +1,28 @@
# configuration for dmgbuild
import os.path
app_path = "Lite XL.app"
app_name = os.path.basename(app_path)
# Image options
format = defines.get("format", "UDZO")
# Content options
files = [(app_path, app_name)]
symlinks = { "Applications": "/Applications" }
icon = "resources/icons/icon.icns"
icon_locations = {
app_name: (144, 248),
"Applications": (336, 248)
}
# Window options
background = "resources/macos/dmg-cover.png"
window_rect = ((360, 360), (480, 380))
default_view = "coverflow"
include_icon_view_settings = True
# Icon view options
icon_size = 80
text_size = 11.0

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

@ -10,7 +10,7 @@ Various scripts and configurations used to configure, build, and package Lite XL
### Package
- **appdmg.sh**: Create a macOS DMG image using [AppDMG][1].
- **appdmg.sh**: Create a macOS DMG image using [dmgbuild][1].
- **appimage.sh**: [AppImage][2] builder.
- **innosetup.sh**: Creates a 32/64 bit [InnoSetup][3] installer package.
- **package.sh**: Creates all binary / DMG image / installer / source packages.
@ -25,6 +25,6 @@ Various scripts and configurations used to configure, build, and package Lite XL
- **generate_header.sh**: Generates a header file for native plugin API
- **keymap-generator**: Generates a JSON file containing the keymap
[1]: https://github.com/LinusU/node-appdmg
[1]: https://github.com/dmgbuild/dmgbuild
[2]: https://docs.appimage.org/
[3]: https://jrsoftware.org/isinfo.php

View File

@ -6,25 +6,4 @@ if [ ! -e "src/api/api.h" ]; then
exit 1
fi
cat > lite-xl-dmg.json << EOF
{
"title": "Lite XL",
"icon": "$(pwd)/resources/icons/icon.icns",
"background": "$(pwd)/resources/macos/appdmg.png",
"window": {
"position": {
"x": 360,
"y": 360
},
"size": {
"width": 480,
"height": 360
}
},
"contents": [
{ "x": 144, "y": 248, "type": "file", "path": "$(pwd)/Lite XL.app" },
{ "x": 336, "y": 248, "type": "link", "path": "/Applications" }
]
}
EOF
~/node_modules/appdmg/bin/appdmg.js lite-xl-dmg.json "$(pwd)/$1.dmg"
dmgbuild -s resources/macos/lite-xl-dmg.py "Lite XL" "$1.dmg"

View File

@ -181,7 +181,7 @@ main() {
# download the subprojects so we can start patching before configure.
# this will prevent reconfiguring the project.
meson subprojects download
lua_subproject_path=$(echo subprojects/lua-*/)
lua_subproject_path="subprojects/$(awk -F ' *= *' '/directory/ { printf $2 }' subprojects/lua.wrap)"
if [[ -d $lua_subproject_path ]]; then
patch -d $lua_subproject_path -p1 --forward < resources/windows/001-lua-unicode.diff
fi

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@"
@ -57,9 +57,13 @@ OutputBaseFilename=LiteXL-{#MyAppVersion}-{#ArchInternal}-setup
LicenseFile={#SourceDir}/LICENSE
SetupIconFile={#SourceDir}/resources/icons/icon.ico
UninstallDisplayIcon={app}\{#MyAppExeName}, 0
WizardImageFile="{#SourceDir}/scripts/innosetup/wizard-modern-image.bmp"
WizardSmallImageFile="{#SourceDir}/scripts/innosetup/litexl-55px.bmp"
; Required for the add to path option to refresh environment
ChangesEnvironment=yes
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
@ -67,11 +71,10 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 6.1; Check: not IsAdminInstallMode
Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked
Name: "envPath"; Description: "Add lite-xl to the PATH variable, allowing it to be run from a command line."
[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]
@ -81,8 +84,78 @@ Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}";
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon; Check: not WizardIsTaskSelected('portablemode')
; Name: "{usersendto}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
[Registry]
Root: "HKA"; Subkey: "Software\Classes\*\shell\{#MyAppName}"; ValueType: string; ValueName: ""; ValueData: "Open with {#MyAppName}"; Flags: uninsdeletekey
Root: "HKA"; Subkey: "Software\Classes\*\shell\{#MyAppName}"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\{#MyAppExeName}, 0"; Flags: uninsdeletekey
Root: "HKA"; Subkey: "Software\Classes\*\shell\{#MyAppName}\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExename}"" ""%1"""; Flags: uninsdeletekey
Root: "HKA"; Subkey: "Software\Classes\directory\shell\{#MyAppName}"; ValueType: string; ValueName: ""; ValueData: "Open with {#MyAppName}"; Flags: uninsdeletekey
Root: "HKA"; Subkey: "Software\Classes\directory\shell\{#MyAppName}"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\{#MyAppExeName}, 0"; Flags: uninsdeletekey
Root: "HKA"; Subkey: "Software\Classes\directory\shell\{#MyAppName}\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExename}"" ""%1"""; Flags: uninsdeletekey
Root: "HKA"; Subkey: "Software\Classes\directory\background\shell\{#MyAppName}"; ValueType: string; ValueName: ""; ValueData: "Open with {#MyAppName}"; Flags: uninsdeletekey
Root: "HKA"; Subkey: "Software\Classes\directory\background\shell\{#MyAppName}"; ValueType: string; ValueName: "Icon"; ValueData: "{app}\{#MyAppExeName}, 0"; Flags: uninsdeletekey
Root: "HKA"; Subkey: "Software\Classes\directory\background\shell\{#MyAppName}\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExename}"" ""%V"""; Flags: uninsdeletekey
[Run]
Filename: "{app}/{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
[Setup]
Uninstallable=not WizardIsTaskSelected('portablemode')
; Code to add installation path to environment taken from:
; https://stackoverflow.com/a/46609047
[Code]
const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
procedure EnvAddPath(Path: string);
var
Paths: string;
begin
{ Retrieve current path (use empty string if entry not exists) }
if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths)
then Paths := '';
{ Skip if string already found in path }
if Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';') > 0 then exit;
{ App string to the end of the path variable }
Paths := Paths + ';'+ Path +';'
{ Overwrite (or create if missing) path environment variable }
if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths)
then Log(Format('The [%s] added to PATH: [%s]', [Path, Paths]))
else Log(Format('Error while adding the [%s] to PATH: [%s]', [Path, Paths]));
end;
procedure EnvRemovePath(Path: string);
var
Paths: string;
P: Integer;
begin
{ Skip if registry entry not exists }
if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) then
exit;
{ Skip if string not found in path }
P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';');
if P = 0 then exit;
{ Update path variable }
Delete(Paths, P - 1, Length(Path) + 1);
{ Overwrite path environment variable }
if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths)
then Log(Format('The [%s] removed from PATH: [%s]', [Path, Paths]))
else Log(Format('Error while removing the [%s] from PATH: [%s]', [Path, Paths]));
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if (CurStep = ssPostInstall) and WizardIsTaskSelected('envPath')
then EnvAddPath(ExpandConstant('{app}'));
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usPostUninstall
then EnvRemovePath(ExpandConstant('{app}'));
end;

View File

@ -57,9 +57,7 @@ main() {
else
brew install bash ninja sdl2
fi
pip3 install meson
cd ~; npm install appdmg; cd -
~/node_modules/appdmg/bin/appdmg.js --version
pip3 install meson dmgbuild
elif [[ "$OSTYPE" == "msys" ]]; then
if [[ $lhelper == true ]]; then
pacman --noconfirm -S \

View File

@ -25,7 +25,7 @@ show_help() {
echo "-A --appimage Create an AppImage (Linux only)."
echo "-B --binary Create a normal / portable package or macOS bundle,"
echo " depending on how the build was configured. (Default.)"
echo "-D --dmg Create a DMG disk image with AppDMG (macOS only)."
echo "-D --dmg Create a DMG disk image with dmgbuild (macOS only)."
echo "-I --innosetup Create a InnoSetup package (Windows only)."
echo "-r --release Strip debugging symbols."
echo "-S --source Create a source code package,"
@ -264,6 +264,11 @@ main() {
$stripcmd "${exe_file}"
fi
if [[ $bundle == true ]]; then
# https://eclecticlight.co/2019/01/17/code-signing-for-the-concerned-3-signing-an-app/
codesign --force --deep -s - "${dest_dir}"
fi
echo "Creating a compressed archive ${package_name}"
if [[ $binary == true ]]; then
rm -f "${package_name}".tar.gz

View File

@ -1,4 +1,5 @@
#include "api.h"
#include "lua.h"
#include <SDL.h>
#include <stdlib.h>
#include <string.h>
@ -25,13 +26,16 @@ int get_mode_dirmonitor();
static int f_check_dir_callback(int watch_id, const char* path, void* L) {
lua_pushvalue(L, -1);
// using absolute indices from f_dirmonitor_check (2: callback, 3: error_callback)
lua_pushvalue(L, 2);
if (path)
lua_pushlstring(L, path, watch_id);
else
lua_pushnumber(L, watch_id);
lua_call(L, 1, 1);
int result = lua_toboolean(L, -1);
int result = 0;
if (lua_pcall(L, 1, 1, 3) == LUA_OK)
result = lua_toboolean(L, -1);
lua_pop(L, 1);
return !result;
}
@ -95,8 +99,20 @@ static int f_dirmonitor_unwatch(lua_State *L) {
}
static int f_noop(lua_State *L) { return 0; }
static int f_dirmonitor_check(lua_State* L) {
struct dirmonitor* monitor = luaL_checkudata(L, 1, API_TYPE_DIRMONITOR);
luaL_checktype(L, 2, LUA_TFUNCTION);
if (!lua_isnoneornil(L, 3)) {
luaL_checktype(L, 3, LUA_TFUNCTION);
} else {
lua_settop(L, 2);
lua_pushcfunction(L, f_noop);
}
lua_settop(L, 3);
SDL_LockMutex(monitor->mutex);
if (monitor->length < 0)
lua_pushnil(L);

View File

@ -34,6 +34,8 @@
typedef DWORD process_error_t;
typedef HANDLE process_stream_t;
typedef HANDLE process_handle_t;
typedef wchar_t process_arglist_t[32767];
typedef wchar_t *process_env_t;
#define HANDLE_INVALID (INVALID_HANDLE_VALUE)
#define PROCESS_GET_HANDLE(P) ((P)->process_information.hProcess)
@ -45,12 +47,20 @@ static volatile long PipeSerialNumber;
typedef int process_error_t;
typedef int process_stream_t;
typedef pid_t process_handle_t;
typedef char **process_arglist_t;
typedef char **process_env_t;
#define HANDLE_INVALID (0)
#define PROCESS_GET_HANDLE(P) ((P)->pid)
#endif
#ifdef __GNUC__
#define UNUSED __attribute__((__unused__))
#else
#define UNUSED
#endif
typedef struct {
bool running, detached;
int returncode, deadline;
@ -342,51 +352,264 @@ static bool signal_process(process_t* proc, signal_e sig) {
return true;
}
static int process_start(lua_State* L) {
int retval = 1;
size_t env_len = 0, key_len, val_len;
const char *cmd[256] = { NULL }, *env_names[256] = { NULL }, *env_values[256] = { NULL }, *cwd = NULL;
bool detach = false, literal = false;
int deadline = 10, new_fds[3] = { STDIN_FD, STDOUT_FD, STDERR_FD };
size_t arg_len = lua_gettop(L), cmd_len;
if (lua_type(L, 1) == LUA_TTABLE) {
#if LUA_VERSION_NUM > 501
lua_len(L, 1);
#else
lua_pushinteger(L, (int)lua_objlen(L, 1));
#endif
cmd_len = luaL_checknumber(L, -1); lua_pop(L, 1);
if (!cmd_len)
// we have not allocated anything here yet, so we can skip cleanup code
// don't do this anywhere else!
return luaL_argerror(L, 1,"table cannot be empty");
for (size_t i = 1; i <= cmd_len; ++i) {
lua_pushinteger(L, i);
lua_rawget(L, 1);
cmd[i-1] = luaL_checkstring(L, -1);
}
static UNUSED char *xstrdup(const char *str) {
char *result = str ? malloc(strlen(str) + 1) : NULL;
if (result) strcpy(result, str);
return result;
}
static int process_arglist_init(process_arglist_t *list, size_t *list_len, size_t nargs) {
*list_len = 0;
#ifdef _WIN32
memset(*list, 0, sizeof(process_arglist_t));
#else
*list = calloc(sizeof(char *), nargs + 1);
if (!*list) return ENOMEM;
#endif
return 0;
}
static int process_arglist_add(process_arglist_t *list, size_t *list_len, const char *arg, bool escape) {
size_t len = *list_len;
#ifdef _WIN32
int arg_len;
wchar_t *cmdline = *list;
wchar_t arg_w[32767];
// this length includes the null terminator!
if (!(arg_len = MultiByteToWideChar(CP_UTF8, 0, arg, -1, arg_w, 32767)))
return GetLastError();
if (arg_len + len > 32767)
return ERROR_NOT_ENOUGH_MEMORY;
if (!escape) {
// replace the current null terminator with a space
if (len > 0) cmdline[len-1] = ' ';
memcpy(cmdline + len, arg_w, arg_len * sizeof(wchar_t));
len += arg_len;
} else {
literal = true;
cmd[0] = luaL_checkstring(L, 1);
cmd_len = 1;
// if the string contains spaces, then we must quote it
bool quote = wcspbrk(arg_w, L" \t\v\r\n");
int backslash = 0, escaped_len = quote ? 2 : 0;
for (int i = 0; i < arg_len; i++) {
if (arg_w[i] == L'\\') {
backslash++;
} else if (arg_w[i] == L'"') {
escaped_len += backslash + 1;
backslash = 0;
} else {
backslash = 0;
}
escaped_len++;
}
// escape_len contains NUL terminator
if (escaped_len + len > 32767)
return ERROR_NOT_ENOUGH_MEMORY;
// replace our previous NUL terminator with space
if (len > 0) cmdline[len-1] = L' ';
if (quote) cmdline[len++] = L'"';
// we are not going to iterate over NUL terminator
for (int i = 0;arg_w[i]; i++) {
if (arg_w[i] == L'\\') {
backslash++;
} else if (arg_w[i] == L'"') {
// add backslash + 1 backslashes
for (int j = 0; j < backslash; j++)
cmdline[len++] = L'\\';
cmdline[len++] = L'\\';
backslash = 0;
} else {
backslash = 0;
}
cmdline[len++] = arg_w[i];
}
if (quote) cmdline[len++] = L'"';
cmdline[len++] = L'\0';
}
#else
char **cmd = *list;
cmd[len] = xstrdup(arg);
if (!cmd[len]) return ENOMEM;
len++;
#endif
*list_len = len;
return 0;
}
static void process_arglist_free(process_arglist_t *list) {
#ifndef _WIN32
char **cmd = *list;
for (int i = 0; cmd[i]; i++)
free(cmd[i]);
free(cmd);
*list = NULL;
#endif
}
static int process_env_init(process_env_t *env_list, size_t *env_len, size_t nenv) {
*env_len = 0;
#ifdef _WIN32
*env_list = NULL;
#else
*env_list = calloc(sizeof(char *), nenv * 2);
if (!*env_list) return ENOMEM;
#endif
return 0;
}
#ifdef _WIN32
static int cmp_name(wchar_t *a, wchar_t *b) {
wchar_t _A[32767], _B[32767], *A = _A, *B = _B, *a_eq, *b_eq;
int na, nb, r;
a_eq = wcschr(a, L'=');
b_eq = wcschr(b, L'=');
assert(a_eq);
assert(b_eq);
na = a_eq - a;
nb = b_eq - b;
r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na);
assert(r == na);
A[na] = L'\0';
r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, b, nb, B, nb);
assert(r == nb);
B[nb] = L'\0';
for (;;) {
wchar_t AA = *A++, BB = *B++;
if (AA > BB)
return 1;
else if (AA < BB)
return -1;
else if (!AA && !BB)
return 0;
}
}
static int process_env_add_variable(process_env_t *env_list, size_t *env_list_len, wchar_t *var, size_t var_len) {
wchar_t *list, *list_p;
size_t block_var_len, list_len;
list = list_p = *env_list;
list_len = *env_list_len;
if (list_len) {
// check if it is already in the block
while ((block_var_len = wcslen(list_p))) {
if (cmp_name(list_p, var) == 0)
return -1; // already installed
list_p += block_var_len + 1;
}
}
// allocate list + 1 characters for the block terminator
list = realloc(list, (list_len + var_len + 1) * sizeof(wchar_t));
if (!list) return ERROR_NOT_ENOUGH_MEMORY;
// copy the env variable to the block
memcpy(list + list_len, var, var_len * sizeof(wchar_t));
// terminate the block again
list[list_len + var_len] = L'\0';
*env_list = list;
*env_list_len = (list_len + var_len);
return 0;
}
static int process_env_add_system(process_env_t *env_list, size_t *env_list_len) {
int retval = 0;
wchar_t *proc_env_block, *proc_env_block_p;
int proc_env_len;
proc_env_block = proc_env_block_p = GetEnvironmentStringsW();
while ((proc_env_len = wcslen(proc_env_block_p))) {
// try to add it to the list
if ((retval = process_env_add_variable(env_list, env_list_len, proc_env_block_p, proc_env_len + 1)) > 0)
goto cleanup;
proc_env_block_p += proc_env_len + 1;
}
retval = 0;
cleanup:
if (proc_env_block) FreeEnvironmentStringsW(proc_env_block);
return retval;
}
#endif
static int process_env_add(process_env_t *env_list, size_t *env_len, const char *key, const char *value) {
#ifdef _WIN32
wchar_t env_var[32767];
int r, var_len = 0;
if (!(r = MultiByteToWideChar(CP_UTF8, 0, key, -1, env_var, 32767)))
return GetLastError();
var_len += r;
env_var[var_len-1] = L'=';
if (!(r = MultiByteToWideChar(CP_UTF8, 0, value, -1, env_var + var_len, 32767 - var_len)))
return GetLastError();
var_len += r;
return process_env_add_variable(env_list, env_len, env_var, var_len);
#else
(*env_list)[*env_len] = xstrdup(key);
if (!(*env_list)[*env_len])
return ENOMEM;
(*env_list)[*env_len + 1] = xstrdup(value);
if (!(*env_list)[*env_len + 1])
return ENOMEM;
*env_len += 2;
#endif
return 0;
}
static void process_env_free(process_env_t *list) {
if (!*list) return;
#ifdef _WIN32
free(*list);
#else
for (size_t i = 0; (*list)[i]; i++) free((*list)[i]);
free(*list);
#endif
*list = NULL;
}
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_env_t env_vars = NULL;
const char *cwd = NULL;
bool detach = false, escape = true;
int deadline = 10, new_fds[3] = { STDIN_FD, STDOUT_FD, STDERR_FD };
if (lua_isstring(L, 1)) {
escape = false;
// create a table that contains the string as the value
lua_createtable(L, 1, 0);
lua_pushvalue(L, 1);
lua_rawseti(L, -2, 1);
lua_replace(L, 1);
}
if (arg_len > 1) {
lua_getfield(L, 2, "env");
if (!lua_isnil(L, -1)) {
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
const char* key = luaL_checklstring(L, -2, &key_len);
const char* val = luaL_checklstring(L, -1, &val_len);
env_names[env_len] = malloc(key_len+1);
strcpy((char*)env_names[env_len], key);
env_values[env_len] = malloc(val_len+1);
strcpy((char*)env_values[env_len], val);
lua_pop(L, 1);
++env_len;
}
} else
lua_pop(L, 1);
luaL_checktype(L, 1, LUA_TTABLE);
#if LUA_VERSION_NUM > 501
lua_len(L, 1);
#else
lua_pushinteger(L, (int)lua_objlen(L, 1));
#endif
cmd_len = luaL_checknumber(L, -1); lua_pop(L, 1);
if (!cmd_len)
return luaL_argerror(L, 1, "table cannot be empty");
// check if each arguments is a string
for (size_t i = 1; i <= cmd_len; ++i) {
lua_rawgeti(L, 1, i);
luaL_checkstring(L, -1);
lua_pop(L, 1);
}
if (lua_istable(L, 2)) {
lua_getfield(L, 2, "detach"); detach = lua_toboolean(L, -1);
lua_getfield(L, 2, "timeout"); deadline = luaL_optnumber(L, -1, deadline);
lua_getfield(L, 2, "cwd"); cwd = luaL_optstring(L, -1, NULL);
@ -394,20 +617,70 @@ static int process_start(lua_State* L) {
lua_getfield(L, 2, "stdout"); new_fds[STDOUT_FD] = luaL_optnumber(L, -1, STDOUT_FD);
lua_getfield(L, 2, "stderr"); new_fds[STDERR_FD] = luaL_optnumber(L, -1, STDERR_FD);
for (int stream = STDIN_FD; stream <= STDERR_FD; ++stream) {
if (new_fds[stream] > STDERR_FD || new_fds[stream] < REDIRECT_PARENT) {
lua_pushfstring(L, "error: redirect to handles, FILE* and paths are not supported");
if (new_fds[stream] > STDERR_FD || new_fds[stream] < REDIRECT_PARENT)
return luaL_error(L, "error: redirect to handles, FILE* and paths are not supported");
}
lua_pop(L, 6); // pop all the values above
luaL_getsubtable(L, 2, "env");
// count environment variobles
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
luaL_checkstring(L, -2);
luaL_checkstring(L, -1);
lua_pop(L, 1);
env_len++;
}
if (env_len) {
if ((r = process_env_init(&env_vars, &env_vars_len, env_len)) != 0) {
retval = -1;
push_error(L, "cannot allocate environment list", r);
goto cleanup;
}
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
if ((r = process_env_add(&env_vars, &env_vars_len, lua_tostring(L, -2), lua_tostring(L, -1))) != 0) {
retval = -1;
push_error(L, "cannot copy environment variable", r);
goto cleanup;
}
lua_pop(L, 1);
env_len++;
}
}
}
// allocate and copy commands
if ((r = process_arglist_init(&arglist, &arglist_len, cmd_len)) != 0) {
retval = -1;
push_error(L, "cannot create argument list", r);
goto cleanup;
}
for (size_t i = 1; i <= cmd_len; i++) {
lua_rawgeti(L, 1, i);
if ((r = process_arglist_add(&arglist, &arglist_len, lua_tostring(L, -1), escape)) != 0) {
retval = -1;
push_error(L, "cannot add argument", r);
goto cleanup;
}
lua_pop(L, 1);
}
process_t* self = lua_newuserdata(L, sizeof(process_t));
memset(self, 0, sizeof(process_t));
luaL_setmetatable(L, API_TYPE_PROCESS);
self->deadline = deadline;
self->detached = detach;
#if _WIN32
if (env_vars) {
if ((r = process_env_add_system(&env_vars, &env_vars_len)) != 0) {
retval = -1;
push_error(L, "cannot add environment variable", r);
goto cleanup;
}
}
for (int i = 0; i < 3; ++i) {
switch (new_fds[i]) {
case REDIRECT_PARENT:
@ -458,7 +731,7 @@ static int process_start(lua_State* L) {
self->child_pipes[i][1] = self->child_pipes[new_fds[i]][1];
}
}
STARTUPINFO siStartInfo;
STARTUPINFOW siStartInfo;
memset(&self->process_information, 0, sizeof(self->process_information));
memset(&siStartInfo, 0, sizeof(siStartInfo));
siStartInfo.cb = sizeof(siStartInfo);
@ -466,48 +739,10 @@ static int process_start(lua_State* L) {
siStartInfo.hStdInput = self->child_pipes[STDIN_FD][0];
siStartInfo.hStdOutput = self->child_pipes[STDOUT_FD][1];
siStartInfo.hStdError = self->child_pipes[STDERR_FD][1];
char commandLine[32767] = { 0 }, environmentBlock[32767], wideEnvironmentBlock[32767*2];
int offset = 0;
if (!literal) {
for (size_t i = 0; i < cmd_len; ++i) {
size_t len = strlen(cmd[i]);
if (offset + len + 2 >= sizeof(commandLine)) break;
if (i > 0)
commandLine[offset++] = ' ';
commandLine[offset++] = '"';
int backslashCount = 0; // Yes, this is necessary.
for (size_t j = 0; j < len && offset + 2 + backslashCount < sizeof(commandLine); ++j) {
if (cmd[i][j] == '\\')
++backslashCount;
else if (cmd[i][j] == '"') {
for (size_t k = 0; k < backslashCount; ++k)
commandLine[offset++] = '\\';
commandLine[offset++] = '\\';
backslashCount = 0;
} else
backslashCount = 0;
commandLine[offset++] = cmd[i][j];
}
if (offset + 1 + backslashCount >= sizeof(commandLine)) break;
for (size_t k = 0; k < backslashCount; ++k)
commandLine[offset++] = '\\';
commandLine[offset++] = '"';
}
commandLine[offset] = 0;
} else {
strncpy(commandLine, cmd[0], sizeof(commandLine));
}
offset = 0;
for (size_t i = 0; i < env_len; ++i) {
if (offset + strlen(env_values[i]) + strlen(env_names[i]) + 1 >= sizeof(environmentBlock))
break;
offset += snprintf(&environmentBlock[offset], sizeof(environmentBlock) - offset, "%s=%s", env_names[i], env_values[i]);
environmentBlock[offset++] = 0;
}
environmentBlock[offset++] = 0;
if (env_len > 0)
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, environmentBlock, offset, (LPWSTR)wideEnvironmentBlock, sizeof(wideEnvironmentBlock));
if (!CreateProcess(NULL, commandLine, NULL, NULL, true, (detach ? DETACHED_PROCESS : CREATE_NO_WINDOW) | CREATE_UNICODE_ENVIRONMENT, env_len > 0 ? wideEnvironmentBlock : NULL, cwd, &siStartInfo, &self->process_information)) {
wchar_t cwd_w[MAX_PATH];
if (cwd) // TODO: error handling
MultiByteToWideChar(CP_UTF8, 0, cwd, -1, cwd_w, MAX_PATH);
if (!CreateProcessW(NULL, arglist, NULL, NULL, true, (detach ? DETACHED_PROCESS : CREATE_NO_WINDOW) | CREATE_UNICODE_ENVIRONMENT, env_vars, cwd ? cwd_w : NULL, &siStartInfo, &self->process_information)) {
push_error(L, NULL, GetLastError());
retval = -1;
goto cleanup;
@ -555,9 +790,9 @@ static int process_start(lua_State* L) {
close(self->child_pipes[stream][stream == STDIN_FD ? 1 : 0]);
}
size_t set;
for (set = 0; set < env_len && setenv(env_names[set], env_values[set], 1) == 0; ++set);
if (set == env_len && (!detach || setsid() != -1) && (!cwd || chdir(cwd) != -1))
execvp(cmd[0], (char** const)cmd);
for (set = 0; set < env_vars_len && setenv(env_vars[set], env_vars[set+1], 1) == 0; set += 2);
if (set == env_vars_len && (!detach || setsid() != -1) && (!cwd || chdir(cwd) != -1))
execvp(arglist[0], (char** const)arglist);
write(control_pipe[1], &errno, sizeof(errno));
_exit(-1);
}
@ -591,16 +826,15 @@ 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 (size_t i = 0; i < env_len; ++i) {
free((char*)env_names[i]);
free((char*)env_values[i]);
}
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);
if (retval == -1)
return lua_error(L);
@ -741,6 +975,10 @@ 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));
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;
}

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);
lua_pushinteger(L, my);
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);
lua_pushinteger(L, e.button.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);
lua_pushinteger(L, e.button.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);
lua_pushinteger(L, e.motion.y);
lua_pushinteger(L, e.motion.xrel);
lua_pushinteger(L, e.motion.yrel);
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));
@ -374,7 +374,7 @@ top:
#ifdef LITE_USE_SDL_RENDERER
rencache_invalidate();
#else
SDL_UpdateWindowSurface(window_renderer.window);
SDL_UpdateWindowSurface(window_renderer->window);
#endif
lua_pushstring(L, e.type == SDL_APP_WILLENTERFOREGROUND ? "enteringforeground" : "enteredforeground");
return 1;
@ -397,6 +397,7 @@ static int f_wait_event(lua_State *L) {
int nargs = lua_gettop(L);
if (nargs >= 1) {
double n = luaL_checknumber(L, 1);
if (n < 0) n = 0;
lua_pushboolean(L, SDL_WaitEventTimeout(NULL, n * 1000));
} else {
lua_pushboolean(L, SDL_WaitEvent(NULL));
@ -439,7 +440,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;
}
@ -449,39 +450,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);
@ -495,22 +496,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) {
@ -548,8 +549,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;
}
@ -876,6 +877,7 @@ static int f_get_time(lua_State *L) {
static int f_sleep(lua_State *L) {
double n = luaL_checknumber(L, 1);
if (n < 0) n = 0;
SDL_Delay(n * 1000);
return 0;
}
@ -929,7 +931,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;
}
@ -1071,7 +1073,7 @@ static int f_load_native_plugin(lua_State *L) {
#endif
/* Special purpose filepath compare function. Corresponds to the
order used in the TreeView view of the project's files. Returns true iff
order used in the TreeView view of the project's files. Returns true if
path1 < path2 in the TreeView order. */
static int f_path_compare(lua_State *L) {
size_t len1, len2;

View File

@ -35,16 +35,6 @@
static SDL_Window *window;
static double get_scale(void) {
#ifndef __APPLE__
float dpi;
if (SDL_GetDisplayDPI(0, NULL, &dpi, NULL) == 0)
return dpi / 96.0;
#endif
return 1.0;
}
static void get_exe_filename(char *buf, int sz) {
#if _WIN32
int len;
@ -204,6 +194,8 @@ int main(int argc, char **argv) {
SDL_SetHint("SDL_MOUSE_DOUBLE_CLICK_RADIUS", "4");
#endif
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
SDL_DisplayMode dm;
SDL_GetCurrentDisplayMode(0, &dm);
@ -217,7 +209,7 @@ int main(int argc, char **argv) {
fprintf(stderr, "Error creating lite-xl window: %s", SDL_GetError());
exit(1);
}
ren_init(window);
window_renderer = ren_init(window);
lua_State *L;
init_lua:
@ -239,9 +231,6 @@ init_lua:
lua_pushstring(L, LITE_ARCH_TUPLE);
lua_setglobal(L, "ARCH");
lua_pushnumber(L, get_scale());
lua_setglobal(L, "SCALE");
char exename[2048];
get_exe_filename(exename, sizeof(exename));
if (*exename) {
@ -314,7 +303,7 @@ init_lua:
// This allows the window to be destroyed before lite-xl is done with
// reaping child processes
ren_free_window_resources(&window_renderer);
ren_free(window_renderer);
lua_close(L);
#if defined(__amigaos4__)

View File

@ -191,8 +191,9 @@ void rencache_draw_rect(RenWindow *window_renderer, RenRect rect, RenColor color
double rencache_draw_text(RenWindow *window_renderer, RenFont **fonts, const char *text, size_t len, double x, int y, RenColor color)
{
double width = ren_font_group_get_width(window_renderer, fonts, text, len);
RenRect rect = { x, y, (int)width, ren_font_group_get_height(fonts) };
int x_offset;
double width = ren_font_group_get_width(window_renderer, fonts, text, len, &x_offset);
RenRect rect = { x + x_offset, y, (int)(width - x_offset), ren_font_group_get_height(fonts) };
if (rects_overlap(last_clip_rect, rect)) {
int sz = len + 1;
DrawTextCommand *cmd = push_command(window_renderer, DRAW_TEXT, sizeof(DrawTextCommand) + sz);

View File

@ -22,7 +22,7 @@
#define MAX_LOADABLE_GLYPHSETS (MAX_UNICODE / GLYPHSET_SIZE)
#define SUBPIXEL_BITMAPS_CACHED 3
RenWindow window_renderer = {0};
RenWindow* window_renderer = NULL;
static FT_Library library;
// draw_rect_surface is used as a 1x1 surface to simplify ren_draw_rect with blending
@ -167,7 +167,7 @@ static void font_load_glyphset(RenFont* font, int idx) {
for (unsigned int column = 0; column < slot->bitmap.width; ++column) {
int current_source_offset = source_offset + (column / 8);
int source_pixel = slot->bitmap.buffer[current_source_offset];
pixels[++target_offset] = ((source_pixel >> (7 - (column % 8))) & 0x1) << 7;
pixels[++target_offset] = ((source_pixel >> (7 - (column % 8))) & 0x1) * 0xFF;
}
} else
memcpy(&pixels[target_offset], &slot->bitmap.buffer[source_offset], slot->bitmap.width);
@ -348,10 +348,11 @@ int ren_font_group_get_height(RenFont **fonts) {
return fonts[0]->height;
}
double ren_font_group_get_width(RenWindow *window_renderer, RenFont **fonts, const char *text, size_t len) {
double ren_font_group_get_width(RenWindow *window_renderer, RenFont **fonts, const char *text, size_t len, int *x_offset) {
double width = 0;
const char* end = text + len;
GlyphMetric* metric = NULL; GlyphSet* set = NULL;
bool set_x_offset = x_offset == NULL;
while (text < end) {
unsigned int codepoint;
text = utf8_to_codepoint(text, &codepoint);
@ -359,8 +360,15 @@ double ren_font_group_get_width(RenWindow *window_renderer, RenFont **fonts, con
if (!metric)
break;
width += (!font || metric->xadvance) ? metric->xadvance : fonts[0]->space_advance;
if (!set_x_offset) {
set_x_offset = true;
*x_offset = metric->bitmap_left; // TODO: should this be scaled by the surface scale?
}
}
const int surface_scale = renwin_get_surface(window_renderer).scale;
if (!set_x_offset) {
*x_offset = 0;
}
return width / surface_scale;
}
@ -493,33 +501,38 @@ void ren_draw_rect(RenSurface *rs, RenRect rect, RenColor color) {
}
/*************** Window Management ****************/
void ren_free_window_resources(RenWindow *window_renderer) {
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);
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);
@ -34,17 +34,17 @@ int ren_font_group_get_height(RenFont **font);
float ren_font_group_get_size(RenFont **font);
void ren_font_group_set_size(RenWindow *window_renderer, RenFont **font, float size);
void ren_font_group_set_tab_size(RenFont **font, int n);
double ren_font_group_get_width(RenWindow *window_renderer, RenFont **font, const char *text, size_t len);
double ren_font_group_get_width(RenWindow *window_renderer, RenFont **font, const char *text, size_t len, int *x_offset);
double ren_draw_text(RenSurface *rs, RenFont **font, const char *text, size_t len, float x, int y, RenColor color);
void ren_draw_rect(RenSurface *rs, RenRect rect, RenColor color);
void ren_init(SDL_Window *win);
RenWindow* ren_init(SDL_Window *win);
void ren_free(RenWindow* window_renderer);
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

@ -41,7 +41,8 @@ static void setup_renderer(RenWindow *ren, int w, int h) {
#endif
void renwin_init_surface(UNUSED RenWindow *ren) {
void renwin_init_surface(RenWindow *ren) {
ren->scale_x = ren->scale_y = 1;
#ifdef LITE_USE_SDL_RENDERER
if (ren->rensurface.surface) {
SDL_FreeSurface(ren->rensurface.surface);
@ -107,6 +108,16 @@ void renwin_resize_surface(UNUSED RenWindow *ren) {
#endif
}
void renwin_update_scale(RenWindow *ren) {
#ifndef LITE_USE_SDL_RENDERER
SDL_Surface *surface = SDL_GetWindowSurface(ren->window);
int window_w = surface->w, window_h = surface->h;
SDL_GetWindowSize(ren->window, &window_w, &window_h);
ren->scale_x = (float)surface->w / window_w;
ren->scale_y = (float)surface->h / window_h;
#endif
}
void renwin_show_window(RenWindow *ren) {
SDL_ShowWindow(ren->window);
}

View File

@ -6,6 +6,8 @@ struct RenWindow {
uint8_t *command_buf;
size_t command_buf_idx;
size_t command_buf_size;
float scale_x;
float scale_y;
#ifdef LITE_USE_SDL_RENDERER
SDL_Renderer *renderer;
SDL_Texture *texture;
@ -19,6 +21,7 @@ void renwin_init_command_buf(RenWindow *ren);
void renwin_clip_to_surface(RenWindow *ren);
void renwin_set_clip_rect(RenWindow *ren, RenRect rect);
void renwin_resize_surface(RenWindow *ren);
void renwin_update_scale(RenWindow *ren);
void renwin_show_window(RenWindow *ren);
void renwin_update_rects(RenWindow *ren, RenRect *rects, int count);
void renwin_free(RenWindow *ren);

View File

@ -1,9 +1,10 @@
[wrap-file]
directory = freetype-2.12.1
source_url = https://download.savannah.gnu.org/releases/freetype/freetype-2.12.1.tar.xz
source_filename = freetype-2.12.1.tar.xz
source_hash = 4766f20157cc4cf0cd292f80bf917f92d1c439b243ac3018debf6b9140c41a7f
wrapdb_version = 2.12.1-2
directory = freetype-2.13.2
source_url = https://download.savannah.gnu.org/releases/freetype/freetype-2.13.2.tar.xz
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/freetype2_2.13.2-1/freetype-2.13.2.tar.xz
source_filename = freetype-2.13.2.tar.xz
source_hash = 12991c4e55c506dd7f9b765933e62fd2be2e06d421505d7950a132e4f1bb484d
wrapdb_version = 2.13.2-1
[provide]
freetype2 = freetype_dep

View File

@ -1,12 +1,14 @@
[wrap-file]
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
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
[provide]
lua-5.4 = lua_dep
lua = lua_dep

View File

@ -1,12 +1,13 @@
[wrap-file]
directory = pcre2-10.42
source_url = https://github.com/PhilipHazel/pcre2/releases/download/pcre2-10.42/pcre2-10.42.tar.bz2
source_url = https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.42/pcre2-10.42.tar.bz2
source_filename = pcre2-10.42.tar.bz2
source_hash = 8d36cd8cb6ea2a4c2bb358ff6411b0c788633a2a45dabbf1aeb4b701d1b5e840
patch_filename = pcre2_10.42-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/pcre2_10.42-1/get_patch
patch_hash = 06969e916dfee663c189810df57d98574f15e0754a44cd93f3f0bc7234b05d89
wrapdb_version = 10.42-1
patch_filename = pcre2_10.42-5_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/pcre2_10.42-5/get_patch
patch_hash = 7ba1730a3786c46f41735658a9884b09bc592af3840716e0ccc552e7ddf5630c
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/pcre2_10.42-5/pcre2-10.42.tar.bz2
wrapdb_version = 10.42-5
[provide]
libpcre2-8 = libpcre2_8

View File

@ -1,12 +1,15 @@
[wrap-file]
directory = SDL2-2.26.0
source_url = https://github.com/libsdl-org/SDL/releases/download/release-2.26.0/SDL2-2.26.0.tar.gz
source_filename = SDL2-2.26.0.tar.gz
source_hash = 8000d7169febce93c84b6bdf376631f8179132fd69f7015d4dadb8b9c2bdb295
patch_filename = sdl2_2.26.0-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/sdl2_2.26.0-1/get_patch
patch_hash = 6fcfd727d71cf7837332723518d5e47ffd64f1e7630681cf4b50e99f2bf7676f
wrapdb_version = 2.26.0-1
directory = SDL2-2.28.1
source_url = https://github.com/libsdl-org/SDL/releases/download/release-2.28.1/SDL2-2.28.1.tar.gz
source_filename = SDL2-2.28.1.tar.gz
source_hash = 4977ceba5c0054dbe6c2f114641aced43ce3bf2b41ea64b6a372d6ba129cb15d
patch_filename = sdl2_2.28.1-2_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/sdl2_2.28.1-2/get_patch
patch_hash = 2dd332226ba2a4373c6d4eb29fa915e9d5414cf7bb9fa2e4a5ef3b16a06e2736
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sdl2_2.28.1-2/SDL2-2.28.1.tar.gz
wrapdb_version = 2.28.1-2
[provide]
sdl2 = sdl2_dep
sdl2main = sdl2main_dep
sdl2_test = sdl2_test_dep