Compare commits

...

258 Commits

Author SHA1 Message Date
George Sokianos ab72e26080 Fixes to make the 2.1 branch working on OS4 - WIP 2022-04-04 11:31:17 +01:00
George Sokianos aa14bd6a88 Applied all the necessary changes to make it run under AmigaOS 4.1 FE 2022-03-27 22:57:25 +01:00
Adam Harrison 7a78b780f4 Apparenlty these were issues? What? 2022-03-24 20:12:45 -04:00
Adam Harrison a9f5e73044 Merge branch 'master-2.1' of github.com:lite-xl/lite-xl into master-2.1 2022-03-24 12:46:48 -04:00
Adam Harrison 0873da9670 Added in notes to the changelog. 2022-03-24 12:46:38 -04:00
Adam a8b97dffab
Soft Line Wrapping (#636)
Added in soft line wrapping.
2022-03-24 12:01:46 -04:00
Adam Harrison a0194ac26b Bumping version numbers. 2022-03-24 11:57:26 -04:00
Jefferson González a2d5a7a904
Merge pull request #894 from adamharrison/fix-anonymous-syntaxes
Fixed anonymous syntaxes.
2022-03-22 22:06:25 -04:00
Adam Harrison 3e7a97737e Re-enabled comment cache. 2022-03-22 10:35:44 -04:00
Adam Harrison 17645ba4ec Fixed anonyous syntaxes. 2022-03-22 10:17:42 -04:00
jgmdev bbac4d1560 treeview: add proper predicate for delete command 2022-03-20 01:58:39 -04:00
jgmdev c3bcf68851 treeview: use root_view:get_primary_node().active_view for focus. 2022-03-20 01:05:07 -04:00
jgmdev f0cc973e38 treeview: also handle focus change from mouse and then commandview 2022-03-20 00:53:13 -04:00
Jefferson González ad25216de7
Merge pull request #890 from Guldoman/PR_treeview_fix_scroll
Fix `TreeView` scroll via scrollbar
2022-03-20 00:31:26 -04:00
Jefferson González 331c78faac
Merge pull request #889 from Guldoman/PR_move_to_selection
Move cursor to the beginning/end of its selection
2022-03-20 00:22:00 -04:00
Guldoman 46f9be2960
Hide hovered `TreeView` item when dragging the scrollbar 2022-03-20 04:46:57 +01:00
Guldoman 699655bebf
Don't specify delta movement when simulating `TreeView:on_mouse_moved` 2022-03-20 04:45:14 +01:00
Guldoman 3765ef1d7a
Move cursor to the beginning or the end of its selection
When using `doc:move-to-{previous,next}-char` in a selection, we were 
moving the cursor to the character before the initial/after the last 
character of the selection.
Now we follow what other editors do and move it to just before the 
initial/just after the final character.
2022-03-20 04:28:26 +01:00
jgmdev b741c204db treeview: better handle previous view when focus/unfocus from CommandView 2022-03-19 23:10:26 -04:00
jgmdev 3ffabced62 treeview: move delete command to proper predicate 2022-03-19 22:33:41 -04:00
Jefferson González b5ead3992e
Merge pull request #888 from Guldoman/PR_treeview_collapse_to_parent
Make `treeview:collapse` select parent if current item can't collapse
2022-03-18 18:01:44 -04:00
Guldoman 3ec0f38446
Make `treeview:collapse` select parent if current item can't collapse 2022-03-18 16:16:55 +01:00
Guldoman 2d5af22dc9
Don't draw `treeview` tooltip if its position is not defined 2022-03-18 16:10:24 +01:00
jgmdev a7fc7b4408 treeview: fix crash on tooltip.x been nil 2022-03-18 06:02:48 -04:00
Jefferson González 30de42f4ab
Merge pull request #755 from Jipok/draw_tab_rework
rootview.lua: Refactor Node:draw_tab
2022-03-18 05:05:55 -04:00
jgmdev e8427ae168 treeview: fixed github merging error 2022-03-18 04:23:32 -04:00
Jefferson González 5a63f6dc2e
Merge pull request #770 from takase1121/treeview-initial-size
add option for treeview initial size
2022-03-18 04:18:56 -04:00
jgmdev 02f6dcc07d treeview: added @AlexSol suggestions
* suggestions included collapse, expand and focus
* also added missing common.merge
* some other minor fixes
2022-03-18 03:57:14 -04:00
takase1121 d5da711b6f add selections in treeview 2022-03-18 03:11:13 -04:00
Guldoman 000caf2e43 Allow opening non existing files from arguments 2022-03-18 01:09:02 -04:00
Guldoman 9763701cbf Reset syntax when a filename is provided 2022-03-18 00:36:25 -04:00
Guldoman fb4a5f3828 Add command to create a new named Doc 2022-03-18 00:34:15 -04:00
Jefferson González 0e323f4a35
Merge pull request #886 from adamharrison/fix-left-click-issues
Fixed a bunch of problems relating to multicursor.
2022-03-17 21:00:51 -04:00
Jefferson González 1f468fca24
Merge pull request #883 from jgmdev/detectindent-improvements
plugin detectident: fixes and improvements
2022-03-17 18:29:46 -04:00
Adam Harrison ba5289dc75 Typo. 2022-03-17 16:57:18 -04:00
Adam Harrison 82325b6a08 Fixed a bunch of problems. Fixed left+click not allowing for square selections, fixed esc not exiting multicursor mode, and allowed cntrl+click to remove a cursor. 2022-03-17 16:55:52 -04:00
Jan 120c769e7e
seperate dirmonitor logic, add build time detection of features (#866)
this also adds libkqueue support
2022-03-17 13:43:01 -04:00
jgmdev 5830b7d9f0 plugin detectindent: pre-compile regexes 2022-03-17 00:14:36 -04:00
Jefferson González 20763ed7ff
Merge pull request #864 from jgmdev/markdown-adjustments
language_md: removed scale adjustment code
2022-03-15 22:14:34 -04:00
jgmdev dcbebef2ab plugin detectident: fixes and improvements
* Improved performance 67x by not using the tokenizer, this means that
  now opening files or saving them where indentation is re-detected
  is much more faster.
* Improved the algorithm to detect the space size.
2022-03-15 21:17:15 -04:00
jgmdev 2ce4dbc8ef language_md: some more improvements
* handle images with links
* handle escaping of * and `
* support coloring a heading custom id
* reverted from regex to lua patterns
2022-03-13 14:43:50 -04:00
Jefferson González 17017b63ae
Merge pull request #875 from jgmdev/language-c-cpp-improvements
Languages c and cpp improvements
2022-03-11 17:44:17 -04:00
Jefferson González 1725a48ce2
Merge pull request #879 from jgmdev/plugins-skip-version
config: added skip_plugins_version
2022-03-11 17:30:36 -04:00
Jefferson González d0d0028472
Merge pull request #877 from jgmdev/statusview-commands
StatusView: added ability to hide and commands
2022-03-11 17:26:02 -04:00
jgmdev 620b669517 statusview: added ability to hide and commands
Also fixed the right panel not been draggable.
2022-03-11 17:23:16 -04:00
Jefferson González 46795972f0
Merge pull request #876 from jgmdev/nagview-scroll
nagview: support vscroll when message is too long
2022-03-11 17:11:38 -04:00
jgmdev b880aa42f9 language_c: fixes and improvements
* Do not compete with language_cpp.lua over the .h and .inl files,
  these files can contain both cpp and c so we choose the former which
  supports both syntaxes.
* Added support for magic and uppercase constants.
2022-03-11 17:05:08 -04:00
jgmdev 4b0531cdfc language_cpp: improvements and fixes
* Removed pcall(require, "plugins.language_c") since it doesn't works
  as it seems to have been intended.
* Removed duplicate keywords
* Added support for magic and uppercase constants.
* Basically merged most changes from the lite-xl-plugins repo.
2022-03-11 17:02:42 -04:00
jgmdev d9909cf4ea config: added skip_plugins_version
This new config flag ignores the plugins version check at startup
which helps a lot when working on new or old plugins that doesn't match
the mod or lite-xl version and you still desire to load them to fix them
by checking with lite-xl it self which errors need to be corrected.
2022-03-10 22:29:33 -04:00
jgmdev 0aa53a0e7f nagview: support vscroll when message is too long
Also some other minor changes:
* fix transition when nagview is closed
* do not draw or update when not visible
* do not process events when not visible
* cleaned a bit the logic on next and show
* fixes #848
2022-03-10 06:57:16 -04:00
Jefferson González e08353ea08
Merge pull request #873 from jgmdev/user-and-project-reload-fix
Fix config overwritten on user/project modules save
2022-03-09 19:40:35 -04:00
jgmdev eeb2b28a03 config overwritten on user/project modules save
When a user modifies and saves the init.lua or a project module file the
reload_customizations() function was performing unnecessary reloading
of core.config and core.style. This resulted on the replacement of config
tables with new tables, breaking all active references been used by
the consumers of this config options. Been redundant this means
that every consumer was using its own copy of a configuration table
different from the one referenced on core.config and user changes not
taking place.
2022-03-09 02:15:01 -04:00
Adam 960b482061
Fixed some issues with inotify and multiple events at the same time. (#872)
* Fixed some issues with inotify and multiple events at the same time. Seems to be working now.

* Cleaned up and simplified function, and commented, and fixed a number of bugs.

* Simplifying and fixing further.

* Improved performance for skipping large amounts of files.

* Added in extra checks, and changed paths. We should probably unify these path styles.

* Fixed stutter.

* Removed extraneous functions.

* Cleaned up more, added more testing; dealt with multiple sequential events correctly.
2022-03-08 19:30:25 -05:00
Jefferson González 52a47e0d73
Merge pull request #870 from lite-xl/treeview-fix
plugin treeview: skip rootview events if not visible.
2022-03-07 18:50:36 -04:00
Jefferson González 8a31a2b169
Merge pull request #871 from Guldoman/fix_bad_clip_draw_text
Always check if the beginning of the text needs to be clipped
2022-03-07 18:49:14 -04:00
Guldoman 5714da81f8
Always check if the beginning of the text needs to be clipped 2022-03-07 23:35:56 +01:00
jgmdev 48e86bb117 plugin treeview: skip rootview events if not visible. 2022-03-07 16:52:32 -04:00
Jefferson González 6386bac4e5
Merge pull request #869 from jgmdev/fix-loading-order
Initialization: load core views before user plugins
2022-03-06 21:22:53 -04:00
jgmdev 9d4e475a2c init: also load default nodes and commands before user plugins and project module. 2022-03-06 21:21:00 -04:00
jgmdev d7d88a2037 init: load core views before user plugins 2022-03-06 21:03:16 -04:00
Jefferson González 1be425d1de
Merge pull request #867 from Guldoman/fix_load_project_module
Load project module on startup
2022-03-06 21:02:22 -04:00
Guldoman 240afa7abd
Load project module on startup 2022-03-06 21:09:22 +01:00
Adam f85612e0f0
Fix Project Scanning (#746)
Removed dmon, and replaced with logic that works across Linux, Mac, FreeBSD and Windows. Have tested on all platforms, and seems to work.

Co-authored-by: Jan200101 <sentrycraft123@gmail.com>
2022-03-06 00:59:22 -05:00
jgmdev 6cb403b450 meson: increased min version to 0.47 and added check flag on run_command. 2022-03-05 20:15:48 -04:00
Jefferson González d82b668a09
Merge pull request #863 from jgmdev/scale-fix
plugin scale: replace non existing font.set_size with font.copy
2022-03-05 20:03:05 -04:00
jgmdev c4f7380a95 language_md: removed scale adjustment code that was needed because of a bug on scale plugin. 2022-03-05 19:13:24 -04:00
jgmdev e5ca08e13f plugin scale: replace non existing font.set_size with font.copy 2022-03-05 19:03:33 -04:00
Jefferson González 1fa1960b05
Merge pull request #862 from jgmdev/markdown-consolidation
language_md: improvements consolidating #814 and #840 thanks to @Not-a-web-Developer and @TorchedSammy
2022-03-04 14:58:25 -04:00
jgmdev 2d3d9a1671 language_md: improvements consolidating #814 and #840 2022-03-04 14:29:29 -04:00
Jefferson González 0e79607895
Merge pull request #859 from jgmdev/commit-on-version
Add git commit on version of devel builds
2022-03-04 14:18:36 -04:00
Jefferson González 2f7da44275
Merge pull request #860 from Guldoman/tokenizer_start_of_line
Allow syntax patterns to match with the beginning of the line
2022-03-04 14:13:14 -04:00
Guldoman caefc9112a
Force syntax patterns starting with `^` to match with the whole line
Before, syntax patterns/regexes that started with `^` didn't have the 
desired effect of matching with the start of the line.

Now those patterns are used only when matching the whole line.
2022-03-04 11:27:01 +01:00
Guldoman fbb893c6b1
Fix `^` regex matching when using an offset
Before, if `offset > 1` was used, the match would have failed because 
the beginning of the string was never met.

Now we force the beginning of the string to be the one specified by the 
offset.
2022-03-03 22:09:48 +01:00
Jefferson González 2736072dce
Merge pull request #858 from Guldoman/move_towards_epsilon
Use epsilon to compare values in `View:move_towards`
2022-03-03 15:21:25 -04:00
jgmdev a587182982 meson: when running git rev-parse use HEAD instead of checking current branch name 2022-03-02 18:55:46 -04:00
jgmdev 1e765b5c28 EmptyView: handle lite-xl version strings that overlap. 2022-03-02 03:55:03 -04:00
jgmdev 05e355d383 meson: append git commit if possible and to non release builds 2022-03-02 03:53:04 -04:00
Guldoman 3d879286a4
Use epsilon to compare values in `move_towards` 2022-03-01 22:41:54 +01:00
Jefferson González be5f94e913
Merge pull request #852 from jgmdev/statusview-enhacements
Improvements to the StatusView, closes #677
2022-02-28 18:19:46 -04:00
jgmdev e9775ced78 StatusView: added items hovered option for bg color and param to on_draw as suggested by @Guldoman 2022-02-28 17:56:17 -04:00
jgmdev 4c80aa7a40 StatusView: reposition left and right panel offsets on window resize. 2022-02-28 17:04:37 -04:00
jgmdev 15a31418ca StatusView: restored indent confirmed 2022-02-24 14:22:52 -04:00
jgmdev 128e10ba47 StatusView: add spaces as separate items instead of pre-pending them to items. 2022-02-24 13:58:40 -04:00
jgmdev 131bdf9fb2 StatusView: implemented and applied get_item_visible_area. 2022-02-24 02:23:29 -04:00
jgmdev b1ce685489 StatusView: better calculate panel sizes and handle out of bounds drags. 2022-02-24 00:21:21 -04:00
jgmdev c5648e1f02 StatusView: implemented dragging and scrolling. 2022-02-23 19:25:25 -04:00
jgmdev 3452d499e5 Show hand cursor on clickable elements as suggested by @Guldoman 2022-02-23 06:07:53 -04:00
jgmdev 90983b22a4 StatusView v2.0, some changes include:
* Items are now objects that can be retrieved and manipulated.
* clip rect is used for left and right panes
* initial support for items to do their own custom drawing by also
  doing a clip rect for them
* a custom background color can be specified for the item.
* a command or function can be executed on item click.
* Introduced functionality to easily hide or show all or specific items.
* Better handling of deprecated get_items()
* Spacing is automatically added to items and cleaned on deprecated
  items.
* Default items where separated and given the names:
  doc:file, doc:position, doc:indentation, doc:lines,
  doc:line-ending, core.commandview.
* Some default right or left click actions where given to the default
  items.
* Started adding required bits to support dragging to left and right
  panes when some items aren't visible.

Note: should work well enough already but maybe some repetitive stuff can be
cleaned out.
2022-02-23 05:15:14 -04:00
jgmdev 7cb4e93d14 Improvements to the StatusView
* Support for predicates by introducing add_item().
* Support for performing click actions on items.
* Support for optional tooltips on item hover.
* Deprecate the usage of get_item().
2022-02-21 03:40:25 -04:00
jgmdev feaa3b2547 Docs: changes process start from method to function. 2022-02-15 20:49:46 -04:00
Adam 61e712db12
Fixed rendering computations for y offset. (#843)
* Fixed rendering computations for y offset.

* Force monospacing if every ascii character has the same integer advance.

* Added in explanatory comment.

* Fixed issues.

* Made lines less long.
2022-02-15 15:57:07 -05:00
Adam 05e1968b0b
Merge pull request #845 from Guldoman/file_drop_override
Add `View:on_file_dropped`
2022-02-15 11:44:37 -05:00
Guldoman 539f929e30
Allow intercepting `filedropped` events
The event is first sent to the underlying `View`; if not handled, it's 
managed as before.
2022-02-15 00:45:59 +01:00
jgmdev 2079e1f707 Plugin projectsearch: set command view text to current document selection. 2022-02-13 01:31:58 -04:00
Francesco Abbate b02aae939c Fix again bug with invalid ignore_files patterns
The pattern cannot be tested in advance as it seems that Lua inspect
the pattern only partially, the part that is actually used.

We resort to use pcall to catch any error when using the pattern.
2022-02-11 23:00:15 +01:00
Adam a6f32ca0d0
Merge pull request #822 from AlexSol/update_contextMenu
Travel by contextMenu using keyboard
2022-02-11 12:10:14 -05:00
Adam 3a0af6ee04
Merge pull request #828 from adamharrison/signal-processes
Changed signalling so it'll target the whole process group.
2022-02-11 12:01:53 -05:00
Adam af6a06bc8b
Merge pull request #832 from jgmdev/autocomplete-update
Update autocomplete with changes needed for latest LSP plugin.
2022-02-11 12:01:35 -05:00
Adam b05a02cb28
Merge pull request #839 from jgmdev/nil-nodes-fix
Fix cases of nil node.
2022-02-11 11:52:43 -05:00
Adam a4833b21e4
Merge pull request #842 from Guldoman/no_scroll_selection
Don't scroll when selecting the whole doc
2022-02-11 11:48:01 -05:00
Francesco Abbate 85531b0d3f
Include addons with build-package for bundles 2022-02-11 06:33:53 +01:00
Guldoman 5526041da3
Check entire selection to trigger `DocView:scroll_to_make_visible`
This is needed for example when a selection has both `line1` and `col1`
at 1, and the left arrow is pressed: `line2` and `col2` change, while
`line1` and `col1` don't, but we still want to scroll.
2022-02-11 06:16:29 +01:00
Guldoman 59ba759167
Don't scroll DocView when executing `doc:select-all` 2022-02-11 06:00:38 +01:00
jgmdev bb4569da53 Fix cases of nil node. 2022-02-09 10:40:02 -04:00
Jefferson González dc5888bc07
Merge pull request #837 from Guldoman/process_running_poll
Update process status when calling `process:running`
2022-02-08 16:36:18 -04:00
Guldoman d4b8155cbc
Update process status when calling `process:running` 2022-02-08 18:10:54 +01:00
Jefferson González 8caccbf6f0
Merge pull request #836 from Guldoman/toggle_comment_empty_line
Ignore empty lines in `line_comment`
2022-02-07 15:31:48 -04:00
Guldoman f23cb33f7c
Ignore empty lines in `line_comment` 2022-02-07 19:22:43 +01:00
jgmdev aec6806d8f Added system.get_process_id() to api docs. 2022-02-04 15:43:42 -04:00
Adam 212d4e2729
Merge pull request #833 from jgmdev/add-system-get_process_id
Implemented system.get_process_id()
2022-02-04 12:24:54 -05:00
jgmdev df0635ad35 Implemented system.get_process_id() 2022-02-03 22:20:42 -04:00
Adam e5f17aea4b
Merge pull request #821 from dheisom-gomes/improvements
Improvements on core.add_thread function
2022-02-03 19:23:54 -05:00
Adam d78f310a4e
Merge pull request #829 from Guldoman/draw_rect_alpha_format
Use SDL to manage color format mapping in `ren_draw_rect`
2022-02-03 17:02:49 -05:00
jgmdev 6773e85cb8 Update autocomplete with changes needed for latest LSP plugin. 2022-02-03 15:01:39 -04:00
Guldoman 9a6cd2b453
Use SDL to manage color format mapping in `ren_draw_rect` 2022-02-03 01:50:43 +01:00
Adam Harrison d2d5617774 Changed signalling so it'll target the whole process group. 2022-01-30 20:51:30 -05:00
Dheisom Gomes f5e9146b1c Merge branch 'master' of https://github.com/lite-xl/lite-xl into improvements 2022-01-30 15:49:37 -03:00
Adam Harrison af76f544be Fixing performance regression. Due to the way the hashes work, we must 0 out the whole thing. 2022-01-29 15:19:22 -05:00
AlexSol efedbae663 Travel by contextMenu using keyboard 2022-01-29 16:23:00 +02:00
Adam 3d40725b8f
Merge pull request #816 from adamharrison/fix-process-api
Fixing up Process API
2022-01-28 23:53:37 -05:00
Dheisom Gomes 13adedb23a Go back to `common.match_pattern` and use `table.unpack` directly on function `core.add_thread` 2022-01-28 18:30:19 -03:00
Adam Harrison 941523868e Merge branch 'fix-process-api' of github.com:adamharrison/lite-xl into fix-process-api 2022-01-28 15:44:16 -05:00
Adam Harrison 3773a812bd Incorporate realtakase's suggestions. 2022-01-28 15:39:57 -05:00
Adam 0a70b13a73
Merge pull request #809 from lite-xl/merge-master-2.0
Merge master 2.0
2022-01-28 14:38:22 -05:00
Dheisom Gomes 22d8f69b5c Error correction getting "unpack" function 2022-01-28 12:13:52 -03:00
Dheisom Gomes 6331a23c6b Added support to use a array of regex to ignore files 2022-01-28 12:02:30 -03:00
Dheisom Gomes 8c0685d440 Added support to pass extra arguments to functions on core.add_thread 2022-01-28 11:53:30 -03:00
Francesco Abbate 3a53b05b29 Do no error out on malformed ignore patterns 2022-01-25 14:16:40 +01:00
Francesco Abbate 3e39da071d Fix problem with project module save hook 2022-01-24 09:34:54 +01:00
Francesco Abbate bc9f8a4075 Use new mutex in dmon to avoid possible lock-up
We rely on one variable _dmon.modify_watches shared between thread to
ensure that we don't lock with the dmon polling thread waiting indefinitely
and helding a lock.

To ensure that the polling thread sees modifications done to 'modify_watches'
we use an additional mutex that act as a memory barrier.
2022-01-24 09:34:54 +01:00
Adam Harrison 456126400a Added in new merge method, and run it on plugins. Also made it so plugin configs can be set anywhere, even if we don't know the plugin beforehand. 2022-01-22 18:39:23 -05:00
Adam 7e6d9df58d
Merge pull request #805 from Jan200101/PR/fallback-force
use lua fallback earlier when fallbacks are forced
2022-01-22 17:30:01 -05:00
Jan200101 227ca7d0e5
use lua fallback earlier when fallbacks are forced 2022-01-22 23:29:28 +01:00
Francesco Abbate f7193c4fa2 Remove unused whitespace_replacements function 2022-01-22 21:46:02 +01:00
Adam Harrison f9ad83e53e Fixed windows not converting utf8 environment block to utf16. 2022-01-22 13:34:47 -05:00
Adam Harrison ed4128bc65 Added in support for env on linux. 2022-01-22 12:36:30 -05:00
Adam Harrison 428c757a13 Implemented @guldoman's suggestion for how to close file handles. 2022-01-22 12:02:59 -05:00
Adam 9cb25acd7b
Merge pull request #815 from takase1121/fix-subprocess-read
fix invalid memory access
2022-01-22 11:18:19 -05:00
takase1121 f24ebf853d
fix invalid memory access 2022-01-22 23:30:48 +08:00
Adam 8c8bd4692c
Merge pull request #808 from adamharrison/fix-commenting
Fix commenting selections.
2022-01-20 23:28:05 -05:00
Adam Harrison b523bd5cee Fixed end of block-style line comments. 2022-01-20 22:17:21 -05:00
Francesco Abbate f6a0e12e31 Merge branch 'master-2.0' 2022-01-19 20:31:33 +01:00
Francesco Abbate cd83df1abf Bump version and changelog to prepare 2.0.5 release 2022-01-19 18:18:25 +01:00
Adam 6025c43241
Merge pull request #743 from takase1121/better-logview
multiple improvements to logging
2022-01-18 23:25:36 -05:00
Adam Harrison cdbfecc5ce Streamlined, and fixed guldo's problem. 2022-01-18 21:38:43 -05:00
Adam Harrison 30cc205cd4 Fixed issue first mentioned in #771. 2022-01-18 21:38:38 -05:00
Adam d3e1636881
Merge pull request #771 from takase1121/multiline-comment-command
add toggle-block-comment
2022-01-18 21:07:46 -05:00
Adam 3b3e41c095
Merge pull request #799 from Guldoman/bit32_polyfill
Improve bit32 polyfill
2022-01-18 21:06:48 -05:00
Adam a4e5d9f043
Merge pull request #798 from Jan200101/PR/wrap
Add fallbacks to all common dependencies
2022-01-18 20:54:36 -05:00
Francesco Abbate 2dd154edeb Remove remaining debug message 2022-01-18 10:42:20 +01:00
Jan200101 192c577966
Add fallbacks to all common dependencies 2022-01-15 00:53:46 +01:00
Francesco Abbate 7e9b2f30da Treat final '/' or '/$' in ignore rule as part of the pattern
Evolve the rule for directory in ignore_files to be more natural
and easy to understand.

When a final '/' or '/$' is found we consider the pattern to match
a directory and the pattern is not modifed. In turns, is used, before
matching a directory's name a final '/' is appended to its name
before checking if it matches the pattern.

With the previous rule a final '/' in the pattern meant also a directory
but the '/' was removed from the pattern.
2022-01-13 16:43:37 +01:00
Francesco Abbate 2456452f65 Fix error to close view when deleting a file 2022-01-13 16:38:20 +01:00
Guldoman e51c76c72b
Assert for negative `field` in bit32 polyfill
This more closely matches the behavior of lua5.2.
2022-01-12 19:56:09 +01:00
Francesco Abbate ae1890d29a Fix project files reading with symlink 2022-01-12 00:32:10 +01:00
Guldoman 51975472a9
Add bit32 polyfill globally 2022-01-12 00:07:53 +01:00
Guldoman 7eb9908f1a
Improve bit32 polyfill 2022-01-12 00:07:14 +01:00
Francesco Abbate 4cdd42de1a Ensure config.plugins are restored on new config
When a user's or project's module configuration file is changed we
make sure that the config.plugins fields are all restored so that
all plugins already loaded can continue to work.
2022-01-10 09:54:47 +01:00
Francesco Abbate 656a89c494 Fix checks when opening new project directory 2022-01-09 23:26:11 +01:00
Francesco Abbate 827f3f876d Remove remaining debug code fragment 2022-01-09 23:26:11 +01:00
Francesco Abbate 648b977c1e Use a timeout in dmon thread with pending events 2022-01-09 23:26:11 +01:00
Francesco Abbate 9be22f0b8d Attempt to fix dmon critical section for windows
Should fix commit bb12f085f3.

When taking the critical section we should always send the
event to wakeup the events thread. In addition use
TryEnterCriticalSection to send the event only if needed
reducing the number of spurious events sent.
2022-01-09 23:26:11 +01:00
Francesco Abbate 19ec86d971 Do not use timeout in dmon linux select
Wait indefinitely in select and wake-up the thread when needed.
2022-01-09 23:26:11 +01:00
Francesco Abbate 44a8dc320b Fix some errors with previous commits 2022-01-09 23:26:11 +01:00
Francesco Abbate 6584bdfd33 On Windows wait indefinitely in dmon thread
Avoid waiting with a finite timeout and wait indefinitely in
dmon thread. When we need to unwatch we send a signal to a special
event meant to wakeup the waiting thread.
2022-01-09 23:26:11 +01:00
Francesco Abbate f0aea5b1a4 Report error codes from dmon_watch_add 2022-01-09 23:26:11 +01:00
Francesco Abbate 39366d3a09 Ensure project rescan thread is terminated
When changing a project we need to ensure that the old threads
are no longer run.
2022-01-09 23:26:11 +01:00
Francesco Abbate 1520c12580 Remove DMON_LOG_ERROR to return an error code 2022-01-09 23:26:11 +01:00
Francesco Abbate 7473fbf32c Fix undue asserts in dmon_extra
Some asserts are placed in case that can effectively occur
so we remove the assertion and we return false. In turn we
adapt the logic accordingly so when false is returned to add
a watch we do not open that directory.
2022-01-09 23:26:11 +01:00
Francesco Abbate 5032e7352e Write an initial project module if not present 2022-01-09 23:26:11 +01:00
Francesco Abbate a703840068 Add some comments for ignore_files logic 2022-01-09 23:26:11 +01:00
Francesco Abbate 295c65da92 Use compiled ignore_files pattern
Try to digest the ignore_files pattern before potentially processing
a lot of files because it may be expensive.
2022-01-09 23:26:11 +01:00
Francesco Abbate 5b154c189f First version of paths in ignore_files
Works correctly and the logic seems sound even if somewhat quirky.

`^%.` match any file of directory whose basename begins with a dot.

`^/node_modules$/"` match a directory named `node_modules` at the project's root.

  Note that the final '/' needs to be at the end. The '/' after the '^' needs to be there to trigger
  a match of the full path filename so we are sure it is at the root.

  PROBLEM: the '/' to trigger full path match could be in a pattern's special expression like:
  [^/]

`^%.git$/` match any directory name '.git' anywhere in the project.

`^/%.git$/` match a directory named '.git' only at the project's root.

`^/subprojects/.+/` match any directory in a top-level folder named "subprojects".

`^/build/` match any top level directory whose name begins with "build"

  PROBLEM: may be surprising, one may expects it matches only a directory named 'build'. It actually acts like
  it was `^/build.*/`.
2022-01-09 23:20:47 +01:00
Adam Harrison 31d448971a Restored floating point time granularity. 2022-01-08 12:59:15 -05:00
Adam 93076bdc41
Merge pull request #781 from Jan200101/PR/lua54
Migrate to Lua 5.4
2022-01-08 12:11:48 -05:00
takase1121 fc809b3172
comment the entire line when using block comment 2022-01-08 18:45:38 +08:00
Francesco Abbate 143b389365 Clear TreeView cache when closing project 2022-01-07 10:41:18 +01:00
takase1121 087314aea4
fix lhelper script 2022-01-07 17:31:24 +08:00
Francesco Abbate 7ded5c8199 Fix problem when reloading project directory 2022-01-06 18:00:48 +01:00
Francesco Abbate 1e7075ca9f Do not force choosing project dir to suggestion
When changing or opening a project directory do not
take the selected item from suggestion but simply the
entered text as it is.

Otherwise the user may be unable to choose a directory
if the text matches the beginning of suggestion.

Close #791
2022-01-05 23:42:47 +01:00
Francesco Abbate 1b57107352 Fix problem with special file types
For special file types like the ones in /dev/ the info
entry's type is neither file neither dir.

We prevent these kind of files from being listed in the
project.
2022-01-05 23:32:26 +01:00
Francesco Abbate 9929ca9c2d Fix logic with project directories suggestions
Attempt to fix issue #791.

The logic set with the previous commit for suggest_directory
is similar to the one we use except the previous expression
was false do to operator precedence for "and" versus "or".

With the modification here, when opening a project directory,
we suggest the recently used projects
if the text is equal to dirname(project_dir) + "/" which
happens to be the text the command view is initially set to.
In addition we do the same if text is "". If the condition is
not met we return the suggestions from common.dir_path_suggest to
match the text entered.

Works well on Linux but may not solve the problem on Windows, it
should be tested.
2022-01-05 23:21:47 +01:00
Francesco Abbate f3cf7ac9c7 Do not reload core.keymap module
Avoid reloading the core.keymap module when user's config
or project module change.

The reason is the plugins like autocomplete can add keymaps
and the additions from plugins would be lost.

Close issue #793
2022-01-04 18:06:30 +01:00
Francesco Abbate bf578d5ee4 Fix logic for file create event
When we get a file or directory creation event we need to
ensure that all the parent directories pass the ignore_files
test.
2022-01-03 18:55:01 +01:00
takase1121 df0f6fb94c
make set_selections consistent 2022-01-02 19:18:08 +08:00
takase1121 e079ddfa37
refactor toggle-block-comments, make command spaces aware, set selections correctly 2022-01-02 19:14:03 +08:00
Adam 067e7cc6cd
Merge pull request #784 from Jan200101/PR/label-update
add Libraries label
2022-01-01 12:18:41 -05:00
Jan200101 73a867fc89
add Libraries label 2021-12-31 13:53:31 +01:00
Jan200101 99ddf1fb92
Migrate to Lua 5.4 2021-12-31 13:53:01 +01:00
Adam 186248911a
Merge pull request #782 from Nightwing13/patch-2
[Fix] Pointer Bug in ToolBar plugin
2021-12-30 19:25:03 -05:00
Francesco Abbate 445c79bb52 Revert "No longer store autocomplete options in config"
This reverts commit 0f1b84040d.

The new mechanism to save config.plugins upon user's configuration
reload let us stay compatible with existing plugins.
2021-12-31 00:22:49 +01:00
Francesco Abbate 03350cc14b Restore config.plugins when reloading config
Some plugins store options in:

config.plugins.<plugin-name>

so we restore all the kay-values of config.plugins when
reloading the user preferences.
2021-12-31 00:20:52 +01:00
Francesco Abbate 85d26adb62 Fix project's module loading when changing project
Fix a bag of subtle problem about when loading the
project module.

We need to load the project's module before to scan
the project directory.
2021-12-30 23:57:23 +01:00
Francesco Abbate 68aea88510 Fix error with ignore_files
There was a double error because the config.ignore_files was
used at two differect places in different ways.

Now we apply coherently the original rule to apply
config.ignore_files to the basename of each file or directory.
2021-12-30 22:24:43 +01:00
Francesco Abbate 9578359b2b Remove inotify recursive directory monitoring
We no longer use in Lite XL recursive directory monitoring as
it was a source of problems.

To enforce this at compile time we use the preprocessor to
remove the implementation of recursive monitoring for the Linux
implementation only.
2021-12-30 15:45:09 +01:00
Francesco Abbate fd074ff39a Fix problem when opening project's module document
It wasn't fine to call core.open_doc without filename argument
and later call Doc:save without providing both the filename and
the absolute filename. It was giving a Doc in an inconsistent
status where self.filename was set but not self.abs_filename.
Added an asset to detect early the problem if ever happens again.

In turn the problem prevented the project's module hook to work if the
file was newly created.
2021-12-30 15:26:40 +01:00
Francesco Abbate adaf023541 Always watch/unwatch subdirectories on all systems
Simplifies and uniformize the logic on the Lua side for the
setting of directories' watches. Now we always use the methods:

systems.watch_dir_add / rm

on all the project's directories at any depth when we are not
in files limit mode.

In files limited mode the functions systems.watch_dir_add/rm are
called only on the expanded folders. The shown_subdir table is also
updated only in files limited mode.

On the C side, using the dmon library, we remove the recursive argument
from the system.watch_dir and we always call it recursively except on
Linux. At the same time the functions:

systems.watch_dir_add / rm

are provided but as dummy functions that does nothing except on Linux
where they work as before to add / remove sub-directories in the inotify
watch.

In this was on the Lua side we always act we if the watches needed to be
set for each sub-directory explicitly, independently of the system.

The important improvement introduced is that we always avoid calling
dmon_watch recursively on Linux. This latter thing is problematic with
inotify and is therefore avoided on Linux.

On the other side we simplifies the logic on the Lua side and remove
conditions based on the OS used.
2021-12-30 15:25:27 +01:00
Nightwing 60322a93a8
Update toolbarview.lua 2021-12-30 11:12:24 +09:00
Nightwing 46aaf57b45
[Fix] Pointer Bug in ToolBar plugin
Fixes an issue where the pointer moves down when "open user module" button is spammed
2021-12-30 06:26:58 +09:00
Adam 1552f18a87
Merge pull request #753 from Jipok/highlight_selection
Add for config.highlight_current_line new variant: no_selection
2021-12-29 14:43:17 -05:00
Adam 416a06c566
Merge pull request #765 from Guldoman/treeview_remove_deleted
Better "Remove changed files/dirs from `TreeView` cache"
2021-12-29 12:41:36 -05:00
Adam 3696937fec
Merge pull request #769 from takase1121/font_gc
fix FontGroup __gc method
2021-12-29 12:18:28 -05:00
Adam d2e02bbed3
Merge pull request #778 from eli-schwartz/meson
Meson: retain compatibility for very old versions
2021-12-29 12:13:59 -05:00
Francesco Abbate 88ed312f6b Fix NagView missing mouse events 2021-12-29 16:00:53 +01:00
Eli Schwartz 7a961c8c8e
meson: lower the minimum buildsystem requirements even more
Only a couple trivial features from meson ~0.50 were being used, and
none of them are really needed:

- configure_file() with the install kwarg has always defaulted to
  inferring its value from whether an install_dir was defined. This is
  fine, we don't need to set `install: true` in that case. The kwarg was
  only even added to meson 0.50 for consistency and to allow
  conditionally overriding the file to not install, even when
  install_dir is set. This project does not need that feature.

- path building could historically be done with the join_paths()
  function. Recent versions of meson (0.49) added cosmetic sugar in the
  form of string operator overloading to allow using the division
  operator on two strings. By removing this and using the backwards
  compatible form, we can support older versions of meson.

- sdl2 dependency lookup with hardcoded config-tool method is very
  opinionated about the correct way to look up sdl2, but meson can try
  multiple methods if you permit it, and there is no reason to think
  that config-tool is the only one that returns correct results.

By removing these features, the minimum can be dropped all the way down
to a version that is available on the oldest supported versions of
Ubuntu (18.04), Debian (oldoldstable / Stretch) and anywhere else of
consequence.
2021-12-28 17:50:10 -05:00
Eli Schwartz fcb3c41082
meson: lower the minimum buildsystem requirements
No features of 0.54 are being used, so 0.50 should be perfectly fine.

This drops the minimum requirement down to a version available in the
latest Ubuntu LTS (20.04), which only has 0.53
2021-12-28 17:19:16 -05:00
Francesco Abbate 8550049db8 Draw NagView in overlay mode
The NagView takes some actual space in the Y and when it appears
it cause the documents' content to be displaced.

The movement of the documents' content is annoying and should be
avoided so we draw the NagView entirely in overlay mode using defer
draw and we always keep its y size to zero to don't affect the
other application contents.
2021-12-28 16:43:27 +01:00
Francesco Abbate 2cf3c6f747 Ensure project reload when changing project module
Changes in project's module required an application restart to work.

Now the project will be re-scanned when the project's module changes.

In addition ensure borderless window config is changed when changed
in user's preferences.
2021-12-28 14:36:21 +01:00
Francesco Abbate 05b003eeb5 Avoid references to project's dir in TreeView
It is not a good practice to keep a reference to the project's
directory object outside of the "core" module itself.

The TreeView was using such a reference in the cache item for each
file or directory entry. Replace the reference to the object with
the absolute name of the project directory.
2021-12-28 14:36:21 +01:00
Francesco Abbate 0f1b84040d No longer store autocomplete options in config
Plugins should not store private stuff in core.config because this
latter can be reloaded due to user changing preferences.
2021-12-28 12:25:48 +01:00
Francesco Abbate 1f0785b73f Scan project folder after project module is loaded
Otherwise the initial scan of the project folder is done without
taking into account the config.ignore_files directives.
2021-12-28 10:59:01 +01:00
Adam 1e6046e499
Merge pull request #763 from Jipok/portable_try
Support portable user config(Fix #762)
2021-12-26 16:59:13 -05:00
takase1121 33f7fe4fda
toggle comment for whole line if nothing is selected 2021-12-26 15:12:28 +08:00
takase1121 69a857bbbf
fallback to toggle-line-comment and vice versa if needed 2021-12-26 15:05:27 +08:00
takase1121 4d31b1bc40
add toggle-block-comment 2021-12-25 12:57:00 +08:00
takase1121 16df6d8bce
add option for initial size 2021-12-24 21:32:28 +08:00
takase1121 84a3906323
fix FontGroup __gc method 2021-12-24 15:04:52 +08:00
Adam 4be8a8b582
Merge pull request #764 from lorsatti/master
Add CFBundleIdentifier to Info.plist.in
2021-12-23 19:55:41 -05:00
Guldoman d16e46dba5
Update website location 2021-12-23 23:26:52 +01:00
Guldoman a122d7e916
Correct `get_key_name` comment 2021-12-23 00:06:24 +01:00
Guldoman eac82e69fb
Add parameters to `core.on_dirmonitor_{modify,delete}` 2021-12-22 23:43:56 +01:00
Francesco Abbate 9155be7a22
Ensure TreeView cache entry is removed on delete
Address issue:

https://github.com/lite-xl/lite-xl/issues/689

Attempt to provide a more accurate fix to commit:

59f64088e1

For this latter what happens is that any change inside a directory
cause the corresponding entry to be folded in the TreeView.

The new change is more accurate because we remove only the stale
entry corresponding to the delete event and we do not reset the
cache of the parent directory using the modify event.
2021-12-22 23:40:54 +01:00
Guldoman 9e7bdf49e9
Revert "Merge pull request #697 from Guldoman/treeview_remove_changed"
This reverts commit 4e078cc217, reversing
changes made to 0c488c9492.
2021-12-22 23:39:26 +01:00
Lorenzo Orsatti 66bc551488
Add CFBundleIdentifier to Info.plist.in
The CFBundleIdentifier key is necessary to associate lite-xl application to all files with a certain extension.
2021-12-22 22:18:22 +01:00
Jipok a19baeacb1 Support portable user config(Fix #762) 2021-12-22 23:36:03 +05:00
takase1121 00e2e281d3
remove unsaved flag from log.txt 2021-12-22 10:54:25 +08:00
takase1121 2f65d5a26f
make the date field consistent 2021-12-22 10:53:53 +08:00
takase1121 8f06ef9b81
ensure date is rendered properly 2021-12-22 10:50:35 +08:00
Adam 590c8bb456
Merge pull request #759 from Jipok/again
Support for remaped special keys(Fix #757)
2021-12-21 19:56:33 -05:00
Adam e0d0d17c4d
Merge pull request #760 from adamharrison/fix-multiline-paste-system-clipboard
Restore External Paste to Non-Multiline Paste
2021-12-21 17:15:03 -05:00
Adam Harrison 978550d2a2 Restores external pastes to be normal pastes. 2021-12-21 16:23:34 -05:00
Jipok 773a85cd2d Support for remaped special keys(Fix #757) 2021-12-22 02:22:34 +05:00
Adam 61379a9ab8
Merge pull request #713 from Jipok/master
Copy/cut whole line if selection empty
2021-12-21 15:53:12 -05:00
takase1121 54f6579e9d
change INFO to use style.text 2021-12-21 17:38:25 +08:00
takase1121 3e175f5ad5
Merge branch 'master' of github.com:lite-xl/lite-xl into better-logview 2021-12-21 17:37:19 +08:00
takase1121 8d7867d118
adapt style.good and style.error 2021-12-21 17:36:32 +08:00
Jipok 6c1c983d1c rootview.lua: Refactor Node:draw_tab 2021-12-21 02:20:31 +05:00
Francesco Abbate 50247fcd92 Move to 2.0.4 version number 2021-12-20 16:19:15 +01:00
Francesco Abbate 3109263c5d Call dmon_unwatch when changing project
Fix a conspicuous omission to call the dmon_unwatch function
when changing project directory.

This uncovered a bug or a quirk of the dmon library where the watch_ids
can change as a result of calling dmon_unwatch because they are just
indexes on a contiguous array. Use a workaround to always unwatch the
first valid watch_id N times.
2021-12-20 14:42:48 +01:00
Jipok c353dd6eda Add for config.highlight_current_line new variant: no_selection 2021-12-20 16:23:01 +05:00
Guldoman 29318be9c7 Consume unmatched character correctly
We must consume the whole UTF-8 character, not just a single byte.
2021-12-20 12:04:20 +01:00
Francesco Abbate 37c00c877a Ensure TreeView cache entry is removed on delete
Address issue:

https://github.com/lite-xl/lite-xl/issues/689

Attempt to provide a more accurate fix to commit:

59f64088e1

For this latter what happens is that any change inside a directory
cause the corresponding entry to be folded in the TreeView.

The new change is more accurate because we remove only the stale
entry corresponding to the delete event and we do not reset the
cache of the parent directory using the modify event.
2021-12-20 11:03:49 +01:00
Francesco Abbate 405bd1c2bd Fix logic in project's file insertion
The function "file_search" in core.init was sometimes giving a wrong index
value, off by one.

The problem happened for example when the entry to search was "less than"
the first entry, the function returned a value of two instead of one as
expected.

The bug was easily observed creating a new directory with a name that comes
as the first in alphabetical order within the project.
2021-12-20 09:05:45 +01:00
Adam Harrison e512c57637 Added an exclusion for lineguide in the commandview. 2021-12-20 08:43:48 +01:00
Guldoman 23f83857c5 Don't search if there are no files 2021-12-20 08:40:43 +01:00
takase1121 695c7bf781
add instruction when logview is open 2021-12-19 09:36:43 +08:00
takase1121 1526cd176c
move selection logic to mouse click 2021-12-19 09:36:24 +08:00
takase1121 becdb99222
center icons to accomodate for size difference 2021-12-18 22:42:33 +08:00
takase1121 31df408d93
make timestamp fix sized 2021-12-18 20:11:50 +08:00
takase1121 fd3b4334ce
add clipping to drawing log items 2021-12-18 20:11:24 +08:00
takase1121 b5dff196f6
make error icon red 2021-12-18 20:10:25 +08:00
takase1121 ab4ecd515b
multiple improvements to logging
- added style.log table
- removed contextmenu
- use ctrl+click to copy individual log entries
- use icon instead of + or - for log items in logview
2021-12-18 10:51:44 +08:00
Jipok 7381a13d6f Revert "Make pasting multiple lines from clipboard same way as a single line"
This reverts commit 6a135f7c06.
2021-12-12 02:23:47 +05:00
Jipok 6a135f7c06 Make pasting multiple lines from clipboard same way as a single line 2021-12-10 19:25:28 +05:00
Jipok 4a563ddea1 Delete old forgotten self.cursor_clipboard 2021-12-10 19:23:49 +05:00
Jipok 4eee123eff Make cursor_clipboard globa, not per doc 2021-12-08 17:34:10 +05:00
Jipok acc7ceefd4 Correct paste after 'Cut/copy whole line' 2021-12-06 17:59:49 +05:00
Jipok 93d9e61a03 Copy/cut whole line if selection empty 2021-12-05 20:30:03 +05:00
101 changed files with 5165 additions and 3303 deletions

3
.github/labeler.yml vendored
View File

@ -33,3 +33,6 @@
"Category: C Core":
- src/**/*
"Category: Libraries":
- lib/**/*

8
.gitignore vendored
View File

@ -2,8 +2,7 @@ build*/
.build*/
lhelper/
submodules/
subprojects/lua/
subprojects/reproc/
subprojects/*/
/appimage*
.ccls-cache
.lite-debug.log
@ -18,3 +17,8 @@ compile_commands.json
error.txt
lite-xl*
LiteXL*
lite
.config/
*.lha
release_files
*.o

85
Makefile.os4 Normal file
View File

@ -0,0 +1,85 @@
#
# Project: Lite XL
#
# Created on: 26-12-2021
#
LiteXL_OBJ := \
src/main.o src/rencache.o src/renderer.o src/renwindow.o \
src/api/dirmonitor/os4.o \
src/api/api.o src/api/dirmonitor.o src/api/regex.o \
src/api/renderer.o src/api/system.o \
src/platform/amigaos4.o
# src/api/process.o
outfile := lite
compiler := gcc
cxxcompiler := g++
INCPATH := -Isrc -Ilib/dmon -I/sdk/local/newlib/include/SDL2 -I/sdk/local/common/include/freetype2
DFLAGS := -D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR -DDIRMONITOR_BACKEND=os4 -DDIRMONITOR_OS4
# -DLITE_USE_SDL_RENDERER
# -Wextra -Wall
CFLAGS := -Werror -Wwrite-strings -O3 -g -std=gnu11 -fno-strict-aliasing
# "-gstabs -finstrument-functions -fno-inline -DPROFILING"
LFLAGS := -mcrt=newlib -static-libgcc -static-libstdc++ -lauto -lpcre2 -lSDL2 -llua -lagg -lfreetype -lm -lunix -lpthread -athread=native
# " -lprofyle"
.PHONY: LiteXL clean release
default: LiteXL
clean:
@echo "Cleaning compiler objects..."
@rm -f $(LiteXL_OBJ)
LiteXL: $(LiteXL_OBJ)
@echo "Linking LiteXL"
@$(cxxcompiler) -o $(outfile) $(LiteXL_OBJ) $(LFLAGS)
.c.o:
@echo "Compiling $<"
@$(compiler) -c $< -o $*.o $(CFLAGS) $(INCPATH) $(DFLAGS)
src/main.o: src/main.c src/api/api.h src/rencache.h \
src/renderer.h src/platform/amigaos4.h
src/rencache.o: src/rencache.c
src/renderer.o: src/renderer.c
src/renwindow.o: src/renwindow.c
src/api/dirmonitor/os4.o: src/api/dirmonitor/os4.c
src/api/api.o: src/api/api.c
src/api/dirmonitor.o: src/api/dirmonitor.c src/api/dirmonitor/os4.c
src/api/regex.o: src/api/regex.c
src/api/renderer.o: src/api/renderer.c
src/api/system.o: src/api/system.c
src/platform/amigaos4.o: src/platform/amigaos4.c
release:
mkdir -p release/LiteXL
cp release_files/* release/LiteXL/ -r
mv release/LiteXL/LiteXL.info release/
cp data release/LiteXL/ -r
cp changelog.md release/LiteXL/
cp lite release/LiteXL/
strip release/LiteXL/lite
cp README.md release/LiteXL/
cp README_OS4.md release/LiteXL/
cp LICENSE release/LiteXL/
lha -aeqr3 a LiteXL.lha release/

View File

@ -154,6 +154,7 @@ See the [licenses] file for details on licenses used by the required dependencie
[Get color themes]: https://github.com/lite-xl/lite-xl-colors
[changelog]: https://github.com/lite-xl/lite-xl/blob/master/changelog.md
[Lite XL plugins repository]: https://github.com/lite-xl/lite-xl-plugins
[plugins repository]: https://github.com/rxi/lite-plugins
[colors repository]: https://github.com/lite-xl/lite-xl-colors
[LICENSE]: LICENSE
[licenses]: licenses/licenses.md

169
README_OS4.md Normal file
View File

@ -0,0 +1,169 @@
# Lite XL v2.1 for AmigaOS 4.1 FE
Lite XL is a lightweight text editor written in Lua.
## Installation
You can extract the Lite XL archive wherever you want and run the *lite*
editor.
## Configuration folder
This editor creates a `.config` folder where the configuration is saved, as
well as plugins, themes etc.. By default this AmigaOS 4.1 FE version uses the
executable folder, but if you want to ovveride it, create an ENV variable
named `HOME` and set there your path.
You can check if there is one already set by executing the following command
in a shell
```
GetEnv HOME
```
If there is one set, then you will see the path at the output.
Otherwise, you can set your home path be executing the following command.
Change the path to the one of your preference.
```
SetEnv SAVE HOME "Sys:home/"
```
## Addons
### Colors
Colors are lua files that set the color scheme of the editor. There are
light and dark themes for you to choose.
To install and use them you have to copy the ones you would like from
`addons/colors/light` or `addons/colors/dark` into the folder
`.config/lite-xl/colors/`. Don't add light or dark folders. Just copy the
.lua files in there.
Then you have to start Lite XL and open your configuration by clicking
at the cog icon at the toolbar (bottom left sixth icon). Go at the line
that looks like below
```
-- core.reload_module("colors.summer")
```
and change the `summer` with the name of your color theme. Also, remove
the two dashes `--` at the start of the line and save the file. If you
did everything right, the color schema should change instantly.
The themes can also be found at
https://github.com/lite-xl/lite-xl-colors
### Plugins
The Lite XL that you are using on AmigaOS 4 is based on version 1.16.12
and not the latest version that is available by the development team.
This means that the latest plugins are not working at all or need some
modifications to work.
To make it easier for you, I gathered some of the plugins that are working
well, and I included them at the `addons/plugins`. For you to install the
ones you would like to use, you have to copy the `.lua` files into the
folder `.config/lite-xl/plugins/` and restart the editor.
The included plugins are the following:
**autoinsert**
Automatically inserts closing brackets and quotes. Also allows selected
text to be wrapped with brackets or quotes.
**autowrap**
Automatically hardwraps lines when typing
**bigclock**
Shows the current time and date in a view with large text
**bracketmatch**
Underlines matching pair for bracket under the caret
**colorpreview**
Underlays color values (eg. `#ff00ff` or `rgb(255, 0, 255)`) with their
resultant color.
**eofnewline-xl**
Make sure the file ends with one blank line.
**ephemeral_tabs**
Preview tabs. Opening a doc will replace the contents of the preview tab.
Marks tabs as non-preview on any change or tab double clicking.
**ghmarkdown**
Opens a preview of the current markdown file in a browser window
**indentguide**
Adds indent guides
**language_make**
Syntax for the Make build system language
**language_sh**
Syntax for shell scripting language
**lfautoinsert**
Automatically inserts indentation and closing bracket/text after newline
**markers**
Add markers to docs and jump between them quickly
**navigate**
Allows moving back and forward between document positions, reducing the
amount of scrolling
**rainbowparen**
Show nesting of parentheses with rainbow colours
**restoretabs**
Keep a list of recently closed tabs, and restore the tab in order on
cntrl+shift+t.
**selectionhighlight**
Highlights regions of code that match the current selection
## Tips and tricks
### Transitions
If you want to disable the transitions and make the scrolling a little faster,
open your configuration by clicking at the cog icon at the toolbar
(bottom left sixth icon) and add the followline at the end of the file and
save it.
```
config.transitions = false
```
### Hide files from the file list
If you would like to hide files or whole folder from the left side bar list,
open your configuration by clicking at the cog icon at the toolbar
(bottom left sixth icon) and add the followline at the end of the file and
save it. This hides all the files that start with a dot, and all the `.info`
files.
```
config.ignore_files = {"^%.", "%.info$"}
```
You can add as many rules as you want in there, to hide fore files or
folders, as you like.
## Know issues
You can find the known issues at
https://git.walkero.gr/walkero/lite-xl/issues
# Changelog
## [2.1r1] - 2022-03-27
### Changed
- Synced with master-v2.1 tag of lite-xl official github page
- Applied all the necessary changes to make it run under AmigaOS 4.1 FE
## [2.0.5r1] - 2022-03-27
### Changed
- Synced with v2.0.5 tag of lite-xl official github page
- Applied all the necessary changes to make it run under AmigaOS 4.1 FE
## [2.0.3.1] - 2022-01-18
### Changed
- Applied all the necessary changes to make it run under AmigaOS 4.1 FE

BIN
README_OS4.md.info Normal file

Binary file not shown.

View File

@ -1,5 +1,123 @@
This files document the changes done in Lite XL for each release.
### 2.1
Upgraded Lua to 5.4, which should improve performance, and provide useful extra functionality.
It should also be more available out of the box with most modern linux/unix-based package
managers.
Removed `dmon`, and implemented independent backends for dirmonitoring. Also more cleanly
split out dirmonitoring into its own class in lua, from core.init. We should now support
FreeBSD; and any other system that uses `kqueue` as their dirmonitoring library. We also
have a dummy-backend, which reverts transparnetly to scanning if there is some issue with
applying OS-level watches (such as system limits).
Removed `libagg` and the font renderer; compacted all font rendering into a single renderer.c
file which uses `libfreetype` directly. Now allows for ad-hoc bolding, italics, and underlining
of fonts.
Removed `reproc` and replaced this with a simple POSIX/Windows implementation in `process.c`.
This allows for greater tweakability (i.e. we can now `break` for debugging purposes),
performance (startup time of subprocesses is noticeably shorter), and simplicity
(we no longer have to link reproc, or winsock, on windows).
Split out `Node` and `EmptyView` into their own lua files, for plugin extensibility reasons.
Revamped StatusView API, so that plugins can more easily coexist with each other.
Removed `cp_replace`, and replaced this with a core plugin, `drawwhitespace.lua`.
Made distinction between line and block comments, and added all appropriate functionality
to the commenting/uncommenting lines.
Added in line paste mode, if you copy without a selection.
May improvements to treeview, including keyboard navigation of treeview, and ability to
specify single vs. double-click behavior.
Added in soft line wrapping as core plugin, under `linewrapping.lua`, with an
F10 to activate.
Bumped plugin mod-version number, as the rendering interface for docviews has changed.
Added in meson wraps for freetype, pcre2, and SDL2 which target public, rather than
lite-xl maintained repos.
Added in the ability to set up font fallback groups in the font renderer, if a token
doesn't have a corresponding glyph.
Added in a native plugin interface that allows for C-level interfacing with a
statically-linked lite-xl. The implementation of this may change in future.
Improved fuzzy_matching to probably give you something closer to what you're
looking for.
Improved handling of alternate keyboard layouts.
Improved ability for plugins to be loaded at a given time, by making the convention
of defining a config for the plugin use `common.merge` to merge existing hashes
together, rather than overwriting.
Added in the ability to specify mouseclicks in the keymap, allowing for easy binds of
`ctrl+lclick`, and the like.
Changed interface for keyhandling; now, all components should return true if they've
handled the event.
Added in a default keymap for `core:restart`, `ctrl+shift+r`.
Many, many, many more changes that are too numerous to list.
### 2.0.5
Revamp the project's user module so that modifications are immediately applied.
Add a mechanism to ignore files or directory based on their project's path.
The new mechanism is backward compatible.*
Essentially there are two mechanisms:
- if a '/' or a '/$' appear at the end of the pattern it will match only directories
- if a '/' appears anywhere in the pattern except at the end the pattern will be
applied to the path
In the first case, when the pattern corresponds to a directory, a '/' will be
appended to the name of each directory before checking the pattern.
In the second case, when the pattern corresponds to a path, the complete path of
the file or directory will be used with an initial '/' added to the path.
Fix several problems with the directory monitoring library.
Now the application should no longer assert when some related system call fails
and we fallback to rescan when an error happens.
On linux no longer use the recursive monitoring which was a source of problem.
Directory monitoring is now aware of symlinks and treat them appropriately.
Fix problem when encountering special files type on linux.
Improve directory monitoring so that the related thread actually waits without using
any CPU time when there are no events.
Improve the suggestion when changing project folder or opening a new one.
Now the previously used directory are suggested but if the path is changed the
actual existing directories that match the pattern are suggested.
In addition always use the text entered in the command view even if a suggested entry
is highlighted.
The NagView warning window now no longer moves the document content.
### 2.0.4
Fix some bugs related to newly introduced directory monitoring using the dmon library.
Fix a problem with plain text search using Lua patterns by error.
Fix a problem with visualization of UTF-8 characters that caused garbage characters
visualization.
Other fixes and improvements contributed by @Guldoman.
### 2.0.3
Replace periodic rescan of project folder with a notification based system using the

36
data/core/bit.lua Normal file
View File

@ -0,0 +1,36 @@
local bit = {}
local LUA_NBITS = 32
local ALLONES = (~(((~0) << (LUA_NBITS - 1)) << 1))
local function trim(x)
return (x & ALLONES)
end
local function mask(n)
return (~((ALLONES << 1) << ((n) - 1)))
end
local function check_args(field, width)
assert(field >= 0, "field cannot be negative")
assert(width > 0, "width must be positive")
assert(field + width < LUA_NBITS and field + width >= 0,
"trying to access non-existent bits")
end
function bit.extract(n, field, width)
local w = width or 1
check_args(field, w)
local m = trim(n)
return m >> field & mask(w)
end
function bit.replace(n, v, field, width)
local w = width or 1
check_args(field, w)
local m = trim(n)
local x = v & mask(width);
return m & ~(mask(w) << field) | (x << field)
end
return bit

View File

@ -64,7 +64,7 @@ end
function command.add_defaults()
local reg = {
"core", "root", "command", "doc", "findreplace",
"files", "drawwhitespace", "dialog"
"files", "drawwhitespace", "dialog", "log", "statusbar"
}
for _, name in ipairs(reg) do
require("core.commands." .. name)

View File

@ -10,8 +10,18 @@ local restore_title_view = false
local function suggest_directory(text)
text = common.home_expand(text)
return common.home_encode_list((text == "" or text == common.home_expand(common.dirname(core.project_dir)))
and core.recent_projects or common.dir_path_suggest(text))
local basedir = common.dirname(core.project_dir)
return common.home_encode_list((basedir and text == basedir .. PATHSEP or text == "") and
core.recent_projects or common.dir_path_suggest(text))
end
local function check_directory_path(path)
local abs_path = system.absolute_path(path)
local info = abs_path and system.get_file_info(abs_path)
if not info or info.type ~= 'dir' then
return nil
end
return abs_path
end
command.add(nil, {
@ -93,6 +103,15 @@ command.add(nil, {
core.root_view:open_doc(core.open_doc())
end,
["core:new-named-doc"] = function()
core.command_view:enter(
"File name",
function(text)
core.root_view:open_doc(core.open_doc(text))
end
)
end,
["core:open-file"] = function()
local view = core.active_view
if view.doc and view.doc.abs_filename then
@ -141,46 +160,50 @@ command.add(nil, {
end,
["core:open-project-module"] = function()
local filename = ".lite_project.lua"
if system.get_file_info(filename) then
core.root_view:open_doc(core.open_doc(filename))
else
local doc = core.open_doc()
core.root_view:open_doc(doc)
doc:save(filename)
if not system.get_file_info(".lite_project.lua") then
core.try(core.write_init_project_module, ".lite_project.lua")
end
local doc = core.open_doc(".lite_project.lua")
core.root_view:open_doc(doc)
doc:save()
end,
["core:change-project-folder"] = function()
local dirname = common.dirname(core.project_dir)
if dirname then
core.command_view:set_text(common.home_encode(dirname))
core.command_view:set_text(common.home_encode(dirname) .. PATHSEP)
end
core.command_view:enter("Change Project Folder", function(text, item)
text = system.absolute_path(common.home_expand(item and item.text or text))
if text == core.project_dir then return end
local path_stat = system.get_file_info(text)
if not path_stat or path_stat.type ~= 'dir' then
core.error("Cannot open folder %q", text)
core.command_view:enter("Change Project Folder", function(text)
local path = common.home_expand(text)
local abs_path = check_directory_path(path)
if not abs_path then
core.error("Cannot open directory %q", path)
return
end
core.confirm_close_docs(core.docs, core.open_folder_project, text)
if abs_path == core.project_dir then return end
core.confirm_close_docs(core.docs, function(dirpath)
core.open_folder_project(dirpath)
end, abs_path)
end, suggest_directory)
end,
["core:open-project-folder"] = function()
local dirname = common.dirname(core.project_dir)
if dirname then
core.command_view:set_text(common.home_encode(dirname))
core.command_view:set_text(common.home_encode(dirname) .. PATHSEP)
end
core.command_view:enter("Open Project", function(text, item)
text = common.home_expand(item and item.text or text)
local path_stat = system.get_file_info(text)
if not path_stat or path_stat.type ~= 'dir' then
core.error("Cannot open folder %q", text)
core.command_view:enter("Open Project", function(text)
local path = common.home_expand(text)
local abs_path = check_directory_path(path)
if not abs_path then
core.error("Cannot open directory %q", path)
return
end
system.exec(string.format("%q %q", EXEFILE, text))
if abs_path == core.project_dir then
core.error("Directory %q is currently opened", abs_path)
return
end
system.exec(string.format("%q %q", EXEFILE, abs_path))
end, suggest_directory)
end,

View File

@ -47,19 +47,32 @@ end
local function cut_or_copy(delete)
local full_text = ""
local text = ""
core.cursor_clipboard = {}
core.cursor_clipboard_whole_line = {}
for idx, line1, col1, line2, col2 in doc():get_selections() do
if line1 ~= line2 or col1 ~= col2 then
local text = doc():get_text(line1, col1, line2, col2)
text = doc():get_text(line1, col1, line2, col2)
full_text = full_text == "" and text or (full_text .. " " .. text)
core.cursor_clipboard_whole_line[idx] = false
if delete then
doc():delete_to_cursor(idx, 0)
end
full_text = full_text == "" and text or (full_text .. "\n" .. text)
doc().cursor_clipboard[idx] = text
else
doc().cursor_clipboard[idx] = ""
else -- Cut/copy whole line
text = doc().lines[line1]
full_text = full_text == "" and text or (full_text .. text)
core.cursor_clipboard_whole_line[idx] = true
if delete then
if line1 < #doc().lines then
doc():remove(line1, 1, line1 + 1, 1)
else
doc():remove(line1 - 1, math.huge, line1, math.huge)
end
end
end
core.cursor_clipboard[idx] = text
end
doc().cursor_clipboard["full"] = full_text
core.cursor_clipboard["full"] = full_text
system.set_clipboard(full_text)
end
@ -84,7 +97,90 @@ local function set_cursor(x, y, snap_type)
core.blink_reset()
end
local selection_commands = {
local function line_comment(comment, line1, col1, line2, col2)
local start_comment = (type(comment) == 'table' and comment[1] or comment) .. " "
local end_comment = (type(comment) == 'table' and " " .. comment[2])
local uncomment = true
local start_offset = math.huge
for line = line1, line2 do
local text = doc().lines[line]
local s = text:find("%S")
if s then
local cs, ce = text:find(start_comment, s, true)
if cs ~= s then
uncomment = false
end
start_offset = math.min(start_offset, s)
end
end
local end_line = col2 == #doc().lines[line2]
for line = line1, line2 do
local text = doc().lines[line]
local s = text:find("%S")
if s and uncomment then
if end_comment and text:sub(#text - #end_comment, #text - 1) == end_comment then
doc():remove(line, #text - #end_comment, line, #text)
end
local cs, ce = text:find(start_comment, s, true)
if ce then
doc():remove(line, cs, line, ce + 1)
end
elseif s then
doc():insert(line, start_offset, start_comment)
if end_comment then
doc():insert(line, #doc().lines[line], " " .. comment[2])
end
end
end
col1 = col1 + (col1 > start_offset and #start_comment or 0) * (uncomment and -1 or 1)
col2 = col2 + (col2 > start_offset and #start_comment or 0) * (uncomment and -1 or 1)
if end_comment and end_line then
col2 = col2 + #end_comment * (uncomment and -1 or 1)
end
return line1, col1, line2, col2
end
local function block_comment(comment, line1, col1, line2, col2)
-- automatically skip spaces
local word_start = doc():get_text(line1, col1, line1, math.huge):find("%S")
local word_end = doc():get_text(line2, 1, line2, col2):find("%s*$")
col1 = col1 + (word_start and (word_start - 1) or 0)
col2 = word_end and word_end or col2
local block_start = doc():get_text(line1, col1, line1, col1 + #comment[1])
local block_end = doc():get_text(line2, col2 - #comment[2], line2, col2)
if block_start == comment[1] and block_end == comment[2] then
-- remove up to 1 whitespace after the comment
local start_len, stop_len = #comment[1], #comment[2]
if doc():get_text(line1, col1 + #comment[1], line1, col1 + #comment[1] + 1):find("%s$") then
start_len = start_len + 1
end
if doc():get_text(line2, col2 - #comment[2] - 1, line2, col2):find("^%s") then
stop_len = stop_len + 1
end
doc():remove(line1, col1, line1, col1 + start_len)
col2 = col2 - (line1 == line2 and start_len or 0)
doc():remove(line2, col2 - stop_len, line2, col2)
return line1, col1, line2, col2 - stop_len
else
doc():insert(line1, col1, comment[1] .. " ")
col2 = col2 + (line1 == line2 and (#comment[1] + 1) or 0)
doc():insert(line2, col2, " " .. comment[2])
return line1, col1, line2, col2 + #comment[2] + 1
end
end
local commands = {
["doc:select-none"] = function()
local line, col = doc():get_selection()
doc():set_selection(line, col)
end,
["doc:cut"] = function()
cut_or_copy(true)
end,
@ -93,13 +189,6 @@ local selection_commands = {
cut_or_copy(false)
end,
["doc:select-none"] = function()
local line, col = doc():get_selection()
doc():set_selection(line, col)
end
}
local commands = {
["doc:undo"] = function()
doc():undo()
end,
@ -111,12 +200,28 @@ local commands = {
["doc:paste"] = function()
local clipboard = system.get_clipboard()
-- If the clipboard has changed since our last look, use that instead
if doc().cursor_clipboard["full"] ~= clipboard then
doc().cursor_clipboard = {}
local external_paste = core.cursor_clipboard["full"] ~= clipboard
if external_paste then
core.cursor_clipboard = {}
core.cursor_clipboard_whole_line = {}
end
local value, whole_line
for idx, line1, col1, line2, col2 in doc():get_selections() do
local value = doc().cursor_clipboard[idx] or clipboard
doc():text_input(value:gsub("\r", ""), idx)
if #core.cursor_clipboard_whole_line == (#doc().selections/4) then
value = core.cursor_clipboard[idx]
whole_line = core.cursor_clipboard_whole_line[idx] == true
else
value = clipboard
whole_line = not external_paste and clipboard:find("\n") ~= nil
end
if whole_line then
doc():insert(line1, 1, value:gsub("\r", ""))
if col1 == 1 then
doc():move_to_cursor(idx, #value)
end
else
doc():text_input(value:gsub("\r", ""), idx)
end
end
end,
@ -171,6 +276,11 @@ local commands = {
["doc:select-all"] = function()
doc():set_selection(1, 1, math.huge, math.huge)
-- avoid triggering DocView:scroll_to_make_visible
dv().last_line1 = 1
dv().last_col1 = 1
dv().last_line2 = #doc().lines
dv().last_col2 = #doc().lines[#doc().lines]
end,
["doc:select-lines"] = function()
@ -263,34 +373,30 @@ local commands = {
end
end,
["doc:toggle-line-comments"] = function()
local comment = doc().syntax.comment
if not comment then return end
local indentation = doc():get_indent_string()
local comment_text = comment .. " "
for idx, line1, _, line2 in doc_multiline_selections(true) do
local uncomment = true
local start_offset = math.huge
for line = line1, line2 do
local text = doc().lines[line]
local s = text:find("%S")
local cs, ce = text:find(comment_text, s, true)
if s and cs ~= s then
uncomment = false
start_offset = math.min(start_offset, s)
end
["doc:toggle-block-comments"] = function()
local comment = doc().syntax.block_comment
if not comment then
if doc().syntax.comment then
command.perform "doc:toggle-line-comments"
end
for line = line1, line2 do
local text = doc().lines[line]
local s = text:find("%S")
if uncomment then
local cs, ce = text:find(comment_text, s, true)
if ce then
doc():remove(line, cs, line, ce + 1)
end
elseif s then
doc():insert(line, start_offset, comment_text)
end
return
end
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
-- if nothing is selected, toggle the whole line
if line1 == line2 and col1 == col2 then
col1 = 1
col2 = #doc().lines[line2]
end
doc():set_selections(idx, block_comment(comment, line1, col1, line2, col2))
end
end,
["doc:toggle-line-comments"] = function()
local comment = doc().syntax.comment or doc().syntax.block_comment
if comment then
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
doc():set_selections(idx, line_comment(comment, line1, col1, line2, col2))
end
end
end,
@ -388,34 +494,45 @@ local commands = {
end
for i,docview in ipairs(core.get_views_referencing_doc(doc())) do
local node = core.root_view.root_node:get_node_for_view(docview)
node:close_view(core.root_view, docview)
node:close_view(core.root_view.root_node, docview)
end
os.remove(filename)
core.log("Removed \"%s\"", filename)
end,
["doc:select-to-cursor"] = function(x, y, clicks)
["doc:select-to-cursor"] = function(x, y, clicks)
local line1, col1 = select(3, doc():get_selection())
local line2, col2 = dv():resolve_screen_position(x, y)
dv().mouse_selecting = { line1, col1, nil }
doc():set_selection(line2, col2, line1, col1)
end,
["doc:set-cursor"] = function(x, y)
set_cursor(x, y, "set")
set_cursor(x, y, "set")
end,
["doc:set-cursor-word"] = function(x, y)
set_cursor(x, y, "word")
end,
["doc:set-cursor-line"] = function(x, y, clicks)
set_cursor(x, y, "lines")
["doc:set-cursor-word"] = function(x, y)
set_cursor(x, y, "word")
end,
["doc:split-cursor"] = function(x, y, clicks)
["doc:set-cursor-line"] = function(x, y, clicks)
set_cursor(x, y, "lines")
end,
["doc:split-cursor"] = function(x, y, clicks)
local line, col = dv():resolve_screen_position(x, y)
doc():add_selection(line, col, line, col)
local removal_target = nil
for idx, line1, col1 in doc():get_selections(true) do
if line1 == line and col1 == col and #doc().selections > 4 then
removal_target = idx
end
end
if removal_target then
doc():remove_selection(removal_target)
else
doc():add_selection(line, col, line, col)
end
dv().mouse_selecting = { line, col, "set" }
end,
["doc:create-cursor-previous-line"] = function()
@ -432,50 +549,49 @@ local commands = {
local translations = {
["previous-char"] = translate.previous_char,
["next-char"] = translate.next_char,
["previous-word-start"] = translate.previous_word_start,
["next-word-end"] = translate.next_word_end,
["previous-block-start"] = translate.previous_block_start,
["next-block-end"] = translate.next_block_end,
["start-of-doc"] = translate.start_of_doc,
["end-of-doc"] = translate.end_of_doc,
["start-of-line"] = translate.start_of_line,
["end-of-line"] = translate.end_of_line,
["start-of-word"] = translate.start_of_word,
["start-of-indentation"] = translate.start_of_indentation,
["end-of-word"] = translate.end_of_word,
["previous-line"] = DocView.translate.previous_line,
["next-line"] = DocView.translate.next_line,
["previous-page"] = DocView.translate.previous_page,
["next-page"] = DocView.translate.next_page,
["previous-char"] = translate,
["next-char"] = translate,
["previous-word-start"] = translate,
["next-word-end"] = translate,
["previous-block-start"] = translate,
["next-block-end"] = translate,
["start-of-doc"] = translate,
["end-of-doc"] = translate,
["start-of-line"] = translate,
["end-of-line"] = translate,
["start-of-word"] = translate,
["start-of-indentation"] = translate,
["end-of-word"] = translate,
["previous-line"] = DocView.translate,
["next-line"] = DocView.translate,
["previous-page"] = DocView.translate,
["next-page"] = DocView.translate,
}
for name, fn in pairs(translations) do
commands["doc:move-to-" .. name] = function() doc():move_to(fn, dv()) end
commands["doc:select-to-" .. name] = function() doc():select_to(fn, dv()) end
commands["doc:delete-to-" .. name] = function() doc():delete_to(fn, dv()) end
for name, obj in pairs(translations) do
commands["doc:move-to-" .. name] = function() doc():move_to(obj[name:gsub("-", "_")], dv()) end
commands["doc:select-to-" .. name] = function() doc():select_to(obj[name:gsub("-", "_")], dv()) end
commands["doc:delete-to-" .. name] = function() doc():delete_to(obj[name:gsub("-", "_")], dv()) end
end
commands["doc:move-to-previous-char"] = function()
for idx, line1, col1, line2, col2 in doc():get_selections(true) do
if line1 ~= line2 or col1 ~= col2 then
doc():set_selections(idx, line1, col1)
else
doc():move_to_cursor(idx, translate.previous_char)
end
end
doc():move_to(translate.previous_char)
end
commands["doc:move-to-next-char"] = function()
for idx, line1, col1, line2, col2 in doc():get_selections(true) do
if line1 ~= line2 or col1 ~= col2 then
doc():set_selections(idx, line2, col2)
else
doc():move_to_cursor(idx, translate.next_char)
end
end
doc():move_to(translate.next_char)
end
command.add("core.docview", commands)
command.add(function()
return core.active_view:is(DocView) and core.active_view.doc:has_any_selection()
end ,selection_commands)

View File

@ -55,7 +55,7 @@ end
local function find(label, search_fn)
last_view, last_sel = core.active_view,
{ core.active_view.doc:get_selection() }
local text = last_view.doc:get_text(unpack(last_sel))
local text = last_view.doc:get_text(table.unpack(last_sel))
found_expression = false
core.command_view:set_text(text, true)

View File

@ -0,0 +1,16 @@
local core = require "core"
local command = require "core.command"
command.add(nil, {
["log:open-as-doc"] = function()
local doc = core.open_doc("logs.txt")
core.root_view:open_doc(doc)
doc:insert(1, 1, core.get_log())
doc.new_file = false
doc:clean()
end,
["log:copy-to-clipboard"] = function()
system.set_clipboard(core.get_log())
end
})

View File

@ -30,7 +30,7 @@ local t = {
for i, v in ipairs(core.docs) do if v ~= active_doc then table.insert(docs, v) end end
core.confirm_close_docs(docs, core.root_view.close_all_docviews, core.root_view, true)
end,
["root:switch-to-previous-tab"] = function()
local node = core.root_view:get_active_node()
local idx = node:get_view_idx(core.active_view)
@ -64,7 +64,7 @@ local t = {
table.insert(node.views, idx + 1, core.active_view)
end
end,
["root:shrink"] = function()
local node = core.root_view:get_active_node()
local parent = node:get_parent_node(core.root_view.root_node)

View File

@ -0,0 +1,71 @@
local core = require "core"
local command = require "core.command"
local common = require "core.common"
local style = require "core.style"
local StatusView = require "core.statusview"
local function status_view_item_names()
local items = core.status_view:get_items_list()
local names = {}
for _, item in ipairs(items) do
table.insert(names, item.name)
end
return names
end
local function status_view_items_data(names)
local data = {}
for _, name in ipairs(names) do
local item = core.status_view:get_item(name)
table.insert(data, {
text = command.prettify_name(item.name),
info = item.alignment == StatusView.Item.LEFT and "Left" or "Right",
name = item.name
})
end
return data
end
local function status_view_get_items(text)
local names = status_view_item_names()
local results = common.fuzzy_match(names, text)
results = status_view_items_data(results)
return results
end
command.add(nil, {
["status-bar:toggle"] = function()
core.status_view:toggle()
end,
["status-bar:show"] = function()
core.status_view:show()
end,
["status-bar:hide"] = function()
core.status_view:hide()
end,
["status-bar:disable-messages"] = function()
core.status_view:display_messages(false)
end,
["status-bar:enable-messages"] = function()
core.status_view:display_messages(true)
end,
["status-bar:hide-item"] = function()
core.command_view:enter("Status bar item to hide",
function(text, item)
core.status_view:hide_items(item.name)
end,
status_view_get_items
)
end,
["status-bar:show-item"] = function()
core.command_view:enter("Status bar item to show",
function(text, item)
core.status_view:show_items(item.name)
end,
status_view_get_items
)
end,
["status-bar:reset-items"] = function()
core.status_view:show_items()
end,
})

View File

@ -56,8 +56,8 @@ function CommandView:get_name()
end
function CommandView:get_line_screen_position()
local x = CommandView.super.get_line_screen_position(self, 1)
function CommandView:get_line_screen_position(line, col)
local x = CommandView.super.get_line_screen_position(self, 1, col)
local _, y = self:get_content_offset()
local lh = self:get_line_height()
return x, y + (self.size.y - lh) / 2
@ -243,6 +243,7 @@ function CommandView:draw_line_gutter(idx, x, y)
x = x + style.padding.x
renderer.draw_text(self:get_font(), self.label, x, y + yoffset, color)
core.pop_clip_rect()
return self:get_line_height()
end

View File

@ -17,6 +17,14 @@ function common.clamp(n, lo, hi)
end
function common.merge(a, b)
local t = {}
for k, v in pairs(a) do t[k] = v end
if b then for k, v in pairs(b) do t[k] = v end end
return t
end
function common.round(n)
return n >= 0 and math.floor(n + 0.5) or math.ceil(n - 0.5)
end
@ -42,7 +50,7 @@ end
function common.distance(x1, y1, x2, y2)
return math.sqrt(math.pow(x2-x1, 2)+math.pow(y2-y1, 2))
return math.sqrt(((x2-x1) ^ 2)+((y2-y1) ^ 2))
end
@ -437,4 +445,5 @@ function common.rm(path, recursively)
return true
end
return common

View File

@ -6,19 +6,19 @@ config.message_timeout = 5
config.mouse_wheel_scroll = 50 * SCALE
config.scroll_past_end = true
config.file_size_limit = 10
config.ignore_files = "^%."
config.ignore_files = { "^%." }
config.symbol_pattern = "[%a_][%w_]*"
config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
config.undo_merge_timeout = 0.3
config.max_undos = 10000
config.max_tabs = 8
config.always_show_tabs = true
-- Possible values: false, true, "no_selection"
config.highlight_current_line = true
config.line_height = 1.2
config.indent_size = 2
config.tab_type = "soft"
config.line_limit = 80
config.max_symbols = 4000
config.max_project_files = 2000
config.transitions = true
config.animation_rate = 1.0
@ -29,10 +29,19 @@ config.borderless = false
config.tab_close_button = true
config.max_clicks = 3
-- Disable plugin loading setting to false the config entry
-- of the same name.
config.plugins = {}
-- set as true to be able to test non supported plugins
config.skip_plugins_version = false
config.plugins = {}
-- Allow you to set plugin configs even if we haven't seen the plugin before.
setmetatable(config.plugins, {
__index = function(t, k)
if rawget(t, k) == nil then rawset(t, k, {}) end
return rawget(t, k)
end
})
-- Disable these plugins by default.
config.plugins.trimwhitespace = false
config.plugins.lineguide = false
config.plugins.drawwhitespace = false

View File

@ -91,6 +91,7 @@ function ContextMenu:show(x, y)
self.position.x, self.position.y = x, y
self.show_context_menu = true
core.request_cursor("arrow")
return true
end
return false
@ -101,6 +102,7 @@ function ContextMenu:hide()
self.items = nil
self.selected = -1
self.height = 0
core.request_cursor(core.active_view.cursor)
end
function ContextMenu:each_item()
@ -126,9 +128,6 @@ function ContextMenu:on_mouse_moved(px, py)
break
end
end
if self.selected >= 0 then
core.request_cursor("arrow")
end
return true
end
@ -140,8 +139,38 @@ function ContextMenu:on_selected(item)
end
end
function ContextMenu:on_mouse_pressed(button, x, y, clicks)
local selected = (self.items or {})[self.selected]
local function change_value(value, change)
return value + change
end
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
self.selected = change_value(self.selected, -1)
end
end
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
self.selected = change_value(self.selected, 1)
end
end
function ContextMenu:get_item_selected()
return (self.items or {})[self.selected]
end
function ContextMenu:call_selected_item()
local selected = self:get_item_selected()
self:hide()
if selected then
self:on_selected(selected)
end
end
function ContextMenu:on_mouse_pressed(button, px, py, clicks)
local selected = self:get_item_selected()
local caught = false
self:hide()
@ -153,7 +182,7 @@ function ContextMenu:on_mouse_pressed(button, x, y, clicks)
end
if button == "right" then
caught = self:show(x, y)
caught = self:show(px, py)
end
return caught
end

224
data/core/dirwatch.lua Normal file
View File

@ -0,0 +1,224 @@
local common = require "core.common"
local config = require "core.config"
local dirwatch = {}
function dirwatch:__index(idx)
local value = rawget(self, idx)
if value ~= nil then return value end
return dirwatch[idx]
end
function dirwatch.new()
local t = {
scanned = {},
watched = {},
reverse_watched = {},
-- monitor = dirmonitor.new(),
monitor = 0,
windows_watch_top = nil,
windows_watch_count = 0
}
setmetatable(t, dirwatch)
return t
end
function dirwatch:scan(directory, bool)
if bool == false then return self:unwatch(directory) end
self.scanned[directory] = system.get_file_info(directory).modified
end
-- Should be called on every directory in a subdirectory.
-- In windows, this is a no-op for anything underneath a top-level directory,
-- but code should be called anyway, so we can ensure that we have a proper
-- experience across all platforms. Should be an absolute path.
function dirwatch:watch(directory, bool)
if bool == false then return self:unwatch(directory) end
if not self.watched[directory] and not self.scanned[directory] then
if PLATFORM == "Windows" then
if not self.windows_watch_top or directory:find(self.windows_watch_top, 1, true) ~= 1 then
-- Get the highest level of directory that is common to this directory, and the original.
local target = directory
while self.windows_watch_top and self.windows_watch_top:find(target, 1, true) ~= 1 do
target = common.dirname(target)
end
if target ~= self.windows_watch_top then
local value = self.monitor:watch(target)
if value and value < 0 then
return self:scan(directory)
end
self.windows_watch_top = target
self.windows_watch_count = self.windows_watch_count + 1
end
end
self.watched[directory] = true
else
-- TODO: Fix value to get it from monitor
-- local value = self.monitor:watch(directory)
local value = -1
-- If for whatever reason, we can't watch this directory, revert back to scanning.
-- Don't bother trying to find out why, for now.
if value and value < 0 then
return self:scan(directory)
end
self.watched[directory] = value
self.reverse_watched[value] = directory
end
end
end
-- this should be an absolute path
function dirwatch:unwatch(directory)
if self.watched[directory] then
if PLATFORM ~= "Windows" then
self.monitor:unwatch(self.watched[directory])
self.reverse_watched[directory] = nil
else
self.windows_watch_count = self.windows_watch_count - 1
if self.windows_watch_count == 0 then
self.windows_watch_top = nil
self.monitor:unwatch(directory)
end
end
self.watched[directory] = nil
elseif self.scanned[directory] then
self.scanned[directory] = nil
end
end
-- designed to be run inside a coroutine.
function dirwatch:check(change_callback, scan_time, wait_time)
self.monitor:check(function(id)
if PLATFORM == "Windows" then
change_callback(common.dirname(self.windows_watch_top .. PATHSEP .. id))
elseif self.reverse_watched[id] then
change_callback(self.reverse_watched[id])
end
end)
local start_time = system.get_time()
for directory, old_modified in pairs(self.scanned) do
if old_modified then
local new_modified = system.get_file_info(directory).modified
if old_modified < new_modified then
change_callback(directory)
self.scanned[directory] = new_modified
end
end
if system.get_time() - start_time > scan_time then
coroutine.yield(wait_time)
start_time = system.get_time()
end
end
end
-- inspect config.ignore_files patterns and prepare ready to use entries.
local function compile_ignore_files()
local ipatterns = config.ignore_files
local compiled = {}
-- config.ignore_files could be a simple string...
if type(ipatterns) ~= "table" then ipatterns = {ipatterns} end
for i, pattern in ipairs(ipatterns) do
-- we ignore malformed pattern that raise an error
if pcall(string.match, "a", pattern) then
table.insert(compiled, {
use_path = pattern:match("/[^/$]"), -- contains a slash but not at the end
-- An '/' or '/$' at the end means we want to match a directory.
match_dir = pattern:match(".+/%$?$"), -- to be used as a boolen value
pattern = pattern -- get the actual pattern
})
end
end
return compiled
end
local function fileinfo_pass_filter(info, ignore_compiled)
if info.size >= config.file_size_limit * 1e6 then return false end
local basename = common.basename(info.filename)
-- replace '\' with '/' for Windows where PATHSEP = '\'
local fullname = "/" .. info.filename:gsub("\\", "/")
for _, compiled in ipairs(ignore_compiled) do
local test = compiled.use_path and fullname or basename
if compiled.match_dir then
if info.type == "dir" and string.match(test .. "/", compiled.pattern) then
return false
end
else
if string.match(test, compiled.pattern) then
return false
end
end
end
return true
end
local function compare_file(a, b)
return a.filename < b.filename
end
-- compute a file's info entry completed with "filename" to be used
-- in project scan or falsy if it shouldn't appear in the list.
local function get_project_file_info(root, file, ignore_compiled)
local info = system.get_file_info(root .. PATHSEP .. file)
-- info can be not nil but info.type may be nil if is neither a file neither
-- a directory, for example for /dev/* entries on linux.
if info and info.type then
info.filename = file
return fileinfo_pass_filter(info, ignore_compiled) and info
end
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.
-- The current path location will therefore always be: root .. path.
-- 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)
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 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
end
end
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
entries_count = n
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
return dirwatch

View File

@ -33,7 +33,6 @@ end
function Doc:reset()
self.lines = { "\n" }
self.selections = { 1, 1, 1, 1 }
self.cursor_clipboard = {}
self.undo_stack = { idx = 1 }
self.redo_stack = { idx = 1 }
self.clean_change_id = 1
@ -55,6 +54,7 @@ end
function Doc:set_filename(filename, abs_filename)
self.filename = filename
self.abs_filename = abs_filename
self:reset_syntax()
end
@ -85,6 +85,8 @@ function Doc:save(filename, abs_filename)
assert(self.filename, "no filename set to default to")
filename = self.filename
abs_filename = self.abs_filename
else
assert(self.filename or abs_filename, "calling save on unnamed doc without absolute path")
end
local fp = assert( io.open(filename, "wb") )
for _, line in ipairs(self.lines) do
@ -94,7 +96,6 @@ function Doc:save(filename, abs_filename)
fp:close()
self:set_filename(filename, abs_filename)
self.new_file = false
self:reset_syntax()
self:clean()
end
@ -197,8 +198,14 @@ function Doc:add_selection(line1, col1, line2, col2, swap)
self:set_selections(target, line1, col1, line2, col2, swap, 0)
end
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.cursor_clipboard = {}, {}
self.selections = {}
self:set_selections(1, line1, col1, line2, col2, swap)
end
@ -208,12 +215,10 @@ function Doc:merge_cursors(idx)
if self.selections[i] == self.selections[j] and
self.selections[i+1] == self.selections[j+1] then
common.splice(self.selections, i, 4)
common.splice(self.cursor_clipboard, i, 1)
break
end
end
end
if #self.selections <= 4 then self.cursor_clipboard = {} end
end
local function selection_iterator(invariant, idx)
@ -356,7 +361,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
-- splice lines into line array
common.splice(self.lines, line, 1, lines)
-- keep cursors where they should be
for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do
if cline1 < line then break end
@ -388,7 +393,7 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
-- splice line into line array
common.splice(self.lines, line1, line2 - line1 + 1, { before .. after })
-- move all cursors back if they share a line with the removed text
for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do
if cline1 < line2 then break end
@ -458,7 +463,7 @@ end
function Doc:replace(fn)
local has_selection, n = false, 0
for idx, line1, col1, line2, col2 in self:get_selections(true) do
if line1 ~= line2 or col1 ~= col2 then
if line1 ~= line2 or col1 ~= col2 then
n = n + self:replace_cursor(idx, line1, col1, line2, col2, fn)
has_selection = true
end

View File

@ -121,14 +121,18 @@ function DocView:get_gutter_width()
end
function DocView:get_line_screen_position(idx)
function DocView:get_line_screen_position(line, col)
local x, y = self:get_content_offset()
local lh = self:get_line_height()
local gw = self:get_gutter_width()
return x + gw, y + (idx-1) * lh + style.padding.y
y = y + (line-1) * lh + style.padding.y
if col then
return x + gw + self:get_col_x_offset(line, col), y
else
return x + gw, y
end
end
function DocView:get_line_text_y_offset()
local lh = self:get_line_height()
local th = self:get_font():get_height()
@ -198,8 +202,9 @@ end
function DocView:scroll_to_line(line, ignore_if_visible, instant)
local min, max = self:get_visible_line_range()
if not (ignore_if_visible and line > min and line < max) then
local lh = self:get_line_height()
self.scroll.to.y = math.max(0, lh * (line - 1) - self.size.y / 2)
local x, y = self:get_line_screen_position(line)
local ox, oy = self:get_content_offset()
self.scroll.to.y = math.max(0, y - oy - self.size.y / 2)
if instant then
self.scroll.y = self.scroll.to.y
end
@ -208,10 +213,10 @@ end
function DocView:scroll_to_make_visible(line, col)
local min = self:get_line_height() * (line - 1)
local max = self:get_line_height() * (line + 2) - self.size.y
self.scroll.to.y = math.min(self.scroll.to.y, min)
self.scroll.to.y = math.max(self.scroll.to.y, max)
local ox, oy = self:get_content_offset()
local _, ly = self:get_line_screen_position(line, col)
local lh = self:get_line_height()
self.scroll.to.y = common.clamp(self.scroll.to.y, ly - oy - self.size.y + lh * 2, ly - oy - lh)
local gw = self:get_gutter_width()
local xoffset = self:get_col_x_offset(line, col)
local xmargin = 3 * self:get_font():get_width(' ')
@ -224,7 +229,6 @@ function DocView:scroll_to_make_visible(line, col)
end
end
function DocView:on_mouse_moved(x, y, ...)
DocView.super.on_mouse_moved(self, x, y, ...)
@ -284,13 +288,15 @@ end
function DocView:update()
-- scroll to make caret visible and reset blink timer if it moved
local line, col = self.doc:get_selection()
if (line ~= self.last_line or col ~= self.last_col) and self.size.x > 0 then
local line1, col1, line2, col2 = self.doc:get_selection()
if (line1 ~= self.last_line1 or col1 ~= self.last_col1 or
line2 ~= self.last_line2 or col2 ~= self.last_col2) and self.size.x > 0 then
if core.active_view == self then
self:scroll_to_make_visible(line, col)
self:scroll_to_make_visible(line1, col1)
end
core.blink_reset()
self.last_line, self.last_col = line, col
self.last_line1, self.last_col1 = line1, col1
self.last_line2, self.last_col2 = line2, col2
end
-- update blink timer
@ -313,14 +319,15 @@ function DocView:draw_line_highlight(x, y)
end
function DocView:draw_line_text(idx, x, y)
function DocView:draw_line_text(line, x, y)
local default_font = self:get_font()
local tx, ty = x, y + self:get_line_text_y_offset()
for _, type, text in self.doc.highlighter:each_token(idx) do
for _, type, text in self.doc.highlighter:each_token(line) do
local color = style.syntax[type]
local font = style.syntax_fonts[type] or default_font
tx = renderer.draw_text(font, text, tx, ty, color)
end
return self:get_line_height()
end
function DocView:draw_caret(x, y)
@ -328,28 +335,37 @@ function DocView:draw_caret(x, y)
renderer.draw_rect(x, y, style.caret_width, lh, style.caret)
end
function DocView:draw_line_body(idx, x, y)
function DocView:draw_line_body(line, x, y)
-- draw highlight if any selection ends on this line
local draw_highlight = false
for lidx, line1, col1, line2, col2 in self.doc:get_selections(false) do
if line1 == idx then
draw_highlight = true
break
local hcl = config.highlight_current_line
if hcl ~= false then
for lidx, line1, col1, line2, col2 in self.doc:get_selections(false) do
if line1 == line then
if hcl == "no_selection" then
if (line1 ~= line2) or (col1 ~= col2) then
draw_highlight = false
break
end
end
draw_highlight = true
break
end
end
end
if draw_highlight and config.highlight_current_line and core.active_view == self then
if draw_highlight and core.active_view == self then
self:draw_line_highlight(x + self.scroll.x, y)
end
-- draw selection if it overlaps this line
local lh = self:get_line_height()
for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
if idx >= line1 and idx <= line2 then
local text = self.doc.lines[idx]
if line1 ~= idx then col1 = 1 end
if line2 ~= idx then col2 = #text + 1 end
local x1 = x + self:get_col_x_offset(idx, col1)
local x2 = x + self:get_col_x_offset(idx, col2)
local lh = self:get_line_height()
if line >= line1 and line <= line2 then
local text = self.doc.lines[line]
if line1 ~= line then col1 = 1 end
if line2 ~= line then col2 = #text + 1 end
local x1 = x + self:get_col_x_offset(line, col1)
local x2 = x + self:get_col_x_offset(line, col2)
if x1 ~= x2 then
renderer.draw_rect(x1, y, x2 - x1, lh, style.selection)
end
@ -357,21 +373,22 @@ function DocView:draw_line_body(idx, x, y)
end
-- draw line's text
self:draw_line_text(idx, x, y)
return self:draw_line_text(line, x, y)
end
function DocView:draw_line_gutter(idx, x, y, width)
function DocView:draw_line_gutter(line, x, y, width)
local color = style.line_number
for _, line1, _, line2 in self.doc:get_selections(true) do
if idx >= line1 and idx <= line2 then
if line >= line1 and line <= line2 then
color = style.line_number2
break
end
end
local yoffset = self:get_line_text_y_offset()
x = x + style.padding.x
common.draw_text(self:get_font(), color, idx, "right", x, y + yoffset, width, self:get_line_height())
local lh = self:get_line_height()
common.draw_text(self:get_font(), color, line, "right", x, y, width, lh)
return lh
end
@ -385,8 +402,7 @@ function DocView:draw_overlay()
and system.window_has_focus() then
if config.disable_blink
or (core.blink_timer - core.blink_start) % T < T / 2 then
local x, y = self:get_line_screen_position(line)
self:draw_caret(x + self:get_col_x_offset(line, col), y)
self:draw_caret(self:get_line_screen_position(line, col))
end
end
end
@ -404,8 +420,7 @@ function DocView:draw()
local x, y = self:get_line_screen_position(minline)
local gw, gpad = self:get_gutter_width()
for i = minline, maxline do
self:draw_line_gutter(i, self.position.x, y, gpad and gw - gpad or gw)
y = y + lh
y = y + (self:draw_line_gutter(i, self.position.x, y, gpad and gw - gpad or gw) or lh)
end
local pos = self.position
@ -414,8 +429,7 @@ function DocView:draw()
-- right side it is redundant with the Node's clip.
core.push_clip_rect(pos.x + gw, pos.y, self.size.x - gw, self.size.y)
for i = minline, maxline do
self:draw_line_body(i, x, y)
y = y + lh
y = y + (self:draw_line_body(i, x, y) or lh)
end
self:draw_overlay()
core.pop_clip_rect()

View File

@ -8,8 +8,18 @@ local function draw_text(x, y, color)
local th = style.big_font:get_height()
local dh = 2 * th + style.padding.y * 2
local x1, y1 = x, y + (dh - th) / 2
x = renderer.draw_text(style.big_font, "Lite XL", x1, y1, color)
renderer.draw_text(style.font, "version " .. VERSION, x1, y1 + th, color)
local xv = x1
local title = "Lite XL"
local version = "version " .. VERSION
local title_width = style.big_font:get_width(title)
local version_width = style.font:get_width(version)
if version_width > title_width then
version = VERSION
version_width = style.font:get_width(version)
xv = x1 - (version_width - title_width)
end
x = renderer.draw_text(style.big_font, title, x1, y1, color)
renderer.draw_text(style.font, version, xv, y1 + th, color)
x = x + style.padding.x
renderer.draw_rect(x, y, math.ceil(1 * SCALE), dh, color)
local lines = {

File diff suppressed because it is too large Load Diff

View File

@ -7,9 +7,10 @@ keymap.map = {}
keymap.reverse_map = {}
local macos = PLATFORM == "Mac OS X"
local os4 = PLATFORM == "AmigaOS 4"
-- Thanks to mathewmariani, taken from his lite-macos github repository.
local modkeys_os = require("core.modkeys-" .. (macos and "macos" or "generic"))
local modkeys_os = require("core.modkeys-" .. (macos and "macos" or os4 and "os4" or "generic"))
local modkey_map = modkeys_os.map
local modkeys = modkeys_os.keys
@ -215,6 +216,7 @@ keymap.add_direct {
["ctrl+l"] = "doc:select-lines",
["ctrl+shift+l"] = { "find-replace:select-add-all", "doc:select-word" },
["ctrl+/"] = "doc:toggle-line-comments",
["ctrl+shift+/"] = "doc:toggle-block-comments",
["ctrl+up"] = "doc:move-lines-up",
["ctrl+down"] = "doc:move-lines-down",
["ctrl+shift+d"] = "doc:duplicate-lines",

View File

@ -1,5 +1,6 @@
local core = require "core"
local common = require "core.common"
local keymap = require "core.keymap"
local style = require "core.style"
local View = require "core.view"
@ -36,12 +37,15 @@ local LogView = View:extend()
LogView.context = "session"
function LogView:new()
LogView.super.new(self)
self.last_item = core.log_items[#core.log_items]
self.expanding = {}
self.scrollable = true
self.yoffset = 0
core.status_view:show_message("i", style.text, "ctrl+click to copy entry")
end
@ -77,25 +81,30 @@ function LogView:each_item()
end
function LogView:on_mouse_moved(px, py, ...)
LogView.super.on_mouse_moved(self, px, py, ...)
local hovered = false
for _, item, x, y, w, h in self:each_item() do
function LogView:on_mouse_pressed(button, px, py, clicks)
if LogView.super.on_mouse_pressed(self, button, px, py, clicks) then
return true
end
local index, selected
for i, item, x, y, w, h in self:each_item() do
if px >= x and py >= y and px < x + w and py < y + h then
hovered = true
self.hovered_item = item
index = i
selected = item
break
end
end
if not hovered then self.hovered_item = nil end
end
function LogView:on_mouse_pressed(button, mx, my, clicks)
if LogView.super.on_mouse_pressed(self, button, mx, my, clicks) then return end
if self.hovered_item then
self:expand_item(self.hovered_item)
if selected then
if keymap.modkeys["ctrl"] then
system.set_clipboard(core.get_log(selected))
core.status_view:show_message("i", style.text, "copied entry #"..index.." to clipboard.")
else
self:expand_item(selected)
end
end
return true
end
@ -131,21 +140,37 @@ local function draw_text_multiline(font, text, x, y, color)
return resx, y
end
-- this is just to get a date string that's consistent
local datestr = os.date()
function LogView:draw()
self:draw_background(style.background)
local th = style.font:get_height()
local lh = th + style.padding.y -- for one line
for _, item, x, y, w in self:each_item() do
local iw = math.max(
style.icon_font:get_width(style.log.ERROR.icon),
style.icon_font:get_width(style.log.INFO.icon)
)
local tw = style.font:get_width(datestr)
for _, item, x, y, w, h in self:each_item() do
core.push_clip_rect(x, y, w, h)
x = x + style.padding.x
x = common.draw_text(
style.icon_font,
style.log[item.level].color,
style.log[item.level].icon,
"center",
x, y, iw, lh
)
x = x + style.padding.x
-- timestamps are always 15% of the width
local time = os.date(nil, item.time)
x = common.draw_text(style.font, style.dim, time, "left", x, y, w, lh)
x = x + style.padding.x
common.draw_text(style.font, style.dim, time, "left", x, y, tw, lh)
x = x + tw + style.padding.x
x = common.draw_text(style.code_font, style.dim, is_expanded(item) and "-" or "+", "left", x, y, w, lh)
x = x + style.padding.x
w = w - (x - self:get_content_offset())
if is_expanded(item) then
@ -165,6 +190,8 @@ function LogView:draw()
end
_, y = common.draw_text(style.font, style.text, line, "left", x, y, w, lh)
end
core.pop_clip_rect()
end
end

15
data/core/modkeys-os4.lua Normal file
View File

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

View File

@ -16,8 +16,12 @@ local NagView = View:extend()
function NagView:new()
NagView.super.new(self)
self.size.y = 0
self.show_height = 0
self.force_focus = false
self.queue = {}
self.scrollable = true
self.target_height = 0
self.on_mouse_pressed_root = nil
end
function NagView:get_title()
@ -46,20 +50,20 @@ function NagView:get_target_height()
return self.target_height + 2 * style.padding.y
end
function NagView:update()
NagView.super.update(self)
if core.active_view == self and self.title then
self:move_towards(self.size, "y", self:get_target_height())
self:move_towards(self, "underline_progress", 1)
function NagView:get_scrollable_size()
local w, h = system.get_window_size()
if self.visible and self:get_target_height() > h then
self.size.y = h
return self:get_target_height()
else
self:move_towards(self.size, "y", 0)
self.size.y = 0
end
return 0
end
function NagView:draw_overlay()
function NagView:dim_window_content()
local ox, oy = self:get_content_offset()
oy = oy + self.size.y
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)
@ -81,7 +85,7 @@ function NagView:each_option()
bh = self:get_buttons_height()
ox,oy = self:get_content_offset()
ox = ox + self.size.x
oy = oy + self.size.y - bh - style.padding.y
oy = oy + self.show_height - bh - style.padding.y
for i = #self.options, 1, -1 do
opt = self.options[i]
@ -94,6 +98,8 @@ function NagView:each_option()
end
function NagView:on_mouse_moved(mx, my, ...)
if not self.visible then return end
core.set_active_view(self)
NagView.super.on_mouse_moved(self, mx, my, ...)
for i, _, x,y,w,h in self:each_option() do
if mx >= x and my >= y and mx < x + w and my < y + h then
@ -103,18 +109,55 @@ function NagView:on_mouse_moved(mx, my, ...)
end
end
local function register_mouse_pressed(self)
if self.on_mouse_pressed_root then return end
-- RootView is loaded locally to avoid NagView and RootView being
-- mutually recursive
local RootView = require "core.rootview"
self.on_mouse_pressed_root = RootView.on_mouse_pressed
local this = self
function RootView:on_mouse_pressed(button, x, y, clicks)
if
not this:on_mouse_pressed(button, x, y, clicks)
then
return this.on_mouse_pressed_root(self, button, x, y, clicks)
else
return true
end
end
self.new_on_mouse_pressed_root = RootView.on_mouse_pressed
end
local function unregister_mouse_pressed(self)
local RootView = require "core.rootview"
if
self.on_mouse_pressed_root
and
-- just in case prevent overwriting what something else may
-- have overwrote after us, but after testing with various
-- plugins this doesn't seems to happen, but just in case
self.new_on_mouse_pressed_root == RootView.on_mouse_pressed
then
RootView.on_mouse_pressed = self.on_mouse_pressed_root
self.on_mouse_pressed_root = nil
self.new_on_mouse_pressed_root = nil
end
end
function NagView:on_mouse_pressed(button, mx, my, clicks)
if NagView.super.on_mouse_pressed(self, button, mx, my, clicks) then return end
if not self.visible then return false end
if NagView.super.on_mouse_pressed(self, button, mx, my, clicks) then return true end
for i, _, x,y,w,h in self:each_option() do
if mx >= x and my >= y and mx < x + w and my < y + h then
self:change_hovered(i)
command.perform "dialog:select"
break
end
end
return true
end
function NagView:on_text_input(text)
if not self.visible then return end
if text:lower() == "y" then
command.perform "dialog:select-yes"
elseif text:lower() == "n" then
@ -122,20 +165,39 @@ function NagView:on_text_input(text)
end
end
function NagView:update()
if not self.visible and self.show_height <= 0 then return end
NagView.super.update(self)
function NagView:draw()
if self.size.y <= 0 or not self.title then return end
if self.visible and core.active_view == self and self.title then
self:move_towards(self, "show_height", self:get_target_height())
self:move_towards(self, "underline_progress", 1)
else
self:move_towards(self, "show_height", 0)
if self.show_height <= 0 then
self.title = nil
self.message = nil
self.options = nil
self.on_selected = nil
end
end
end
self:draw_overlay()
self:draw_background(style.nagbar)
local function draw_nagview_message(self)
self:dim_window_content()
-- draw message's background
local ox, oy = self:get_content_offset()
renderer.draw_rect(ox, oy, self.size.x, self.show_height, style.nagbar)
ox = ox + style.padding.x
core.push_clip_rect(ox, oy, self.size.x, self.show_height)
-- if there are other items, show it
if #self.queue > 0 then
local str = string.format("[%d]", #self.queue)
ox = common.draw_text(style.font, style.nagbar_text, str, "left", ox, oy, self.size.x, self.size.y)
ox = common.draw_text(style.font, style.nagbar_text, str, "left", ox, oy, self.size.x, self.show_height)
ox = ox + style.padding.x
end
@ -168,6 +230,17 @@ function NagView:draw()
common.draw_text(opt.font, style.nagbar_text, opt.text, "center", fx,fy,fw,fh)
end
self:draw_scrollbar()
core.pop_clip_rect()
end
function NagView:draw()
if (not self.visible and self.show_height <= 0) or not self.title then
return
end
core.root_view:defer_draw(draw_nagview_message, self)
end
function NagView:get_message_height()
@ -178,23 +251,31 @@ function NagView:get_message_height()
return h
end
function NagView:next()
local opts = table.remove(self.queue, 1) or {}
self.title = opts.title
self.message = opts.message and opts.message .. "\n"
self.options = opts.options
self.on_selected = opts.on_selected
if self.message and self.options then
if opts.title and opts.message and opts.options then
self.visible = true
self.title = opts.title
self.message = opts.message and opts.message .. "\n"
self.options = opts.options
self.on_selected = opts.on_selected
local message_height = self:get_message_height()
-- self.target_height is the nagview height needed to display the message and
-- the buttons, excluding the top and bottom padding space.
self.target_height = math.max(message_height, self:get_buttons_height())
self:change_hovered(common.find_index(self.options, "default_yes"))
self.force_focus = true
core.set_active_view(self)
-- We add a hook to manage all the mouse_pressed events.
register_mouse_pressed(self)
else
self.force_focus = false
core.set_active_view(core.next_active_view or core.last_active_view)
self.visible = false
unregister_mouse_pressed(self)
end
self.force_focus = self.message ~= nil
core.set_active_view(self.message ~= nil and self or
core.next_active_view or core.last_active_view)
end
function NagView:show(title, message, options, on_select)
@ -204,7 +285,7 @@ function NagView:show(title, message, options, on_select)
opts.options = assert(options, "No options")
opts.on_selected = on_select or noop
table.insert(self.queue, opts)
if #self.queue > 0 and not self.title then self:next() end
self:next()
end
return NagView

View File

@ -260,8 +260,8 @@ end
local function close_button_location(x, w)
local cw = style.icon_font:get_width("C")
local pad = style.padding.y
return x + w - pad - cw, cw, pad
local pad = style.padding.x / 2
return x + w - cw - pad, cw, pad
end
@ -476,51 +476,59 @@ function Node:update()
end
end
function Node:draw_tab(text, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
function Node:draw_tab_title(view, font, is_active, is_hovered, x, y, w, h)
local text = view and view:get_name() or ""
local dots_width = font:get_width("")
local align = "center"
if font:get_width(text) > w then
align = "left"
for i = 1, #text do
local reduced_text = text:sub(1, #text - i)
if font:get_width(reduced_text) + dots_width <= w then
text = reduced_text .. ""
break
end
end
end
local color = style.dim
if is_active then color = style.text end
if is_hovered then color = style.text end
common.draw_text(font, color, text, align, x, y, w, h)
end
function Node:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalone)
-- Tabs deviders
local ds = style.divider_size
local dots_width = style.font:get_width("")
local color = style.dim
local padding_y = style.padding.y
renderer.draw_rect(x + w, y + padding_y, ds, h - padding_y * 2, style.dim)
renderer.draw_rect(x + w, y + padding_y, ds, h - padding_y*2, style.dim)
if standalone then
renderer.draw_rect(x-1, y-1, w+2, h+2, style.background2)
end
-- Full border
if is_active then
color = style.text
renderer.draw_rect(x, y, w, h, style.background)
renderer.draw_rect(x + w, y, ds, h, style.divider)
renderer.draw_rect(x - ds, y, ds, h, style.divider)
end
local cx, cw, cspace = close_button_location(x, w)
return x + ds, y, w - ds*2, h
end
function Node:draw_tab(view, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone)
x, y, w, h = self:draw_tab_borders(view, is_active, is_hovered, x, y, w, h, standalone)
-- Close button
local cx, cw, cpad = close_button_location(x, w)
local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button)
if show_close_button then
local close_style = is_close_hovered and style.text or style.dim
common.draw_text(style.icon_font, close_style, "C", nil, cx, y, 0, h)
common.draw_text(style.icon_font, close_style, "C", nil, cx, y, cw, h)
end
if is_hovered then
color = style.text
end
local padx = style.padding.x
-- Normally we should substract "cspace" from text_avail_width and from the
-- clipping width. It is the padding space we give to the left and right of the
-- close button. However, since we are using dots to terminate filenames, we
-- choose to ignore "cspace" accepting that the text can possibly "touch" the
-- close button.
local text_avail_width = cx - x - padx
core.push_clip_rect(x, y, cx - x, h)
x, w = x + padx, w - padx * 2
local align = "center"
if style.font:get_width(text) > text_avail_width then
align = "left"
for i = 1, #text do
local reduced_text = text:sub(1, #text - i)
if style.font:get_width(reduced_text) + dots_width <= text_avail_width then
text = reduced_text .. ""
break
end
end
end
common.draw_text(style.font, color, text, align, x, y, w, h)
-- Title
x = x + cpad
w = cx - x
core.push_clip_rect(x, y, w, h)
self:draw_tab_title(view, style.font, is_active, is_hovered, x, y, w, h)
core.pop_clip_rect()
end
@ -547,7 +555,7 @@ function Node:draw_tabs()
for i = self.tab_offset, self.tab_offset + tabs_number - 1 do
local view = self.views[i]
local x, y, w, h = self:get_tab_rect(i)
self:draw_tab(view:get_name(), view == self.active_view,
self:draw_tab(view, view == self.active_view,
i == self.hovered_tab, i == self.hovered_close,
x, y, w, h)
end
@ -688,7 +696,7 @@ function Node:get_split_type(mouse_x, mouse_y)
local local_mouse_x = mouse_x - x
local local_mouse_y = mouse_y - y
if local_mouse_y < 0 then
return "tab"
else

View File

@ -30,7 +30,9 @@ end
function RootView:get_active_node()
return self.root_node:get_node_for_view(core.active_view)
local node = self.root_node:get_node_for_view(core.active_view)
if not node then node = self:get_primary_node() end
return node
end
@ -46,6 +48,7 @@ end
function RootView:get_active_node_default()
local node = self.root_node:get_node_for_view(core.active_view)
if not node then node = self:get_primary_node() end
if node.locked then
local default_view = self:get_primary_node().views[1]
assert(default_view, "internal error: cannot find original document node.")
@ -254,7 +257,7 @@ function RootView:on_mouse_moved(x, y, dx, dy)
self.root_node:on_mouse_moved(x, y, dx, dy)
self.overlapping_node = self.root_node:get_child_overlapping_point(x, y)
local div = self.root_node:get_divider_overlapping_point(x, y)
local tab_index = self.overlapping_node and self.overlapping_node:get_tab_overlapping_point(x, y)
if self.overlapping_node and self.overlapping_node:get_scroll_button_index(x, y) then
@ -269,6 +272,12 @@ function RootView:on_mouse_moved(x, y, dx, dy)
end
function RootView:on_file_dropped(filename, x, y)
local node = self.root_node:get_child_overlapping_point(x, y)
return node and node.active_view:on_file_dropped(filename, x, y)
end
function RootView:on_mouse_wheel(...)
local x, y = self.mouse.x, self.mouse.y
local node = self.root_node:get_child_overlapping_point(x, y)
@ -381,8 +390,8 @@ function RootView:draw_grabbed_tab()
local _,_, w, h = dn.node:get_tab_rect(dn.idx)
local x = self.mouse.x - w / 2
local y = self.mouse.y - h / 2
local text = dn.node.views[dn.idx] and dn.node.views[dn.idx]:get_name() or ""
self.root_node:draw_tab(text, true, true, false, x, y, w, h, true)
local view = dn.node.views[dn.idx]
self.root_node:draw_tab(view, true, true, false, x, y, w, h, true)
end

View File

@ -1,6 +1,6 @@
-- this file is used by lite-xl to setup the Lua environment when starting
VERSION = "@PROJECT_VERSION@"
MOD_VERSION = "2"
VERSION = "2.1beta"
MOD_VERSION = "3"
SCALE = tonumber(os.getenv("LITE_SCALE") or os.getenv("GDK_SCALE") or os.getenv("QT_SCALE_FACTOR")) or SCALE
PATHSEP = package.config:sub(1, 1)
@ -12,8 +12,9 @@ else
local prefix = EXEDIR:match("^(.+)[/\\]bin$")
DATADIR = prefix and (prefix .. '/share/lite-xl') or (EXEDIR .. '/data')
end
USERDIR = (os.getenv("XDG_CONFIG_HOME") and os.getenv("XDG_CONFIG_HOME") .. "/lite-xl")
or (HOME and (HOME .. '/.config/lite-xl') or (EXEDIR .. '/user'))
USERDIR = (system.get_file_info(EXEDIR .. '/user') and (EXEDIR .. '/user'))
or ((os.getenv("XDG_CONFIG_HOME") and os.getenv("XDG_CONFIG_HOME") .. "/lite-xl"))
or (HOME and (HOME .. '/.config/lite-xl'))
package.path = DATADIR .. '/?.lua;' .. package.path
package.path = DATADIR .. '/?/init.lua;' .. package.path
@ -31,3 +32,5 @@ end }
table.pack = table.pack or pack or function(...) return {...} end
table.unpack = table.unpack or unpack
bit32 = bit32 or require "core.bit"

File diff suppressed because it is too large Load Diff

View File

@ -72,4 +72,9 @@ style.syntax["function"] = { common.color "#93DDFA" }
style.syntax_fonts = {}
-- style.syntax_fonts["comment"] = renderer.font.load(path_to_font, size_of_font, rendering_options)
style.log = {
INFO = { icon = "i", color = style.text },
ERROR = { icon = "!", color = style.error }
}
return style

View File

@ -136,20 +136,42 @@ function tokenizer.tokenize(incoming_syntax, text, state)
end
local function find_text(text, p, offset, at_start, close)
local target, res = p.pattern or p.regex, { 1, offset - 1 }, p.regex
local code = type(target) == "table" and target[close and 2 or 1] or target
local target, res = p.pattern or p.regex, { 1, offset - 1 }
local p_idx = close and 2 or 1
local code = type(target) == "table" and target[p_idx] or target
if p.whole_line == nil then p.whole_line = { } end
if p.whole_line[p_idx] == nil then
-- Match patterns that start with '^'
p.whole_line[p_idx] = code:match("^%^") and true or false
if p.whole_line[p_idx] then
-- Remove '^' from the beginning of the pattern
if type(target) == "table" then
target[p_idx] = code:sub(2)
else
p.pattern = p.pattern and code:sub(2)
p.regex = p.regex and code:sub(2)
end
end
end
if p.regex and type(p.regex) ~= "table" then
p._regex = p._regex or regex.compile(p.regex)
code = p._regex
end
end
repeat
local next = res[2] + 1
-- If the pattern contained '^', allow matching only the whole line
if p.whole_line[p_idx] and next > 1 then
return
end
-- go to the start of the next utf-8 character
while text:byte(next) and common.is_utf8_cont(text, next) do
next = next + 1
end
res = p.pattern and { text:find(at_start and "^" .. code or code, next) }
or { regex.match(code, text, next, at_start and regex.ANCHORED or 0) }
res = p.pattern and { text:find((at_start or p.whole_line[p_idx]) and "^" .. code or code, next) }
or { regex.match(code, text, next, (at_start or p.whole_line[p_idx]) and regex.ANCHORED or 0) }
if res[1] and close and target[3] then
local count = 0
for i = res[1] - 1, 1, -1 do

View File

@ -25,7 +25,8 @@ function View:move_towards(t, k, dest, rate)
return self:move_towards(self, t, k, dest, rate)
end
local val = t[k]
if not config.transitions or math.abs(val - dest) < 0.5 then
local diff = math.abs(val - dest)
if not config.transitions or diff < 0.5 then
t[k] = dest
else
rate = rate or 0.5
@ -35,7 +36,7 @@ function View:move_towards(t, k, dest, rate)
end
t[k] = common.lerp(val, dest, rate)
end
if val ~= dest then
if diff > 1e-8 then
core.redraw = true
end
end
@ -98,6 +99,11 @@ function View:on_mouse_moved(x, y, dx, dy)
end
function View:on_file_dropped(filename, x, y)
return false
end
function View:on_text_input(text)
-- no-op
end

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local common = require "core.common"
local config = require "core.config"
@ -10,14 +10,18 @@ local RootView = require "core.rootview"
local DocView = require "core.docview"
local Doc = require "core.doc"
config.plugins.autocomplete = {
-- Amount of characters that need to be written for autocomplete
min_len = 3,
-- The max amount of visible items
max_height = 6,
-- The max amount of scrollable items
max_suggestions = 100,
}
config.plugins.autocomplete = common.merge({
-- Amount of characters that need to be written for autocomplete
min_len = 3,
-- The max amount of visible items
max_height = 6,
-- The max amount of scrollable items
max_suggestions = 100,
-- Maximum amount of symbols to cache per document
max_symbols = 4000,
-- Font size of the description box
desc_font_size = 12
}, config.plugins.autocomplete)
local autocomplete = {}
@ -33,7 +37,7 @@ local triggered_manually = false
local mt = { __tostring = function(t) return t.text end }
function autocomplete.add(t, triggered_manually)
function autocomplete.add(t, manually_triggered)
local items = {}
for text, info in pairs(t.items) do
if type(info) == "table" then
@ -43,9 +47,10 @@ function autocomplete.add(t, triggered_manually)
{
text = text,
info = info.info,
desc = info.desc, -- Description shown on item selected
cb = info.cb, -- A callback called once when item is selected
data = info.data -- Optional data that can be used on cb
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
data = info.data -- Optional data that can be used on cb
},
mt
)
@ -56,7 +61,7 @@ function autocomplete.add(t, triggered_manually)
end
end
if not triggered_manually then
if not manually_triggered then
autocomplete.map[t.name] = { files = t.files or ".*", items = items }
else
autocomplete.map_manually[t.name] = { files = t.files or ".*", items = items }
@ -66,7 +71,7 @@ end
--
-- Thread that scans open document symbols and cache them
--
local max_symbols = config.max_symbols
local max_symbols = config.plugins.autocomplete.max_symbols
core.add_thread(function()
local cache = setmetatable({}, { __mode = "k" })
@ -85,7 +90,9 @@ core.add_thread(function()
doc.disable_symbols = true
core.status_view:show_message("!", style.accent,
"Too many symbols in document "..doc.filename..
": stopping auto-complete for this document according to config.max_symbols.")
": stopping auto-complete for this document according to "..
"config.plugins.autocomplete.max_symbols."
)
collectgarbage('collect')
return {}
end
@ -159,16 +166,6 @@ local function reset_suggestions()
end
end
local function in_table(value, table_array)
for i, element in pairs(table_array) do
if element == value then
return true
end
end
return false
end
local function update_suggestions()
local doc = core.active_view.doc
local filename = doc and doc.filename or ""
@ -199,6 +196,7 @@ local function update_suggestions()
j = j + 1
end
end
suggestions_idx = 1
end
local function get_partial_symbol()
@ -249,6 +247,11 @@ local function get_suggestions_rect(av)
max_width = 150
end
-- if portion not visiable to right, reposition to DocView right margin
if (x - av.position.x) + max_width > av.size.x then
x = (av.size.x + av.position.x) - max_width - (style.padding.x * 2)
end
return
x - style.padding.x,
y - style.padding.y,
@ -256,20 +259,99 @@ local function get_suggestions_rect(av)
max_items * (th + style.padding.y) + style.padding.y
end
local function wrap_line(line, max_chars)
if #line > max_chars then
local lines = {}
local line_len = #line
local new_line = ""
local prev_char = ""
local position = 0
local indent = line:match("^%s+")
for char in line:gmatch(".") do
position = position + 1
if #new_line < max_chars then
new_line = new_line .. char
prev_char = char
if position >= line_len then
table.insert(lines, new_line)
end
else
if
not prev_char:match("%s")
and
not string.sub(line, position+1, 1):match("%s")
and
position < line_len
then
new_line = new_line .. "-"
end
table.insert(lines, new_line)
if indent then
new_line = indent .. char
else
new_line = char
end
end
end
return lines
end
return line
end
local previous_scale = SCALE
local desc_font = style.code_font:copy(
config.plugins.autocomplete.desc_font_size * SCALE
)
local function draw_description_box(text, av, sx, sy, sw, sh)
if previous_scale ~= SCALE then
desc_font = style.code_font:copy(
config.plugins.autocomplete.desc_font_size * SCALE
)
previous_scale = SCALE
end
local font = desc_font
local lh = font:get_height()
local y = sy + style.padding.y
local x = sx + sw + style.padding.x / 4
local width = 0
local char_width = font:get_width(" ")
local draw_left = false;
local max_chars = 0
if sx - av.position.x < av.size.x - (sx - av.position.x) - sw then
max_chars = (((av.size.x+av.position.x) - x) / char_width) - 5
else
draw_left = true;
max_chars = (
(sx - av.position.x - (style.padding.x / 4) - style.scrollbar_size)
/ char_width
) - 5
end
local lines = {}
for line in string.gmatch(text.."\n", "(.-)\n") do
width = math.max(width, style.font:get_width(line))
table.insert(lines, line)
local wrapper_lines = wrap_line(line, max_chars)
if type(wrapper_lines) == "table" then
for _, wrapped_line in pairs(wrapper_lines) do
width = math.max(width, font:get_width(wrapped_line))
table.insert(lines, wrapped_line)
end
else
width = math.max(width, font:get_width(line))
table.insert(lines, line)
end
end
local height = #lines * style.font:get_height()
if draw_left then
x = sx - (style.padding.x / 4) - width - (style.padding.x * 2)
end
local height = #lines * font:get_height()
-- draw background rect
renderer.draw_rect(
sx + sw + style.padding.x / 4,
x,
sy,
width + style.padding.x * 2,
height + style.padding.y * 2,
@ -277,13 +359,10 @@ local function draw_description_box(text, av, sx, sy, sw, sh)
)
-- draw text
local lh = style.font:get_height()
local y = sy + style.padding.y
local x = sx + sw + style.padding.x / 4
for _, line in pairs(lines) do
common.draw_text(
style.font, style.text, line, "left", x + style.padding.x, y, width, lh
font, style.text, line, "left",
x + style.padding.x, y, width, lh
)
y = y + lh
end
@ -320,10 +399,9 @@ local function draw_suggestions_box(av)
end
y = y + lh
if suggestions_idx == i then
if s.cb then
s.cb(suggestions_idx, s)
s.cb = nil
s.data = nil
if s.onhover then
s.onhover(suggestions_idx, s)
s.onhover = nil
end
if s.desc and #s.desc > 0 then
draw_description_box(s.desc, av, rx, ry, rw, rh)
@ -487,10 +565,17 @@ command.add(predicate, {
["autocomplete:complete"] = function()
local doc = core.active_view.doc
local line, col = doc:get_selection()
local text = suggestions[suggestions_idx].text
doc:insert(line, col, text)
doc:remove(line, col, line, col - #partial)
doc:set_selection(line, col + #text - #partial)
local item = suggestions[suggestions_idx]
local text = item.text
local inserted = false
if item.onselect then
inserted = item.onselect(suggestions_idx, item)
end
if not inserted then
doc:insert(line, col, text)
doc:remove(line, col, line, col - #partial)
doc:set_selection(line, col + #text - #partial)
end
reset_suggestions()
end,

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local config = require "core.config"
local Doc = require "core.doc"

View File

@ -1,8 +1,9 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local command = require "core.command"
local keymap = require "core.keymap"
local ContextMenu = require "core.contextmenu"
local DocView = require "core.docview"
local RootView = require "core.rootview"
local menu = ContextMenu()
@ -32,7 +33,7 @@ function RootView:draw(...)
menu:draw()
end
command.add(nil, {
command.add(function() return getmetatable(core.active_view) == DocView end, {
["context:show"] = function()
menu:show(core.active_view.position.x, core.active_view.position.y)
end
@ -42,23 +43,24 @@ keymap.add {
["menu"] = "context:show"
}
local function copy_log()
local item = core.active_view.hovered_item
if item then
system.set_clipboard(core.get_log(item))
end
end
local function open_as_doc()
local doc = core.open_doc("logs.txt")
core.root_view:open_doc(doc)
doc:insert(1, 1, core.get_log())
end
menu:register("core.logview", {
{ text = "Copy entry", command = copy_log },
{ text = "Open as file", command = open_as_doc }
command.add(function() return menu.show_context_menu == true end, {
["context:focus-previous"] = function()
menu:focus_previous()
end,
["context:focus-next"] = function()
menu:focus_next()
end,
["context:hide"] = function()
menu:hide()
end,
["context:on-selected"] = function()
menu:call_selected_item()
end,
})
keymap.add { ["return"] = "context:on-selected" }
keymap.add { ["up"] = "context:focus-previous" }
keymap.add { ["down"] = "context:focus-next" }
keymap.add { ["escape"] = "context:hide" }
if require("plugins.scale") then
menu:register("core.docview", {

View File

@ -1,95 +1,256 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local command = require "core.command"
local common = require "core.common"
local config = require "core.config"
local core_syntax = require "core.syntax"
local DocView = require "core.docview"
local Doc = require "core.doc"
local tokenizer = require "core.tokenizer"
local cache = setmetatable({}, { __mode = "k" })
local comments_cache = {}
local auto_detect_max_lines = 150
local function add_to_stat(stat, val)
for i = 1, #stat do
if val == stat[i][1] then
stat[i][2] = stat[i][2] + 1
return
end
local function indent_occurrences_more_than_once(stat, idx)
if stat[idx-1] and stat[idx-1] == stat[idx] then
return true
elseif stat[idx+1] and stat[idx+1] == stat[idx] then
return true
end
stat[#stat + 1] = {val, 1}
return false
end
local function optimal_indent_from_stat(stat)
if #stat == 0 then return nil, 0 end
local bins = {}
for k = 1, #stat do
local indent = stat[k][1]
table.sort(stat, function(a, b) return a > b end)
local best_indent = 0
local best_score = 0
local count = #stat
for x=1, count do
local indent = stat[x]
local score = 0
local mult_prev, lines_prev
for i = k, #stat do
if stat[i][1] % indent == 0 then
local mult = stat[i][1] / indent
if not mult_prev or (mult_prev + 1 == mult and lines_prev / stat[i][2] > 0.1) then
-- we add the number of lines to the score only if the previous
-- multiple of "indent" was populated with enough lines.
score = score + stat[i][2]
end
mult_prev, lines_prev = mult, stat[i][2]
for y=1, count do
if y ~= x and stat[y] % indent == 0 then
score = score + 1
elseif
indent > stat[y]
and
indent_occurrences_more_than_once(stat, y)
then
score = 0
break
end
end
bins[#bins + 1] = {indent, score}
end
table.sort(bins, function(a, b) return a[2] > b[2] end)
return bins[1][1], bins[1][2]
end
-- return nil if it is a comment or blank line or the initial part of the
-- line otherwise.
-- we don't need to have the whole line to detect indentation.
local function get_first_line_part(tokens)
local i, n = 1, #tokens
while i + 1 <= n do
local ttype, ttext = tokens[i], tokens[i + 1]
if ttype ~= "comment" and ttext:gsub("%s+", "") ~= "" then
return ttext
if score > best_score then
best_indent = indent
best_score = score
end
if score > 0 then
break
end
i = i + 2
end
return best_score > 0 and best_indent or nil, best_score
end
local function escape_comment_tokens(token)
local special_chars = "*-%[].()+?^$"
local escaped = ""
for x=1, token:len() do
local found = false
for y=1, special_chars:len() do
if token:sub(x, x) == special_chars:sub(y, y) then
escaped = escaped .. "%" .. token:sub(x, x)
found = true
break
end
end
if not found then
escaped = escaped .. token:sub(x, x)
end
end
return escaped
end
local function get_comment_patterns(syntax)
if comments_cache[syntax] then
if #comments_cache[syntax] > 0 then
return comments_cache[syntax]
else
return nil
end
end
local comments = {}
for idx=1, #syntax.patterns do
local pattern = syntax.patterns[idx]
local startp = ""
if
type(pattern.type) == "string"
and
(pattern.type == "comment" or pattern.type == "string")
then
local not_is_string = pattern.type ~= "string"
if pattern.pattern then
startp = type(pattern.pattern) == "table"
and pattern.pattern[1] or pattern.pattern
if not_is_string and startp:sub(1, 1) ~= "^" then
startp = "^%s*" .. startp
elseif not_is_string then
startp = "^%s*" .. startp:sub(2, startp:len())
end
if type(pattern.pattern) == "table" then
table.insert(comments, {"p", startp, pattern.pattern[2]})
elseif not_is_string then
table.insert(comments, {"p", startp})
end
elseif pattern.regex then
startp = type(pattern.regex) == "table"
and pattern.regex[1] or pattern.regex
if not_is_string and startp:sub(1, 1) ~= "^" then
startp = "^\\s*" .. startp
elseif not_is_string then
startp = "^\\s*" .. startp:sub(2, startp:len())
end
if type(pattern.regex) == "table" then
table.insert(comments, {
"r", regex.compile(startp), regex.compile(pattern.regex[2])
})
elseif not_is_string then
table.insert(comments, {"r", regex.compile(startp)})
end
end
elseif pattern.syntax then
local subsyntax = type(pattern.syntax) == 'table' and pattern.syntax
or core_syntax.get("file"..pattern.syntax, "")
local sub_comments = get_comment_patterns(subsyntax)
if sub_comments then
for s=1, #sub_comments do
table.insert(comments, sub_comments[s])
end
end
end
end
if #comments == 0 then
local single_line_comment = syntax.comment
and escape_comment_tokens(syntax.comment) or nil
local block_comment = nil
if syntax.block_comment then
block_comment = {
escape_comment_tokens(syntax.block_comment[1]),
escape_comment_tokens(syntax.block_comment[2])
}
end
if single_line_comment then
table.insert(comments, {"p", "^%s*" .. single_line_comment})
end
if block_comment then
table.insert(comments, {"p", "^%s*" .. block_comment[1], block_comment[2]})
end
end
comments_cache[syntax] = comments
if #comments > 0 then
return comments
end
return nil
end
local function get_non_empty_lines(syntax, lines)
return coroutine.wrap(function()
local tokens, state
local comments = get_comment_patterns(syntax)
local i = 0
local end_regex = nil
local end_pattern = nil
local inside_comment = false
for _, line in ipairs(lines) do
tokens, state = tokenizer.tokenize(syntax, line, state)
local line_start = get_first_line_part(tokens)
if line_start then
i = i + 1
coroutine.yield(i, line_start)
if line:gsub("^%s+", "") ~= "" then
local is_comment = false
if comments then
if not inside_comment then
for c=1, #comments do
local comment = comments[c]
if comment[1] == "p" then
if comment[3] then
local start, ending = line:find(comment[2])
if start then
if not line:find(comment[3], ending+1) then
is_comment = true
inside_comment = true
end_pattern = comment[3]
end
break
end
elseif line:find(comment[2]) then
is_comment = true
break
end
else
if comment[3] then
local start, ending = regex.match(
comment[2], line, 1, regex.ANCHORED
)
if start then
if not regex.match(
comment[3], line, ending+1, regex.ANCHORED
)
then
is_comment = true
inside_comment = true
end_regex = comment[3]
end
break
end
elseif regex.match(comment[2], line, 1, regex.ANCHORED) then
is_comment = true
break
end
end
end
elseif end_pattern and line:find(end_pattern) then
is_comment = true
inside_comment = false
end_pattern = nil
elseif end_regex and regex.match(end_regex, line) then
is_comment = true
inside_comment = false
end_regex = nil
end
end
if
not is_comment
and
not inside_comment
then
i = i + 1
coroutine.yield(i, line)
end
end
end
end)
end
local auto_detect_max_lines = 100
local function detect_indent_stat(doc)
local stat = {}
local tab_count = 0
local runs = 1
local max_lines = auto_detect_max_lines
for i, text in get_non_empty_lines(doc.syntax, doc.lines) do
local str = text:match("^ %s+%S")
if str then add_to_stat(stat, #str - 1) end
local str = text:match("^\t+")
if str then tab_count = tab_count + 1 end
local spaces = text:match("^ +")
if spaces then table.insert(stat, spaces:len()) 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
if i == max_lines and runs < 5 and #stat == 0 and tab_count == 0 then
max_lines = max_lines + auto_detect_max_lines
runs = runs + 1
-- Stop parsing when files is very long. Not needed for euristic determination.
if i > auto_detect_max_lines then break end
elseif i > max_lines then break end
end
table.sort(stat, function(a, b) return a[1] < b[1] end)
local indent, score = optimal_indent_from_stat(stat)
if tab_count > score then
return "hard", config.indent_size, tab_count
@ -101,7 +262,7 @@ end
local function update_cache(doc)
local type, size, score = detect_indent_stat(doc)
local score_threshold = 4
local score_threshold = 2
if score < score_threshold then
-- use default values
type = config.tab_type
@ -130,9 +291,11 @@ end
local function set_indent_type(doc, type)
local _, indent_size = doc:get_indent_info()
cache[doc] = {type = type,
size = indent_size,
confirmed = true}
cache[doc] = {
type = type,
size = indent_size,
confirmed = true
}
doc.indent_info = cache[doc]
end
@ -158,9 +321,11 @@ end
local function set_indent_size(doc, size)
local indent_type = doc:get_indent_info()
cache[doc] = {type = indent_type,
size = size,
confirmed = true}
cache[doc] = {
type = indent_type,
size = size,
confirmed = true
}
doc.indent_info = cache[doc]
end
@ -168,14 +333,14 @@ local function set_indent_size_command()
core.command_view:enter(
"Specify indent size for current file",
function(value) -- submit
local value = math.floor(tonumber(value))
value = math.floor(tonumber(value))
local doc = core.active_view.doc
set_indent_size(doc, value)
end,
nil, -- suggest
nil, -- cancel
function(value) -- validate
local value = tonumber(value)
value = tonumber(value)
return value ~= nil and value >= 1
end
)
@ -187,20 +352,24 @@ command.add("core.docview", {
["indent:set-file-indent-size"] = set_indent_size_command
})
command.add(function()
command.add(
function()
return core.active_view:is(DocView)
and cache[core.active_view.doc]
and cache[core.active_view.doc].type == "soft"
and cache[core.active_view.doc]
and cache[core.active_view.doc].type == "soft"
end, {
["indent:switch-file-to-tabs-indentation"] = function() set_indent_type(core.active_view.doc, "hard") end
["indent:switch-file-to-tabs-indentation"] = function()
set_indent_type(core.active_view.doc, "hard")
end
})
command.add(function()
command.add(
function()
return core.active_view:is(DocView)
and cache[core.active_view.doc]
and cache[core.active_view.doc].type == "hard"
and cache[core.active_view.doc]
and cache[core.active_view.doc].type == "hard"
end, {
["indent:switch-file-to-spaces-indentation"] = function() set_indent_type(core.active_view.doc, "soft") end
["indent:switch-file-to-spaces-indentation"] = function()
set_indent_type(core.active_view.doc, "soft")
end
})

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local style = require "core.style"
local DocView = require "core.docview"

View File

@ -1,10 +1,11 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local syntax = require "core.syntax"
syntax.add {
name = "C",
files = { "%.c$", "%.h$", "%.inl$" },
files = { "%.c$" },
comment = "//",
block_comment = { "/*", "*/" },
patterns = {
{ pattern = "//.-\n", type = "comment" },
{ pattern = { "/%*", "%*/" }, type = "comment" },
@ -16,10 +17,21 @@ syntax.add {
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
{ pattern = "struct%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{ pattern = "union%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{
pattern = "^%s*#define%s+()[%a_][%a%d_]*",
type = { "keyword", "symbol" }
},
-- Uppercase constants of at least 2 chars in len
{
pattern = "_?%u[%u_][%u%d_]*%f[%s%+%*%-%.%(%)%?%^%%=/<>~|&;:,!]",
type = "number"
},
-- Magic constants
{ pattern = "__[%u%l]+__", type = "number" },
{ pattern = "[%a_][%w_]*%f[(]", type = "function" },
{ pattern = "[%a_][%w_]*", type = "symbol" },
{ pattern = "#include%s()<.->", type = {"keyword", "string"} },
{ pattern = "#[%a_][%w_]*", type = "keyword" },
{ pattern = "[%a_][%w_]*", type = "symbol" },
},
symbols = {
["if"] = "keyword",
@ -44,6 +56,8 @@ syntax.add {
["case"] = "keyword",
["default"] = "keyword",
["auto"] = "keyword",
["struct"] = "keyword",
["union"] = "keyword",
["void"] = "keyword2",
["int"] = "keyword2",
["short"] = "keyword2",
@ -60,6 +74,7 @@ syntax.add {
["#if"] = "keyword",
["#ifdef"] = "keyword",
["#ifndef"] = "keyword",
["#elif"] = "keyword",
["#else"] = "keyword",
["#elseif"] = "keyword",
["#endif"] = "keyword",

View File

@ -1,6 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
pcall(require, "plugins.language_c")
-- mod-version:3 -- lite-xl 2.1
local syntax = require "core.syntax"
syntax.add {
@ -10,28 +8,54 @@ syntax.add {
"%.c++$", "%.hh$", "%.H$", "%.hxx$", "%.hpp$", "%.h++$"
},
comment = "//",
block_comment = { "/*", "*/" },
patterns = {
{ pattern = "//.-\n", type = "comment" },
{ pattern = { "/%*", "%*/" }, type = "comment" },
{ pattern = { '"', '"', '\\' }, type = "string" },
{ pattern = { "'", "'", '\\' }, type = "string" },
{ pattern = "0x%x+", type = "number" },
{ pattern = "%d+[%d%.eE]*f?", type = "number" },
{ pattern = "%.?%d+f?", type = "number" },
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
{ pattern = "//.-\n", type = "comment" },
{ pattern = { "/%*", "%*/" }, type = "comment" },
{ pattern = { '"', '"', '\\' }, type = "string" },
{ pattern = { "'", "'", '\\' }, type = "string" },
{ pattern = "0x%x+", type = "number" },
{ pattern = "%d+[%d%.eE]*f?", type = "number" },
{ pattern = "%.?%d+f?", type = "number" },
{
pattern = "^%s*#define%s+()[%a_][%a%d_]*",
type = { "keyword", "symbol" }
},
{
pattern = "#include%s+()<.->",
type = { "keyword", "string" }
},
{ pattern = "[%+%-=/%*%^%%<>!~|:&]", type = "operator" },
{ pattern = "struct%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{ pattern = "class%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{ pattern = "union%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{ pattern = "namespace%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{ pattern = "[%a_][%w_]*::", type = "symbol" },
{ pattern = "::", type = "symbol" },
{ pattern = "[%a_][%w_]*", type = "symbol" },
{ pattern = "#include%s()<.->", type = {"keyword", "string"} },
{ pattern = "#[%a_][%w_]*", type = "keyword" },
{ pattern = "[%a_][%w_]*%f[(]", type = "function" },
-- Match scope operator element access
{ pattern = "[%a_][%w_]*[%s]*%f[:]", type = "literal" },
-- Uppercase constants of at least 2 chars in len
{
pattern = "_?%u[%u_][%u%d_]*%f[%s%+%*%-%.%(%)%?%^%%=/<>~|&;:,!]",
type = "number"
},
-- Magic constants
{ pattern = "__[%u%l]+__", type = "number" },
-- Somehow makes macros properly work
{ pattern = "#[%a_][%w_]*", type = "normal" },
-- Everything else to make the tokenizer work properly
{ pattern = "[%a_][%w_]*", type = "symbol" },
},
symbols = {
["alignof"] = "keyword",
["alignas"] = "keyword",
["and"] = "keyword",
["and_eq"] = "keyword",
["not"] = "keyword",
["not_eq"] = "keyword",
["or"] = "keyword",
["or_eq"] = "keyword",
["xor"] = "keyword",
["xor_eq"] = "keyword",
["private"] = "keyword",
["protected"] = "keyword",
["public"] = "keyword",
@ -39,9 +63,12 @@ syntax.add {
["nullptr"] = "keyword",
["operator"] = "keyword",
["asm"] = "keyword",
["bitand"] = "keyword",
["bitor"] = "keyword",
["catch"] = "keyword",
["throw"] = "keyword",
["try"] = "keyword",
["class"] = "keyword",
["compl"] = "keyword",
["explicit"] = "keyword",
["export"] = "keyword",
@ -51,8 +78,8 @@ syntax.add {
["constinit"] = "keyword",
["const_cast"] = "keyword",
["dynamic_cast"] = "keyword",
["reinterpret_cast"] = "keyword",
["static_cast"] = "keyword",
["reinterpret_cast"] = "keyword",
["static_cast"] = "keyword",
["static_assert"] = "keyword",
["template"] = "keyword",
["this"] = "keyword",
@ -63,7 +90,6 @@ syntax.add {
["co_yield"] = "keyword",
["decltype"] = "keyword",
["delete"] = "keyword",
["export"] = "keyword",
["friend"] = "keyword",
["typeid"] = "keyword",
["typename"] = "keyword",
@ -71,6 +97,7 @@ syntax.add {
["override"] = "keyword",
["virtual"] = "keyword",
["using"] = "keyword",
["namespace"] = "keyword",
["new"] = "keyword",
["noexcept"] = "keyword",
["if"] = "keyword",
@ -84,6 +111,8 @@ syntax.add {
["continue"] = "keyword",
["return"] = "keyword",
["goto"] = "keyword",
["struct"] = "keyword",
["union"] = "keyword",
["typedef"] = "keyword",
["enum"] = "keyword",
["extern"] = "keyword",
@ -95,7 +124,6 @@ syntax.add {
["case"] = "keyword",
["default"] = "keyword",
["auto"] = "keyword",
["const"] = "keyword",
["void"] = "keyword2",
["int"] = "keyword2",
["short"] = "keyword2",
@ -105,12 +133,18 @@ syntax.add {
["char"] = "keyword2",
["unsigned"] = "keyword2",
["bool"] = "keyword2",
["true"] = "keyword2",
["false"] = "keyword2",
["true"] = "literal",
["false"] = "literal",
["NULL"] = "literal",
["wchar_t"] = "keyword2",
["char8_t"] = "keyword2",
["char16_t"] = "keyword2",
["char32_t"] = "keyword2",
["#include"] = "keyword",
["#if"] = "keyword",
["#ifdef"] = "keyword",
["#ifndef"] = "keyword",
["#elif"] = "keyword",
["#else"] = "keyword",
["#elseif"] = "keyword",
["#endif"] = "keyword",
@ -118,6 +152,5 @@ syntax.add {
["#warning"] = "keyword",
["#error"] = "keyword",
["#pragma"] = "keyword",
},
},
}

View File

@ -1,9 +1,10 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local syntax = require "core.syntax"
syntax.add {
name = "CSS",
files = { "%.css$" },
block_comment = { "/*", "*/" },
patterns = {
{ pattern = "\\.", type = "normal" },
{ pattern = "//.-\n", type = "comment" },

View File

@ -1,31 +1,32 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local syntax = require "core.syntax"
syntax.add {
name = "HTML",
files = { "%.html?$" },
block_comment = { "<!--", "-->" },
patterns = {
{
pattern = {
{
pattern = {
"<%s*[sS][cC][rR][iI][pP][tT]%s+[tT][yY][pP][eE]%s*=%s*" ..
"['\"]%a+/[jJ][aA][vV][aA][sS][cC][rR][iI][pP][tT]['\"]%s*>",
"<%s*/[sS][cC][rR][iI][pP][tT]>"
},
syntax = ".js",
type = "function"
},
{
pattern = {
"<%s*[sS][cC][rR][iI][pP][tT]%s*>",
"<%s*/%s*[sS][cC][rR][iI][pP][tT]>"
"<%s*/[sS][cC][rR][iI][pP][tT]>"
},
syntax = ".js",
type = "function"
},
{
pattern = {
"<%s*[sS][tT][yY][lL][eE][^>]*>",
"<%s*/%s*[sS][tT][yY][lL][eE]%s*>"
{
pattern = {
"<%s*[sS][cC][rR][iI][pP][tT]%s*>",
"<%s*/%s*[sS][cC][rR][iI][pP][tT]>"
},
syntax = ".js",
type = "function"
},
{
pattern = {
"<%s*[sS][tT][yY][lL][eE][^>]*>",
"<%s*/%s*[sS][tT][yY][lL][eE]%s*>"
},
syntax = ".css",
type = "function"

View File

@ -1,10 +1,11 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local syntax = require "core.syntax"
syntax.add {
name = "JavaScript",
files = { "%.js$", "%.json$", "%.cson$" },
comment = "//",
block_comment = { "/*", "*/" },
patterns = {
{ pattern = "//.-\n", type = "comment" },
{ pattern = { "/%*", "%*/" }, type = "comment" },

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local syntax = require "core.syntax"
syntax.add {
@ -6,6 +6,7 @@ syntax.add {
files = "%.lua$",
headers = "^#!.*[ /]lua",
comment = "--",
block_comment = { "--[[", "]]" },
patterns = {
{ pattern = { '"', '"', '\\' }, type = "string" },
{ pattern = { "'", "'", '\\' }, type = "string" },

View File

@ -1,56 +1,223 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
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
-- no way to copy user custom font with additional attributes :(
style.syntax_fonts["markdown_"..attr] = renderer.font.load(
DATADIR .. "/fonts/JetBrainsMono-Regular.ttf",
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 = "^%(%)"
syntax.add {
name = "Markdown",
files = { "%.md$", "%.markdown$" },
block_comment = { "<!--", "-->" },
patterns = {
{ pattern = "\\.", type = "normal" },
{ pattern = { "<!%-%-", "%-%->" }, type = "comment" },
---- HTML rules imported and adapted from language_html
---- to not conflict with markdown rules
-- Inline JS and CSS
{
pattern = {
"<%s*[sS][cC][rR][iI][pP][tT]%s+[tT][yY][pP][eE]%s*=%s*" ..
"['\"]%a+/[jJ][aA][vV][aA][sS][cC][rR][iI][pP][tT]['\"]%s*>",
"<%s*/[sS][cC][rR][iI][pP][tT]>"
},
syntax = ".js",
type = "function"
},
{
pattern = {
"<%s*[sS][cC][rR][iI][pP][tT]%s*>",
"<%s*/%s*[sS][cC][rR][iI][pP][tT]>"
},
syntax = ".js",
type = "function"
},
{
pattern = {
"<%s*[sS][tT][yY][lL][eE][^>]*>",
"<%s*/%s*[sS][tT][yY][lL][eE]%s*>"
},
syntax = ".css",
type = "function"
},
-- Comments
{ pattern = { "<!%-%-", "%-%->" }, type = "comment" },
-- Tags
{ pattern = "%f[^<]![%a_][%w_]*", type = "keyword2" },
{ pattern = "%f[^<][%a_][%w_]*", type = "function" },
{ pattern = "%f[^<]/[%a_][%w_]*", type = "function" },
-- Attributes
{
pattern = "[a-z%-]+%s*()=%s*()\".-\"",
type = { "keyword", "operator", "string" }
},
{
pattern = "[a-z%-]+%s*()=%s*()'.-'",
type = { "keyword", "operator", "string" }
},
{
pattern = "[a-z%-]+%s*()=%s*()%-?%d[%d%.]*",
type = { "keyword", "operator", "number" }
},
-- Entities
{ pattern = "&#?[a-zA-Z0-9]+;", type = "keyword2" },
---- Markdown rules
-- code blocks
{ pattern = { "```c++", "```" }, type = "string", syntax = ".cpp" },
{ pattern = { "```cpp", "```" }, type = "string", syntax = ".cpp" },
{ pattern = { "```python", "```" }, type = "string", syntax = ".py" },
{ pattern = { "```ruby", "```" }, type = "string", syntax = ".rb" },
{ pattern = { "```perl", "```" }, type = "string", syntax = ".pl" },
{ pattern = { "```php", "```" }, type = "string", syntax = ".php" },
{ pattern = { "```javascript", "```" }, type = "string", syntax = ".js" },
{ pattern = { "```json", "```" }, type = "string", syntax = ".js" },
{ pattern = { "```html", "```" }, type = "string", syntax = ".html" },
{ pattern = { "```ini", "```" }, type = "string", syntax = ".ini" },
{ pattern = { "```xml", "```" }, type = "string", syntax = ".xml" },
{ pattern = { "```css", "```" }, type = "string", syntax = ".css" },
{ pattern = { "```lua", "```" }, type = "string", syntax = ".lua" },
{ pattern = { "```bash", "```" }, type = "string", syntax = ".sh" },
{ pattern = { "```sh", "```" }, type = "string", syntax = ".sh" },
{ pattern = { "```java", "```" }, type = "string", syntax = ".java" },
{ pattern = { "```c#", "```" }, type = "string", syntax = ".cs" },
{ pattern = { "```cmake", "```" }, type = "string", syntax = ".cmake" },
{ pattern = { "```d", "```" }, type = "string", syntax = ".d" },
{ pattern = { "```glsl", "```" }, type = "string", syntax = ".glsl" },
{ pattern = { "```c", "```" }, type = "string", syntax = ".c" },
{ pattern = { "```julia", "```" }, type = "string", syntax = ".jl" },
{ pattern = { "```rust", "```" }, type = "string", syntax = ".rs" },
{ pattern = { "```dart", "```" }, type = "string", syntax = ".dart" },
{ pattern = { "```julia", "```" }, type = "string", syntax = ".jl" },
{ pattern = { "```rust", "```" }, type = "string", syntax = ".rs" },
{ pattern = { "```dart", "```" }, type = "string", syntax = ".dart" },
{ pattern = { "```v", "```" }, type = "string", syntax = ".v" },
{ pattern = { "```toml", "```" }, type = "string", syntax = ".toml" },
{ pattern = { "```yaml", "```" }, type = "string", syntax = ".yaml" },
{ pattern = { "```php", "```" }, type = "string", syntax = ".php" },
{ pattern = { "```nim", "```" }, type = "string", syntax = ".nim" },
{ pattern = { "```typescript", "```" }, type = "string", syntax = ".ts" },
{ pattern = { "```rescript", "```" }, type = "string", syntax = ".res" },
{ pattern = { "```moon", "```" }, type = "string", syntax = ".moon" },
{ pattern = { "```go", "```" }, type = "string", syntax = ".go" },
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
{ pattern = { "```", "```" }, type = "string" },
{ pattern = { "``", "``", "\\" }, type = "string" },
{ pattern = { "`", "`", "\\" }, type = "string" },
{ pattern = { "~~", "~~", "\\" }, type = "keyword2" },
{ pattern = "%-%-%-+", type = "comment" },
{ pattern = "%*%s+", type = "operator" },
{ pattern = { "%*", "[%*\n]", "\\" }, type = "operator" },
{ pattern = { "%_", "[%_\n]", "\\" }, type = "keyword2" },
{ pattern = "#.-\n", type = "keyword" },
{ pattern = "!?%[.-%]%(.-%)", type = "function" },
{ pattern = "https?://%S+", type = "function" },
{ pattern = { "```toml", "```" }, type = "string", syntax = ".toml" },
{ pattern = { "```yaml", "```" }, type = "string", syntax = ".yaml" },
{ pattern = { "```nim", "```" }, type = "string", syntax = ".nim" },
{ pattern = { "```typescript", "```" }, type = "string", syntax = ".ts" },
{ pattern = { "```rescript", "```" }, type = "string", syntax = ".res" },
{ pattern = { "```moon", "```" }, type = "string", syntax = ".moon" },
{ pattern = { "```go", "```" }, type = "string", syntax = ".go" },
{ pattern = { "```lobster", "```" }, type = "string", syntax = ".lobster" },
{ pattern = { "```liquid", "```" }, type = "string", syntax = ".liquid" },
{ pattern = { "```", "```" }, type = "string" },
{ pattern = { "``", "``" }, type = "string" },
{ pattern = { "%f[\\`]%`[%S]", "`" }, type = "string" },
-- strike
{ pattern = { "~~", "~~" }, type = "keyword2" },
-- highlight
{ pattern = { "==", "==" }, type = "literal" },
-- lines
{ pattern = "^%-%-%-+\n", type = "comment" },
{ pattern = "^%*%*%*+\n", type = "comment" },
{ pattern = "^___+\n", type = "comment" },
-- bullets
{ pattern = "^%s*%*%s", type = "number" },
{ pattern = "^%s*%-%s", type = "number" },
{ pattern = "^%s*%+%s", type = "number" },
-- numbered bullet
{ pattern = "^%s*[0-9]+%.%s", type = "number" },
-- blockquote
{ pattern = "^%s*>+%s", type = "string" },
-- bold and italic
{ pattern = { "%*%*%*%S", "%*%*%*" }, type = "markdown_bold_italic" },
{ pattern = { "%*%*%S", "%*%*" }, type = "markdown_bold" },
-- handle edge case where asterisk can be at end of line and not close
{
pattern = { "%f[\\%*]%*[%S]", "%*%f[^%*]" },
type = "markdown_italic"
},
-- alternative bold italic formats
{ pattern = "^___[%s%p%w]+___%s" , type = "markdown_bold_italic" },
{ pattern = "^__[%s%p%w]+__%s" , type = "markdown_bold" },
{ pattern = "^_[%s%p%w]+_%s" , type = "markdown_italic" },
{ pattern = { "%s___", "___%f[%s]" }, type = "markdown_bold_italic" },
{ pattern = { "%s__", "__%f[%s]" }, type = "markdown_bold" },
{ pattern = { "%s_[%S]", "_%f[%s]" }, type = "markdown_italic" },
-- heading with custom id
{
pattern = "^#+%s[%w%s%p]+(){()#[%w%-]+()}",
type = { "keyword", "function", "string", "function" }
},
-- headings
{ pattern = "^#+%s.+\n", type = "keyword" },
-- superscript and subscript
{
pattern = "%^()%d+()%^",
type = { "function", "number", "function" }
},
{
pattern = "%~()%d+()%~",
type = { "function", "number", "function" }
},
-- definitions
{ pattern = "^:%s.+", type = "function" },
-- emoji
{ pattern = ":[a-zA-Z0-9_%-]+:", type = "literal" },
-- images and link
{
pattern = "!?%[!?%[()["..in_squares_match.."]+()%]%(()["..in_parenthesis_match.."]+()%)%]%(()["..in_parenthesis_match.."]+()%)",
type = { "function", "string", "function", "number", "function", "number", "function" }
},
{
pattern = "!?%[!?%[?()["..in_squares_match.."]+()%]?%]%(()["..in_parenthesis_match.."]+()%)",
type = { "function", "string", "function", "number", "function" }
},
-- reference links
{
pattern = "%[()["..in_squares_match.."]+()%] *()%[()["..in_squares_match.."]+()%]",
type = { "function", "string", "function", "function", "number", "function" }
},
{
pattern = "^%s*%[%^()["..in_squares_match.."]+()%]: ",
type = { "function", "number", "function" }
},
{
pattern = "^%s*%[%^?()["..in_squares_match.."]+()%]:%s+.+\n",
type = { "function", "number", "function" }
},
{
pattern = "!?%[%^?()["..in_squares_match.."]+()%]",
type = { "function", "number", "function" }
},
-- url's and email
{
pattern = "<[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+%.[a-zA-Z0-9-.]+>",
type = "function"
},
{ pattern = "<https?://%S+>", type = "function" },
{ pattern = "https?://%S+", type = "function" }
},
symbols = { },
}
-- Adjust the color on theme changes
core.add_thread(function()
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"]
end
initial_color = style.syntax["keyword2"]
end
coroutine.yield(1)
end
end)

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local syntax = require "core.syntax"
syntax.add {

View File

@ -1,10 +1,11 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local syntax = require "core.syntax"
syntax.add {
name = "XML",
files = { "%.xml$" },
headers = "<%?xml",
block_comment = { "<!--", "-->" },
patterns = {
{ pattern = { "<!%-%-", "%-%->" }, type = "comment" },
{ pattern = { '%f[^>][^<]', '%f[<]' }, type = "normal" },

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local config = require "core.config"
local style = require "core.style"
local DocView = require "core.docview"

View File

@ -0,0 +1,523 @@
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local common = require "core.common"
local DocView = require "core.docview"
local Doc = require "core.doc"
local style = require "core.style"
local config = require "core.config"
local command = require "core.command"
local keymap = require "core.keymap"
local translate = require "core.doc.translate"
config.plugins.linewrapping = {
-- The type of wrapping to perform. Can be "letter" or "word".
mode = "letter",
-- If nil, uses the DocView's size, otherwise, uses this exact width.
width_override = nil,
-- Whether or not to draw a guide
guide = true,
-- Whether or not we should indent ourselves like the first line of a wrapped block.
indent = true,
-- Whether or not to enable wrapping by default when opening files.
enable_by_default = true
}
local LineWrapping = {}
-- Computes the breaks for a given line, width and mode. Returns a list of columns
-- at which the line should be broken.
function LineWrapping.compute_line_breaks(doc, default_font, line, width, mode)
local xoffset, last_i, i, last_space, last_width, begin_width = 0, 1, 1, 1, 0, 0
local splits = { 1 }
for idx, type, text in doc.highlighter:each_token(line) do
local font = style.syntax_fonts[type] or default_font
if idx == 1 and config.plugins.linewrapping.indent then
local _, indent_end = text:find("^%s+")
if indent_end then begin_width = font:get_width(text:sub(1, indent_end)) end
end
local w = font:get_width(text)
if xoffset + w > width then
for char in common.utf8_chars(text) do
w = font:get_width(char)
xoffset = xoffset + w
if xoffset > width then
if mode == "word" then
table.insert(splits, last_space + 1)
xoffset = xoffset - last_width + w + begin_width
else
table.insert(splits, i)
xoffset = w + begin_width
end
elseif char == ' ' then
last_space = i
last_width = xoffset
end
i = i + #char
end
else
xoffset = xoffset + w
i = i + #text
end
end
return splits, begin_width
end
-- breaks are held in a single table that contains n*2 elements, where n is the amount of line breaks.
-- each element represents line and column of the break. line_offset will check from the specified line
-- if the first line has not changed breaks, it will stop there.
function LineWrapping.reconstruct_breaks(docview, default_font, width, line_offset)
if width ~= math.huge then
local doc = docview.doc
-- two elements per wrapped line; first maps to original line number, second to column number.
docview.wrapped_lines = { }
-- one element per actual line; maps to the first index of in wrapped_lines for this line
docview.wrapped_line_to_idx = { }
-- one element per actual line; gives the indent width for the acutal line
docview.wrapped_line_offsets = { }
docview.wrapped_settings = { ["width"] = width, ["font"] = default_font }
for i = line_offset or 1, #doc.lines do
local breaks, offset = LineWrapping.compute_line_breaks(doc, default_font, i, width, config.plugins.linewrapping.mode)
table.insert(docview.wrapped_line_offsets, offset)
for k, col in ipairs(breaks) do
table.insert(docview.wrapped_lines, i)
table.insert(docview.wrapped_lines, col)
end
end
-- list of indices for wrapped_lines, that are based on original line number
-- holds the index to the first in the wrapped_lines list.
local last_wrap = nil
for i = 1, #docview.wrapped_lines, 2 do
if not last_wrap or last_wrap ~= docview.wrapped_lines[i] then
table.insert(docview.wrapped_line_to_idx, (i + 1) / 2)
last_wrap = docview.wrapped_lines[i]
end
end
else
docview.wrapped_lines = nil
docview.wrapped_line_to_idx = nil
docview.wrapped_line_offsets = nil
docview.wrapped_settings = nil
end
end
-- When we have an insertion or deletion, we have four sections of text.
-- 1. The unaffected section, located prior to the cursor. This is completely ignored.
-- 2. The beginning of the affected line prior to the insertion or deletion. Begins on column 1 of the selection.
-- 3. The removed/pasted lines.
-- 4. Every line after the modification, begins one line after the selection in the initial document.
function LineWrapping.update_breaks(docview, old_line1, old_line2, net_lines)
-- Step 1: Determine the index for the line for #2.
local old_idx1 = docview.wrapped_line_to_idx[old_line1] or 1
-- Step 2: Determine the index of the line for #4.
local old_idx2 = (docview.wrapped_line_to_idx[old_line2 + 1] or ((#docview.wrapped_lines / 2) + 1)) - 1
-- Step 3: Remove all old breaks for the old lines from the table, and all old widths from wrapped_line_offsets.
local offset = (old_idx1 - 1) * 2 + 1
for i = old_idx1, old_idx2 do
table.remove(docview.wrapped_lines, offset)
table.remove(docview.wrapped_lines, offset)
end
for i = old_line1, old_line2 do
table.remove(docview.wrapped_line_offsets, old_line1)
end
-- Step 4: Shift the line number of wrapped_lines past #4 by the amount of inserted/deleted lines.
if net_lines ~= 0 then
for i = offset, #docview.wrapped_lines, 2 do
docview.wrapped_lines[i] = docview.wrapped_lines[i] + net_lines
end
end
-- Step 5: Compute the breaks and offsets for the lines for #2 and #3. Insert them into the table.
local new_line1 = old_line1
local new_line2 = old_line2 + net_lines
for line = new_line1, new_line2 do
local breaks, begin_width = LineWrapping.compute_line_breaks(docview.doc, docview.wrapped_settings.font, line, docview.wrapped_settings.width, config.plugins.linewrapping.mode)
table.insert(docview.wrapped_line_offsets, line, begin_width)
for i,b in ipairs(breaks) do
table.insert(docview.wrapped_lines, offset, b)
table.insert(docview.wrapped_lines, offset, line)
offset = offset + 2
end
end
-- Step 6: Recompute the wrapped_line_to_idx cache from #2.
local line = old_line1
offset = (old_idx1 - 1) * 2 + 1
while offset < #docview.wrapped_lines do
if docview.wrapped_lines[offset + 1] == 1 then
docview.wrapped_line_to_idx[line] = ((offset - 1) / 2) + 1
line = line + 1
end
offset = offset + 2
end
while line <= #docview.wrapped_line_to_idx do
table.remove(docview.wrapped_line_to_idx)
end
end
-- Draws a guide if applicable to show where wrapping is occurring.
function LineWrapping.draw_guide(docview)
if config.plugins.linewrapping.guide and docview.wrapped_settings.width ~= math.huge then
local x, y = docview:get_content_offset()
local gw = docview:get_gutter_width()
renderer.draw_rect(x + gw + docview.wrapped_settings.width, y, 1, core.root_view.size.y, style.selection)
end
end
function LineWrapping.update_docview_breaks(docview)
local x,y,w,h = docview:get_scrollbar_rect()
local width = 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
docview.scroll.to.x = 0
LineWrapping.reconstruct_breaks(docview, docview:get_font(), width)
end
end
local function get_idx_line_col(docview, idx)
local doc = docview.doc
if not docview.wrapped_settings then
if idx > #doc.lines then return #doc.lines, #doc.lines[#doc.lines] + 1 end
return idx, 1
end
if idx < 1 then return 1, 1 end
local offset = (idx - 1) * 2 + 1
if offset > #docview.wrapped_lines then return #doc.lines, #doc.lines[#doc.lines] + 1 end
return docview.wrapped_lines[offset], docview.wrapped_lines[offset + 1]
end
local function get_idx_line_length(docview, idx)
local doc = docview.doc
if not docview.wrapped_settings then
if idx > #doc.lines then return #doc.lines[#doc.lines] + 1 end
return #doc.lines[idx]
end
local offset = (idx - 1) * 2 + 1
local start = docview.wrapped_lines[offset + 1]
if docview.wrapped_lines[offset + 2] and docview.wrapped_lines[offset + 2] == docview.wrapped_lines[offset] then
return docview.wrapped_lines[offset + 3] - docview.wrapped_lines[offset + 1]
else
return #doc.lines[docview.wrapped_lines[offset]] - docview.wrapped_lines[offset + 1] + 1
end
end
local function get_total_wrapped_lines(docview)
if not docview.wrapped_settings then return docview.doc and #docview.doc.lines end
return #docview.wrapped_lines / 2
end
-- If line end, gives the end of an index line, rather than the first character of the next line.
local function get_line_idx_col_count(docview, line, col, line_end, ndoc)
local doc = docview.doc
if not docview.wrapped_settings then return common.clamp(line, 1, #doc.lines), col, 1, 1 end
if line > #doc.lines then return get_line_idx_col_count(docview, #doc.lines, #doc.lines[#doc.lines] + 1) end
line = math.max(line, 1)
local idx = docview.wrapped_line_to_idx[line] or 1
local ncol, scol = 1, 1
if col then
local i = idx + 1
while line == docview.wrapped_lines[(i - 1) * 2 + 1] and col >= docview.wrapped_lines[(i - 1) * 2 + 2] do
local nscol = docview.wrapped_lines[(i - 1) * 2 + 2]
if line_end and col == nscol then
break
end
scol = nscol
i = i + 1
idx = idx + 1
end
ncol = (col - scol) + 1
end
local count = (docview.wrapped_line_to_idx[line + 1] or (get_total_wrapped_lines(docview) + 1)) - (docview.wrapped_line_to_idx[line] or get_total_wrapped_lines(docview))
return idx, ncol, count, scol
end
local function get_line_col_from_index_and_x(docview, idx, x)
local doc = docview.doc
local line, col = get_idx_line_col(docview, idx)
if idx < 1 then return 1, 1 end
local xoffset, last_i, i = (col ~= 1 and docview.wrapped_line_offsets[line] or 0), col, 1
if x < xoffset then return line, col end
local default_font = docview:get_font()
for _, type, text in docview.doc.highlighter:each_token(line) do
local font, w = style.syntax_fonts[type] or default_font, 0
for char in common.utf8_chars(text) do
if i >= col then
if xoffset >= x then
return line, (xoffset - x > (w / 2) and last_i or i)
end
w = font:get_width(char)
xoffset = xoffset + w
end
last_i = i
i = i + #char
end
end
return line, #doc.lines[line]
end
local open_files = {}
local old_doc_insert = Doc.raw_insert
function Doc:raw_insert(line, col, text, undo_stack, time)
local old_lines = #self.lines
old_doc_insert(self, line, col, text, undo_stack, time)
if open_files[self] then
for i,docview in ipairs(open_files[self]) do
if docview.wrapped_settings then
local lines = #self.lines - old_lines
LineWrapping.update_breaks(docview, line, line, lines)
end
end
end
end
local old_doc_remove = Doc.raw_remove
function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
local old_lines = #self.lines
old_doc_remove(self, line1, col1, line2, col2, undo_stack, time)
if open_files[self] then
for i,docview in ipairs(open_files[self]) do
if docview.wrapped_settings then
local lines = #self.lines - old_lines
LineWrapping.update_breaks(docview, line1, line2, lines)
end
end
end
end
local old_doc_update = DocView.update
function DocView:update()
old_doc_update(self)
if self.wrapped_settings and self.size.x > 0 then
LineWrapping.update_docview_breaks(self)
end
end
function DocView:get_scrollable_size()
if not config.scroll_past_end then
return self:get_line_height() * get_total_wrapped_lines(self) + style.padding.y * 2
end
return self:get_line_height() * (get_total_wrapped_lines(self) - 1) + self.size.y
end
local old_new = DocView.new
function DocView:new(doc)
old_new(self, doc)
if not open_files[doc] then open_files[doc] = {} end
table.insert(open_files[doc], self)
if config.plugins.linewrapping.enable_by_default then
LineWrapping.update_docview_breaks(self)
end
end
local old_scroll_to_make_visible = DocView.scroll_to_make_visible
function DocView:scroll_to_make_visible(line, col)
old_scroll_to_make_visible(self, line, col)
if self.wrapped_settings then self.scroll.to.x = 0 end
end
local old_get_visible_line_range = DocView.get_visible_line_range
function DocView:get_visible_line_range()
if not self.wrapped_settings then return old_get_visible_line_range(self) end
local x, y, x2, y2 = self:get_content_bounds()
local lh = self:get_line_height()
local minline = get_idx_line_col(self, math.max(1, math.floor(y / lh)))
local maxline = get_idx_line_col(self, math.min(get_total_wrapped_lines(self), math.floor(y2 / lh) + 1))
return minline, maxline
end
local old_get_x_offset_col = DocView.get_x_offset_col
function DocView:get_x_offset_col(line, x)
if not self.wrapped_settings then return old_get_x_offset_col(self, line, x) end
local idx = get_line_idx_col_count(self, line)
return get_line_col_from_index_and_x(self, idx, x)
end
-- If line end is true, returns the end of the previous line, in a multi-line break.
local old_get_col_x_offset = DocView.get_col_x_offset
function DocView:get_col_x_offset(line, col, line_end)
if not self.wrapped_settings then return old_get_col_x_offset(self, line, col) end
local idx, ncol, count, scol = get_line_idx_col_count(self, line, col, line_end)
local xoffset, i = (scol ~= 1 and self.wrapped_line_offsets[line] or 0), 1
local default_font = self:get_font()
for _, type, text in self.doc.highlighter:each_token(line) do
if i + #text >= scol then
if i < scol then
text = text:sub(scol - i + 1)
i = scol
end
local font = style.syntax_fonts[type] or default_font
for char in common.utf8_chars(text) do
if i >= col then
return xoffset
end
xoffset = xoffset + font:get_width(char)
i = i + #char
end
else
i = i + #text
end
end
return xoffset
end
local old_get_line_screen_position = DocView.get_line_screen_position
function DocView:get_line_screen_position(line, col)
if not self.wrapped_settings then return old_get_line_screen_position(self, line, col) end
local idx, ncol, count = get_line_idx_col_count(self, line, col)
local x, y = self:get_content_offset()
local lh = self:get_line_height()
local gw = self:get_gutter_width()
return x + gw + (col and self:get_col_x_offset(line, col) or 0), y + (idx-1) * lh + style.padding.y
end
local old_resolve_screen_position = DocView.resolve_screen_position
function DocView:resolve_screen_position(x, y)
if not self.wrapped_settings then return old_resolve_screen_position(self, x, y) end
local ox, oy = self:get_line_screen_position(1)
local idx = common.clamp(math.floor((y - oy) / self:get_line_height()) + 1, 1, get_total_wrapped_lines(self))
return get_line_col_from_index_and_x(self, idx, x - ox)
end
local old_draw_line_text = DocView.draw_line_text
function DocView:draw_line_text(line, x, y)
if not self.wrapped_settings then return old_draw_line_text(self, line, x, y) end
local default_font = self:get_font()
local tx, ty, begin_width = x, y + self:get_line_text_y_offset(), self.wrapped_line_offsets[line]
local lh = self:get_line_height()
local idx, _, count = get_line_idx_col_count(self, line)
local total_offset = 1
for _, type, text in self.doc.highlighter:each_token(line) do
local color = style.syntax[type]
local font = style.syntax_fonts[type] or default_font
local token_offset = 1
-- Split tokens if we're at the end of the document.
while text ~= nil and token_offset <= #text do
local next_line, next_line_start_col = get_idx_line_col(self, idx + 1)
if next_line ~= line then
next_line_start_col = #self.doc.lines[line]
end
local max_length = next_line_start_col - total_offset
local rendered_text = text:sub(token_offset, token_offset + max_length - 1)
tx = renderer.draw_text(font, rendered_text, tx, ty, color)
total_offset = total_offset + #rendered_text
if total_offset ~= next_line_start_col or max_length == 0 then break end
token_offset = token_offset + #rendered_text
idx = idx + 1
tx, ty = x + begin_width, ty + lh
end
end
return lh * count
end
local old_draw_line_body = DocView.draw_line_body
function DocView:draw_line_body(line, x, y)
if not self.wrapped_settings then return old_draw_line_body(self, line, x, y) end
local lh = self:get_line_height()
local idx0 = get_line_idx_col_count(self, line)
for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
if line >= line1 and line <= line2 then
if line1 ~= line then col1 = 1 end
if line2 ~= line then col2 = #self.doc.lines[line] + 1 end
if col1 ~= col2 then
local idx1, ncol1 = get_line_idx_col_count(self, line, col1)
local idx2, ncol2 = get_line_idx_col_count(self, line, col2)
for i = idx1, idx2 do
local x1, x2 = x + (idx1 == i and self:get_col_x_offset(line1, col1) or 0)
if idx2 == i then
x2 = x + self:get_col_x_offset(line, col2)
else
x2 = x + self:get_col_x_offset(line, get_idx_line_length(self, i, line) + 1, true)
end
renderer.draw_rect(x1, y + (i - idx0) * lh, x2 - x1, lh, style.selection)
end
end
end
end
local draw_highlight = nil
for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do
-- draw line highlight if caret is on this line
if draw_highlight ~= false and config.highlight_current_line
and line1 == line and core.active_view == self then
draw_highlight = (line1 == line2 and col1 == col2)
end
end
if draw_highlight then
local _, _, count = get_line_idx_col_count(self, line)
for i=1,count do
self:draw_line_highlight(x + self.scroll.x, y + lh * (i - 1))
end
end
-- draw line's text
return self:draw_line_text(line, x, y)
end
local old_draw = DocView.draw
function DocView:draw()
old_draw(self)
if self.wrapped_settings then
LineWrapping.draw_guide(self)
end
end
local old_draw_line_gutter = DocView.draw_line_gutter
function DocView:draw_line_gutter(line, x, y, width)
local lh = self:get_line_height()
local _, _, count = get_line_idx_col_count(self, line)
return (old_draw_line_gutter(self, line, x, y, width) or lh) * count
end
local old_translate_end_of_line = translate.end_of_line
function translate.end_of_line(doc, line, col)
if not core.active_view or core.active_view.doc ~= doc or not core.active_view.wrapped_settings then old_translate_end_of_line(doc, line, col) end
local idx, ncol = get_line_idx_col_count(core.active_view, line, col)
local nline, ncol2 = get_idx_line_col(core.active_view, idx + 1)
if nline ~= line then return line, math.huge end
return line, ncol2 - 1
end
local old_translate_start_of_line = translate.start_of_line
function translate.start_of_line(doc, line, col)
if not core.active_view or core.active_view.doc ~= doc or not core.active_view.wrapped_settings then old_translate_start_of_line(doc, line, col) end
local idx, ncol = get_line_idx_col_count(core.active_view, line, col)
local nline, ncol2 = get_idx_line_col(core.active_view, idx - 1)
if nline ~= line then return line, 1 end
return line, ncol2 + 1
end
local old_previous_line = DocView.translate.previous_line
function DocView.translate.previous_line(doc, line, col, dv)
if not dv.wrapped_settings then return old_previous_line(doc, line, col, dv) end
local idx, ncol = get_line_idx_col_count(dv, line, col)
return get_line_col_from_index_and_x(dv, idx - 1, dv:get_col_x_offset(line, col))
end
local old_next_line = DocView.translate.next_line
function DocView.translate.next_line(doc, line, col, dv)
if not dv.wrapped_settings then return old_next_line(doc, line, col, dv) end
local idx, ncol = get_line_idx_col_count(dv, line, col)
return get_line_col_from_index_and_x(dv, idx + 1, dv:get_col_x_offset(line, col))
end
command.add(nil, {
["line-wrapping:enable"] = function()
if core.active_view and core.active_view.doc then
LineWrapping.update_docview_breaks(core.active_view)
end
end,
["line-wrapping:disable"] = function()
if core.active_view and core.active_view.doc then
LineWrapping.reconstruct_breaks(core.active_view, core.active_view:get_font(), math.huge)
end
end,
["line-wrapping:toggle"] = function()
if core.active_view and core.active_view.doc and core.active_view.wrapped_settings then
command.perform("line-wrapping:disable")
else
command.perform("line-wrapping:enable")
end
end
})
keymap.add {
["f10"] = "line-wrapping:toggle",
}
return LineWrapping

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local command = require "core.command"
local keymap = require "core.keymap"

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local common = require "core.common"
local keymap = require "core.keymap"
@ -176,7 +176,7 @@ function ResultsView:draw()
local text
if self.searching then
if files_number then
text = string.format("Searching %d%% (%d of %d files, %d matches) for %q...",
text = string.format("Searching %.f%% (%d of %d files, %d matches) for %q...",
per * 100, self.last_file_idx, files_number,
#self.results, self.query)
else
@ -229,8 +229,21 @@ local function begin_search(text, fn)
end
local function set_command_view_text()
local view = core.active_view
local doc = (view and view.doc) and view.doc or nil
if doc then
core.command_view:set_text(
doc:get_text(table.unpack({ doc:get_selection() })),
true
)
end
end
command.add(nil, {
["project-search:find"] = function()
set_command_view_text()
core.command_view:enter("Find Text In Project", function(text)
text = text:lower()
begin_search(text, function(line_text)
@ -243,12 +256,13 @@ command.add(nil, {
core.command_view:enter("Find Regex In Project", function(text)
local re = regex.compile(text, "i")
begin_search(text, function(line_text)
return regex.cmatch(re, line_text)
return regex.cmatch(re, line_text)
end)
end)
end,
["project-search:fuzzy-find"] = function()
set_command_view_text()
core.command_view:enter("Fuzzy Find Text In Project", function(text)
begin_search(text, function(line_text)
return common.fuzzy_match(line_text, text) and 1
@ -278,22 +292,22 @@ command.add(ResultsView, {
["project-search:refresh"] = function()
core.active_view:refresh()
end,
["project-search:move-to-previous-page"] = function()
local view = core.active_view
view.scroll.to.y = view.scroll.to.y - view.size.y
end,
["project-search:move-to-next-page"] = function()
local view = core.active_view
view.scroll.to.y = view.scroll.to.y + view.size.y
end,
["project-search:move-to-start-of-doc"] = function()
local view = core.active_view
view.scroll.to.y = 0
end,
["project-search:move-to-end-of-doc"] = function()
local view = core.active_view
view.scroll.to.y = view:get_scrollable_size()

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local command = require "core.command"
local keymap = require "core.keymap"

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local config = require "core.config"
local command = require "core.command"

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local common = require "core.common"
local command = require "core.command"
@ -8,10 +8,10 @@ local style = require "core.style"
local RootView = require "core.rootview"
local CommandView = require "core.commandview"
config.plugins.scale = {
config.plugins.scale = common.merge({
mode = "code",
use_mousewheel = true
}
}, config.plugins.scale)
local scale_steps = 0.05
@ -50,12 +50,8 @@ local function set_scale(scale)
style.code_font = renderer.font.copy(style.code_font, s * style.code_font:get_size())
end
for _, font in pairs(style.syntax_fonts) do
renderer.font.set_size(font, s * font:get_size())
end
for _, font in pairs(style.syntax_fonts) do
renderer.font.set_size(font, s * font:get_size())
for name, font in pairs(style.syntax_fonts) do
style.syntax_fonts[name] = renderer.font.copy(font, s * font:get_size())
end
-- restore scroll positions

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local command = require "core.command"
local translate = require "core.doc.translate"

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local common = require "core.common"
local command = require "core.command"
@ -84,11 +84,12 @@ end
function ToolbarView:on_mouse_pressed(button, x, y, clicks)
local caught = ToolbarView.super.on_mouse_pressed(self, button, x, y, clicks)
if caught then return end
if caught then return caught end
core.set_active_view(core.last_active_view)
if self.hovered_item then
command.perform(self.hovered_item.command)
end
return true
end

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local common = require "core.common"
local command = require "core.command"
@ -8,9 +8,15 @@ local style = require "core.style"
local View = require "core.view"
local ContextMenu = require "core.contextmenu"
local RootView = require "core.rootview"
local CommandView = require "core.commandview"
config.plugins.treeview = common.merge({
-- Amount of clicks to open a file
clicks_to_open = 2,
-- Default treeview width
size = 200 * SCALE
}, config.plugins.treeview)
local default_treeview_size = 200 * SCALE
local tooltip_offset = style.font:get_height()
local tooltip_border = 1
local tooltip_delay = 0.5
@ -39,19 +45,26 @@ function TreeView:new()
self.scrollable = true
self.visible = true
self.init_size = true
self.target_size = default_treeview_size
self.target_size = config.plugins.treeview.size
self.cache = {}
self.tooltip = { x = 0, y = 0, begin = 0, alpha = 0 }
self.cursor_pos = { x = 0, y = 0 }
self.item_icon_width = 0
self.item_text_spacing = 0
local on_dirmonitor_modify = core.on_dirmonitor_modify
function core.on_dirmonitor_modify(dir, filepath)
if self.cache[dir.name] then
self.cache[dir.name][filepath] = nil
end
on_dirmonitor_modify(dir, filepath)
self:add_core_hooks()
end
function TreeView:add_core_hooks()
-- When a file or directory is deleted we delete the corresponding cache entry
-- because if the entry is recreated we may use wrong information from cache.
local on_delete = core.on_dirmonitor_delete
core.on_dirmonitor_delete = function(dir, filepath)
local cache = self.cache[dir.name]
if cache then cache[filepath] = nil end
on_delete(dir, filepath)
end
end
@ -90,7 +103,7 @@ function TreeView:get_cached(dir, item, dirname)
end
t.name = basename
t.type = item.type
t.dir = dir -- points to top level "dir" item
t.dir_name = dir.name -- points to top level "dir" item
dir_cache[cache_name] = t
end
return t
@ -170,6 +183,21 @@ function TreeView:each_item()
end
function TreeView:set_selection(selection, selection_y)
self.selected_item = selection
if selection and selection_y
and (selection_y <= 0 or selection_y >= self.size.y) then
local lh = self:get_item_height()
if selection_y >= self.size.y - lh then
selection_y = selection_y - self.size.y + lh
end
local _, y = self:get_content_offset()
self.scroll.to.y = selection and (selection_y - y)
end
end
function TreeView:get_text_bounding_box(item, x, y, w, h)
local icon_width = style.icon_font:get_width("D")
local xoffset = item.depth * style.padding.x + style.padding.x + icon_width
@ -180,8 +208,14 @@ end
function TreeView:on_mouse_moved(px, py, ...)
if not self.visible then return end
TreeView.super.on_mouse_moved(self, px, py, ...)
if self.dragging_scrollbar then return end
self.cursor_pos.x = px
self.cursor_pos.y = py
if self.dragging_scrollbar then
self.hovered_item = nil
return
end
local item_changed, tooltip_changed
for item, x,y,w,h in self:each_item() do
@ -217,32 +251,28 @@ end
function TreeView:on_mouse_pressed(button, x, y, clicks)
if not self.visible then return end
local caught = TreeView.super.on_mouse_pressed(self, button, x, y, clicks)
if caught or button ~= "left" then
return true
end
local hovered_item = self.hovered_item
if not hovered_item then
return false
elseif hovered_item.type == "dir" then
if self.hovered_item then
self:set_selection(self.hovered_item)
if keymap.modkeys["ctrl"] and button == "left" then
create_directory_in(hovered_item)
else
hovered_item.expanded = not hovered_item.expanded
if hovered_item.dir.files_limit then
core.update_project_subdir(hovered_item.dir, hovered_item.filename, hovered_item.expanded)
core.project_subdir_set_show(hovered_item.dir, hovered_item.filename, hovered_item.expanded)
end
create_directory_in(self.selected_item)
elseif self.selected_item.type == "dir"
or (self.selected_item.type == "file"
and clicks == config.plugins.treeview.clicks_to_open
)
then
command.perform "treeview:open"
end
else
core.try(function()
if core.last_active_view and core.active_view == self then
core.set_active_view(core.last_active_view)
end
local doc_filename = core.normalize_to_project_dir(hovered_item.abs_filename)
core.root_view:open_doc(core.open_doc(doc_filename))
end)
return false
end
return true
end
@ -257,6 +287,8 @@ function TreeView:update()
self:move_towards(self.size, "x", dest)
end
if not self.visible then return end
local duration = system.get_time() - self.tooltip.begin
if self.hovered_item and self.tooltip.x and duration > tooltip_delay then
self:move_towards(self.tooltip, "alpha", tooltip_alpha, tooltip_alpha_rate)
@ -267,6 +299,13 @@ function TreeView:update()
self.item_icon_width = style.icon_font:get_width("D")
self.item_text_spacing = style.icon_font:get_width("f") / 2
-- this will make sure hovered_item is updated
-- we don't want events when the thing is scrolling fast
local dy = math.abs(self.scroll.to.y - self.scroll.y)
if self.scroll.to.y ~= 0 and dy < self:get_item_height() then
self:on_mouse_moved(self.cursor_pos.x, self.cursor_pos.y, 0, 0)
end
TreeView.super.update(self)
end
@ -350,6 +389,10 @@ end
function TreeView:draw_item_background(item, active, hovered, x, y, w, h)
if hovered then
local hover_color = { table.unpack(style.line_highlight) }
hover_color[4] = 160
renderer.draw_rect(x, y, w, h, hover_color)
elseif active then
renderer.draw_rect(x, y, w, h, style.line_highlight)
end
end
@ -366,6 +409,7 @@ end
function TreeView:draw()
if not self.visible then return end
self:draw_background(style.background2)
local _y, _h = self.position.y, self.size.y
@ -375,18 +419,46 @@ function TreeView:draw()
for item, x,y,w,h in self:each_item() do
if y + h >= _y and y < _y + _h then
self:draw_item(item,
item.abs_filename == active_filename,
item == self.selected_item,
item == self.hovered_item,
x, y, w, h)
end
end
self:draw_scrollbar()
if self.hovered_item and self.tooltip.alpha > 0 then
if self.hovered_item and self.tooltip.x and self.tooltip.alpha > 0 then
core.root_view:defer_draw(self.draw_tooltip, self)
end
end
function TreeView:get_parent(item)
local parent_path = common.dirname(item.abs_filename)
if not parent_path then return end
for it, _, y in self:each_item() do
if it.abs_filename == parent_path then
return it, y
end
end
end
function TreeView:toggle_expand(toggle)
local item = self.selected_item
if not item then return end
if item.type == "dir" then
if type(toggle) == "boolean" then
item.expanded = toggle
else
item.expanded = not item.expanded
end
local hovered_dir = core.project_dir_by_name(item.dir_name)
if hovered_dir and hovered_dir.files_limit then
core.update_project_subdir(hovered_dir, item.filename, item.expanded)
end
end
end
-- init
local view = TreeView()
@ -405,7 +477,7 @@ if config.plugins.toolbarview ~= false and toolbar_plugin then
toolbar_view = ToolbarView()
treeview_node:split("down", toolbar_view, {y = true})
local min_toolbar_width = toolbar_view:get_min_width()
view:set_target_size("x", math.max(default_treeview_size, min_toolbar_width))
view:set_target_size("x", math.max(config.plugins.treeview.size, min_toolbar_width))
command.add(nil, {
["toolbar:toggle"] = function()
toolbar_view:toggle_visible()
@ -446,6 +518,12 @@ function RootView:draw(...)
menu:draw()
end
local on_quit_project = core.on_quit_project
function core.on_quit_project()
view.cache = {}
on_quit_project()
end
local function is_project_folder(path)
return core.project_dir == path
end
@ -476,65 +554,131 @@ menu:register(
}
)
local previous_view = nil
-- Register the TreeView commands and keymap
command.add(nil, {
["treeview:toggle"] = function()
view.visible = not view.visible
end})
end,
command.add(function() return view.hovered_item ~= nil end, {
["treeview:rename"] = function()
local old_filename = view.hovered_item.filename
local old_abs_filename = view.hovered_item.abs_filename
core.command_view:set_text(old_filename)
core.command_view:enter("Rename", function(filename)
filename = core.normalize_to_project_dir(filename)
local abs_filename = core.project_absolute_path(filename)
local res, err = os.rename(old_abs_filename, abs_filename)
if res then -- successfully renamed
for _, doc in ipairs(core.docs) do
if doc.abs_filename and old_abs_filename == doc.abs_filename then
doc:set_filename(filename, abs_filename) -- make doc point to the new filename
doc:reset_syntax()
break -- only first needed
end
end
core.log("Renamed \"%s\" to \"%s\"", old_filename, filename)
["treeview:toggle-focus"] = function()
if not core.active_view:is(TreeView) then
if core.active_view:is(CommandView) then
previous_view = core.last_active_view
else
core.error("Error while renaming \"%s\" to \"%s\": %s", old_abs_filename, abs_filename, err)
previous_view = core.active_view
end
if not previous_view then
previous_view = core.root_view:get_primary_node().active_view
end
core.set_active_view(view)
if not view.selected_item then
for it, _, y in view:each_item() do
view:set_selection(it, y)
break
end
end
end, common.path_suggest)
end,
["treeview:new-file"] = function()
if not is_project_folder(view.hovered_item.abs_filename) then
core.command_view:set_text(view.hovered_item.filename .. "/")
else
core.set_active_view(
previous_view or core.root_view:get_primary_node().active_view
)
end
core.command_view:enter("Filename", function(filename)
local doc_filename = core.project_dir .. PATHSEP .. filename
local file = io.open(doc_filename, "a+")
file:write("")
file:close()
core.root_view:open_doc(core.open_doc(doc_filename))
core.log("Created %s", doc_filename)
end, common.path_suggest)
end,
end
})
["treeview:new-folder"] = function()
if not is_project_folder(view.hovered_item.abs_filename) then
core.command_view:set_text(view.hovered_item.filename .. "/")
command.add(TreeView, {
["treeview:next"] = function()
local item = view.selected_item
local item_y
local stop = false
for it, _, y in view:each_item() do
if item == it then
stop = true
elseif stop then
item = it
item_y = y
break
end
end
core.command_view:enter("Folder Name", function(filename)
local dir_path = core.project_dir .. PATHSEP .. filename
common.mkdirp(dir_path)
core.log("Created %s", dir_path)
end, common.path_suggest)
view:set_selection(item, item_y)
end,
["treeview:previous"] = function()
local last_item
local last_item_y
for it, _, y in view:each_item() do
if it == view.selected_item then
if not last_item then
last_item = it
last_item_y = y
end
break
end
last_item = it
last_item_y = y
end
view:set_selection(last_item, last_item_y)
end,
["treeview:open"] = function()
local item = view.selected_item
if not item then return end
if item.type == "dir" then
view:toggle_expand()
else
core.try(function()
if core.last_active_view and core.active_view == view then
core.set_active_view(core.last_active_view)
end
local doc_filename = core.normalize_to_project_dir(item.abs_filename)
core.root_view:open_doc(core.open_doc(doc_filename))
end)
end
end,
["treeview:deselect"] = function()
view.selected_item = nil
end,
["treeview:collapse"] = function()
if view.selected_item then
if view.selected_item.type == "dir" and view.selected_item.expanded then
view:toggle_expand(false)
else
local parent_item, y = view:get_parent(view.selected_item)
if parent_item then
view:set_selection(parent_item, y)
end
end
end
end,
["treeview:expand"] = function()
view:toggle_expand(true)
end,
})
local function treeitem() return view.hovered_item or view.selected_item end
command.add(
function()
return treeitem() ~= nil
and (
core.active_view == view or core.active_view == menu
or (view.toolbar and core.active_view == view.toolbar)
-- sometimes the context menu is shown on top of statusbar
or core.active_view == core.status_view
)
end, {
["treeview:delete"] = function()
local filename = view.hovered_item.abs_filename
local relfilename = view.hovered_item.filename
local filename = treeitem().abs_filename
local relfilename = treeitem().filename
local file_info = system.get_file_info(filename)
local file_type = file_info.type == "dir" and "Directory" or "File"
-- Ask before deleting
@ -568,22 +712,83 @@ command.add(function() return view.hovered_item ~= nil end, {
end
end
)
end
})
command.add(function() return treeitem() ~= nil end, {
["treeview:rename"] = function()
local old_filename = treeitem().filename
local old_abs_filename = treeitem().abs_filename
core.command_view:set_text(old_filename)
core.command_view:enter("Rename", function(filename)
filename = core.normalize_to_project_dir(filename)
local abs_filename = core.project_absolute_path(filename)
local res, err = os.rename(old_abs_filename, abs_filename)
if res then -- successfully renamed
for _, doc in ipairs(core.docs) do
if doc.abs_filename and old_abs_filename == doc.abs_filename then
doc:set_filename(filename, abs_filename) -- make doc point to the new filename
doc:reset_syntax()
break -- only first needed
end
end
core.log("Renamed \"%s\" to \"%s\"", old_filename, filename)
else
core.error("Error while renaming \"%s\" to \"%s\": %s", old_abs_filename, abs_filename, err)
end
end, common.path_suggest)
end,
["treeview:new-file"] = function()
if not is_project_folder(treeitem().abs_filename) then
core.command_view:set_text(treeitem().filename .. "/")
end
core.command_view:enter("Filename", function(filename)
local doc_filename = core.project_dir .. PATHSEP .. filename
local file = io.open(doc_filename, "a+")
file:write("")
file:close()
core.root_view:open_doc(core.open_doc(doc_filename))
core.log("Created %s", doc_filename)
end, common.path_suggest)
end,
["treeview:new-folder"] = function()
if not is_project_folder(treeitem().abs_filename) then
core.command_view:set_text(treeitem().filename .. "/")
end
core.command_view:enter("Folder Name", function(filename)
local dir_path = core.project_dir .. PATHSEP .. filename
common.mkdirp(dir_path)
core.log("Created %s", dir_path)
end, common.path_suggest)
end,
["treeview:open-in-system"] = function()
local hovered_item = view.hovered_item
local hovered_item = treeitem()
if PLATFORM == "Windows" then
system.exec(string.format("start \"\" %q", hovered_item.abs_filename))
elseif string.find(PLATFORM, "Mac") then
system.exec(string.format("open %q", hovered_item.abs_filename))
elseif PLATFORM == "Linux" then
elseif PLATFORM == "Linux" or string.find(PLATFORM, "BSD") then
system.exec(string.format("xdg-open %q", hovered_item.abs_filename))
end
end,
})
keymap.add { ["ctrl+\\"] = "treeview:toggle" }
keymap.add {
["ctrl+\\"] = "treeview:toggle",
["up"] = "treeview:previous",
["down"] = "treeview:next",
["left"] = "treeview:collapse",
["right"] = "treeview:expand",
["return"] = "treeview:open",
["escape"] = "treeview:deselect",
["delete"] = "treeview:delete",
["ctrl+return"] = "treeview:new-folder"
}
-- Return the treeview with toolbar and contextmenu to allow
-- user or plugin modifications

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local command = require "core.command"
local Doc = require "core.doc"

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3 -- lite-xl 2.1
local core = require "core"
local common = require "core.common"
local DocView = require "core.docview"

View File

@ -124,7 +124,7 @@ process.options = {}
---@return process | nil
---@return string errmsg
---@return process.errortype | integer errcode
function process:start(command_and_params, options) end
function process.start(command_and_params, options) end
---
---Translates an error code into a useful text message

View File

@ -192,6 +192,12 @@ function system.get_clipboard() end
---@param text string
function system.set_clipboard(text) end
---
---Get the process id of lite-xl it self.
---
---@return integer
function system.get_process_id() end
---
---Get amount of iterations since the application was launched
---also known as SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency()

File diff suppressed because it is too large Load Diff

View File

@ -1,162 +0,0 @@
#ifndef __DMON_EXTRA_H__
#define __DMON_EXTRA_H__
//
// Copyright 2021 Sepehr Taghdisian (septag@github). All rights reserved.
// License: https://github.com/septag/dmon#license-bsd-2-clause
//
// Extra header functionality for dmon.h for the backend based on inotify
//
// Add/Remove directory functions:
// dmon_watch_add: Adds a sub-directory to already valid watch_id. sub-directories are assumed to be relative to watch root_dir
// dmon_watch_add: Removes a sub-directory from already valid watch_id. sub-directories are assumed to be relative to watch root_dir
// Reason: The inotify backend does not work well when watching in recursive mode a root directory of a large tree, it may take
// quite a while until all inotify watches are established, and events will not be received in this time. Also, since one
// inotify watch will be established per subdirectory, it is possible that the maximum amount of inotify watches per user
// will be reached. The default maximum is 8192.
// When using inotify backend users may turn off the DMON_WATCHFLAGS_RECURSIVE flag and add/remove selectively the
// sub-directories to be watched based on application-specific logic about which sub-directory actually needs to be watched.
// The function dmon_watch_add and dmon_watch_rm are used to this purpose.
//
#ifndef __DMON_H__
#error "Include 'dmon.h' before including this file"
#endif
#ifdef __cplusplus
extern "C" {
#endif
DMON_API_DECL bool dmon_watch_add(dmon_watch_id id, const char* subdir);
DMON_API_DECL bool dmon_watch_rm(dmon_watch_id id, const char* watchdir);
#ifdef __cplusplus
}
#endif
#ifdef DMON_IMPL
#if DMON_OS_LINUX
DMON_API_IMPL bool dmon_watch_add(dmon_watch_id id, const char* watchdir)
{
DMON_ASSERT(id.id > 0 && id.id <= DMON_MAX_WATCHES);
bool skip_lock = pthread_self() == _dmon.thread_handle;
if (!skip_lock)
pthread_mutex_lock(&_dmon.mutex);
dmon__watch_state* watch = &_dmon.watches[id.id - 1];
// check if the directory exists
// if watchdir contains absolute/root-included path, try to strip the rootdir from it
// else, we assume that watchdir is correct, so save it as it is
struct stat st;
dmon__watch_subdir subdir;
if (stat(watchdir, &st) == 0 && (st.st_mode & S_IFDIR)) {
dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watchdir);
if (strstr(subdir.rootdir, watch->rootdir) == subdir.rootdir) {
dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watchdir + strlen(watch->rootdir));
}
} else {
char fullpath[DMON_MAX_PATH];
dmon__strcpy(fullpath, sizeof(fullpath), watch->rootdir);
dmon__strcat(fullpath, sizeof(fullpath), watchdir);
if (stat(fullpath, &st) != 0 || (st.st_mode & S_IFDIR) == 0) {
_DMON_LOG_ERRORF("Watch directory '%s' is not valid", watchdir);
if (!skip_lock)
pthread_mutex_unlock(&_dmon.mutex);
return false;
}
dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watchdir);
}
int dirlen = (int)strlen(subdir.rootdir);
if (subdir.rootdir[dirlen - 1] != '/') {
subdir.rootdir[dirlen] = '/';
subdir.rootdir[dirlen + 1] = '\0';
}
// check that the directory is not already added
for (int i = 0, c = stb_sb_count(watch->subdirs); i < c; i++) {
if (strcmp(subdir.rootdir, watch->subdirs[i].rootdir) == 0) {
_DMON_LOG_ERRORF("Error watching directory '%s', because it is already added.", watchdir);
if (!skip_lock)
pthread_mutex_unlock(&_dmon.mutex);
return false;
}
}
const uint32_t inotify_mask = IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE | IN_MODIFY;
char fullpath[DMON_MAX_PATH];
dmon__strcpy(fullpath, sizeof(fullpath), watch->rootdir);
dmon__strcat(fullpath, sizeof(fullpath), subdir.rootdir);
int wd = inotify_add_watch(watch->fd, fullpath, inotify_mask);
if (wd == -1) {
_DMON_LOG_ERRORF("Error watching directory '%s'. (inotify_add_watch:err=%d)", watchdir, errno);
if (!skip_lock)
pthread_mutex_unlock(&_dmon.mutex);
return false;
}
stb_sb_push(watch->subdirs, subdir);
stb_sb_push(watch->wds, wd);
if (!skip_lock)
pthread_mutex_unlock(&_dmon.mutex);
return true;
}
DMON_API_IMPL bool dmon_watch_rm(dmon_watch_id id, const char* watchdir)
{
DMON_ASSERT(id.id > 0 && id.id <= DMON_MAX_WATCHES);
bool skip_lock = pthread_self() == _dmon.thread_handle;
if (!skip_lock)
pthread_mutex_lock(&_dmon.mutex);
dmon__watch_state* watch = &_dmon.watches[id.id - 1];
char subdir[DMON_MAX_PATH];
dmon__strcpy(subdir, sizeof(subdir), watchdir);
if (strstr(subdir, watch->rootdir) == subdir) {
dmon__strcpy(subdir, sizeof(subdir), watchdir + strlen(watch->rootdir));
}
int dirlen = (int)strlen(subdir);
if (subdir[dirlen - 1] != '/') {
subdir[dirlen] = '/';
subdir[dirlen + 1] = '\0';
}
int i, c = stb_sb_count(watch->subdirs);
for (i = 0; i < c; i++) {
if (strcmp(watch->subdirs[i].rootdir, subdir) == 0) {
break;
}
}
if (i >= c) {
_DMON_LOG_ERRORF("Watch directory '%s' is not valid", watchdir);
if (!skip_lock)
pthread_mutex_unlock(&_dmon.mutex);
return false;
}
inotify_rm_watch(watch->fd, watch->wds[i]);
/* Remove entry from subdirs and wds by swapping position with the last entry */
watch->subdirs[i] = stb_sb_last(watch->subdirs);
stb_sb_pop(watch->subdirs);
watch->wds[i] = stb_sb_last(watch->wds);
stb_sb_pop(watch->wds);
if (!skip_lock)
pthread_mutex_unlock(&_dmon.mutex);
return true;
}
#endif // DMON_OS_LINUX
#endif // DMON_IMPL
#endif // __DMON_EXTRA_H__

View File

@ -1 +0,0 @@
lite_includes += include_directories('.')

View File

@ -22,33 +22,6 @@ 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.
## septag/dmon
Copyright 2019 Sepehr Taghdisian. All rights reserved.
https://github.com/septag/dmon
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## Fira Sans
Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.

View File

@ -1,18 +1,41 @@
project('lite-xl',
['c'],
version : '2.0.3',
version : '2.0.5',
license : 'MIT',
meson_version : '>= 0.54',
default_options : ['c_std=gnu11']
meson_version : '>= 0.47',
default_options : [
'c_std=gnu11',
'wrap_mode=nofallback'
]
)
#===============================================================================
# Project version including git commit if possible
#===============================================================================
version = meson.project_version()
if get_option('buildtype') != 'release'
git_command = find_program('git', required : false)
if git_command.found()
git_commit = run_command(
[git_command, 'rev-parse', 'HEAD'],
check : false
).stdout().strip()
if git_commit != ''
version += ' (git-' + git_commit.substring(0, 8) + ')'
endif
endif
endif
#===============================================================================
# Configuration
#===============================================================================
conf_data = configuration_data()
conf_data.set('PROJECT_BUILD_DIR', meson.current_build_dir())
conf_data.set('PROJECT_SOURCE_DIR', meson.current_source_dir())
conf_data.set('PROJECT_VERSION', meson.project_version())
conf_data.set('PROJECT_VERSION', version)
#===============================================================================
# Compiler Settings
@ -24,7 +47,7 @@ endif
cc = meson.get_compiler('c')
lite_includes = []
lite_cargs = []
lite_cargs = ['-DSDL_MAIN_HANDLED', '-DPCRE2_STATIC']
# On macos we need to use the SDL renderer to support retina displays
if get_option('renderer') or host_machine.system() == 'darwin'
lite_cargs += '-DLITE_USE_SDL_RENDERER'
@ -46,14 +69,32 @@ endif
if not get_option('source-only')
libm = cc.find_library('m', required : false)
libdl = cc.find_library('dl', required : false)
threads_dep = dependency('threads')
lua_dep = dependency('lua5.2', fallback: ['lua', 'lua_dep'],
default_options: ['shared=false', 'use_readline=false', 'app=false']
lua_fallback = ['lua', 'lua_dep']
lua_quick_fallback = []
if get_option('wrap_mode') == 'forcefallback'
lua_quick_fallback = lua_fallback
endif
lua_dep = dependency('lua5.4', fallback: lua_quick_fallback, required : false)
if not lua_dep.found()
lua_dep = dependency('lua', fallback: ['lua', 'lua_dep'],
default_options: ['default_library=static', 'line_editing=false', 'interpreter=false']
)
endif
pcre2_dep = dependency('libpcre2-8', fallback: ['pcre2', 'libpcre2_8'],
default_options: ['default_library=static', 'grep=false', 'test=false']
)
pcre2_dep = dependency('libpcre2-8')
freetype_dep = dependency('freetype2')
sdl_dep = dependency('sdl2', method: 'config-tool')
lite_deps = [lua_dep, sdl_dep, freetype_dep, pcre2_dep, libm, libdl, threads_dep]
freetype_dep = dependency('freetype2', fallback: ['freetype2', 'freetype_dep'],
default_options: ['default_library=static', 'zlib=disabled', 'bzip2=disabled', 'png=disabled', 'harfbuzz=disabled', 'brotli=disabled']
)
sdl_dep = dependency('sdl2', fallback: ['sdl2', 'sdl2_dep'],
default_options: ['default_library=static']
)
lite_deps = [lua_dep, sdl_dep, freetype_dep, pcre2_dep, libm, libdl]
endif
#===============================================================================
# Install Configuration
@ -94,21 +135,19 @@ endif
install_data('licenses/licenses.md', install_dir : lite_docdir)
install_subdir('data' / 'core' , install_dir : lite_datadir, exclude_files : 'start.lua')
install_subdir('data/core' , install_dir : lite_datadir, exclude_files : 'start.lua')
foreach data_module : ['fonts', 'plugins', 'colors']
install_subdir('data' / data_module , install_dir : lite_datadir)
install_subdir(join_paths('data', data_module), install_dir : lite_datadir)
endforeach
configure_file(
input : 'data/core/start.lua',
output : 'start.lua',
configuration : conf_data,
install : true,
install_dir : lite_datadir / 'core',
install_dir : join_paths(lite_datadir, 'core'),
)
if not get_option('source-only')
subdir('lib/dmon')
subdir('src')
subdir('scripts')
endif

View File

@ -2,3 +2,4 @@ option('bundle', type : 'boolean', value : false, description: 'Build a macOS bu
option('source-only', type : 'boolean', value : false, description: 'Configure source files only, doesn\'t checks for dependencies')
option('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', 'kqueue', 'win32', 'dummy'], description: 'define what dirmonitor backend to use')

View File

@ -17,11 +17,11 @@
<screenshots>
<screenshot type="default">
<caption>The editor window</caption>
<image>https://lite-xl.github.io/assets/img/screenshots/editor.png</image>
<image>https://lite-xl.com/assets/img/editor.png</image>
</screenshot>
</screenshots>
<url type="homepage">https://lite-xl.github.io</url>
<url type="homepage">https://lite-xl.com</url>
<provides>
<binary>lite-xl</binary>

View File

@ -8,6 +8,8 @@
<string>lite-xl</string>
<key>CFBundleIconFile</key>
<string>icon.icns</string>
<key>CFBundleIdentifier</key>
<string>com.lite-xl</string>
<key>CFBundleName</key>
<string>Lite XL</string>
<key>CFBundlePackageType</key>

View File

@ -1,54 +0,0 @@
`core.set_project_dir`:
Reset project directories and set its directory.
It chdir into the directory, empty the `core.project_directories` and add
the given directory.
`core.add_project_directory`:
Add a new top-level directory to the project.
Also called from modules and commands outside core.init.
local function `scan_project_folder`:
Scan all files for a given top-level project directory.
Can emit a warning about file limit.
Called only from within core.init module.
`core.scan_project_subdir`: (before was named `core.scan_project_folder`)
scan a single folder, without recursion. Used when too many files.
Local function `scan_project_folder`:
Populate the project folder top directory. Done only once when the directory
is added to the project.
`core.add_project_directory`:
Add a new top-level folder to the project.
`core.set_project_dir`:
Set the initial project directory.
`core.dir_rescan_add_job`:
Add a job to rescan after an elapsed time a project's subdirectory to fix for any
changes.
Local function `rescan_project_subdir`:
Rescan a project's subdirectory, compare to the current version and patch the list if
a difference is found.
`core.project_scan_thread`:
Should disappear now that we use dmon.
`core.project_scan_topdir`:
New function to scan a top level project folder.
`config.project_scan_rate`:
`core.project_scan_thread_id`:
`core.reschedule_project_scan`:
`core.project_files_limit`:
A eliminer.
`core.get_project_files`:
To be fixed. Use `find_project_files_co` for a single directory
In TreeView remove usage of self.last to detect new scan that changed the files list.

View File

@ -1,88 +1,88 @@
#define MyAppName "Lite XL"
#define MyAppVersion "@PROJECT_VERSION@"
#define MyAppPublisher "Lite XL Team"
#define MyAppURL "https://lite-xl.github.io"
#define MyAppExeName "lite-xl.exe"
#define BuildDir "@PROJECT_BUILD_DIR@"
#define SourceDir "@PROJECT_SOURCE_DIR@"
; Use /dArch option to create a setup for a different architecture, e.g.:
; iscc /dArch=x86 innosetup.iss
#ifndef Arch
#define Arch "x64"
#endif
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; To generate a new GUID, click Tools | Generate GUID inside the InnoSetup IDE.
AppId={{06761240-D97C-43DE-B9ED-C15F765A2D65}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
#if Arch=="x64"
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
#define ArchInternal "x86_64"
#else
#define ArchInternal "i686"
#endif
AllowNoIcons=yes
Compression=lzma
SolidCompression=yes
DefaultDirName={autopf}/{#MyAppName}
DefaultGroupName={#MyAppPublisher}
UninstallFilesDir={app}
; Uncomment the following line to run in non administrative install mode
; (install for current user only.)
;PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
; The [Icons] "quicklaunchicon" entry uses {userappdata}
; but its [Tasks] entry has a proper IsAdminInstallMode Check.
UsedUserAreasWarning=no
OutputDir=.
OutputBaseFilename=LiteXL-{#MyAppVersion}-{#ArchInternal}-setup
;DisableDirPage=yes
;DisableProgramGroupPage=yes
LicenseFile={#SourceDir}/LICENSE
SetupIconFile={#SourceDir}/resources/icons/icon.ico
WizardImageFile="{#SourceDir}/scripts/innosetup/wizard-modern-image.bmp"
WizardSmallImageFile="{#SourceDir}/scripts/innosetup/litexl-55px.bmp"
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
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
[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
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: not WizardIsTaskSelected('portablemode')
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"; Check: not WizardIsTaskSelected('portablemode')
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon; Check: not WizardIsTaskSelected('portablemode')
; Name: "{usersendto}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
[Run]
Filename: "{app}/{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
[Setup]
Uninstallable=not WizardIsTaskSelected('portablemode')
#define MyAppName "Lite XL"
#define MyAppVersion "@PROJECT_VERSION@"
#define MyAppPublisher "Lite XL Team"
#define MyAppURL "https://lite-xl.com"
#define MyAppExeName "lite-xl.exe"
#define BuildDir "@PROJECT_BUILD_DIR@"
#define SourceDir "@PROJECT_SOURCE_DIR@"
; Use /dArch option to create a setup for a different architecture, e.g.:
; iscc /dArch=x86 innosetup.iss
#ifndef Arch
#define Arch "x64"
#endif
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; To generate a new GUID, click Tools | Generate GUID inside the InnoSetup IDE.
AppId={{06761240-D97C-43DE-B9ED-C15F765A2D65}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
#if Arch=="x64"
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
#define ArchInternal "x86_64"
#else
#define ArchInternal "i686"
#endif
AllowNoIcons=yes
Compression=lzma
SolidCompression=yes
DefaultDirName={autopf}/{#MyAppName}
DefaultGroupName={#MyAppPublisher}
UninstallFilesDir={app}
; Uncomment the following line to run in non administrative install mode
; (install for current user only.)
;PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
; The [Icons] "quicklaunchicon" entry uses {userappdata}
; but its [Tasks] entry has a proper IsAdminInstallMode Check.
UsedUserAreasWarning=no
OutputDir=.
OutputBaseFilename=LiteXL-{#MyAppVersion}-{#ArchInternal}-setup
;DisableDirPage=yes
;DisableProgramGroupPage=yes
LicenseFile={#SourceDir}/LICENSE
SetupIconFile={#SourceDir}/resources/icons/icon.ico
WizardImageFile="{#SourceDir}/scripts/innosetup/wizard-modern-image.bmp"
WizardSmallImageFile="{#SourceDir}/scripts/innosetup/litexl-55px.bmp"
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
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
[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
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: not WizardIsTaskSelected('portablemode')
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"; Check: not WizardIsTaskSelected('portablemode')
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon; Check: not WizardIsTaskSelected('portablemode')
; Name: "{usersendto}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
[Run]
Filename: "{app}/{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
[Setup]
Uninstallable=not WizardIsTaskSelected('portablemode')

View File

@ -63,10 +63,10 @@ main() {
elif [[ "$OSTYPE" == "msys" ]]; then
if [[ $lhelper == true ]]; then
pacman --noconfirm -S \
${MINGW_PACKAGE_PREFIX}-{gcc,meson,ninja,ntldd,pkg-config} unzip
${MINGW_PACKAGE_PREFIX}-{gcc,meson,ninja,ntldd,pkg-config,mesa} unzip
else
pacman --noconfirm -S \
${MINGW_PACKAGE_PREFIX}-{gcc,meson,ninja,ntldd,pkg-config,freetype,pcre2,SDL2} unzip
${MINGW_PACKAGE_PREFIX}-{gcc,meson,ninja,ntldd,pkg-config,mesa,freetype,pcre2,SDL2} unzip
fi
fi
}

View File

@ -72,4 +72,4 @@ main() {
fi
}
main
main "$@"

View File

@ -216,11 +216,11 @@ main() {
rm -rf "Lite XL.app"; mv "${dest_dir}" "Lite XL.app"
dest_dir="Lite XL.app"
exe_file="$(pwd)/${dest_dir}/Contents/MacOS/lite-xl"
data_dir="$(pwd)/${dest_dir}/Contents/Resources"
fi
fi
if [[ $bundle == false && $portable == false ]]; then
echo "Creating a compressed archive..."
data_dir="$(pwd)/${dest_dir}/$prefix/share/lite-xl"
exe_file="$(pwd)/${dest_dir}/$prefix/bin/lite-xl"
fi
@ -240,6 +240,7 @@ main() {
$stripcmd "${exe_file}"
echo "Creating a compressed archive ${package_name}"
if [[ $binary == true ]]; then
rm -f "${package_name}".tar.gz
rm -f "${package_name}".zip

View File

@ -1,20 +1,23 @@
#include "api.h"
int luaopen_system(lua_State *L);
int luaopen_renderer(lua_State *L);
int luaopen_regex(lua_State *L);
int luaopen_process(lua_State *L);
// int luaopen_process(lua_State *L);
int luaopen_dirmonitor(lua_State* L);
static const luaL_Reg libs[] = {
{ "system", luaopen_system },
{ "renderer", luaopen_renderer },
{ "regex", luaopen_regex },
{ "process", luaopen_process },
{ "system", luaopen_system },
{ "renderer", luaopen_renderer },
{ "regex", luaopen_regex },
// { "process", luaopen_process },
{ "dirmonitor", luaopen_dirmonitor },
{ NULL, NULL }
};
void api_load_libs(lua_State *L) {
for (int i = 0; libs[i].name; i++)
luaL_requiref(L, libs[i].name, libs[i].func, 1);
}

View File

@ -7,6 +7,7 @@
#define API_TYPE_FONT "Font"
#define API_TYPE_PROCESS "Process"
#define API_TYPE_DIRMONITOR "Dirmonitor"
#define API_CONSTANT_DEFINE(L, idx, key, n) (lua_pushnumber(L, n), lua_setfield(L, idx - 1, key))

99
src/api/dirmonitor.c Normal file
View File

@ -0,0 +1,99 @@
#include "api.h"
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#elif __linux__
#include <sys/inotify.h>
#include <limits.h>
#elif __amigaos4__
// #define DIRMONITOR_BACKEND 'os4'
#else
#include <sys/event.h>
#endif
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <stdbool.h>
#ifndef DIRMONITOR_BACKEND
#error No dirmonitor backend defined
#endif
#define GLUE_HELPER(x, y) x##y
#define GLUE(x, y) GLUE_HELPER(x, y)
#define init_dirmonitor GLUE(init_dirmonitor_, DIRMONITOR_BACKEND)
#define deinit_dirmonitor GLUE(deinit_dirmonitor_, DIRMONITOR_BACKEND)
#define check_dirmonitor GLUE(check_dirmonitor_, DIRMONITOR_BACKEND)
#define add_dirmonitor GLUE(add_dirmonitor_, DIRMONITOR_BACKEND)
#define remove_dirmonitor GLUE(remove_dirmonitor_, DIRMONITOR_BACKEND)
struct dirmonitor {}; // dirmonitor struct is defined in each backend
// define functions so we know their signature
struct dirmonitor* init_dirmonitor();
void deinit_dirmonitor(struct dirmonitor*);
int check_dirmonitor(struct dirmonitor*, int (*)(int, const char*, void*), void*);
int add_dirmonitor(struct dirmonitor*, const char*);
void remove_dirmonitor(struct dirmonitor*, int);
static int f_check_dir_callback(int watch_id, const char* path, void* L) {
lua_pushvalue(L, -1);
#ifdef DIRMONITOR_WIN32
char buffer[PATH_MAX*4];
int count = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)path, watch_id, buffer, PATH_MAX*4 - 1, NULL, NULL);
lua_pushlstring(L, buffer, count);
#else
lua_pushnumber(L, watch_id);
#endif
lua_call(L, 1, 1);
int result = lua_toboolean(L, -1);
lua_pop(L, 1);
return !result;
}
static int f_dirmonitor_new(lua_State* L) {
struct dirmonitor** monitor = lua_newuserdata(L, sizeof(struct dirmonitor**));
*monitor = init_dirmonitor();
luaL_setmetatable(L, API_TYPE_DIRMONITOR);
return 1;
}
static int f_dirmonitor_gc(lua_State* L) {
deinit_dirmonitor(*((struct dirmonitor**)luaL_checkudata(L, 1, API_TYPE_DIRMONITOR)));
return 0;
}
static int f_dirmonitor_watch(lua_State *L) {
lua_pushnumber(L, add_dirmonitor(*(struct dirmonitor**)luaL_checkudata(L, 1, API_TYPE_DIRMONITOR), luaL_checkstring(L, 2)));
return 1;
}
static int f_dirmonitor_unwatch(lua_State *L) {
remove_dirmonitor(*(struct dirmonitor**)luaL_checkudata(L, 1, API_TYPE_DIRMONITOR), luaL_checknumber(L, 2));
return 0;
}
static int f_dirmonitor_check(lua_State* L) {
lua_pushnumber(L, check_dirmonitor(*(struct dirmonitor**)luaL_checkudata(L, 1, API_TYPE_DIRMONITOR), f_check_dir_callback, L));
return 1;
}
static const luaL_Reg dirmonitor_lib[] = {
{ "new", f_dirmonitor_new },
{ "__gc", f_dirmonitor_gc },
{ "watch", f_dirmonitor_watch },
{ "unwatch", f_dirmonitor_unwatch },
{ "check", f_dirmonitor_check },
{NULL, NULL}
};
int luaopen_dirmonitor(lua_State* L) {
luaL_newmetatable(L, API_TYPE_DIRMONITOR);
luaL_setfuncs(L, dirmonitor_lib, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
return 1;
}

View File

@ -0,0 +1,22 @@
#include <stdlib.h>
struct dirmonitor {
};
struct dirmonitor* init_dirmonitor_dummy() {
return NULL;
}
void deinit_dirmonitor_dummy(struct dirmonitor* monitor) {
}
int check_dirmonitor_dummy(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
return -1;
}
int add_dirmonitor_dummy(struct dirmonitor* monitor, const char* path) {
return -1;
}
void remove_dirmonitor_dummy(struct dirmonitor* monitor, int fd) {
}

View File

@ -0,0 +1,58 @@
#include <sys/inotify.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
struct dirmonitor {
int fd;
};
struct dirmonitor* init_dirmonitor_inotify() {
struct dirmonitor* monitor = calloc(sizeof(struct dirmonitor), 1);
monitor->fd = inotify_init();
fcntl(monitor->fd, F_SETFL, O_NONBLOCK);
return monitor;
}
void deinit_dirmonitor_inotify(struct dirmonitor* monitor) {
close(monitor->fd);
free(monitor);
}
int check_dirmonitor_inotify(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
char buf[PATH_MAX + sizeof(struct inotify_event)];
ssize_t offset = 0;
while (1) {
ssize_t len = read(monitor->fd, &buf[offset], sizeof(buf) - offset);
if (len == -1 && errno != EAGAIN) {
return errno;
}
if (len <= 0) {
return 0;
}
while (len > sizeof(struct inotify_event) && len >= ((struct inotify_event*)buf)->len + sizeof(struct inotify_event)) {
change_callback(((const struct inotify_event *)buf)->wd, NULL, data);
len -= sizeof(struct inotify_event) + ((struct inotify_event*)buf)->len;
memmove(buf, &buf[sizeof(struct inotify_event) + ((struct inotify_event*)buf)->len], len);
offset = len;
}
}
}
int add_dirmonitor_inotify(struct dirmonitor* monitor, const char* path) {
return inotify_add_watch(monitor->fd, path, IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO);
}
void remove_dirmonitor_inotify(struct dirmonitor* monitor, int fd) {
inotify_rm_watch(monitor->fd, fd);
}

View File

@ -0,0 +1,53 @@
#include <sys/event.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
struct dirmonitor {
int fd;
};
struct dirmonitor* init_dirmonitor_kqueue() {
struct dirmonitor* monitor = calloc(sizeof(struct dirmonitor), 1);
monitor->fd = kqueue();
return monitor;
}
void deinit_dirmonitor_kqueue(struct dirmonitor* monitor) {
close(monitor->fd);
free(monitor);
}
int check_dirmonitor_kqueue(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
struct kevent event;
while (1) {
struct timespec tm = {0};
int nev = kevent(monitor->fd, NULL, 0, &event, 1, &tm);
if (nev == -1) {
return errno;
}
if (nev <= 0) {
return 0;
}
change_callback(event.ident, NULL, data);
}
}
int add_dirmonitor_kqueue(struct dirmonitor* monitor, const char* path) {
int fd = open(path, O_RDONLY);
struct kevent change;
EV_SET(&change, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME, 0, (void*)path);
kevent(monitor->fd, &change, 1, NULL, 0, NULL);
return fd;
}
void remove_dirmonitor_kqueue(struct dirmonitor* monitor, int fd) {
close(fd);
}

23
src/api/dirmonitor/os4.c Normal file
View File

@ -0,0 +1,23 @@
#include <stdlib.h>
struct dirmonitor {
};
struct dirmonitor* init_dirmonitor_os4() {
return NULL;
}
void deinit_dirmonitor_os4(struct dirmonitor* monitor) {
}
int check_dirmonitor_os4(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
return -1;
}
int add_dirmonitor_os4(struct dirmonitor* monitor, const char* path) {
return -1;
}
void remove_dirmonitor_os4(struct dirmonitor* monitor, int fd) {
}

View File

@ -0,0 +1,77 @@
#include <stdbool.h>
#include <windows.h>
struct dirmonitor {
HANDLE handle;
char buffer[8192];
OVERLAPPED overlapped;
bool running;
};
struct dirmonitor* init_dirmonitor_win32() {
struct dirmonitor* monitor = calloc(sizeof(struct dirmonitor), 1);
return monitor;
}
static void close_monitor_handle(struct dirmonitor* monitor) {
if (monitor->handle) {
if (monitor->running) {
BOOL result = CancelIoEx(monitor->handle, &monitor->overlapped);
DWORD error = GetLastError();
if (result == TRUE || error != ERROR_NOT_FOUND) {
DWORD bytes_transferred;
GetOverlappedResult( monitor->handle, &monitor->overlapped, &bytes_transferred, TRUE );
}
monitor->running = false;
}
CloseHandle(monitor->handle);
}
monitor->handle = NULL;
}
void deinit_dirmonitor_win32(struct dirmonitor* monitor) {
close_monitor_handle(monitor);
free(monitor);
}
int check_dirmonitor_win32(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
if (!monitor->running) {
if (ReadDirectoryChangesW(monitor->handle, monitor->buffer, sizeof(monitor->buffer), TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &monitor->overlapped, NULL) == 0) {
return GetLastError();
}
monitor->running = true;
}
DWORD bytes_transferred;
if (!GetOverlappedResult(monitor->handle, &monitor->overlapped, &bytes_transferred, FALSE)) {
int error = GetLastError();
return error == ERROR_IO_PENDING || error == ERROR_IO_INCOMPLETE ? 0 : error;
}
monitor->running = false;
for (FILE_NOTIFY_INFORMATION* info = (FILE_NOTIFY_INFORMATION*)monitor->buffer; (char*)info < monitor->buffer + sizeof(monitor->buffer); info = (FILE_NOTIFY_INFORMATION*)((char*)info) + info->NextEntryOffset) {
change_callback(info->FileNameLength, (char*)info->FileName, data);
if (!info->NextEntryOffset)
break;
}
monitor->running = false;
return 0;
}
int add_dirmonitor_win32(struct dirmonitor* monitor, const char* path) {
close_monitor_handle(monitor);
monitor->handle = CreateFileA(path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
if (monitor->handle && monitor->handle != INVALID_HANDLE_VALUE) {
return 1;
}
monitor->handle = NULL;
return -1;
}
void remove_dirmonitor_win32(struct dirmonitor* monitor, int fd) {
close_monitor_handle(monitor);
}

View File

@ -2,6 +2,7 @@
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <SDL.h>
@ -59,17 +60,18 @@ typedef enum {
#ifdef _WIN32
static volatile long PipeSerialNumber;
static void close_fd(HANDLE handle) { CloseHandle(handle); }
static void close_fd(HANDLE* handle) { if (*handle) CloseHandle(*handle); *handle = NULL; }
#else
static void close_fd(int fd) { close(fd); }
static void close_fd(int* fd) { if (*fd) close(*fd); *fd = 0; }
#endif
static bool poll_process(process_t* proc, int timeout) {
if (!proc->running)
return false;
unsigned int ticks = SDL_GetTicks();
uint32_t ticks = SDL_GetTicks();
if (timeout == WAIT_DEADLINE)
timeout = proc->deadline;
do {
#ifdef _WIN32
DWORD exit_code = -1;
@ -90,13 +92,8 @@ static bool poll_process(process_t* proc, int timeout) {
if (timeout)
SDL_Delay(5);
} while (timeout == WAIT_INFINITE || SDL_GetTicks() - ticks < timeout);
if (!proc->running) {
close_fd(proc->child_pipes[STDIN_FD ][1]);
close_fd(proc->child_pipes[STDOUT_FD][0]);
close_fd(proc->child_pipes[STDERR_FD][0]);
return false;
}
return true;
return proc->running;
}
static bool signal_process(process_t* proc, signal_e sig) {
@ -109,9 +106,9 @@ static bool signal_process(process_t* proc, signal_e sig) {
}
#else
switch (sig) {
case SIGNAL_TERM: terminate = kill(proc->pid, SIGTERM) == 1; break;
case SIGNAL_KILL: terminate = kill(proc->pid, SIGKILL) == 1; break;
case SIGNAL_INTERRUPT: kill(proc->pid, SIGINT); break;
case SIGNAL_TERM: terminate = kill(-proc->pid, SIGTERM) == 1; break;
case SIGNAL_KILL: terminate = kill(-proc->pid, SIGKILL) == 1; break;
case SIGNAL_INTERRUPT: kill(-proc->pid, SIGINT); break;
}
#endif
if (terminate)
@ -121,19 +118,19 @@ static bool signal_process(process_t* proc, signal_e sig) {
static int process_start(lua_State* L) {
size_t env_len = 0, key_len, val_len;
const char *cmd[256], *env[256] = { NULL }, *cwd = NULL;
const char *cmd[256], *env_names[256] = { NULL }, *env_values[256] = { NULL }, *cwd = NULL;
bool detach = false;
int deadline = 10, new_fds[3] = { STDIN_FD, STDOUT_FD, STDERR_FD };
luaL_checktype(L, 1, LUA_TTABLE);
#if LUA_VERSION_NUM > 501
lua_len(L, 1);
#else
lua_pushnumber(L, (int)lua_objlen(L, 1));
lua_pushinteger(L, (int)lua_objlen(L, 1));
#endif
size_t cmd_len = luaL_checknumber(L, -1); lua_pop(L, 1);
size_t arg_len = lua_gettop(L);
for (size_t i = 1; i <= cmd_len; ++i) {
lua_pushnumber(L, i);
lua_pushinteger(L, i);
lua_rawget(L, 1);
cmd[i-1] = luaL_checkstring(L, -1);
}
@ -145,9 +142,12 @@ static int process_start(lua_State* 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[env_len] = malloc(key_len+val_len+2);
snprintf((char*)env[env_len++], key_len+val_len+2, "%s=%s", key, val);
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);
@ -162,7 +162,6 @@ static int process_start(lua_State* L) {
return luaL_error(L, "redirect to handles, FILE* and paths are not supported");
}
}
env[env_len] = NULL;
process_t* self = lua_newuserdata(L, sizeof(process_t));
memset(self, 0, sizeof(process_t));
@ -217,26 +216,28 @@ 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];
int offset = 0;
char commandLine[32767] = { 0 }, environmentBlock[32767], wideEnvironmentBlock[32767*2];
strcpy(commandLine, cmd[0]);
int offset = 0;
for (size_t i = 1; i < cmd_len; ++i) {
size_t len = strlen(cmd[i]);
if (offset + len + 1 >= sizeof(commandLine))
offset += len + 1;
if (offset >= sizeof(commandLine))
break;
strcat(commandLine, " ");
strcat(commandLine, cmd[i]);
}
offset = 0;
for (size_t i = 0; i < env_len; ++i) {
size_t len = strlen(env[i]);
if (offset + len >= sizeof(environmentBlock))
if (offset + strlen(env_values[i]) + strlen(env_names[i]) + 1 >= sizeof(environmentBlock))
break;
memcpy(&environmentBlock[offset], env[i], len);
offset += len;
offset += snprintf(&environmentBlock[offset], sizeof(environmentBlock) - offset, "%s=%s", env_names[i], env_values[i]);
environmentBlock[offset++] = 0;
}
environmentBlock[offset++] = 0;
if (!CreateProcess(NULL, commandLine, NULL, NULL, true, (detach ? DETACHED_PROCESS : CREATE_NO_WINDOW) | CREATE_UNICODE_ENVIRONMENT, env_len > 0 ? environmentBlock : NULL, cwd, &siStartInfo, &self->process_information))
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))
return luaL_error(L, "Error creating a process: %d.", GetLastError());
self->pid = (long)self->process_information.dwProcessId;
if (detach)
@ -255,6 +256,7 @@ static int process_start(lua_State* L) {
}
return luaL_error(L, "Error running fork: %s.", strerror(errno));
} else if (!self->pid) {
setpgrp();
for (int stream = 0; stream < 3; ++stream) {
if (new_fds[stream] == REDIRECT_DISCARD) { // Close the stream if we don't want it.
close(self->child_pipes[stream][stream == STDIN_FD ? 0 : 1]);
@ -263,17 +265,21 @@ static int process_start(lua_State* L) {
dup2(self->child_pipes[new_fds[stream]][new_fds[stream] == STDIN_FD ? 0 : 1], stream);
close(self->child_pipes[stream][stream == STDIN_FD ? 1 : 0]);
}
if ((!detach || setsid() != -1) && (!cwd || chdir(cwd) != -1))
execvp((const char*)cmd[0], (char* const*)cmd);
int 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((const char*)cmd[0], (char* const*)cmd);
const char* msg = strerror(errno);
int result = write(STDERR_FD, msg, strlen(msg)+1);
exit(result == strlen(msg)+1 ? -1 : -2);
_exit(result == strlen(msg)+1 ? -1 : -2);
}
#endif
for (size_t i = 0; i < env_len; ++i)
free((char*)env[i]);
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)
close_fd(self->child_pipes[stream][stream == STDIN_FD ? 0 : 1]);
close_fd(&self->child_pipes[stream][stream == STDIN_FD ? 0 : 1]);
self->running = true;
return 1;
}
@ -306,7 +312,7 @@ static int g_read(lua_State* L, int stream, unsigned long read_size) {
#else
luaL_Buffer b;
luaL_buffinit(L, &b);
uint8_t* buffer = (uint8_t*)luaL_prepbuffer(&b);
uint8_t* buffer = (uint8_t*)luaL_prepbuffsize(&b, READ_BUF_SIZE);
length = read(self->child_pipes[stream][0], buffer, read_size > READ_BUF_SIZE ? READ_BUF_SIZE : read_size);
if (length == 0 && !poll_process(self, WAIT_NONE))
return 0;
@ -330,8 +336,9 @@ static int f_write(lua_State* L) {
#if _WIN32
DWORD dwWritten;
if (!WriteFile(self->child_pipes[STDIN_FD][1], data, data_size, &dwWritten, NULL)) {
int lastError = GetLastError();
signal_process(self, SIGNAL_TERM);
return luaL_error(L, "error writing to process: %d", GetLastError());
return luaL_error(L, "error writing to process: %d", lastError);
}
length = dwWritten;
#else
@ -339,18 +346,19 @@ static int f_write(lua_State* L) {
if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
length = 0;
else if (length < 0) {
const char* lastError = strerror(errno);
signal_process(self, SIGNAL_TERM);
return luaL_error(L, "error writing to process: %s", strerror(errno));
return luaL_error(L, "error writing to process: %s", lastError);
}
#endif
lua_pushnumber(L, length);
lua_pushinteger(L, length);
return 1;
}
static int f_close_stream(lua_State* L) {
process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS);
int stream = luaL_checknumber(L, 2);
close_fd(self->child_pipes[stream][stream == STDIN_FD ? 1 : 0]);
close_fd(&self->child_pipes[stream][stream == STDIN_FD ? 1 : 0]);
lua_pushboolean(L, 1);
return 1;
}
@ -375,7 +383,7 @@ static int f_tostring(lua_State* L) {
static int f_pid(lua_State* L) {
process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS);
lua_pushnumber(L, self->pid);
lua_pushinteger(L, self->pid);
return 1;
}
@ -383,7 +391,7 @@ static int f_returncode(lua_State *L) {
process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS);
if (self->running)
return 0;
lua_pushnumber(L, self->returncode);
lua_pushinteger(L, self->returncode);
return 1;
}
@ -404,7 +412,7 @@ static int f_wait(lua_State* L) {
int timeout = luaL_optnumber(L, 2, 0);
if (poll_process(self, timeout))
return 0;
lua_pushnumber(L, self->returncode);
lua_pushinteger(L, self->returncode);
return 1;
}
@ -417,11 +425,19 @@ 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); }
static int f_gc(lua_State* L) { return self_signal(L, SIGNAL_TERM); }
static int f_gc(lua_State* L) {
process_t* self = (process_t*) luaL_checkudata(L, 1, API_TYPE_PROCESS);
signal_process(self, SIGNAL_TERM);
close_fd(&self->child_pipes[STDIN_FD ][1]);
close_fd(&self->child_pipes[STDOUT_FD][0]);
close_fd(&self->child_pipes[STDERR_FD][0]);
poll_process(self, 10);
return 0;
}
static int f_running(lua_State* L) {
process_t* self = (process_t*)luaL_checkudata(L, 1, API_TYPE_PROCESS);
lua_pushboolean(L, self->running);
process_t* self = (process_t*)luaL_checkudata(L, 1, API_TYPE_PROCESS);
lua_pushboolean(L, poll_process(self, WAIT_NONE));
return 1;
}

View File

@ -60,12 +60,14 @@ static int f_pcre_match(lua_State *L) {
const char* str = luaL_checklstring(L, 2, &len);
if (lua_gettop(L) > 2)
offset = luaL_checknumber(L, 3);
offset -= 1;
len -= offset;
if (lua_gettop(L) > 3)
opts = luaL_checknumber(L, 4);
lua_rawgeti(L, 1, 1);
pcre2_code* re = (pcre2_code*)lua_touserdata(L, -1);
pcre2_match_data* md = pcre2_match_data_create_from_pattern(re, NULL);
int rc = pcre2_match(re, (PCRE2_SPTR)str, len, offset - 1, opts, md, NULL);
int rc = pcre2_match(re, (PCRE2_SPTR)&str[offset], len, 0, opts, md, NULL);
if (rc < 0) {
pcre2_match_data_free(md);
if (rc != PCRE2_ERROR_NOMATCH) {
@ -86,7 +88,7 @@ static int f_pcre_match(lua_State *L) {
return 0;
}
for (int i = 0; i < rc*2; i++)
lua_pushnumber(L, ovector[i]+1);
lua_pushnumber(L, ovector[i]+offset+1);
pcre2_match_data_free(md);
return rc*2;
}
@ -104,17 +106,17 @@ int luaopen_regex(lua_State *L) {
lua_setfield(L, -2, "__name");
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "regex");
lua_pushnumber(L, PCRE2_ANCHORED);
lua_pushinteger(L, PCRE2_ANCHORED);
lua_setfield(L, -2, "ANCHORED");
lua_pushnumber(L, PCRE2_ANCHORED) ;
lua_pushinteger(L, PCRE2_ANCHORED) ;
lua_setfield(L, -2, "ENDANCHORED");
lua_pushnumber(L, PCRE2_NOTBOL);
lua_pushinteger(L, PCRE2_NOTBOL);
lua_setfield(L, -2, "NOTBOL");
lua_pushnumber(L, PCRE2_NOTEOL);
lua_pushinteger(L, PCRE2_NOTEOL);
lua_setfield(L, -2, "NOTEOL");
lua_pushnumber(L, PCRE2_NOTEMPTY);
lua_pushinteger(L, PCRE2_NOTEMPTY);
lua_setfield(L, -2, "NOTEMPTY");
lua_pushnumber(L, PCRE2_NOTEMPTY_ATSTART);
lua_pushinteger(L, PCRE2_NOTEMPTY_ATSTART);
lua_setfield(L, -2, "NOTEMPTY_ATSTART");
return 1;
}

View File

@ -92,7 +92,7 @@ static int f_font_copy(lua_State *L) {
return 1;
}
static int f_font_group(lua_State* L) {
static int f_font_group(lua_State* L) {
luaL_checktype(L, 1, LUA_TTABLE);
luaL_setmetatable(L, API_TYPE_FONT);
return 1;
@ -106,8 +106,10 @@ static int f_font_set_tab_size(lua_State *L) {
}
static int f_font_gc(lua_State *L) {
if (lua_istable(L, 1)) return 0; // do not run if its FontGroup
RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT);
ren_font_free(*self);
return 0;
}

View File

@ -1,4 +1,5 @@
#include <SDL.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>
#include <dirent.h>
@ -6,14 +7,15 @@
#include <errno.h>
#include <sys/stat.h>
#include "api.h"
#include "../dirmonitor.h"
#include "../rencache.h"
#include "rencache.h"
#ifdef _WIN32
#include <direct.h>
#include <windows.h>
#include <fileapi.h>
#elif __linux__
#include <sys/vfs.h>
#elif __amigaos4__
#include "platform/amigaos4.h"
#endif
extern SDL_Window *window;
@ -109,9 +111,14 @@ static const char *get_key_name(const SDL_Event *e, char *buf) {
/* We need to correctly handle non-standard layouts such as dvorak.
Therefore, if a Latin letter(code<128) is pressed in the current layout,
then we transmit it as it is. But we also need to support shortcuts in
other languages, so for non-Latin characters we pass the scancode that
matches the letter in the QWERTY layout. */
if (e->key.keysym.sym < 128)
other languages, so for non-Latin characters(code>128) we pass the
scancode based name that matches the letter in the QWERTY layout.
In SDL, the codes of all special buttons such as control, shift, arrows
and others, are masked with SDLK_SCANCODE_MASK, which moves them outside
the unicode range (>0x10FFFF). Users can remap these buttons, so we need
to return the correct name, not scancode based. */
if ((e->key.keysym.sym < 128) || (e->key.keysym.sym & SDLK_SCANCODE_MASK))
strcpy(buf, SDL_GetKeyName(e->key.keysym.sym));
else
strcpy(buf, SDL_GetScancodeName(scancode));
@ -140,8 +147,8 @@ top:
ren_resize_window();
lua_pushstring(L, "resized");
/* The size below will be in points. */
lua_pushnumber(L, e.window.data1);
lua_pushnumber(L, e.window.data2);
lua_pushinteger(L, e.window.data1);
lua_pushinteger(L, e.window.data2);
return 3;
} else if (e.window.event == SDL_WINDOWEVENT_EXPOSED) {
rencache_invalidate();
@ -174,8 +181,8 @@ top:
SDL_GetWindowPosition(window, &wx, &wy);
lua_pushstring(L, "filedropped");
lua_pushstring(L, e.drop.file);
lua_pushnumber(L, mx - wx);
lua_pushnumber(L, my - wy);
lua_pushinteger(L, mx - wx);
lua_pushinteger(L, my - wy);
SDL_free(e.drop.file);
return 4;
@ -215,17 +222,17 @@ top:
if (e.button.button == 1) { SDL_CaptureMouse(1); }
lua_pushstring(L, "mousepressed");
lua_pushstring(L, button_name(e.button.button));
lua_pushnumber(L, e.button.x);
lua_pushnumber(L, e.button.y);
lua_pushnumber(L, e.button.clicks);
lua_pushinteger(L, e.button.x);
lua_pushinteger(L, e.button.y);
lua_pushinteger(L, e.button.clicks);
return 5;
case SDL_MOUSEBUTTONUP:
if (e.button.button == 1) { SDL_CaptureMouse(0); }
lua_pushstring(L, "mousereleased");
lua_pushstring(L, button_name(e.button.button));
lua_pushnumber(L, e.button.x);
lua_pushnumber(L, e.button.y);
lua_pushinteger(L, e.button.x);
lua_pushinteger(L, e.button.y);
return 4;
case SDL_MOUSEMOTION:
@ -238,37 +245,17 @@ top:
e.motion.yrel += event_plus.motion.yrel;
}
lua_pushstring(L, "mousemoved");
lua_pushnumber(L, e.motion.x);
lua_pushnumber(L, e.motion.y);
lua_pushnumber(L, e.motion.xrel);
lua_pushnumber(L, e.motion.yrel);
lua_pushinteger(L, e.motion.x);
lua_pushinteger(L, e.motion.y);
lua_pushinteger(L, e.motion.xrel);
lua_pushinteger(L, e.motion.yrel);
return 5;
case SDL_MOUSEWHEEL:
lua_pushstring(L, "mousewheel");
lua_pushnumber(L, e.wheel.y);
lua_pushinteger(L, e.wheel.y);
return 2;
case SDL_USEREVENT:
lua_pushstring(L, "dirchange");
lua_pushnumber(L, e.user.code >> 16);
switch (e.user.code & 0xffff) {
case DMON_ACTION_DELETE:
lua_pushstring(L, "delete");
break;
case DMON_ACTION_CREATE:
lua_pushstring(L, "create");
break;
case DMON_ACTION_MODIFY:
lua_pushstring(L, "modify");
break;
default:
return luaL_error(L, "unknown dmon event action: %d", e.user.code & 0xffff);
}
lua_pushstring(L, e.user.data1);
free(e.user.data1);
return 4;
default:
goto top;
}
@ -335,7 +322,11 @@ static int f_set_window_mode(lua_State *L) {
int n = luaL_checkoption(L, 1, "normal", window_opts);
SDL_SetWindowFullscreen(window,
n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
if (n == WIN_NORMAL) { SDL_RestoreWindow(window); }
if (n == WIN_NORMAL)
{
ren_resize_window();
SDL_RestoreWindow(window);
}
if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window); }
if (n == WIN_MINIMIZED) { SDL_MinimizeWindow(window); }
return 0;
@ -366,10 +357,10 @@ static int f_get_window_size(lua_State *L) {
int x, y, w, h;
SDL_GetWindowSize(window, &w, &h);
SDL_GetWindowPosition(window, &x, &y);
lua_pushnumber(L, w);
lua_pushnumber(L, h);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushinteger(L, w);
lua_pushinteger(L, h);
lua_pushinteger(L, x);
lua_pushinteger(L, y);
return 4;
}
@ -516,6 +507,9 @@ static int f_list_dir(lua_State *L) {
#include <windows.h>
#define realpath(x, y) _fullpath(y, x, MAX_PATH)
#endif
#ifdef __amigaos4__
#define realpath(x, y) _fullpath(x)
#endif
static int f_absolute_path(lua_State *L) {
const char *path = luaL_checkstring(L, 1);
@ -539,10 +533,10 @@ static int f_get_file_info(lua_State *L) {
}
lua_newtable(L);
lua_pushnumber(L, s.st_mtime);
lua_pushinteger(L, s.st_mtime);
lua_setfield(L, -2, "modified");
lua_pushnumber(L, s.st_size);
lua_pushinteger(L, s.st_size);
lua_setfield(L, -2, "size");
if (S_ISREG(s.st_mode)) {
@ -554,6 +548,14 @@ static int f_get_file_info(lua_State *L) {
}
lua_setfield(L, -2, "type");
#if __linux__
if (S_ISDIR(s.st_mode)) {
if (lstat(path, &s) == 0) {
lua_pushboolean(L, S_ISLNK(s.st_mode));
lua_setfield(L, -2, "symlink");
}
}
#endif
return 1;
}
@ -578,23 +580,26 @@ static struct f_type_names fs_names[] = {
{ 0x0, NULL },
};
#endif
static int f_get_fs_type(lua_State *L) {
const char *path = luaL_checkstring(L, 1);
struct statfs buf;
int status = statfs(path, &buf);
if (status != 0) {
return luaL_error(L, "error calling statfs on %s", path);
}
for (int i = 0; fs_names[i].magic; i++) {
if (fs_names[i].magic == buf.f_type) {
lua_pushstring(L, fs_names[i].name);
return 1;
#if __linux__
const char *path = luaL_checkstring(L, 1);
struct statfs buf;
int status = statfs(path, &buf);
if (status != 0) {
return luaL_error(L, "error calling statfs on %s", path);
}
}
for (int i = 0; fs_names[i].magic; i++) {
if (fs_names[i].magic == buf.f_type) {
lua_pushstring(L, fs_names[i].name);
return 1;
}
}
#endif
lua_pushstring(L, "unknown");
return 1;
}
#endif
static int f_mkdir(lua_State *L) {
@ -632,6 +637,16 @@ static int f_set_clipboard(lua_State *L) {
}
static int f_get_process_id(lua_State *L) {
#ifdef _WIN32
lua_pushinteger(L, GetCurrentProcessId());
#else
lua_pushinteger(L, getpid());
#endif
return 1;
}
static int f_get_time(lua_State *L) {
double n = SDL_GetPerformanceCounter() / (double) SDL_GetPerformanceFrequency();
lua_pushnumber(L, n);
@ -688,7 +703,7 @@ static int f_fuzzy_match(lua_State *L) {
strTarget += increment;
}
if (ptnTarget >= ptn && *ptnTarget) { return 0; }
lua_pushnumber(L, score - (int)strLen * 10);
lua_pushinteger(L, score - (int)strLen * 10);
return 1;
}
@ -713,13 +728,13 @@ static void* api_require(const char* symbol) {
P(error), P(gc), P(getallocf), P(getfield),
P(gethook), P(gethookcount), P(gethookmask), P(getinfo), P(getlocal),
P(getmetatable), P(getstack), P(gettable), P(gettop), P(getupvalue),
P(insert), P(isnumber), P(isstring), P(isuserdata),
P(load), P(newstate), P(newthread), P(newuserdata), P(next),
P(isnumber), P(isstring), P(isuserdata),
P(load), P(newstate), P(newthread), P(next),
P(pushboolean), P(pushcclosure), P(pushfstring), P(pushinteger),
P(pushlightuserdata), P(pushlstring), P(pushnil), P(pushnumber),
P(pushstring), P(pushthread), P(pushvalue),
P(pushvfstring), P(rawequal), P(rawget), P(rawgeti),
P(rawset), P(rawseti), P(remove), P(replace), P(resume),
P(rawset), P(rawseti), P(resume),
P(setallocf), P(setfield), P(sethook), P(setlocal),
P(setmetatable), P(settable), P(settop), P(setupvalue),
P(status), P(tocfunction), P(tointegerx), P(tolstring), P(toboolean),
@ -732,12 +747,12 @@ static void* api_require(const char* symbol) {
U(newstate), U(setfuncs), U(buffinit), U(addlstring), U(addstring),
U(addvalue), U(pushresult),
#if LUA_VERSION_NUM >= 502
P(absindex), P(arith), P(callk), P(compare), P(getctx), P(getglobal), P(getuservalue),
P(len), P(pcallk), P(pushunsigned), P(rawgetp), P(rawlen), P(rawsetp), P(setglobal),
P(iscfunction), P(setuservalue), P(tounsignedx), P(yieldk),
U(checkversion_), U(tolstring), U(checkunsigned), U(len), U(getsubtable), U(prepbuffsize),
P(absindex), P(arith), P(callk), P(compare), P(getglobal),
P(len), P(pcallk), P(rawgetp), P(rawlen), P(rawsetp), P(setglobal),
P(iscfunction), P(yieldk),
U(checkversion_), U(tolstring), U(len), U(getsubtable), U(prepbuffsize),
U(pushresultsize), U(buffinitsize), U(checklstring), U(checkoption), U(gsub), U(loadbufferx),
U(loadfilex), U(optinteger), U(optlstring), U(optunsigned), U(requiref), U(traceback)
U(loadfilex), U(optinteger), U(optlstring), U(requiref), U(traceback)
#else
P(objlen)
#endif
@ -786,34 +801,6 @@ static int f_load_native_plugin(lua_State *L) {
return result;
}
static int f_watch_dir(lua_State *L) {
const char *path = luaL_checkstring(L, 1);
const int recursive = lua_toboolean(L, 2);
uint32_t dmon_flags = (recursive ? DMON_WATCHFLAGS_RECURSIVE : 0);
dmon_watch_id watch_id = dmon_watch(path, dirmonitor_watch_callback, dmon_flags, NULL);
if (watch_id.id == 0) { luaL_error(L, "directory monitoring watch failed"); }
lua_pushnumber(L, watch_id.id);
return 1;
}
#if __linux__
static int f_watch_dir_add(lua_State *L) {
dmon_watch_id watch_id;
watch_id.id = luaL_checkinteger(L, 1);
const char *subdir = luaL_checkstring(L, 2);
lua_pushboolean(L, dmon_watch_add(watch_id, subdir));
return 1;
}
static int f_watch_dir_rm(lua_State *L) {
dmon_watch_id watch_id;
watch_id.id = luaL_checkinteger(L, 1);
const char *subdir = luaL_checkstring(L, 2);
lua_pushboolean(L, dmon_watch_rm(watch_id, subdir));
return 1;
}
#endif
#ifdef _WIN32
#define PATHSEP '\\'
#else
@ -893,19 +880,15 @@ static const luaL_Reg lib[] = {
{ "get_file_info", f_get_file_info },
{ "get_clipboard", f_get_clipboard },
{ "set_clipboard", f_set_clipboard },
{ "get_process_id", f_get_process_id },
{ "get_time", f_get_time },
{ "sleep", f_sleep },
{ "exec", f_exec },
{ "fuzzy_match", f_fuzzy_match },
{ "set_window_opacity", f_set_window_opacity },
{ "load_native_plugin", f_load_native_plugin },
{ "watch_dir", f_watch_dir },
{ "path_compare", f_path_compare },
#if __linux__
{ "watch_dir_add", f_watch_dir_add },
{ "watch_dir_rm", f_watch_dir_rm },
{ "get_fs_type", f_get_fs_type },
#endif
{ NULL, NULL }
};

View File

@ -1,59 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <SDL.h>
#define DMON_IMPL
#include "dmon.h"
#include "dmon_extra.h"
#include "dirmonitor.h"
static void send_sdl_event(dmon_watch_id watch_id, dmon_action action, const char *filepath) {
SDL_Event ev;
const int size = strlen(filepath) + 1;
/* The string allocated below should be deallocated as soon as the event is
treated in the SDL main loop. */
char *new_filepath = malloc(size);
if (!new_filepath) return;
memcpy(new_filepath, filepath, size);
#ifdef _WIN32
for (int i = 0; i < size; i++) {
if (new_filepath[i] == '/') {
new_filepath[i] = '\\';
}
}
#endif
SDL_zero(ev);
ev.type = SDL_USEREVENT;
ev.user.code = ((watch_id.id & 0xffff) << 16) | (action & 0xffff);
ev.user.data1 = new_filepath;
SDL_PushEvent(&ev);
}
void dirmonitor_init() {
dmon_init();
/* In theory we should register our user event but since we
have just one type of user event this is not really needed. */
/* sdl_dmon_event_type = SDL_RegisterEvents(1); */
}
void dirmonitor_deinit() {
dmon_deinit();
}
void dirmonitor_watch_callback(dmon_watch_id watch_id, dmon_action action, const char *rootdir,
const char *filepath, const char *oldfilepath, void *user)
{
(void) rootdir;
(void) user;
switch (action) {
case DMON_ACTION_MOVE:
send_sdl_event(watch_id, DMON_ACTION_DELETE, oldfilepath);
send_sdl_event(watch_id, DMON_ACTION_CREATE, filepath);
break;
default:
send_sdl_event(watch_id, action, filepath);
}
}

View File

@ -1,15 +0,0 @@
#ifndef DIRMONITOR_H
#define DIRMONITOR_H
#include <stdint.h>
#include "dmon.h"
#include "dmon_extra.h"
void dirmonitor_init();
void dirmonitor_deinit();
void dirmonitor_watch_callback(dmon_watch_id watch_id, dmon_action action, const char *rootdir,
const char *filepath, const char *oldfilepath, void *user);
#endif

View File

@ -7,15 +7,15 @@
#ifdef _WIN32
#include <windows.h>
#elif __linux__
#elif __linux__ || __FreeBSD__
#include <unistd.h>
#include <signal.h>
#elif __APPLE__
#include <mach-o/dyld.h>
#elif __amigaos4__
#include "platform/amigaos4.h"
#endif
#include "dirmonitor.h"
SDL_Window *window;
@ -45,6 +45,8 @@ static void get_exe_filename(char *buf, int sz) {
char exepath[size];
_NSGetExecutablePath(exepath, &size);
realpath(exepath, buf);
#elif __amigaos4__
strcpy(buf, _fullpath("./lite"));
#else
strcpy(buf, "./lite");
#endif
@ -108,11 +110,10 @@ int main(int argc, char **argv) {
SDL_DisplayMode dm;
SDL_GetCurrentDisplayMode(0, &dm);
dirmonitor_init();
window = SDL_CreateWindow(
"", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8,
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
SDL_SetWindowDisplayMode(window, &dm);
init_window_icon();
ren_init(window);
@ -154,6 +155,9 @@ init_lua:
" HOME = os.getenv('" LITE_OS_HOME "')\n"
" local exedir = EXEFILE:match('^(.*)" LITE_PATHSEP_PATTERN LITE_NONPATHSEP_PATTERN "$')\n"
" local prefix = exedir:match('^(.*)" LITE_PATHSEP_PATTERN "bin$')\n"
" if not HOME then\n"
" HOME = exedir\n"
" end\n"
" dofile((MACOS_RESOURCES or (prefix and prefix .. '/share/lite-xl' or exedir .. '/data')) .. '/core/start.lua')\n"
" core = require(os.getenv('LITE_XL_RUNTIME') or 'core')\n"
" core.init()\n"
@ -192,7 +196,6 @@ init_lua:
lua_close(L);
ren_free_window_resources();
dirmonitor_deinit();
return EXIT_SUCCESS;
}

View File

@ -4,13 +4,47 @@ lite_sources = [
'api/regex.c',
'api/system.c',
'api/process.c',
'dirmonitor.c',
'renderer.c',
'renwindow.c',
'rencache.c',
'main.c',
]
# dirmonitor backend
if get_option('dirmonitor_backend') == ''
if cc.has_function('inotify_init', prefix : '#include<sys/inotify.h>')
dirmonitor_backend = 'inotify'
elif cc.has_function('kqueue', prefix : '#include<sys/event.h>')
dirmonitor_backend = 'kqueue'
elif dependency('libkqueue', required : false).found()
dirmonitor_backend = 'kqueue'
elif host_machine.system() == 'windows'
dirmonitor_backend = 'win32'
else
dirmonitor_backend = 'dummy'
warning('no suitable backend found, defaulting to dummy backend')
endif
else
dirmonitor_backend = get_option('dirmonitor_backend')
endif
message('dirmonitor_backend: @0@'.format(dirmonitor_backend))
if dirmonitor_backend == 'kqueue'
libkqueue_dep = dependency('libkqueue', required : false)
if libkqueue_dep.found()
lite_deps += libkqueue_dep
endif
endif
lite_sources += [
'api/dirmonitor.c',
'api/dirmonitor/' + dirmonitor_backend + '.c',
]
lite_cargs += '-DDIRMONITOR_BACKEND=' + dirmonitor_backend
lite_cargs += '-DDIRMONITOR_' + dirmonitor_backend.to_upper()
lite_rc = []
if host_machine.system() == 'windows'
windows = import('windows')

61
src/platform/amigaos4.c Normal file
View File

@ -0,0 +1,61 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "amigaos4.h"
static char *getFullPath(const char *path)
{
char *appPath = malloc(sizeof(char) * MAX_DOS_NAME);
BPTR pathLock = Lock(path, SHARED_LOCK);
if (pathLock)
{
NameFromLock(pathLock, appPath, sizeof(char) * MAX_DOS_NAME);
UnLock(pathLock);
return appPath;
}
return NULL;
}
static char *getCurrentPath(void)
{
char *appPath = malloc(sizeof(char) * MAX_DOS_NAME);
BPTR pathLock = GetCurrentDir();
if (pathLock)
{
NameFromLock(pathLock, appPath, sizeof(char) * MAX_DOS_NAME);
return appPath;
}
return NULL;
}
char *_fullpath(const char *path)
{
static char prvPath[MAX_DOS_NAME];
static char result[MAX_DOS_NAME];
if (!strcmp(path, prvPath))
{
return result;
}
strcpy(prvPath, path);
if (!strcmp(path, "./lite"))
{
// TODO: Add code to get the name of the executable
strcpy(result, getFullPath("PROGDIR:lite"));
return result;
}
if (!strcmp(path, "."))
{
strcpy(result, getCurrentPath());
return result;
}
strcpy(result, getFullPath(path));
return result;
}

16
src/platform/amigaos4.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef _AMIGAOS4_H
#define _AMIGAOS4_H
#include <proto/dos.h>
#include <proto/exec.h>
#define VSTRING "Lite XL OS4 2.1beta (27.03.2022)"
#define VERSTAG "\0$VER: " VSTRING
static CONST_STRPTR stack USED = "$STACK:102400";
static CONST_STRPTR version USED = VERSTAG;
char *_fullpath(const char *);
#endif

View File

@ -95,7 +95,7 @@ static Command* push_command(int type, int size) {
return NULL;
}
command_buf_idx = n;
memset(cmd, 0, COMMAND_BARE_SIZE);
memset(cmd, 0, size);
cmd->type = type;
cmd->size = size;
return cmd;

View File

@ -1,5 +1,6 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <assert.h>
#include <math.h>
#include <ft2build.h>
@ -35,14 +36,14 @@ typedef struct {
typedef struct {
SDL_Surface* surface;
GlyphMetric metrics[MAX_GLYPHSET];
GlyphMetric metrics[MAX_GLYPHSET];
} GlyphSet;
typedef struct RenFont {
FT_Face face;
GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][MAX_LOADABLE_GLYPHSETS];
float size, space_advance, tab_advance;
short max_height;
short max_height, baseline, height;
ERenFontAntialiasing antialiasing;
ERenFontHinting hinting;
unsigned char style;
@ -66,7 +67,7 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
}
static int font_set_load_options(RenFont* font) {
int load_target = font->antialiasing == FONT_ANTIALIASING_NONE ? FT_LOAD_TARGET_MONO
int load_target = font->antialiasing == FONT_ANTIALIASING_NONE ? FT_LOAD_TARGET_MONO
: (font->hinting == FONT_HINTING_SLIGHT ? FT_LOAD_TARGET_LIGHT : FT_LOAD_TARGET_NORMAL);
int hinting = font->hinting == FONT_HINTING_NONE ? FT_LOAD_NO_HINTING : FT_LOAD_FORCE_AUTOHINT;
return load_target | hinting;
@ -113,8 +114,10 @@ static void font_load_glyphset(RenFont* font, int idx) {
font->sets[j][idx] = set;
for (int i = 0; i < MAX_GLYPHSET; ++i) {
int glyph_index = FT_Get_Char_Index(font->face, i + idx * MAX_GLYPHSET);
if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option | FT_LOAD_BITMAP_METRICS_ONLY) || font_set_style(&font->face->glyph->outline, j * (64 / SUBPIXEL_BITMAPS_CACHED), font->style) || FT_Render_Glyph(font->face->glyph, render_option))
if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option | FT_LOAD_BITMAP_METRICS_ONLY)
|| font_set_style(&font->face->glyph->outline, j * (64 / SUBPIXEL_BITMAPS_CACHED), font->style) || FT_Render_Glyph(font->face->glyph, render_option)) {
continue;
}
FT_GlyphSlot slot = font->face->glyph;
int glyph_width = slot->bitmap.width / byte_width;
if (font->antialiasing == FONT_ANTIALIASING_NONE)
@ -122,6 +125,13 @@ static void font_load_glyphset(RenFont* font, int idx) {
set->metrics[i] = (GlyphMetric){ pen_x, pen_x + glyph_width, 0, slot->bitmap.rows, true, slot->bitmap_left, slot->bitmap_top, (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f};
pen_x += glyph_width;
font->max_height = slot->bitmap.rows > font->max_height ? slot->bitmap.rows : font->max_height;
// In order to fix issues with monospacing; we need the unhinted xadvance; as FreeType doesn't correctly report the hinted advance for spaces on monospace fonts (like RobotoMono). See #843.
if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, (load_option | FT_LOAD_BITMAP_METRICS_ONLY | FT_LOAD_NO_HINTING) & ~FT_LOAD_FORCE_AUTOHINT)
|| font_set_style(&font->face->glyph->outline, j * (64 / SUBPIXEL_BITMAPS_CACHED), font->style) || FT_Render_Glyph(font->face->glyph, render_option)) {
continue;
}
slot = font->face->glyph;
set->metrics[i].xadvance = slot->advance.x / 64.0f;
}
if (pen_x == 0)
continue;
@ -134,7 +144,7 @@ static void font_load_glyphset(RenFont* font, int idx) {
FT_GlyphSlot slot = font->face->glyph;
font_set_style(&slot->outline, (64 / bitmaps_cached) * j, font->style);
if (FT_Render_Glyph(slot, render_option))
continue;
continue;
for (int line = 0; line < slot->bitmap.rows; ++line) {
int target_offset = set->surface->pitch * line + set->metrics[i].x0 * byte_width;
int source_offset = line * slot->bitmap.pitch;
@ -184,13 +194,15 @@ RenFont* ren_font_load(const char* path, float size, ERenFontAntialiasing antial
strcpy(font->path, path);
font->face = face;
font->size = size;
font->height = (short)((face->height / (float)face->units_per_EM) * font->size);
font->baseline = (short)((face->bbox.yMax / (float)face->units_per_EM) * font->size);
font->antialiasing = antialiasing;
font->hinting = hinting;
font->style = style;
font->space_advance = (int)font_get_glyphset(font, ' ', 0)->metrics[' '].xadvance;
font->space_advance = font_get_glyphset(font, ' ', 0)->metrics[' '].xadvance;
font->tab_advance = font->space_advance * 2;
return font;
failure:
failure:
FT_Done_Face(face);
return NULL;
}
@ -215,7 +227,7 @@ void ren_font_free(RenFont* font) {
void ren_font_group_set_tab_size(RenFont **fonts, int n) {
for (int j = 0; j < FONT_FALLBACK_MAX && fonts[j]; ++j) {
for (int i = 0; i < (fonts[j]->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? SUBPIXEL_BITMAPS_CACHED : 1); ++i)
for (int i = 0; i < (fonts[j]->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? SUBPIXEL_BITMAPS_CACHED : 1); ++i)
font_get_glyphset(fonts[j], '\t', i)->metrics['\t'].xadvance = fonts[j]->space_advance * n;
}
}
@ -228,7 +240,7 @@ float ren_font_group_get_size(RenFont **fonts) {
return fonts[0]->size;
}
int ren_font_group_get_height(RenFont **fonts) {
return fonts[0]->size + 3;
return fonts[0]->height;
}
float ren_font_group_get_width(RenFont **fonts, const char *text) {
@ -238,8 +250,8 @@ float ren_font_group_get_width(RenFont **fonts, const char *text) {
while (text < end) {
unsigned int codepoint;
text = utf8_to_codepoint(text, &codepoint);
font_group_get_glyph(&set, &metric, fonts, codepoint, 0);
width += metric->xadvance ? metric->xadvance : fonts[0]->space_advance;
RenFont* font = font_group_get_glyph(&set, &metric, fonts, codepoint, 0);
width += (!font || metric->xadvance) ? metric->xadvance : fonts[0]->space_advance;
}
const int surface_scale = renwin_surface_scale(&window_renderer);
return width / surface_scale;
@ -256,11 +268,11 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor
const char* end = text + strlen(text);
unsigned char* destination_pixels = surface->pixels;
int clip_end_x = clip.x + clip.width, clip_end_y = clip.y + clip.height;
while (text < end) {
unsigned int codepoint, r, g, b;
text = utf8_to_codepoint(text, &codepoint);
GlyphSet* set = NULL; GlyphMetric* metric = NULL;
GlyphSet* set = NULL; GlyphMetric* metric = NULL;
RenFont* font = font_group_get_glyph(&set, &metric, fonts, codepoint, (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED));
int start_x = floor(pen_x) + metric->bitmap_left;
int end_x = (metric->x1 - metric->x0) + start_x;
@ -270,14 +282,14 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor
if (set->surface && color.a > 0 && end_x >= clip.x && start_x < clip_end_x) {
unsigned char* source_pixels = set->surface->pixels;
for (int line = metric->y0; line < metric->y1; ++line) {
int target_y = line + y - metric->y0 - metric->bitmap_top + font->size * surface_scale;
int target_y = line + y - metric->bitmap_top + font->baseline * surface_scale;
if (target_y < clip.y)
continue;
if (target_y >= clip_end_y)
break;
if (start_x + (glyph_end - glyph_start) >= clip_end_x)
glyph_end = glyph_start + (clip_end_x - start_x);
else if (start_x < clip.x) {
if (start_x < clip.x) {
int offset = clip.x - start_x;
start_x += offset;
glyph_start += offset;
@ -331,18 +343,29 @@ void ren_draw_rect(RenRect rect, RenColor color) {
y2 = y2 > clip.y + clip.height ? clip.y + clip.height : y2;
SDL_Surface *surface = renwin_get_surface(&window_renderer);
RenColor *d = (RenColor*) surface->pixels;
uint32_t *d = surface->pixels;
#ifdef __amigaos4__
d += x1 + y1 * surface->pitch/sizeof(uint32_t);
int dr = surface->pitch/sizeof(uint32_t) - (x2 - x1);
#else
d += x1 + y1 * surface->w;
int dr = surface->w - (x2 - x1);
unsigned int translated = SDL_MapRGB(surface->format, color.r, color.g, color.b);
#endif
if (color.a == 0xff) {
uint32_t translated = SDL_MapRGB(surface->format, color.r, color.g, color.b);
SDL_Rect rect = { x1, y1, x2 - x1, y2 - y1 };
SDL_FillRect(surface, &rect, translated);
} else {
RenColor translated_color = (RenColor){ translated & 0xFF, (translated >> 8) & 0xFF, (translated >> 16) & 0xFF, color.a };
RenColor current_color;
RenColor blended_color;
for (int j = y1; j < y2; j++) {
for (int i = x1; i < x2; i++, d++)
*d = blend_pixel(*d, translated_color);
{
SDL_GetRGB(*d, surface->format, &current_color.r, &current_color.g, &current_color.b);
blended_color = blend_pixel(current_color, color);
*d = SDL_MapRGB(surface->format, blended_color.r, blended_color.g, blended_color.b);
}
d += dr;
}
}

View File

@ -9,6 +9,18 @@ static int query_surface_scale(RenWindow *ren) {
SDL_GetWindowSize(ren->window, &w_points, &h_points);
/* We consider that the ratio pixel/point will always be an integer and
it is the same along the x and the y axis. */
#ifdef __amigaos4__
// This is a workaround when the w_pixels != w_points and h_pixels != h_points
// because of redraw delays, especially when the "Resize with contents" is enabled
if (w_pixels != w_points) {
w_pixels = w_points;
}
if (h_pixels != h_points) {
h_pixels = h_points;
}
#endif
assert(w_pixels % w_points == 0 && h_pixels % h_points == 0 && w_pixels / w_points == h_pixels / h_points);
return w_pixels / w_points;
}
@ -20,7 +32,7 @@ static void setup_renderer(RenWindow *ren, int w, int h) {
SDL_DestroyTexture(ren->texture);
SDL_DestroyRenderer(ren->renderer);
}
ren->renderer = SDL_CreateRenderer(ren->window, -1, 0);
ren->renderer = SDL_CreateRenderer(ren->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
ren->texture = SDL_CreateTexture(ren->renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, w, h);
ren->surface_scale = query_surface_scale(ren);
}

View File

@ -0,0 +1,9 @@
[wrap-file]
directory = freetype-2.11.1
source_url = https://download.savannah.gnu.org/releases/freetype/freetype-2.11.1.tar.gz
source_filename = freetype-2.11.1.tar.gz
source_hash = f8db94d307e9c54961b39a1cc799a67d46681480696ed72ecf78d4473770f09b
[provide]
freetype2 = freetype_dep

View File

@ -1,4 +1,12 @@
[wrap-git]
directory = lua
url = https://github.com/franko/lua
revision = v5.2.4-7
[wrap-file]
directory = lua-5.4.3
source_url = https://www.lua.org/ftp/lua-5.4.3.tar.gz
source_filename = lua-5.4.3.tar.gz
source_hash = f8612276169e3bfcbcfb8f226195bfc6e466fe13042f1076cbde92b7ec96bbfb
patch_filename = lua_5.4.3-2_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/lua_5.4.3-2/get_patch
patch_hash = 3c23ec14a3f000d80fe2e2fdddba63a56e13c758d74195daa4ff0da7bfdb02da
[provide]
lua-5.4 = lua_dep

15
subprojects/pcre2.wrap Normal file
View File

@ -0,0 +1,15 @@
[wrap-file]
directory = pcre2-10.39
source_url = https://github.com/PhilipHazel/pcre2/releases/download/pcre2-10.39/pcre2-10.39.tar.bz2
source_filename = pcre2-10.39.tar.bz2
source_hash = 0f03caf57f81d9ff362ac28cd389c055ec2bf0678d277349a1a4bee00ad6d440
patch_filename = pcre2_10.39-2_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/pcre2_10.39-2/get_patch
patch_hash = c4cfffff83e7bb239c8c330339b08f4367b019f79bf810f10c415e35fb09cf14
[provide]
libpcre2-8 = -libpcre2_8
libpcre2-16 = -libpcre2_16
libpcre2-32 = -libpcre2_32
libpcre2-posix = -libpcre2_posix

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