Compare commits

...

546 Commits

Author SHA1 Message Date
George Sokianos 0cb20ab7b2 a small fix in case the text is nil 2023-01-08 20:42:19 +00:00
George Sokianos 789004ea2c Updates and fixes for the latest available code, for 2.1 release 2022-10-10 19:06:19 +01:00
George Sokianos 2bdfd5a694 Merge branch 'master' into amiga-2.0 2022-09-26 17:27:35 +01:00
George Sokianos 209de02c36 Merge branch 'master' of ssh://git.walkero.gr:2222/walkero/lite-xl 2022-09-26 17:06:22 +01:00
George Sokianos 1ecb66c807 innosetup changes 2022-09-26 17:06:05 +01:00
jgmdev e0e928c31e gh workflow: don't make release on tag pushes to prevent double release when executed from github web interface. 2022-09-25 18:20:08 -04:00
jgmdev 42eeaf44a5 updated changelog 2022-09-25 17:39:36 -04:00
jgmdev e2884895ce core start: added missing ';' to package.cpath 2022-09-25 17:13:07 -04:00
Adam 6bb08fc68f
Added in ability to have `init.so` as a require for cpath. (#1126) 2022-09-25 17:08:21 -04:00
Jefferson González 9f1294fea2
Added Release Workflow and Fixed some build script issues (#1013)
* ci linux: make builds properly static
* test workflow_dispatch
* install wayland-protocols
* append missing portable
* make debug builds by default
* auto enable some video subsystems for proper wayland support
* added release workflow
* make line shorter in innosetup bash script
* disable some video subsystems on darwin and windows
* fix default build dir on msys
* print output of ntldd
* properly set msys arch
* disable opengl on windows
* copy mingw dependencies on package
* innosetup script copy from generated package dir
* changed license to reflect team work
* adjusted the ci windows install name
* add all language plugins to addons
* disabled generation of source tarballs
* removed language_cpp from plugins repo
* enabled lua utf8 patch for windows build
* added open_ext to addons
* moved away from deprecated virtual environments
* make minimal build and with addons
* simplified CI build.yml
2022-09-25 16:59:01 -04:00
Jefferson González a640360d0d
api: expose api_load_libs to native plugins (#1125)
This function is currently needed for the threading plugin.
2022-09-22 20:30:24 -04:00
jgmdev a4713505f0 docs: documented ARCH global. 2022-09-22 12:37:07 -04:00
Dmitry Atamanov 4546ce13e4
Update unidata to version 15.0 (#1122) 2022-09-19 21:27:54 -04:00
Adam 5ada80b9df
Added in native modules suffixes. (#1111)
* Added in native modules with suffixes, giving priority to those with matching architectures and platforms.

* PowerPC isn't x86, and it's x86_64.

* Changed things over to allow compiler to set a tuple, makes more sense from a build perspective.

* Spelling mistake.

* Added in arm target tuples.
2022-09-18 18:27:50 -04:00
jgmdev 4134b30ffd core: remapped core:restart to `ctrl+alt+r`
Commonly `ctrl+shift+r` is used in most editors for find and
replace operations, also the regexreplacepreview.lua plugin makes
a more appropriate use of this binding.
2022-09-16 11:31:05 -04:00
Jefferson González 5a80d3547e
contextmenu: resize on scale changes (#1117) 2022-09-15 00:57:45 -04:00
Jefferson González 862ed9ad6a
plugin scale: added option to set default scale (#1115) 2022-09-15 00:54:44 -04:00
Adam 10d810b7d7
Added in simple directory search to treeview. (#1110) 2022-09-14 00:14:13 -04:00
Guldoman a19cfb4f80
Strictly limit find/replace commands to `core.docview` (#1108)
Without this, find/replace commands applied to the `CommandView` too,
with buggy results.
2022-09-13 22:29:52 -04:00
jgmdev bead59a898 autocomplete: properly replace current partial symbol 2022-09-12 22:41:50 -04:00
Jefferson González 334ef427c1
Merge pull request #1105 from jgmdev/PR/sdl-update
updated sdl2 wrap to 2.24
2022-09-07 18:45:19 -04:00
Adam 4f17667d42 Changed setpgrp to a more portable form. 2022-09-02 20:26:13 -04:00
jgmdev 20be2e4f7d updated sdl2 wrap to 2.24 2022-08-24 01:04:09 -04:00
Guldoman c25f83da90
Make predicate for some `TreeView` commands stricter
This avoids performing the `treeview:new-folder` command on ctrl + 
double click.
This happens because `ctrl+lclick` (which is the keybinding for 
`treeview:new-folder`) is triggered also by ctrl + double click, which 
isn't captured by anything else.
2022-08-21 20:03:32 +02:00
Guldoman cbe0fd63bf
Reduce double click radius
SDL uses 32 pixels by default, which is a bit too much and causes 
problems with, for example, adding multiple close selections too 
quickly.
2022-08-21 19:08:32 +02:00
Aqil Contractor 9b1bfeacac
Added a smoothing and strikethrough option to font loading. (#1087)
* Added a smoothing option to font loading.
* Added a font strikethrough option to font loading.
* Fixed underline applying incorrectly in cases of non-underlined fallback fonts being used.

Co-authored-by: Guldoman <giulio.lettieri@gmail.com>
2022-08-20 16:15:08 -04:00
Jefferson González f07b62f852
Merge pull request #1100 from Guldoman/PR_fix_path_suggest
Remove dot slash from suggested paths in `common.path_suggest`
2022-08-17 13:59:19 -04:00
Jefferson González 76f71b2846
Merge pull request #1098 from Guldoman/PR_command_predicate_params
Allow command predicates to manage parameters, allow overwriting commands
2022-08-16 18:20:30 -04:00
Guldoman 4c186b07a3
Be more lenient with appending `PATHSEP` in `common.path_suggest` in Windows
This allows to use the Unix separator without resulting in ugly 
suggestions that added the Windows separator too.

For example:
Before: `data/` -> `data/\core\`
After: `data/` -> `data/core\`
2022-08-17 00:11:19 +02:00
Guldoman e8ca861512
Remove final `PATHSEP` in `common.normalize_volume` 2022-08-16 23:53:03 +02:00
Guldoman a4355c6536
Add `PATHSEP` before listing the directory in `common.path_suggest`
Before, in Windows, listing `.` instead of `.\` resulted in unexpected 
results.
2022-08-16 23:51:12 +02:00
Jefferson González cefb6787f8
Merge pull request #1101 from benwalksaway/python_async_await
language_python: add syntax support for async/await
2022-08-16 16:23:15 -04:00
Guldoman 6aa96556c0
Replace `assert` with `core.log_quiet` on command replace
This caused issues when saving the user module with commands defined
inside it, as it resulted in the user-defined commands trying to
overwrite themselves and failing.
2022-08-16 22:18:50 +02:00
Guldoman d944bd85ec
Add predicate memoization to `command.get_all_valid` 2022-08-16 22:13:25 +02:00
Guldoman 06b9953777
Use `Object:is` instead of direct metatable comparison in `autocomplete` 2022-08-16 22:13:25 +02:00
Guldoman cf29a6a45f
Allow command predicates to manage parameters passed to the commands
When a command is performed with parameters, those are now passed to the
predicate.
The predicate can then return, after the validity boolean, any other
value that will then be passed to the actual command.
If the predicate only returns the validity boolean, the original
parameters are passed through to the actual command.

This allows predicates to manipulate the received parameters, and allows
them to pass the result of an expensive computation to the actual
command, which won't have to recalculate it.

String and table predicates will now also return `core.active_view`.
2022-08-16 22:13:16 +02:00
Ben Larisch a6bbd3c8a9
language_python: add syntax support for async/await 2022-08-16 09:52:46 +02:00
Guldoman 4b4c54ba65
Remove dot slash from suggested paths in `common.path_suggest`
When no `root` is specified and the initial `path` is empty, the initial 
`path` becomes `.`.
This results in returned files/dirs that are prepended with `./`.

Now, in that case, `./` is removed.
2022-08-16 08:08:04 +02:00
Adam 6ccc5f6dde
Steps to generalize toolbar and treeview. (#1088) 2022-08-09 14:53:30 -04:00
Wahyu Wiyoko 77cebd8341
Fix [build] link and add [lite] link on Overview (#1096) 2022-08-07 17:09:54 -04:00
Guldoman 63c818ac45
Enable SDL timers 2022-08-03 17:28:43 +02:00
Guldoman 4db71836af
Clear default Lua require path (#1085)
This is mainly done to avoid requiring from the current working 
directory of the editor.

This also avoids requiring from system paths, as it was already the case 
for the native modules search path.
2022-08-03 11:13:26 -04:00
ian` 2667f9476b
Fix "hard" indent column info on status view. (#1078)
* Fix "hard" indent column info on status view.

* Update tabs calculation and add "byte" number info

* Add config.show_char_byte_info

* Add show char byte toggle command.

it should be added on the commands/statusbar.lua, but there is no config module loaded before and i won't to add it.

* Update config.lua

* Update statusview.lua
2022-07-22 19:01:54 +02:00
Jefferson González e4bef5c5b6
Merge pull request #1080 from jgmdev/PR/fix-commandview-caret
CommandView: do not change caret size with config.line_height
2022-07-17 12:29:52 -04:00
Guldoman 06b2fada07
Merge pull request #1079 from cisoun/master
Add font style options in user module
2022-07-16 04:27:39 +02:00
jgmdev c7781a76a9 CommandView: do not change caret size with config.line_height 2022-07-15 11:27:27 -04:00
cisoun 366cadf605
Add font style options in user module 2022-07-15 12:05:59 +02:00
Guldoman d7f9b30d05
`drawwhitespace`: Invalidate cache on indent size change 2022-07-15 06:54:03 +02:00
Jefferson González 86d45458f8
Merge pull request #1070 from Guldoman/PR_get_selections_swap_return
Make `Doc:get_selection[s]` return if the selection was actually sorted
2022-07-13 11:13:43 -04:00
Cyriaque Skrapits af3e2c971c
`language_python`: Add new patterns (#1074)
* `language_python`:  Add new patterns

This commit provides:
 - multiline comments support;
 - unicode string symbol highlighting;
 - class names as keyword2.
2022-07-13 11:06:42 -04:00
Guldoman 2dac3667cb
Merge pull request #1073 from ncarrezdev/ncarrezdev/synxtax-corrections
[chore]: Update syntax
2022-07-13 15:05:26 +02:00
NCarrezDev 7f9287a7e7 [chore]: Delete useless $
follow up on Guldoman's comment
2022-07-13 09:16:32 +02:00
Jefferson González dc88b1b56d
Merge pull request #1075 from Guldoman/PR_StatusView_add_item_table
Make `StatusView:add_item` accept a table
2022-07-13 01:32:14 -04:00
Guldoman 9cfa3ecd56
Make `StatusView:add_item` accept a table 2022-07-13 07:22:42 +02:00
Guldoman 693bd11b22
Don't wrap around if there are no suggestions in `CommandView` 2022-07-11 23:28:08 +02:00
Guldoman 86024586fd
Start dirmonitor check thread only after a watch is added (#1072) 2022-07-11 17:14:50 -04:00
Guldoman e7c4bdfe8e
Make `Doc:get_selection[s]` return if the selection was actually sorted 2022-07-11 05:39:55 +02:00
NCarrezDev 030dcc1e62 [chore]: Update syntax
Harmonizing the syntax files
Now inline comment are a single string instead of a table of strings.

-(language_c): Removed whitespaces breaking indent
-(language_html): Removed trailing whitespace
2022-07-06 10:15:44 +02:00
Jefferson González e646f2fb28
Merge pull request #1065 from Guldoman/PR_log_size
Increase `config.max_log_items`, add a scrollbar and optimize `LogView`
2022-07-05 18:07:33 -04:00
Jefferson González f49fd1b477
Merge pull request #1064 from Guldoman/PR_md_single_math
`language_md`: Limit inline math mode to the current line
2022-07-05 18:01:59 -04:00
Jefferson González f611556f1a
Merge pull request #1062 from Guldoman/PR_fix_logview_on_error
Fix opening `LogView` when reloading customizations
2022-07-05 18:00:56 -04:00
Jefferson González 58336f6060
Merge pull request #1060 from Guldoman/PR_drag_whitespace_newline
On `doc:newline` remove line content if it contains only whitespace
2022-07-05 17:56:13 -04:00
Jefferson González 041456cf38
Merge pull request #1059 from Guldoman/PR_no_indent_empty_lines
Don't indent empty lines in a selection
2022-07-05 17:52:17 -04:00
Jefferson González fccd7dbaa9
Merge pull request #1063 from lite-xl/port-build-lhelper-2
Adapt build job to lhelper-2
2022-07-05 17:49:17 -04:00
Guldoman f5fbdd80f6
Merge pull request #1066 from benwalksaway/match_case_syntax_support
language_python: add syntax support for the match-case statement
2022-07-03 15:28:54 +02:00
Ben Larisch ce0d8b313c
language_python: add syntax support for match-case statement 2022-07-03 15:15:51 +02:00
Guldoman 7888bf08e9
`LogView`: Show scrollbar 2022-07-01 05:29:55 +02:00
Guldoman b84a510d42
Increase default maximum number of log items
The low number in some cases resulted in hidden errors.
2022-07-01 05:29:54 +02:00
Guldoman caf106be1d
`LogView`: Don't draw invisible items 2022-07-01 05:29:45 +02:00
Francesco Abbate ed99623ad3 Adapt build job to lhelper-2 2022-06-30 09:42:01 +02:00
Guldoman 79dd8779c4
`autocomplete`: Fix "Too many symbols" message when `Doc` has no name
Before, when a `Doc` had no name, an error was thrown.
2022-06-29 05:54:37 +02:00
Guldoman 92c2815aa4
`language_md`: Limit inline math mode to the current line 2022-06-28 20:09:36 +02:00
Guldoman 259de54c33
Fix opening `LogView` when reloading customizations
As `reload_customizations` was called during save operations, opening 
the `LogView` changed `core.active_view` which caused some errors to be 
thrown.
2022-06-28 04:21:43 +02:00
Guldoman c792b13666
On `doc:newline` remove line content if it contains only whitespace 2022-06-25 03:57:47 +02:00
Guldoman d6ce9e1ac6
Don't indent empty lines in a selection 2022-06-25 03:32:47 +02:00
Guldoman 438ed6984a
`drawwhitespace`: Invalidate cache on font size change 2022-06-25 03:30:33 +02:00
Adam e784513222 Missing header. 2022-06-24 20:21:15 -04:00
Guldoman 9cd47aa12e
Fix full line cut when only a single line remains 2022-06-24 05:49:20 +02:00
Jefferson González 6a8eed45c7
Merge pull request #1057 from jgmdev/PR/font-additions
renderer fonts: additions and improvements
2022-06-23 18:09:53 -04:00
jgmdev d2f9eeea07 renderer fonts: additions and improvements
* Allow passing font options to renderer.font:copy().
* Added renderer.font:get_path()
* Reintroduced set_size() for more faster font size changes
* Swapped copy wiht set_size on scale plugin for better performance
* Use code_font:copy() instead of renderer.font.load() on language_md to
  properly match user font now that font options are supported on copy.
* Added new changes to renderer docs
2022-06-23 18:08:04 -04:00
jgmdev 31d2024283 lineguide: added config spec 2022-06-23 18:07:27 -04:00
Adam d48fa9ef74 Fixed minor issue on windows. 2022-06-23 17:43:49 -04:00
Chris 1e91080680
Improve `lineguide` plugin (#1056)
* Add more options to lineguide
* Allow lineguide plugin to load but remain disabled
* Use config.line_limit for default ruler
2022-06-23 15:24:57 -04:00
Guldoman ec0e3018a8
`findreplace`: Sort last searches 2022-06-23 05:16:47 +02:00
Katrina Grace beefb16469
language_html: Improve subsyntax highlighting (#1043)
* language_html: Improve subsyntax highlighting

- Adjusted `<script>`/`<style>` tag detection to not break when attributes like `defer` are added
2022-06-22 22:56:41 -04:00
Jefferson González 880e6e4f0f
Merge pull request #1040 from Guldoman/PR_tokenizer_errors_alert
Add more tokenizer errors/warnings
2022-06-22 19:43:51 -04:00
Guldoman 522d8a8094
`findreplace`: Fix number of total replacements 2022-06-23 00:16:16 +02:00
jgmdev e0859e1e39 treeview: scale fallback sizes as pointed out by @Guldoman 2022-06-22 01:35:10 -04:00
jgmdev 0629542cf7 updated changelog 2022-06-22 01:10:16 -04:00
Jefferson González 3cf1bee1e6
Merge pull request #1054 from jgmdev/PR/fix-toolbarview-requirement
treeview: restore ability to disable toolbarview
2022-06-22 01:01:09 -04:00
jgmdev 7b411c3ea9 treeview: restore ability to disable toolbarview 2022-06-22 00:42:26 -04:00
Guldoman 76c1db97f5
`drawwhitespace`: Use `Docview` vertical line offset 2022-06-20 19:56:53 +02:00
Guldoman 205e554d87
Merge pull request #1047 from jgmdev/PR/cv-draw-visible
CommandView: improve performance by only drawing visible
2022-06-20 17:35:06 +02:00
Jefferson González 1f4f2e52db
Merge pull request #1048 from DMClVG/patch-1
Add .cjs and .mjs for js syntax highlighting
2022-06-20 10:22:07 -04:00
a 8fd00b12cf
Add .cjs and .mjs for js syntax highlighting 2022-06-20 16:12:25 +02:00
jgmdev 665c2cdd4d CommandView: improve performance by only drawing visible 2022-06-20 10:01:28 -04:00
jgmdev 173dd3aeb4 plugin treeview: fix crash
When the max_project_files is set to a higher value than the allowed
system maximum file descriptors, and opening a project directory that
causes dirmonitor to open a watch on a lot of files or directories, at
least on MacOSX it causes all system.* file functions to return nil
(for too many opened files) which breaks the project files scan.
2022-06-17 15:35:23 -04:00
jgmdev 3c682512e7 build script: fix lua subproject expansion 2022-06-17 11:04:53 -04:00
jgmdev c2befaa832 changelog: added latest changes 2022-06-17 10:15:14 -04:00
Jefferson González 64cb6a290d
Merge pull request #1041 from takase1121/lua-utf8-windows
Support UTF-8 on Windows (Lua)
2022-06-17 09:47:02 -04:00
Takase 4e1ce07610
make system.* functions support UTF8 filenames (#1042)
* make system.* functions support UTF8 filenames
* move utfconv.h into ifdef guard
* fix wrong null check
2022-06-17 09:31:52 -04:00
Jefferson González 3dadbd3a49
Merge pull request #1038 from takase1121/PR/scale-step-gc
run GC between scale to prevent ram from exploding
2022-06-16 03:04:53 -04:00
takase1121 bccc02743a
add option to patch lua with UTF8 support 2022-06-16 13:35:38 +08:00
Jefferson González 380cfb9a24
Merge pull request #1030 from Guldoman/PR_cache_draw_whitespace
`drawwhitespace`: Cache whitespace location
2022-06-15 21:08:09 -04:00
Guldoman 2d3abd2533
`drawwhitespace`: Invalidate cache on config changes 2022-06-16 00:03:25 +02:00
Jefferson González d2fd5c9df7
Merge pull request #1034 from Guldoman/PR_escape_start_patterns
Check if "open" pattern is escaped
2022-06-15 16:51:34 -04:00
Jefferson González eeea47a2e5
Merge pull request #1033 from Guldoman/PR_utf8_regex_fix
Convert more byte offsets to utf-8 pos in regex tokenizer
2022-06-15 16:51:11 -04:00
Jefferson González bde056350a
Merge pull request #1037 from takase1121/PR/dlopen-omit-filename
omit filename when printing error for library load
2022-06-15 16:37:41 -04:00
Jefferson González 0c7a132ec5
Merge pull request #1039 from takase1121/PR/font-retrieve-leak
fix memory leak and wrong check in font_retrieve
2022-06-15 16:36:06 -04:00
Jefferson González 2dc0a5664d
Merge pull request #1029 from Guldoman/PR_math_md
`language_md`: Add math delimiters
2022-06-15 15:32:57 -04:00
Guldoman d169619f69
Warn if token type is a table when not needed 2022-06-15 21:31:16 +02:00
Guldoman 2e37e85a48
Add helper function to report bad patterns in tokenizer 2022-06-15 21:28:46 +02:00
Guldoman 5027a0f12b
Fix malformed pattern check for group patterns in tokenizer
If the token type was a simple string (and not a table), the size of the 
string was used instead of `1`.
2022-06-15 19:33:58 +02:00
jgmdev d8a3987aa4 changeslog: added performance section and listed PR 1032 2022-06-15 12:15:14 -04:00
takase1121 7ecc174094
fix memory leak and wrong check in font_retrieve 2022-06-15 23:23:13 +08:00
takase1121 42e0028f1c
run GC between scale to prevent ram from exploding 2022-06-15 22:56:52 +08:00
takase1121 0de90d542b
omit filename from error message 2022-06-15 16:05:20 +08:00
takase1121 093ae837ee
add .cache to gitignore 2022-06-15 16:04:37 +08:00
Jefferson González 2d412ef64a
Merge pull request #1032 from takase1121/font-load-optimization
load space metrics only when creating font
2022-06-14 19:56:14 -04:00
Guldoman 5b6b48320f
Check if "open" pattern is escaped
Previously this check was only done for "close" patterns.
2022-06-12 04:19:05 +02:00
Guldoman c947e8a4d1
Convert more byte offsets to utf-8 pos in regex tokenizer 2022-06-12 02:55:36 +02:00
takase1121 73cd768a19
load space metrics only instead of all metrics of the 1st 256 characters 2022-06-12 08:22:01 +08:00
takase1121 dc2e9621ca lua patch for utf-8 support 2022-06-11 21:01:12 +08:00
Guldoman f38723ea46
`drawwhitespace`: Cache whitespace location 2022-06-11 06:30:13 +02:00
Guldoman 685956cbdb
Add `Highlighter:update_notify` to keep track of retokenized lines
This is helpful for plugins that need to know when a line has been 
retokenized.
2022-06-11 06:21:55 +02:00
Guldoman 99d328cfd7
Downgrade `StatusView:get_items` deprecation message to warning 2022-06-11 05:13:37 +02:00
Guldoman ed02a55cc1
`language_md`: Add math delimiters 2022-06-10 23:55:41 +02:00
jgmdev 82915bb217 changelog: added links to releases 2022-06-07 22:29:21 -04:00
jgmdev 3f206db69a initial documentation for better code completion 2022-06-07 22:09:34 -04:00
Jefferson González ff641cdb06
Merge pull request #1022 from Guldoman/PR_annoy_on_reload_error
Open `LogView` on user/project module reload error
2022-06-07 20:06:56 -04:00
Jefferson González 92c6f1c04f
Merge pull request #1021 from Guldoman/PR_lax_common_merge
Make `common.merge` work with invalid arguments
2022-06-07 20:05:22 -04:00
Jefferson González 61015dd382
Merge pull request #1014 from Guldoman/PR_commandview_options2
Add `text`, `select_text` and `show_suggestions` options to `CommandView`
2022-06-07 19:05:32 -04:00
Jefferson González 439c27447f
Merge branch 'master' into PR_commandview_options2 2022-06-07 19:03:55 -04:00
jgmdev 1c6573aa1b changelog: include treeview fixes 2022-06-07 18:47:19 -04:00
Jefferson González 2fc20330a3
Merge pull request #1010 from Guldoman/PR_improve_multiproject_treeview
`TreeView` improvements for multi-project
2022-06-07 18:24:54 -04:00
jgmdev 2caa96e9b9 ChangesLog: more details to 2.1.0 release 2022-06-07 03:00:44 -04:00
Guldoman 237f0c91cb
Open `LogView` on user/project module reload error 2022-06-05 04:29:24 +02:00
Guldoman 0b96be7af2
Make `common.merge` work with invalid arguments
This is needed because users could try to enable plugins with 
`config.plugins.plugin_name = true`.
Before, this would result in `common.merge` throwing an error; now it 
just returns a copy of the "base" table.
2022-06-05 04:10:51 +02:00
Jefferson González b70069572e
Merge pull request #1018 from jgmdev/PR/linewrapping-priority
plugin linewrapping: added priority
2022-06-03 13:28:03 -04:00
jgmdev de63574b53 plugin linewrapping: added priority
Since the linewrapping plugin modifies some of the DocView line
calculation and positioning functions we need to make sure of loading it
before other plugins. This way we make sure that plugins that also overwrite
and depend on DocView functionality aren't using the original methods without
the linewrapping changes, which leads to wrong line and column calculations.
2022-06-03 04:13:54 -04:00
jgmdev 5da7467a5c plugin drawwhitespace: return line height on draw_line_text 2022-06-03 03:16:18 -04:00
Jefferson González 92a008614f
Merge pull request #1017 from jgmdev/PR/separate-default-theme
style: move default colors to its own style file
2022-06-02 20:12:07 -04:00
jgmdev ca46d8e261 style: move default colors to its own style file 2022-06-02 19:20:54 -04:00
Guldoman ec58b1f0bd
Add `text` and `select_text` to `CommandView` options 2022-06-02 19:30:51 +02:00
Guldoman 6c89a3e575
Add `show_suggestions` to `CommandView` options 2022-06-02 19:30:22 +02:00
Guldoman 8ce1ecb897
Fix `CommandView:enter` deprecation log 2022-06-02 06:42:58 +02:00
Jefferson González cd80270feb
Merge pull request #1015 from jgmdev/PR/drawwhitespace-config-spec
plugin drawwhitespace: added config spec
2022-06-02 00:08:38 -04:00
jgmdev 4157dd867c plugin drawwhitespace: added config spec 2022-06-02 00:07:34 -04:00
Jefferson González 59a5839ac9
Merge pull request #908 from Guldoman/PR_improve_whitespace
Improve `drawwhitespace` plugin
2022-06-01 21:59:45 -04:00
Adam Harrison 67066fc93a Added in defaults for dirwatch. 2022-06-01 18:36:00 -04:00
Guldoman d390eb248e
Fix tooltip not getting removed after `find-replace:replace` 2022-06-01 18:19:33 +02:00
Guldoman 730ea0c91b
Make `TreeView` more multi-project-dir aware 2022-06-01 06:58:04 +02:00
Guldoman e94c996a26
Add `TreeView` helper functions to get previous/next item 2022-06-01 06:58:04 +02:00
Guldoman 295e6b7e5a
Allow `common.path_suggest` to specify a root directory
This will make relative paths start from `root`.
2022-06-01 06:57:38 +02:00
Guldoman 9a428648a9
Add `common.is_absolute_path` 2022-06-01 04:42:50 +02:00
Jefferson González 13d062479a
Merge pull request #1005 from Guldoman/PR_improve_logs
Add `warn` log level and backtraces
2022-05-31 16:38:47 -04:00
Jefferson González c09715d0e1
Merge pull request #1004 from Guldoman/PR_commandview_options
Add options table to `CommandView:enter`
2022-05-31 16:38:36 -04:00
jgmdev 2d8a15f3ab plugins: dropped --lite-xl version tag 2022-05-31 16:34:14 -04:00
jgmdev 214f36157a plugins: only check mod version 2022-05-31 16:29:14 -04:00
jgmdev 83f368cdbe core: check for sdl initialization errors 2022-05-31 14:57:26 -04:00
Jefferson González c62cf5ce8c
Merge pull request #980 from jgmdev/PR/plugins-load-priority
plugins: add load priority support with '--priority:###' closes #978
2022-05-31 11:18:37 -04:00
Jefferson González bd742d5b3d
Merge pull request #999 from Guldoman/tokenizer_regex_groups
Allow regexes in `tokenizer` to split tokens with groups
2022-05-31 11:04:48 -04:00
Guldoman fae9af96bf
Expose function to create custom log entries 2022-05-31 02:35:56 +02:00
Guldoman db2d30caaf
Add `warn` log level 2022-05-31 02:26:42 +02:00
Guldoman c92f6a7b7f
Always show backtrace for `error` log entries 2022-05-31 02:26:18 +02:00
Guldoman d8efb1ab53
Show error if language plugin pattern has mismatching number of groups
The number of results from a pattern with groups must never be greater
than the number of token types for that pattern.

Also if a token type was undefined, it's now pushed as a `normal` one.
2022-05-31 02:05:37 +02:00
Guldoman 7ac776bef6
Fix UTF-8 matches in regex group `tokenizer` 2022-05-31 01:59:14 +02:00
Guldoman 4f0d45d6ab
Don't check unnamed files in `autoreload` plugin 2022-05-31 01:03:59 +02:00
Jefferson González 7dc069aa8f
Merge pull request #995 from jgmdev/PR/plugins-settings-gui
plugins: added settings gui support
2022-05-30 16:12:49 -04:00
Guldoman 11e27c6fda
Use new `CommandView:enter` options table 2022-05-30 22:08:13 +02:00
Guldoman e4a806a9d0
Add options to `CommandView:enter`
Now `CommandView:enter` can accept a table that specifies its behavior.

The old behavior is kept for compatibility.
2022-05-30 22:07:39 +02:00
Jefferson González c92c545fb9
Merge pull request #997 from jgmdev/PR/meson-fix-static
meson: fix not obeyed forcefallback for lua
2022-05-30 13:27:28 -04:00
jgmdev e7d2b03e62 meson: fix not obeyed forcefallback for lua 2022-05-30 13:24:53 -04:00
Jefferson González f1f61f034b
Merge pull request #1003 from zesterer/master
Added suggestion wrapping for CommandView
2022-05-30 12:53:06 -04:00
Joshua Barretto 86e3f4a690 Made wrapping overflow optional 2022-05-30 17:13:21 +01:00
Jefferson González a71bc8af35
Merge pull request #1002 from zesterer/master
Made moving to a line beyond the end of a document move the caret column to the end of the line
2022-05-30 10:43:43 -04:00
Joshua Barretto b3278f6360 Made moving to a line beyond the end of a document move the caret column to the end of the line 2022-05-30 15:40:46 +01:00
Guldoman 2a41002355
Allow using regex groups to split tokens
Before, this was only supported by Lua patterns.

This expects the regex to use the same syntax used for patterns. That 
is, the token should be split by empty groups.
2022-05-28 01:38:22 +02:00
Guldoman 14be51b1ec
Make `regex.match` return all the results 2022-05-28 01:21:41 +02:00
Guldoman 74f7389cac
Make regex API return integers 2022-05-28 01:20:41 +02:00
Jefferson González 807d7a07fe
Merge pull request #994 from takase1121/better-windows-custom-frame
Improvements to borderless mode on Windows
2022-05-25 01:02:59 -04:00
jgmdev 8bbca7c0b0 plugins: added settings gui support 2022-05-23 17:50:10 -04:00
takase1121 3bd679da12 make lite-xl respect the taskbar and allow aero-drop in borderless mode 2022-05-23 13:10:11 +08:00
jgmdev b9bb64a2f0 Merge branch 'master' into master-2.1 2022-05-22 22:26:47 -04:00
jgmdev 26e47f7583 plugin contextmenu: simplify predicate 2022-05-15 17:17:28 -04:00
Jefferson González ac1d15f235
Merge pull request #990 from jgmdev/PR/allow-strict-predicate
command predicates: added support for strict matching
2022-05-15 16:23:34 -04:00
jgmdev 4d3e8d8bd0 command predicates: added support for strict matching by appending '!' on string predicates 2022-05-15 16:10:57 -04:00
Jefferson González 0a66163c10
Merge pull request #987 from jgmdev/PR/fix-object-is-add-extends
object: made is() stricter and added extends()
2022-05-15 15:32:01 -04:00
Jefferson González 28346f13d9
Merge pull request #984 from jgmdev/PR/tokenizer-fix-utf8-bug
tokenizer: fix next utf8 char retrieval bug
2022-05-15 15:28:42 -04:00
Jefferson González 2b9f58b4f6
Merge pull request #982 from jgmdev/PR/keymap-changes
keymap: changes and docs
2022-05-15 15:27:58 -04:00
Jefferson González 425b85a536
Merge pull request #979 from jgmdev/PR/install-docs
meson: install docs/api to datadir for lsp support
2022-05-15 15:27:33 -04:00
Adam Harrison daeb2a8e04 Made sure we redrew things, added in a contingency in 'save' for times when we load a non-existent file, and added some checks. 2022-05-15 15:25:02 -04:00
Adam Harrison 5a0d213f3b Changed things over to use dirwatch. 2022-05-15 15:24:44 -04:00
Adam Harrison 20dc101229 As per request from jgmdev, added in ability to show nagview always. 2022-05-15 15:24:28 -04:00
Adam Harrison 173370694e Split out reload functionality to actual document, and added in a thread to check the document, in the cases where it wouldn't be covered by dirwatch. 2022-05-15 15:24:17 -04:00
Adam Harrison d56f4e1ee5 Modified autoreload to use new dirwatch infrastructure, and added in nagview to verify that fs changes don't stomp on our changes, unless you want them to. 2022-05-15 15:23:59 -04:00
Adam 36c4d5d9ed
Autoreload Nagview (#942)
* Modified autoreload to use new dirwatch infrastructure, and added in nagview to verify that fs changes don't stomp on our changes, unless you want them to.

* Split out reload functionality to actual document, and added in a thread to check the document, in the cases where it wouldn't be covered by dirwatch.

* As per request from jgmdev, added in ability to show nagview always.

* Changed things over to use dirwatch.

* Made sure we redrew things, added in a contingency in 'save' for times when we load a non-existent file, and added some checks.
2022-05-15 15:21:26 -04:00
Jefferson González f89b370369
Merge pull request #988 from jgmdev/PR/adjust-renderer-api-doc
docs: added font.group to renderer and other adjustments
2022-05-14 20:35:17 -04:00
jgmdev 8bda5d4198 docs: added font.group to renderer and other adjustments 2022-05-14 20:16:59 -04:00
jgmdev 94430bcbd2 tokenizer: fix next utf8 char retrieval bug 2022-05-13 11:21:46 -04:00
jgmdev 59d91087e9 adjust and consolidate duplicated predicate code 2022-05-12 22:15:29 -04:00
jgmdev b8ed4a43f6 keymap: changes and docs
* Prevent adding duplicate bindings
* Clean reverse_map on overwrite or add direct
* Added get_bindings to complement get_binding
* Added doc comments for easier comprehension
* Check if command is function on add_direct
2022-05-12 21:16:02 -04:00
jgmdev fd0a433f59 object: made is() stricter and added extends()
Currently some plugins had/have issues with predicates that check
if active view is a docview to perform certain operations like draw
in the case of minimap or lineguide. Since is() was checking the
entire inheritance tree it was returning true for views that inherit
from the same parent, which caused CommandView to be matched along
DocView, etc... This change does the following to solve the issue:

* Make Object:is() only match the top level parent of the object which
  is more in line with what one would expect from a method named 'is'.
* Introduces Object:extends() which keeps the same functionality that
  Object:is() offered before.
2022-05-12 20:33:01 -04:00
Jefferson González e747dce7fe
Merge pull request #986 from jgmdev/PR/doc-upper-lower-utf8
Add utf8 support on doc lower and upper commands
2022-05-12 18:56:13 -04:00
Jefferson González 2a009186cf
Merge pull request #961 from Guldoman/PR_fix_doc_replace_results
Manage return values from "replacer" function in `Doc:replace`
2022-05-12 18:51:31 -04:00
jgmdev 1d1b3e0a09 Add utf8 support on doc lower and upper commands 2022-05-12 18:33:56 -04:00
Jefferson González 0665da49ae
Merge pull request #983 from jgmdev/PR/utf8-fix-conflicts
c core: fix extra utf8 build conflict on windows
2022-05-12 11:39:44 -04:00
jgmdev 359880e963 c core: fix extra utf8 build conflict on windows 2022-05-11 00:22:01 -04:00
Adam Harrison 0315d397bd Removed undefined behaviour by using `poll` over `select`. 2022-05-09 23:12:43 -04:00
Adam 6229f74ccd
Merge branch 'master' into master-2.1 2022-05-09 21:36:10 -04:00
Adam f1f8a9b3f2 TreeView Changes (#898)
* Change to 1 click as per RFC on discord, with 100% in favour.

* Added in the ability to specify  as a view name, so it doesn't modify the title, and also fixed a bug where if you clicked *over* the amount of times your config says, it wouldn't regsiter.

* Changed plugin to use keymap.
2022-05-09 21:33:22 -04:00
Adam Harrison ddc3a8842b Fixed dirwatch dummy, and scanning. 2022-05-09 21:30:13 -04:00
Adam Harrison d8436d1e92 Bumped verison number in meson. 2022-05-06 11:51:30 -04:00
jgmdev b3fea8f880 plugins: add load priority support with '--priority:###' 2022-05-05 18:17:32 -04:00
jgmdev 93be54e9c3 meson: install docs/api to datadir for lsp support 2022-05-05 13:02:34 -04:00
Jefferson González 2c968073e4
Merge pull request #972 from Guldoman/PR_pretty_serialize
Add pretty printing to `common.serialize`
2022-05-05 01:35:04 -04:00
Jefferson González 308431d32a
Merge pull request #971 from jgmdev/PR/core-private-to-public
core: expose rescan_project_directories and configure_borderless_window
2022-05-05 01:34:32 -04:00
Adam Harrison 2eaba8ab92 Erroneously added padding. 2022-05-04 20:08:08 -04:00
Adam Harrison 09bfb8d869 Updated linewrap and autocomplete to use the new get_line_screen_position which takes a col. 2022-05-04 20:05:41 -04:00
Guldoman 8156836126 Fix `ren_font_group_get_tab_size` returning unexpected values
We were casting the `xadvance` to an int, so in some cases the resulting 
tab size was wrong.
2022-05-04 10:26:48 -04:00
Adam Harrison 8345a04d04 Updated treeview to match convention. 2022-05-03 23:13:49 -04:00
Guldoman 0ca0e36009
Open `LogView` in correct `Node`
Using `get_active_node` might result in a locked `Node`; calling 
`add_view` on that `Node` throws an error.

`get_active_node_default` always returns an unlocked `Node`.
2022-05-04 02:58:34 +02:00
Guldoman f8622efc01
Add pretty printing to `common.serialize` 2022-05-03 06:28:34 +02:00
Adam Harrison 94abf66444 Fixed minor race condition. 2022-05-02 22:36:54 -04:00
Adam Harrison 91797d65d1 Fixed minor race condition. 2022-05-02 22:35:21 -04:00
jgmdev b5fe333345 core: expose rescan_project_directories and configure_borderless_window 2022-05-02 13:55:25 -04:00
Guldoman d3c38d699c
Merge pull request #966 from adamharrison/check-plugin-load-time
Added plugin load-time log.
2022-05-02 01:59:12 +02:00
Adam Harrison 548dbf67c2 Added in log to show total time. 2022-04-30 16:23:05 -04:00
Guldoman 2e0d0995d6
Add typeahead to `CommandView` (#963) 2022-04-30 16:09:40 -04:00
Guldoman 9de75988ba
Send `mouseleft` event when the mouse leaves the window (#928)
* Send `mouseleft` event when the mouse leaves the window

* Call `View:on_mouse_left` when the mouse leaves the `View`

Previously `View:on_mouse_left` was called only when the mouse left the
window, and it was called on every visible `View`.

Now it gets also called when the mouse "changes" `View`, and only the
last `View` the mouse was on will receive the event.
2022-04-28 21:50:34 -04:00
Adam Harrison b7db7cd533 Added plugin load-time log. 2022-04-28 21:42:53 -04:00
Guldoman ac42e6457a
Check if `USERDIR` doesn't exist in `core.delete_temp_files` 2022-04-28 01:55:07 +02:00
Guldoman f92f56d42e
Manage return values from "replacer" function in `Doc:replace`
Before the addition of multi-cursor support, we just returned the second 
return value of the "replacer" function to the caller.

With the introduction of multi-cursors, we naively summed the second 
return values for each cursor.
In some cases the "replacer" function doesn't return any second value, 
so we tried to do math with `nil`, thus throwing errors.

Now the second return value is added to a table which is then returned 
to the caller.
2022-04-27 21:53:35 +02:00
Guldoman 3950406750
Catch mouse clicks if `contextmenu` is open
Also disallow re-opening the `contextmenu` if it's already visible.
2022-04-27 17:55:46 +02:00
Adam 4934e741b3
TreeView Changes (#898)
* Change to 1 click as per RFC on discord, with 100% in favour.

* Added in the ability to specify  as a view name, so it doesn't modify the title, and also fixed a bug where if you clicked *over* the amount of times your config says, it wouldn't regsiter.

* Changed plugin to use keymap.
2022-04-26 18:29:05 -04:00
Guldoman a7ea84ae8f
Clamp scroll position when dragging the scrollbar without animations 2022-04-26 22:50:44 +02:00
Adam Harrison 9f7c6974ae Make sure pipes are closed on exec. 2022-04-26 12:14:38 -04:00
Adam 4bf4851736 Asynchronous Reads for Dirmonitor (#930)
Change dirmonitor reads to be synchronous, in a secondary thread.
2022-04-26 12:13:39 -04:00
Adam Harrison 6f65168b0d Fixed windows dirmonitor issues. 2022-04-26 12:04:41 -04:00
Guldoman 444b3e3c66
Don't animate scrolling by dragging the scroll bar (#940)
Also added the `config.animate_drag_scroll` option to re-enable the 
behavior.
2022-04-26 09:54:11 -04:00
Guldoman 0c456eb664
Allow functions in `keymap` (#948)
This allows `keymap.add` to map shortcuts to functions.

If the function returns `false`, the next command is executed (as if the
`predicate` of a `command` failed).
2022-04-26 09:48:59 -04:00
Jefferson González e572c58f24
Add utf8 support to tokenizer (#945)
* add utf8 support to tokenizer

* wrap utf8 functions in string table using a 'u' prefix

* document new utf8 functions
2022-04-26 09:42:02 -04:00
Guldoman 7dd83bb737
Fix `ren_font_group_get_tab_size` returning unexpected values
We were casting the `xadvance` to an int, so in some cases the resulting 
tab size was wrong.
2022-04-26 04:34:18 +02:00
Guldoman f42dbb0060
Add animation categories to enable finer transitions control (#941)
* Allow finer control over transitions

* Add categories to transitions
2022-04-25 20:35:35 -04:00
Adam Harrison d8f202e251 Make sure pipes are closed on exec. 2022-04-24 21:13:18 -04:00
Adam 5df1640595
Merge branch 'master' into master-2.1 2022-04-24 13:42:24 -04:00
Adam 97174706fe
Asynchronous Reads for Dirmonitor (#930)
Change dirmonitor reads to be synchronous, in a secondary thread.
2022-04-24 13:40:58 -04:00
Guldoman b9957138ac
Add `launchable` tag to AppStream xml 2022-04-21 00:41:15 +02:00
Guldoman 1439b59d65
Improve performance of `ren_draw_rect` (#935)
We color a 1x1 `SDL_Surface` with the desired color. This surface is 
then stretched over the area we need to cover using `SDL_BlitScaled`.
This way we avoid having to do the blending ourselves.
2022-04-20 17:13:42 -04:00
Guldoman bbac7e479c
Set the correct working directory for the AppImage version (#937) 2022-04-20 17:00:48 -04:00
Jan 915625b74f
Update SDL to 2.0.20 (#884) 2022-04-20 16:30:41 -04:00
Guldoman 9a5f8e72d0
Add `DATADIR` and `USERDIR` explanation in created user module 2022-04-18 21:14:50 +02:00
Philip Bergwerf 5453a27f91
Add triple single quotes multiline strings to `language_python` 2022-04-18 20:22:25 +02:00
Adam Harrison c112bd8d7c Significantly improved performance of tokenization for larger documents by by default not requiring tokenization. 2022-04-17 13:01:45 -04:00
Jan fff10a2612
Cleanup (#826)
* Update meson.build
- add logic to loop over more lua names (in the future more names might be discovered)
- disable warnings and errors on dependencies

* adding missing includes and checks, correct data types, pointer mess […]
- various functions from string.h were used but never defined
- logic was done across multiple different data types with different signedness, got all of them up to snuff
- give 0 sized array size of 1 (array of size 0 is illegal, but rewriting the code is out of the scope of this commit)
- add preprocessor that marks possibly unused argument as such (does not mean they will get optimized out or anything)
- correctly initialize structs with all data needed

All these were found by generating the project using `meson -Dwarning_level=3 -Dwerror=true`

* remove undefined behavior, correct data types

* Comment manual bit manipulation to be investigated

* check for more edge cases, replace multiple cleanups with goto

* remove system specific includes
2022-04-15 11:34:46 -04:00
Adam Harrison d323917538
Added in check in case of nil. 2022-04-15 00:07:20 +02:00
Jefferson González c0970f41c0
Merge pull request #916 from Guldoman/PR_enhanced_scrollbar
Enhance scrollbar
2022-04-14 13:42:18 -04:00
Guldoman 93b31211cb
Apply `drawwhitespace` plugin only to `DocView`s 2022-04-12 04:05:59 +02:00
Guldoman 43086a9c24
Fix missing pixel in scrollbar 2022-04-12 03:20:27 +02:00
Guldoman 4f434d1a41
Show `arrow` cursor when hovering `DocView` scrollbar track 2022-04-12 02:56:41 +02:00
Guldoman 48c371a638
Add scrollbar "track" and resize on hover 2022-04-12 02:56:30 +02:00
Guldoman 052c140787
Fix `DocView:on_mouse_released` not considering all parameters 2022-04-12 02:38:30 +02:00
Guldoman 202e42b568
Avoid calling `View:scrollbar_overlaps_point` uselessly
`View:on_mouse_moved` already updated `self.hovered_scrollbar`, so use 
that instead.
2022-04-12 02:37:21 +02:00
Francesco 16fcb2e751
Merge pull request #913 from lite-xl/auto-complete-syntax-symbols
Add syntax symbols for auto-complete
2022-04-11 20:42:17 +02:00
Francesco Abbate d4f84e1aff Add syntax symbols for auto-complete 2022-04-10 10:32:43 +02:00
Guldoman e5c55e8abc
Early `break` if `autocomplete` needs to update 2022-04-10 04:20:46 +02:00
Guldoman ffe698cef7
Consider last document line to gather `autocomplete` symbols 2022-04-10 04:11:59 +02:00
Adam 683d23e3ba
Changed calculation to use ascender, which should work better. (#912) 2022-04-04 10:38:43 -04:00
Adam Harrison 04adb10f97 Added in check in case of nil. 2022-04-03 16:44:02 -04:00
Adam Harrison 974fd9c8d5 Fixed windows dirmonitor issues. 2022-04-03 16:44:02 -04:00
Adam Harrison 562e284d04 Fixed some minor issues with linewrapping. 2022-04-03 16:44:02 -04:00
Adam Harrison 11dfb5b1ca Apparenlty these were issues? What? 2022-04-03 16:44:02 -04:00
Adam 3479890ce5 Soft Line Wrapping (#636)
Added in soft line wrapping.
2022-04-03 16:44:02 -04:00
Adam Harrison 048d250f5e Added in notes to the changelog. 2022-04-03 16:44:02 -04:00
Adam Harrison 45a0382d50 Bumping version numbers. 2022-04-03 16:44:02 -04:00
Guldoman 50acf2e7e6
Fix keeping scroll position when restoring a `DocView` (#910)
Since 5526041da3 we check a pair of 
line&column to decide if we should scroll to the cursor.
Previously we only considered a single line&column.
2022-04-03 16:24:39 -04:00
Guldoman c70b5130a9
Improve `drawwhitespace` plugin
Add configuration options to specify:
- characters to substitute and their substitution;
- whether to substitute at the beginning, middle or end of the line;
- the color of the substitution;
- the color for the beginning, middle or end;
- the minimum number of white space to show in the middle.
2022-04-01 18:18:50 +02:00
jgmdev 23bd21a191 language_md: remove extra empty line 2022-03-30 09:30:55 -04:00
Jefferson González 693bf2cf29
Merge pull request #907 from jgmdev/PR/less-hacky-tokenizer-fix
syntax: remove pattern re-ordering on optimization
2022-03-29 22:26:50 -04:00
jgmdev b0c005a5ac syntax: remove pattern re-ordering on optimization
* Introduces a flag that syntax writers can turn off named
  space_handling, turning it off means that your syntax will take care
  of handling the excessive amount of spaces that can slow down the
  tokenizer.
* Adds another pattern at the end of every single table that also
  improves tokenizer performance by matching words that weren't match by
  any of the synxtax patterns.
* Modifies language_md to turn off the provided space_handling and do its
  own since it has rules that require a space at the beginning, also
  handles long consecutives amount of dashes used in tables that degrade
  performance.
* This changes where discussed in collaboration with @Guldoman and
  @takase1121 thanks to all!
2022-03-29 22:11:14 -04:00
Jefferson González 7372d2f82d
Merge pull request #906 from jgmdev/PR/temp-file-other-dir
core: fixes and changes to temp files
2022-03-29 16:33:08 -04:00
Jefferson González aca1cd6b6b
Merge pull request #892 from jgmdev/PR/status-view-move-item
statusview: added functions for easy custom item ordering
2022-03-29 16:25:31 -04:00
Jefferson González 61ad6b052e
Merge pull request #895 from jgmdev/PR/c-cpp-fixes-improvements
language_c/cpp: fixes and improvements
2022-03-29 16:20:22 -04:00
jgmdev e74761da95 language_c/cpp: fixes and improvements
* support colorization of function and variables type declarations
* support the macro concatenation operator ##
* support what seems to be new cpp number notation format #'###
* improved uppercase constants matching
2022-03-29 16:16:12 -04:00
Jefferson González fac54d2ff4
Merge pull request #904 from jgmdev/PR/fix-syntax-optimization
syntax: fix conflicts introduced with #896
2022-03-29 15:44:19 -04:00
Jefferson González 29968b6f35
Merge pull request #905 from jgmdev/PR/md-add-parenthesis-bullet
language_md: parenthesis support to numbered bullets
2022-03-29 15:41:54 -04:00
jgmdev ca37644aa9 core: fixes and changes to temp files
* fix delete_temp_files() deleting in EXEDIR but temp_filename() was
  creating temp files in USERDIR
* make delete_temp_files() public so it can be used by plugins
* add optional `dir` parameter to both delete_temp_files() and
  temp_filename() to allow specifying a different directory, this is
  for example useful when generting markdown previews, the temp file
  should be generated in the project dir in case the readme references
  images that are relative to it, so the web browser can find them.
2022-03-28 22:36:49 -04:00
jgmdev 5f9d45895d language_md: parenthesis support to numbered bullets 2022-03-28 21:03:59 -04:00
jgmdev e862fe9052 syntax: fix conflicts introduced with #896
* mainly the language_md got affected which has some exotic rules
* some other languages are also using spaces at start of pattern
  and even if not affected this change tackles that
2022-03-28 20:51:09 -04:00
Guldoman 3e1fdc4157
Merge pull request #900 from buffet/add-lua-fallback-version
Add minimum version to lua fallback dependency to avoid confusion
2022-03-28 22:33:24 +02:00
George Sokianos 9cfb20bd0e Merge branch 'master' of ssh://git.walkero.gr:2222/walkero/lite-xl 2022-03-27 20:42:42 +01:00
George Sokianos 9c2abb38db innosetup changes 2022-03-27 20:42:04 +01:00
buffet 9808378511 Add minimum version to lua fallback dependency to avoid confusion 2022-03-25 20:29:28 +00:00
Jefferson González 951f0913da
syntax: add pattern to boost tokenizer performance (#896) 2022-03-25 11:25:32 -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 c82d6b08d9 statusview: added functions for easy custom item ordering 2022-03-21 18:40:14 -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
127 changed files with 14735 additions and 4771 deletions

3
.github/labeler.yml vendored
View File

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

View File

@ -1,45 +1,20 @@
name: CI
# All builds use lhelper only for releases,
# otherwise for normal builds dependencies are dynamically linked.
on:
push:
branches:
- '*'
# tags:
# - 'v[0-9]*'
- '*'
pull_request:
branches:
- '*'
- '*'
workflow_dispatch:
jobs:
archive_source_code:
name: Source Code Tarball
runs-on: ubuntu-18.04
# Only on tags/releases
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v2
- name: Python Setup
uses: actions/setup-python@v2
with:
python-version: 3.6
- name: Install Dependencies
run: |
sudo apt-get install -qq ninja-build
pip3 install meson
- name: Package
shell: bash
run: bash scripts/package.sh --version ${GITHUB_REF##*/} --debug --source
- uses: actions/upload-artifact@v2
with:
name: Source Code Tarball
path: "lite-xl-*-src.tar.gz"
build_linux:
name: Linux
runs-on: ubuntu-18.04
runs-on: ubuntu-22.04
strategy:
matrix:
config:
@ -49,103 +24,71 @@ jobs:
CC: ${{ matrix.config.cc }}
CXX: ${{ matrix.config.cxx }}
steps:
- name: Set Environment Variables
if: ${{ matrix.config.cc == 'gcc' }}
run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-linux-$(uname -m)" >> "$GITHUB_ENV"
- uses: actions/checkout@v2
- name: Python Setup
uses: actions/setup-python@v2
with:
python-version: 3.6
- name: Update Packages
run: sudo apt-get update
- name: Install Dependencies
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
run: bash scripts/install-dependencies.sh --debug
- name: Install Release Dependencies
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: |
bash scripts/install-dependencies.sh --debug --lhelper
bash scripts/lhelper.sh --debug
- name: Build
run: |
bash --version
bash scripts/build.sh --debug --forcefallback
- name: Package
if: ${{ matrix.config.cc == 'gcc' }}
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --binary
- name: AppImage
if: ${{ matrix.config.cc == 'gcc' && startsWith(github.ref, 'refs/tags/') }}
run: bash scripts/appimage.sh --nobuild --version ${INSTALL_REF}
- name: Upload Artifacts
uses: actions/upload-artifact@v2
if: ${{ matrix.config.cc == 'gcc' }}
with:
name: Linux Artifacts
path: |
${{ env.INSTALL_NAME }}.tar.gz
LiteXL-${{ env.INSTALL_REF }}-x86_64.AppImage
- name: Set Environment Variables
if: ${{ matrix.config.cc == 'gcc' }}
run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-linux-$(uname -m)-portable" >> "$GITHUB_ENV"
- uses: actions/checkout@v2
- name: Python Setup
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Update Packages
run: sudo apt-get update
- name: Install Dependencies
run: bash scripts/install-dependencies.sh --debug
- name: Build
run: |
bash --version
bash scripts/build.sh --debug --forcefallback --portable
- name: Package
if: ${{ matrix.config.cc == 'gcc' }}
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --binary
- name: Upload Artifacts
uses: actions/upload-artifact@v2
if: ${{ matrix.config.cc == 'gcc' }}
with:
name: Linux Artifacts
path: ${{ env.INSTALL_NAME }}.tar.gz
build_macos:
name: macOS (x86_64)
runs-on: macos-10.15
runs-on: macos-11
env:
CC: clang
CXX: clang++
steps:
- name: System Information
run: |
system_profiler SPSoftwareDataType
bash --version
gcc -v
xcodebuild -version
- name: Set Environment Variables
run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-macos-$(uname -m)" >> "$GITHUB_ENV"
- uses: actions/checkout@v2
- name: Python Setup
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install Dependencies
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
run: bash scripts/install-dependencies.sh --debug
- name: Install Release Dependencies
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: |
bash scripts/install-dependencies.sh --debug --lhelper
bash scripts/lhelper.sh --debug
- name: Build
run: |
bash --version
bash scripts/build.sh --bundle --debug --forcefallback
- name: Error Logs
if: failure()
run: |
mkdir ${INSTALL_NAME}
cp /usr/var/lhenv/lite-xl/logs/* ${INSTALL_NAME}
tar czvf ${INSTALL_NAME}.tar.gz ${INSTALL_NAME}
# - name: Package
# if: ${{ !startsWith(github.ref, 'refs/tags/') }}
# run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons
- name: Create DMG Image
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --dmg
- name: Upload DMG Image
uses: actions/upload-artifact@v2
with:
name: macOS DMG Image
path: ${{ env.INSTALL_NAME }}.dmg
- name: Upload Error Logs
uses: actions/upload-artifact@v2
if: failure()
with:
name: Error Logs
path: ${{ env.INSTALL_NAME }}.tar.gz
- name: System Information
run: |
system_profiler SPSoftwareDataType
bash --version
gcc -v
xcodebuild -version
- name: Set Environment Variables
run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-macos-$(uname -m)" >> "$GITHUB_ENV"
- uses: actions/checkout@v2
- name: Python Setup
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install Dependencies
run: bash scripts/install-dependencies.sh --debug
- name: Build
run: |
bash --version
bash scripts/build.sh --bundle --debug --forcefallback
- name: Create DMG Image
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --dmg
- name: Upload DMG Image
uses: actions/upload-artifact@v2
with:
name: macOS DMG Image
path: ${{ env.INSTALL_NAME }}.dmg
build_windows_msys2:
name: Windows
@ -160,7 +103,6 @@ jobs:
- uses: actions/checkout@v2
- uses: msys2/setup-msys2@v2
with:
#msystem: MINGW64
msystem: ${{ matrix.msystem }}
update: true
install: >-
@ -170,83 +112,22 @@ jobs:
- name: Set Environment Variables
run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-windows-$(uname -m)" >> "$GITHUB_ENV"
echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
if [[ "${MSYSTEM}" == "MINGW64" ]]; then
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-windows-x86_64" >> "$GITHUB_ENV"
else
echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-windows-i686" >> "$GITHUB_ENV"
fi
- name: Install Dependencies
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
run: bash scripts/install-dependencies.sh --debug
- name: Install Release Dependencies
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: bash scripts/install-dependencies.sh --debug --lhelper
- name: Build
run: |
bash --version
bash scripts/build.sh --debug --forcefallback
- name: Error Logs
if: failure()
run: |
mkdir ${INSTALL_NAME}
cp /usr/var/lhenv/lite-xl/logs/* ${INSTALL_NAME}
tar czvf ${INSTALL_NAME}.tar.gz ${INSTALL_NAME}
bash scripts/build.sh -U --debug --forcefallback
- name: Package
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --binary
- name: Build Installer
if: ${{ startsWith(github.ref, 'refs/tags/') }}
run: bash scripts/innosetup/innosetup.sh --debug
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --binary
- name: Upload Artifacts
uses: actions/upload-artifact@v2
with:
name: Windows Artifacts
path: |
LiteXL*.exe
${{ env.INSTALL_NAME }}.zip
- name: Upload Error Logs
uses: actions/upload-artifact@v2
if: failure()
with:
name: Error Logs
path: ${{ env.INSTALL_NAME }}.tar.gz
deploy:
name: Deployment
runs-on: ubuntu-18.04
# if: startsWith(github.ref, 'refs/tags/')
if: false
needs:
- archive_source_code
- build_linux
- build_macos
- build_windows_msys2
steps:
- name: Set Environment Variables
run: echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV"
- uses: actions/download-artifact@v2
with:
name: Linux Artifacts
- uses: actions/download-artifact@v2
with:
name: macOS DMG Image
- uses: actions/download-artifact@v2
with:
name: Source Code Tarball
- uses: actions/download-artifact@v2
with:
name: Windows Artifacts
- name: Display File Information
shell: bash
run: ls -lR
# Note: not using `actions/create-release@v1`
# because it cannot update an existing release
# see https://github.com/actions/create-release/issues/29
- uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ env.INSTALL_REF }}
name: Release ${{ env.INSTALL_REF }}
draft: false
prerelease: false
files: |
lite-xl-${{ env.INSTALL_REF }}-*
LiteXL*.AppImage
LiteXL*.exe
path: ${{ env.INSTALL_NAME }}.zip

183
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,183 @@
name: Release
on:
workflow_dispatch:
inputs:
version:
description: Release Version
default: v2.1.0
required: true
jobs:
release:
name: Create Release
runs-on: ubuntu-20.04
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
version: ${{ steps.tag.outputs.version }}
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Fetch Version
id: tag
run: |
if [[ "${{ github.event.inputs.version }}" != "" ]]; then
echo ::set-output name=version::${{ github.event.inputs.version }}
else
echo ::set-output name=version::${GITHUB_REF/refs\/tags\//}
fi
- name: Create Release
id: create_release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.tag.outputs.version }}
name: Lite XL ${{ steps.tag.outputs.version }}
draft: true
prerelease: false
body_path: changelog.md
generate_release_notes: true
build_linux:
name: Linux
needs: release
runs-on: ubuntu-20.04
env:
CC: gcc
CXX: g++
steps:
- name: Set Environment Variables
run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_REF=${{ needs.release.outputs.version }}" >> "$GITHUB_ENV"
- uses: actions/checkout@v2
- name: Python Setup
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Update Packages
run: sudo apt-get update
- name: Install Dependencies
run: |
bash scripts/install-dependencies.sh --debug
sudo apt-get install -y ccache
- name: Build Portable
run: |
bash --version
bash scripts/build.sh --debug --forcefallback --portable --release
- name: Package Portables
run: |
bash scripts/package.sh --version ${INSTALL_REF} --debug --binary --release
bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --binary --release
- name: Build AppImages
run: |
bash scripts/appimage.sh --debug --static --version ${INSTALL_REF} --release
bash scripts/appimage.sh --debug --nobuild --addons --version ${INSTALL_REF}
- name: Upload Files
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.release.outputs.version }}
files: |
lite-xl-${{ env.INSTALL_REF }}-linux-x86_64-portable.tar.gz
lite-xl-${{ env.INSTALL_REF }}-addons-linux-x86_64-portable.tar.gz
LiteXL-${{ env.INSTALL_REF }}-x86_64.AppImage
LiteXL-${{ env.INSTALL_REF }}-addons-x86_64.AppImage
build_macos:
name: macOS (x86_64)
needs: release
runs-on: macos-11
env:
CC: clang
CXX: clang++
steps:
- name: System Information
run: |
system_profiler SPSoftwareDataType
bash --version
gcc -v
xcodebuild -version
- name: Set Environment Variables
run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_REF=${{ needs.release.outputs.version }}" >> "$GITHUB_ENV"
echo "INSTALL_NAME=lite-xl-${{ needs.release.outputs.version }}-macos-$(uname -m)" >> "$GITHUB_ENV"
echo "INSTALL_NAME_ADDONS=lite-xl-${{ needs.release.outputs.version }}-addons-macos-$(uname -m)" >> "$GITHUB_ENV"
- uses: actions/checkout@v2
- name: Python Setup
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install Dependencies
run: bash scripts/install-dependencies.sh --debug
- name: Build
run: |
bash --version
bash scripts/build.sh --bundle --debug --forcefallback --release
- name: Create DMG Image
run: |
bash scripts/package.sh --version ${INSTALL_REF} --debug --dmg --release
bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --dmg --release
- name: Upload Files
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.release.outputs.version }}
files: |
${{ env.INSTALL_NAME }}.dmg
${{ env.INSTALL_NAME_ADDONS }}.dmg
build_windows_msys2:
name: Windows
needs: release
runs-on: windows-2019
strategy:
matrix:
msystem: [MINGW32, MINGW64]
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v2
- uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msystem }}
update: true
install: >-
base-devel
git
zip
- name: Set Environment Variables
run: |
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
echo "INSTALL_REF=${{ needs.release.outputs.version }}" >> "$GITHUB_ENV"
if [[ "${MSYSTEM}" == "MINGW64" ]]; then
echo "BUILD_ARCH=x86_64" >> "$GITHUB_ENV"
echo "INSTALL_NAME=lite-xl-${{ needs.release.outputs.version }}-windows-x86_64" >> "$GITHUB_ENV"
echo "INSTALL_NAME_ADDONS=lite-xl-${{ needs.release.outputs.version }}-addons-windows-x86_64" >> "$GITHUB_ENV"
else
echo "BUILD_ARCH=i686" >> "$GITHUB_ENV"
echo "INSTALL_NAME=lite-xl-${{ needs.release.outputs.version }}-windows-i686" >> "$GITHUB_ENV"
echo "INSTALL_NAME_ADDONS=lite-xl-${{ needs.release.outputs.version }}-addons-windows-i686" >> "$GITHUB_ENV"
fi
- name: Install Dependencies
run: bash scripts/install-dependencies.sh --debug
- name: Build
run: |
bash --version
bash scripts/build.sh -U --debug --forcefallback --release
- name: Package
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --binary --release
- name: Build Installer
run: bash scripts/innosetup/innosetup.sh --debug --version ${INSTALL_REF}
- name: Package With Addons
run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --binary --release
- name: Build Installer With Addons
run: bash scripts/innosetup/innosetup.sh --debug --version ${INSTALL_REF} --addons
- name: Upload Files
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.release.outputs.version }}
files: |
${{ env.INSTALL_NAME }}.zip
${{ env.INSTALL_NAME_ADDONS }}.zip
LiteXL-${{ env.INSTALL_REF }}-${{ env.BUILD_ARCH }}-setup.exe
LiteXL-${{ env.INSTALL_REF }}-addons-${{ env.BUILD_ARCH }}-setup.exe

7
.gitignore vendored
View File

@ -2,9 +2,10 @@ build*/
.build*/
lhelper/
submodules/
subprojects/lua/
subprojects/reproc/
subprojects/*/
/appimage*
.vscode
.cache
.ccls-cache
.lite-debug.log
.run*
@ -25,3 +26,5 @@ release_files
*.o
*.snalyzerinfo
!resources/windows/*.diff

View File

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

View File

@ -5,19 +5,21 @@
#
LiteXL_OBJ := \
src/dirmonitor.o src/main.o src/rencache.o src/renderer.o \
src/renwindow.o src/api/api.o src/api/regex.o \
src/api/renderer.o src/api/system.o src/platform/morphos.o
src/main.o src/rencache.o src/renderer.o src/renwindow.o \
src/api/api.o src/api/dirmonitor.o \
src/api/regex.o src/api/renderer.o src/api/system.o \
src/api/utf8.o src/platform/morphos.o \
src/api/dirmonitor/mos.o
outfile := lite
compiler := gcc
cxxcompiler := g++
INCPATH := -Isrc -Ilib/dmon -I/sdk/gg/usr/local/include/SDL2 -I/sdk/gg/usr/include/freetype -I/sdk/gg/usr/include/lua5.2
DFLAGS := -D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR
INCPATH := -Isrc -Ilib/dmon -I/sdk/gg/usr/local/include/SDL2 -I/sdk/gg/usr/include/freetype -I/sdk/gg/usr/include/lua5.4
DFLAGS := -D__USE_INLINE__
CFLAGS := -Wall -Wwrite-strings -O2 -noixemul -g -std=gnu11 -fno-strict-aliasing
LFLAGS := -noixemul -lpcre2 -lSDL2 -llua52 -lagg -lfreetype -lm -lc -L/usr/local/lib
LFLAGS := -noixemul -lpcre2 -lSDL2 -llua54 -lagg -lfreetype -lm -lc -L/usr/local/lib
.PHONY: LiteXL clean release
@ -38,10 +40,8 @@ LiteXL: $(LiteXL_OBJ)
@echo "Compiling $<"
@$(compiler) -c $< -o $*.o $(CFLAGS) $(INCPATH) $(DFLAGS)
src/dirmonitor.o: src/dirmonitor.c src/platform/morphos.h
src/main.o: src/main.c src/api/api.h src/rencache.h \
src/renderer.h src/platform/morphos.h src/dirmonitor.h
src/renderer.h src/platform/morphos.h
src/rencache.o: src/rencache.c
@ -59,6 +59,11 @@ src/api/system.o: src/api/system.c
src/platform/morphos.o: src/platform/morphos.c
src/api/dirmonitor.o: src/api/dirmonitor.c src/api/dirmonitor/mos.c
src/api/utf8.o: src/api/utf8.c
src/api/dirmonitor/mos.o: src/api/dirmonitor/mos.c

View File

@ -5,9 +5,11 @@
#
LiteXL_OBJ := \
src/dirmonitor.o src/main.o src/rencache.o src/renderer.o \
src/renwindow.o src/api/api.o src/api/regex.o \
src/api/renderer.o src/api/system.o src/platform/amigaos4.o
src/main.o src/rencache.o src/renderer.o src/renwindow.o \
src/api/api.o src/api/dirmonitor.o \
src/api/regex.o src/api/renderer.o src/api/system.o \
src/api/utf8.o src/platform/amigaos4.o \
src/api/dirmonitor/os4.o
outfile := lite
@ -15,14 +17,14 @@ compiler := gcc
cxxcompiler := g++
INCPATH := -Isrc -Ilib/dmon -I/sdk/local/newlib/include/SDL2 \
-I/sdk/local/common/include/lua52 -I/sdk/local/common/include/freetype2
-I/sdk/local/common/include/lua54 -I/sdk/local/common/include/freetype2
DFLAGS += -D__USE_INLINE__ -DLITE_XL_DATA_USE_EXEDIR
CFLAGS += -Werror -Wwrite-strings -O2 -g -std=gnu11 -fno-strict-aliasing
LFLAGS += -mcrt=newlib -lauto \
-lpcre2 -lSDL2 -llua52 -lfreetype -lz -lm -lpthread -athread=native
-lpcre2 -lSDL2 -llua54 -lfreetype -lz -lm -lpthread -athread=native
ifeq ($(DEBUG),1)
CFLAGS += -gstabs
@ -51,12 +53,11 @@ LiteXL: $(LiteXL_OBJ)
.c.o:
@echo "Compiling $<"
$(compiler) -c $< -o $*.o $(CFLAGS) $(INCPATH) $(DFLAGS)
@$(compiler) -c $< -o $*.o $(CFLAGS) $(INCPATH) $(DFLAGS)
src/dirmonitor.o: src/dirmonitor.c src/platform/amigaos4.h
src/main.o: src/main.c src/api/api.h src/rencache.h \
src/renderer.h src/platform/amigaos4.h src/dirmonitor.h
src/renderer.h src/platform/amigaos4.h
src/rencache.o: src/rencache.c
@ -74,8 +75,11 @@ src/api/system.o: src/api/system.c
src/platform/amigaos4.o: src/platform/amigaos4.c
src/api/dirmonitor.o: src/api/dirmonitor.c src/api/dirmonitor/os4.c
src/api/utf8.o: src/api/utf8.c
src/api/dirmonitor/os4.o: src/api/dirmonitor/os4.c
release:
@echo "Creating release files..."

View File

@ -27,7 +27,7 @@ The changes and differences between Lite XL and rxi/lite are listed in the
## Overview
Lite XL is derived from lite.
Lite XL is derived from [lite].
It is a lightweight text editor written mostly in Lua — it aims to provide
something practical, pretty, *small* and fast easy to modify and extend,
or to use without doing either.
@ -148,12 +148,13 @@ See the [licenses] file for details on licenses used by the required dependencie
[screenshot-dark]: https://user-images.githubusercontent.com/433545/111063905-66943980-84b1-11eb-9040-3876f1133b20.png
[lite]: https://github.com/rxi/lite
[website]: https://lite-xl.com
[build]: https://lite-xl.com/en/documentation/build/
[build]: https://lite-xl.com/en/documentation/build
[Get Lite XL]: https://github.com/lite-xl/lite-xl/releases/latest
[Get plugins]: https://github.com/lite-xl/lite-xl-plugins
[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

View File

@ -10,23 +10,14 @@ It might crash from time to time, if there is a path problem, but overall
it works pretty well. This is my daily editor for any kind of development.
If it crashes on your system, try to delete to `.config` folder.
## New features against Lite XL v1
- Faster file scrolling
- Faster switch between tabs
- Reposition tabs at the side or at the bottom of other tabs, making
multiple columns/rows of opened files
- Multiple cursor editing
- Better font manipulation and appearance
- Faster transitions
## Installation
## 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 version uses the
executable folder, but if you want to overide it, you can create an ENV
executable folder, but if you want to override it, you can create an ENV
variable named `HOME` and set there your prefferable path.
You can check if there is one already set by executing the following command
@ -84,7 +75,11 @@ The included plugins are the following:
**autoinsert**
Automatically inserts closing brackets and quotes. Also allows selected
text to be wrapped with brackets or quotes.
text to be wrapped with brackets or quotes.
**autosaveonfocuslost**
Automatically saves files that were changed when the main window loses
focus by switching to another application
**autowrap**
Automatically hardwraps lines when typing
@ -99,19 +94,24 @@ Underlines matching pair for bracket under the caret
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
Opens a preview of the current markdown file in a browser window.
On AmigaOS 4 it uses *urlopen* and on MorphOS it uses *openurl* to load
the generated html in the browser.
**indentguide**
Adds indent guides
**language_guide**
Syntax for the AmigaGuide scripting language
**language_hws**
Syntax for the Hollywood language
**language_make**
Syntax for the Make build system language
@ -132,6 +132,17 @@ this plugin will make the editor slower on file loading and scrolling.
Allows moving back and forward between document positions, reducing the
amount of scrolling
**nonicons**
File icons set for TreeView. Download TTF font to your config/fonts
folder from https://github.com/yamatsum/nonicons/tree/master/dist
**opacity**
Change the opaqueness/transparency of lite-xl using shift+mousewheel
or a command.
**openfilelocation**
Opens the parent directory of the current file in the file manager
**rainbowparen**
Show nesting of parentheses with rainbow colours
@ -139,6 +150,10 @@ Show nesting of parentheses with rainbow colours
Keep a list of recently closed tabs, and restore the tab in order on
cntrl+shift+t.
**select_colorscheme**
Select a color theme, like VScode, Sublime Text.
(plugin saves changes)
**selectionhighlight**
Highlights regions of code that match the current selection
@ -195,6 +210,30 @@ https://git.walkero.gr/walkero/lite-xl/issues
# Changelog
## [2.1.0r1] - 2022-10-10
### Added
- This version of LiteXL recognises changes that are done outside the editor
in files and folders, and updates the items when it gets focus again.
### Changed
- Synced the code with the latest upstream master branch, which means that
this version is based on the latest available source
- Now the plugins need to be version 3. The older versions will not work.
All the included plugins are updated to the latest available version.
- Compiled with SDL 2.24
- Compiled with Lua 5.4
### Fixed
- Fixed regex issues with some plugins
- Fixed "Open in System" on AmigaOS 4 and MorphOS. When you right click
at a file or a folder at the treeview at the left side, then depending
the type of the item opens on Workbench. A folder opens in a list view
and a file opens with its default tool
- Fixed markdown preview on MorphOS. Now, it calls openurl with the html
file (#20)
- Fixed locale issues on MorphOS (again), since the previous fix didn't
actually fixed the problem
## [2.0.3r3] - 2022-09-26
### Added
- Added plugin for AmigaGuide files

View File

@ -37,6 +37,7 @@ show_help() {
echo "-D --dmg Create a DMG disk image (macOS only)."
echo " Requires NPM and AppDMG."
echo "-I --innosetup Create an InnoSetup installer (Windows only)."
echo "-r --release Compile in release mode."
echo "-S --source Create a source code package,"
echo " including subprojects dependencies."
echo
@ -58,6 +59,7 @@ main() {
local innosetup
local portable
local pgo
local release
for i in "$@"; do
case $i in
@ -109,6 +111,10 @@ main() {
portable="--portable"
shift
;;
-r|--release)
release="--release"
shift
;;
-S|--source)
source="--source"
shift
@ -145,6 +151,7 @@ main() {
$force_fallback \
$bundle \
$portable \
$release \
$pgo
source scripts/package.sh \
@ -158,6 +165,7 @@ main() {
$appimage \
$dmg \
$innosetup \
$release \
$source
}

8
build.lhelper Normal file
View File

@ -0,0 +1,8 @@
CC="${CC:-gcc}"
CXX="${CXX:-g++}"
CFLAGS=
CXXFLAGS=
LDFLAGS=
BUILD_TYPE=Release
packages=(pcre2 freetype2 sdl2 lua)

View File

@ -1,20 +1,405 @@
This files document the changes done in Lite XL for each release.
# Changes Log
### 2.0.3
## [2.1.0] - 2022-09-25
Replace periodic rescan of project folder with a notification based system using the
[dmon library](https://github.com/septag/dmon). Improves performance especially for
large project folders since the application no longer needs to rescan.
The application also reports immediatly any change in the project directory even
when the application is unfocused.
### New Features
* Make distinction between
[line and block comments](https://github.com/lite-xl/lite-xl/pull/771),
and added all appropriate functionality to the commenting/uncommenting lines.
* [Added in line paste mode](https://github.com/lite-xl/lite-xl/pull/713),
if you copy without a selection.
* Many [improvements to treeview](https://github.com/lite-xl/lite-xl/pull/732),
including keyboard navigation of treeview, and ability to specify single vs.
double-click behavior.
* Added in [soft line wrapping](https://github.com/lite-xl/lite-xl/pull/636)
as core plugin, under `linewrapping.lua`, use `F10` to activate.
* Revamped [StatusView](https://github.com/lite-xl/lite-xl/pull/852) API with
new features that include:
* Support for predicates, click actions, tooltips on item hover
and custom drawing of added items.
* Hide items that are too huge by rendering with clip_rect.
* Ability to drag or scroll the left or right if too many items to display.
* New status bar commands accessible from the command palette that
include: toggling status bar visibility, toggling specific item visibility,
enable/disable status messages, etc...
* Added `renderer.font.group` interface to set up
[font fallback groups](https://github.com/lite-xl/lite-xl/pull/616) in
the font renderer, if a token doesn't have a corresponding glyph.
**Example:**
```lua
local emoji_font = renderer.font.load(USERDIR .. "/fonts/NotoEmoji-Regular.ttf", 15 * SCALE)
local nonicons = renderer.font.load(USERDIR .. "/fonts/nonicons.ttf", 15 * SCALE)
style.code_font = renderer.font.group({style.code_font, nonicons, emoji_font})
```
* Added in the ability to specify
[mouse clicks](https://github.com/lite-xl/lite-xl/pull/589) in the
keymap, allowing for easy binds of `ctrl+lclick`, and the like.
**Example:**
```lua
keymap.add { ["ctrl+shift+3lclick"] = "core:open-log" }
```
* Improved ability for plugins to be loaded at a given time, by making the
convention of defining a config for the plugin using `common.merge` to merge
existing hashes together, rather than overwriting.
* Releases will now include all language plugins and the
[settings gui](https://github.com/lite-xl/lite-xl-plugins/pull/65) plugin.
* New [core.warn](https://github.com/lite-xl/lite-xl/pull/1005) was introduced.
* Added [suggestions warping](https://github.com/lite-xl/lite-xl/pull/1003)
for `CommandView`.
* Allow regexes in tokenizer to
[split tokens with group](https://github.com/lite-xl/lite-xl/pull/999).
* Added [settings gui support](https://github.com/lite-xl/lite-xl/pull/995)
to core plugins.
* Support for [stricter predicates](https://github.com/lite-xl/lite-xl/pull/990)
by appending a `!`, eg: `"core.docview!"`.
* [UTF8 support in tokenizer](https://github.com/lite-xl/lite-xl/pull/945)
and new utf8 counter parts of string functions,
eg: `string.ulen`, `string.ulower`, etc...
* Added [utf8 support](https://github.com/lite-xl/lite-xl/pull/986) on doc
lower and upper commands.
* Allow syntax patterns to match with the
[beginning of the line](https://github.com/lite-xl/lite-xl/pull/860).
**Example:**
```lua
{ pattern = "^my_pattern_starting_at_beginning", type="symbol" }
```
* [Add View:on_file_dropped](https://github.com/lite-xl/lite-xl/pull/845).
* Implemented new function to retrieve current process id of lite-xl
[system.get_process_id()](https://github.com/lite-xl/lite-xl/pull/833).
* [Allow functions in keymap](https://github.com/lite-xl/lite-xl/pull/948).
* [Add type ahead to CommandView](https://github.com/lite-xl/lite-xl/pull/963).
* Add syntax symbols to
[auto-complete](https://github.com/lite-xl/lite-xl/pull/913).
* Add [animation categories](https://github.com/lite-xl/lite-xl/pull/941)
to enable finer transitions control.
* Added in a [native plugin](https://github.com/lite-xl/lite-xl/pull/527)
interface that allows for C-level interfacing with a statically-linked
lite-xl. The implementation of this may change in future.
* Config: added new development option to prevent plugin version checking at
startup named [skip_plugins_version](https://github.com/lite-xl/lite-xl/pull/879)
* Added a smoothing and strikethrough option to font loading
([#1087](https://github.com/lite-xl/lite-xl/pull/1087))
* Allow command predicates to manage parameters, allow overwriting commands
([#1098](https://github.com/lite-xl/lite-xl/pull/1098))
* Added in simple directory search to treeview.
([#1110](https://github.com/lite-xl/lite-xl/pull/1110))
* Added in native modules suffixes.
([#1111](https://github.com/lite-xl/lite-xl/pull/1111))
* plugin scale: added option to set default scale
([#1115](https://github.com/lite-xl/lite-xl/pull/1115))
* Added in ability to have init.so as a require for cpath.
([#1126](https://github.com/lite-xl/lite-xl/pull/1126))
### Performance Improvements
* [Load space metrics only when creating font](https://github.com/lite-xl/lite-xl/pull/1032)
* [Performance improvement](https://github.com/lite-xl/lite-xl/pull/883)
of detect indent plugin.
* Improve performance of
[ren_draw_rect](https://github.com/lite-xl/lite-xl/pull/935).
* Improved [tokenizer performance](https://github.com/lite-xl/lite-xl/pull/896).
* drawwhitespace: [Cache whitespace location](https://github.com/lite-xl/lite-xl/pull/1030)
* CommandView: improve performance by
[only drawing visible](https://github.com/lite-xl/lite-xl/pull/1047)
### Backward Incompatible Changes
* [Upgraded Lua to 5.4](https://github.com/lite-xl/lite-xl/pull/781), 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.
* Bumped plugin mod-version number as various interfaces like: `DocView`,
`StatusView` and `CommandView` have changed which should require a revision
from plugin developers to make sure their plugins work with this new release.
* Changed interface for key handling; now, all components should return true if
they've handled the event.
* For plugin developers, declaring config options by directly assigning
to the plugin table (eg: `config.plugins.plugin_name.myvalue = 10`) was
deprecated in favor of using `common.merge`.
**Example:**
```lua
config.plugins.autowrap = common.merge({
enabled = false,
files = { "%.md$", "%.txt$" }
}, config.plugins.autowrap)
```
* `DocView:draw_text_line` and related functions been used by plugin developers
require a revision, since some of this interfaces were updated to support
line wrapping.
* Removed `cp_replace`, and replaced this with a core plugin,
[drawwhitespace.lua](https://github.com/lite-xl/lite-xl/pull/908).
### Deprecated Features
* For plugins the usage of the `--lite-xl` version tag was dropped
in favor of `--mod-version`.
* Overriding `StatusView:get_items()` has been deprecated in favor of
the new dedicated interface to insert status bar items:
**New Interface:**
```lua
------@return StatusView.Item
function StatusView:add_item(
{ predicate, name, alignment, get_item, command, position, tooltip, separator }
) end
```
**Example:**
```lua
core.status_view:add_item({
predicate = nil,
name = "status:memory-usage",
alignment = StatusView.Item.RIGHT,
get_item = function()
return {
style.text,
string.format(
"%.2f MB",
(math.floor(collectgarbage("count") / 10.24) / 100)
)
}
end,
command = nil,
position = 1,
tooltip = "lua memory usage",
separator = core.status_view.separator2
})
```
* [CommandView:enter](https://github.com/lite-xl/lite-xl/pull/1004) now accepts
a single options table as a parameter, meaning that the old way of calling
this function will now show a deprecation message. Also `CommandView:set_text`
and `CommandView:set_hidden_suggestions` has been
[deprecated](https://github.com/lite-xl/lite-xl/pull/1014).
**Example:**
```lua
core.command_view:enter("Title", {
submit = function() end,
suggest = function() return end,
cancel = function() end,
validate = function() return true end,
text = "",
select_text = false,
show_suggestions = true,
typeahead = true,
wrap = true
})
```
### Other Changes
* 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 dir monitoring library. We also have a dummy-backend, which reverts
transparently 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`](https://github.com/lite-xl/lite-xl/pull/715)
into their own lua files, for plugin extensibility reasons.
* Improved fuzzy_matching to probably give you something closer to what you're
looking for.
* Improved handling of alternate keyboard layouts.
* Added in a default keymap for `core:restart`, `ctrl+shift+r`.
* Improvements to the [C and C++](https://github.com/lite-xl/lite-xl/pull/875)
syntax files.
* Improvements to [markdown](https://github.com/lite-xl/lite-xl/pull/862)
syntax file.
* [Improvements to borderless](https://github.com/lite-xl/lite-xl/pull/994)
mode on Windows.
* Fixed a bunch of problems relating to
[multi-cursor](https://github.com/lite-xl/lite-xl/pull/886).
* NagView: [support vscroll](https://github.com/lite-xl/lite-xl/pull/876) when
message is too long.
* Meson improvements which include:
* Added in meson wraps for freetype, pcre2, and SDL2 which target public,
rather than lite-xl maintained repos.
* [Seperate dirmonitor logic](https://github.com/lite-xl/lite-xl/pull/866),
add build time detection of features.
* Add [fallbacks](https://github.com/lite-xl/lite-xl/pull/798) to all
common dependencies.
* [Update SDL to 2.0.20](https://github.com/lite-xl/lite-xl/pull/884).
* install [docs/api](https://github.com/lite-xl/lite-xl/pull/979) to datadir
for lsp support.
* Always check if the beginning of the
[text needs to be clipped](https://github.com/lite-xl/lite-xl/pull/871).
* Added [git commit](https://github.com/lite-xl/lite-xl/pull/859)
on development builds.
* Update [autocomplete](https://github.com/lite-xl/lite-xl/pull/832)
with changes needed for latest LSP plugin.
* Use SDL to manage color format mapping in
[ren_draw_rect](https://github.com/lite-xl/lite-xl/pull/829).
* Various code [clean ups](https://github.com/lite-xl/lite-xl/pull/826).
* [Autoreload Nagview](https://github.com/lite-xl/lite-xl/pull/942).
* [Enhancements to scrollbar](https://github.com/lite-xl/lite-xl/pull/916).
* Set the correct working directory for the
[AppImage version](https://github.com/lite-xl/lite-xl/pull/937).
* Core: fixes and changes to
[temp file](https://github.com/lite-xl/lite-xl/pull/906) functions.
* [Added plugin load-time log](https://github.com/lite-xl/lite-xl/pull/966).
* TreeView improvements for
[multi-project](https://github.com/lite-xl/lite-xl/pull/1010).
* Open LogView on user/project
[module reload error](https://github.com/lite-xl/lite-xl/pull/1022).
* Check if ["open" pattern is escaped](https://github.com/lite-xl/lite-xl/pull/1034)
* Support [UTF-8 on Windows](https://github.com/lite-xl/lite-xl/pull/1041) (Lua)
* Make system.* functions support
[UTF8 filenames on windows](https://github.com/lite-xl/lite-xl/pull/1042)
* [Fix memory leak](https://github.com/lite-xl/lite-xl/pull/1039) and wrong
check in font_retrieve
* Many, many, many more changes that are too numerous to list.
* CommandView: do not change caret size with config.line_height
([#1080](https://github.com/lite-xl/lite-xl/pull/1080))
## [2.0.5] - 2022-01-29
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] - 2021-12-20
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] - 2021-10-23
Replace periodic rescan of project folder with a notification based system
using the [dmon library](https://github.com/septag/dmon). Improves performance
especially for large project folders since the application no longer needs to
rescan. The application also reports immediately any change in the project
directory even when the application is unfocused.
Improved find-replace reverse and forward search.
Fixed a bug in incremental syntax highlighting affecting documents with multiple-lines
comments or strings.
Fixed a bug in incremental syntax highlighting affecting documents with
multiple-lines comments or strings.
The application now always shows the tabs in the documents' view even when a single
document is opened. Can be changed with the option `config.always_show_tabs`.
The application now always shows the tabs in the documents' view even when
a single document is opened. Can be changed with the option
`config.always_show_tabs`.
Fix problem with numeric keypad function keys not properly working.
@ -22,32 +407,36 @@ Fix problem with pixel not correctly drawn at the window's right edge.
Treat correctly and open network paths on Windows.
Add some improvements for very slow network filesystems.
Add some improvements for very slow network file systems.
Fix problem with python syntax highliting, contributed by @dflock.
Fix problem with python syntax highlighting, contributed by @dflock.
### 2.0.2
## [2.0.2] - 2021-09-10
Fix problem project directory when starting the application from Launcher on macOS.
Fix problem project directory when starting the application from Launcher on
macOS.
Improved LogView. Entries can now be expanded and there is a context menu to copy the item's content.
Improved LogView. Entries can now be expanded and there is a context menu to
copy the item's content.
Change the behavior of `ctrl+d` to add a multi-cursor selection to the next occurrence.
The old behavior to move the selection to the next occurrence is now done using the shortcut `ctrl+f3`.
Change the behavior of `ctrl+d` to add a multi-cursor selection to the next
occurrence. The old behavior to move the selection to the next occurrence is
now done using the shortcut `ctrl+f3`.
Added a command to create a multi-cursor with all the occurrences of the current selection.
Activated with the shortcut `ctrl+shift+l`.
Added a command to create a multi-cursor with all the occurrences of the
current selection. Activated with the shortcut `ctrl+shift+l`.
Fix problem when trying to close an unsaved new document.
No longer shows an error for the `-psn` argument passed to the application on macOS.
No longer shows an error for the `-psn` argument passed to the application on
macOS.
Fix `treeview:open-in-system` command on Windows.
Fix rename command to update name of document if opened.
Improve the find and replace dialog so that previously used expressions can be recalled
using "up" and "down" keys.
Improve the find and replace dialog so that previously used expressions can be
recalled using "up" and "down" keys.
Build package script rewrite with many improvements.
@ -55,63 +444,76 @@ Use bigger fonts by default.
Other minor improvements and fixes.
With many thanks to the contributors: @adamharrison, @takase1121, @Guldoman, @redtide, @Timofffee, @boppyt, @Jan200101.
With many thanks to the contributors: @adamharrison, @takase1121, @Guldoman,
@redtide, @Timofffee, @boppyt, @Jan200101.
### 2.0.1
## [2.0.1] - 2021-08-28
Fix a few bugs and we mandate the mod-version 2 for plugins.
This means that users should ensure they have up-to-date plugins for Lite XL 2.0.
Here some details about the bug fixes:
- fix a bug that created a fatal error when using the command to change project folder or when closing all the active documents
- add a limit to avoid scaling fonts too much and fix a related invalid memory access for very small fonts
- fix a bug that created a fatal error when using the command to change project
folder or when closing all the active documents
- add a limit to avoid scaling fonts too much and fix a related invalid memory
access for very small fonts
- fix focus problem with NagView when switching project directory
- fix error that prevented the verification of plugins versions
- fix error on X11 that caused a bug window event on exit
### 2.0
## [2.0] - 2021-08-16
The 2.0 version of lite contains *breaking changes* to lite, in terms of how plugin settings are structured;
any custom plugins may need to be adjusted accordingly (see note below about plugin namespacing).
The 2.0 version of lite contains *breaking changes* to lite, in terms of how
plugin settings are structured; any custom plugins may need to be adjusted
accordingly (see note below about plugin namespacing).
Contains the following new features:
Full PCRE (regex) support for find and replace, as well as in language syntax definitions. Can be accessed
programatically via the lua `regex` module.
Full PCRE (regex) support for find and replace, as well as in language syntax
definitions. Can be accessed programatically via the lua `regex` module.
A full, finalized subprocess API, using libreproc. Subprocess can be started and interacted with using
`Process.new`.
A full, finalized subprocess API, using libreproc. Subprocess can be started
and interacted with using `Process.new`.
Support for multi-cursor editing. Cursors can be created by either ctrl+clicking on the screen, or by using
the keyboard shortcuts ctrl+shift+up/down to create an additional cursor on the previous/next line.
Support for multi-cursor editing. Cursors can be created by either ctrl+clicking
on the screen, or by using the keyboard shortcuts ctrl+shift+up/down to create
an additional cursor on the previous/next line.
All build systems other than meson removed.
A more organized directory structure has been implemented; in particular a docs folder which contains C api
documentation, and a resource folder which houses all build resources.
A more organized directory structure has been implemented; in particular a docs
folder which contains C api documentation, and a resource folder which houses
all build resources.
Plugin config namespacing has been implemented. This means that instead of using `config.myplugin.a`,
to read settings, and `config.myplugin = false` to disable plugins, this has been changed to
`config.plugins.myplugin.a`, and `config.plugins.myplugin = false` repsectively. This may require changes to
Plugin config namespacing has been implemented. This means that instead of
using `config.myplugin.a`, to read settings, and `config.myplugin = false` to
disable plugins, this has been changed to `config.plugins.myplugin.a`, and
`config.plugins.myplugin = false` respectively. This may require changes to
your user plugin, or to any custom plugins you have.
A context menu on right click has been added.
Changes to how we deal with indentation have been implemented; in particular, hitting home no longer brings you
to the start of a line, it'll bring you to the start of indentation, which is more in line with other editors.
Changes to how we deal with indentation have been implemented; in particular,
hitting home no longer brings you to the start of a line, it'll bring you to
the start of indentation, which is more in line with other editors.
Lineguide, and scale plugins moved into the core, and removed from `lite-plugins`. This may also require you to
adjust your personal plugin folder to remove these if they're present.
Lineguide, and scale plugins moved into the core, and removed from
`lite-plugins`. This may also require you to adjust your personal plugin
folder to remove these if they're present.
In addition, there have been many other small fixes and improvements, too numerous to list here.
In addition, there have been many other small fixes and improvements, too
numerous to list here.
### 1.16.11
## [1.16.11] - 2021-05-28
When opening directories with too many files lite-xl now keep diplaying files and directories in the treeview.
The application remains functional and the directories can be explored without using too much memory.
In this operating mode the files of the project are not indexed so the command "Core: Find File" will act as the "Core: Open File" command.
The "Project Search: Find" will work by searching all the files present in the project directory even if they are not indexed.
When opening directories with too many files lite-xl now keep displaying files
and directories in the treeview. The application remains functional and the
directories can be explored without using too much memory. In this operating
mode the files of the project are not indexed so the command "Core: Find File"
will act as the "Core: Open File" command.The "Project Search: Find" will work
by searching all the files present in the project directory even if they are
not indexed.
Implemented changing fonts per syntax group by @liquidev.
@ -131,30 +533,30 @@ Fix bug with close button not working in borderless window mode.
Fix problem with normalization of filename for opened documents.
### 1.16.10
## [1.16.10] - 2021-05-22
Improved syntax highlight system thanks to @liquidev and @adamharrison.
Thanks to the new system we provide more a accurate syntax highlighting for Lua, C and C++.
Other syntax improvements contributed by @vincens2005.
Thanks to the new system we provide more a accurate syntax highlighting for
Lua, C and C++. Other syntax improvements contributed by @vincens2005.
Move to JetBrains Mono and Fira Sans fonts for code and UI respectively.
Thet are provided under the SIL Open Font License, Version 1.1.
They are provided under the SIL Open Font License, Version 1.1.
See `doc/licenses.md` for license details.
Fixed bug with fonts and rencache module.
Under very specific situations the application was crashing due to invalid memory access.
Fixed bug with fonts and rencache module. Under very specific situations the
application was crashing due to invalid memory access.
Add documentation for keymap binding, thanks to @Janis-Leuenberger.
Added a contributors page in `doc/contributors.md`.
### 1.16.9
## [1.16.9] - 2021-05-06
Fix a bug related to nested panes resizing.
Fix problem preventing creating a new file.
### 1.16.8
## [1.16.8] - 2021-05-06
Fix application crash when using the command `core:restart`.
@ -176,27 +578,28 @@ Both kind of tags can appear in new plugins in the form:
where the old tag needs to appear at the end for compatibility.
### 1.16.7
## [1.16.7] - 2021-05-01
Add support for retina displays on Mac OS X.
Fix a few problems related to file paths.
### 1.16.6
## [1.16.6] - 2021-04-21
Implement a system to check the compatibility of plugins by checking a release tag.
Plugins that don't have the release tag will not be loaded.
Implement a system to check the compatibility of plugins by checking a release
tag. Plugins that don't have the release tag will not be loaded.
Improve and extend the NagView with keyboard commands.
Special thanks to @takase1121 for the implementation and @liquidev for proposing and
discussing the enhancements.
Special thanks to @takase1121 for the implementation and @liquidev for proposing
and discussing the enhancements.
Add support to build on Mac OS X and create an application bundle.
Special thanks to @mathewmariani for his lite-macos fork, the Mac OS specific
resources and his support.
Add hook function `DocView.on_text_change` so that plugin can accurately react on document changes.
Thanks to @vincens2005 for the suggestion and testing the implementation.
Add hook function `DocView.on_text_change` so that plugin can accurately react
on document changes. Thanks to @vincens2005 for the suggestion and testing the
implementation.
Enable borderless window mode using the `config.borderless` variable.
If enable the system window's bar will be replaced by a title bar provided
@ -214,13 +617,14 @@ commands `draw-whitespace:toggle`, `draw-whitespace:enable`,
Improve the NagView to accept keyboard commands and introduce dialog commands.
Add hook function `Doc:on_text_change` called on document changes, to be used by plugins.
Add hook function `Doc:on_text_change` called on document changes, to be
used by plugins.
### 1.16.5
## [1.16.5] - 2021-03-20
Hotfix for Github's issue https://github.com/franko/lite-xl/issues/122
### 1.16.4
## [1.16.4] - 2021-03-20
Add tooltips to show full file names from the tree-view.
@ -235,7 +639,7 @@ Made borders between tabs look cleaner.
Fix problem with files using hard tabs.
### 1.16.2
## [1.16.2] - 2021-03-05
Implement close button for tabs.
@ -243,12 +647,12 @@ Make the command view list of suggestion scrollable to see all the items.
Improve update/resize behavior of treeview and toolbar.
### 1.16.1
## [1.16.1] - 2021-02-25
Improve behavior of commands to move, delete and duplicate multiple lines:
no longer include the last line if it does not contain any selection.
Fix graphical artefacts when rendering some fonts like FiraSans.
Fix graphical artifacts when rendering some fonts like FiraSans.
Introduce the `config.transitions` boolean variable.
When false the transitions will be disabled and changes will be done immediately.
@ -257,7 +661,7 @@ Very useful for remote sessions where visual transitions doesn't work well.
Fix many small problems related to the new toolbar and the tooptips.
Fix problem with spacing in treeview when using monospace fonts.
### 1.16
## [1.16] - 2021-02-19
Implement a toolbar shown in the bottom part of the tree-view.
The toolbar is especially meant for new users to give an easy, visual, access
@ -269,8 +673,8 @@ are actually resizable.
Add config mechanism to disable a plugin by setting
`config.<plugin-name> = false`.
Improve the "detect indent" plugin to take into account the syntax and exclude comments
for much accurate results.
Improve the "detect indent" plugin to take into account the syntax and exclude
comments for much accurate results.
Add command `root:close-all` to close all the documents currently opened.
@ -278,21 +682,24 @@ Show the full path filename of the active document in the window's title.
Fix problem with user's module reload not always enabled.
### 1.15
## [1.15] - 2021-01-04
**Project directories**
Extend your project by adding more directories using the command `core:add-directory`.
To remove them use the corresponding command `core:remove-directory`.
Extend your project by adding more directories using the command
`core:add-directory`. To remove them use the corresponding command
`core:remove-directory`.
**Workspaces**
The workspace plugin from rxi/lite-plugins is now part of Lite XL.
In addition to the functionalities of the original plugin the extended version will
also remember the window size and position and the additonal project directories.
To not interfere with the project's files the workspace file is saved in the personal
Lite's configuration folder.
On unix-like systems it will be in: `$HOME/.config/lite-xl/ws`.
In addition to the functionalities of the original plugin the extended version
will also remember the window size and position and the additional project
directories.
To not interfere with the project's files the workspace file is saved in the
personal Lite's configuration folder. On unix-like systems it will be in:
`$HOME/.config/lite-xl/ws`.
**Scrolling the Tree View**
@ -304,10 +711,11 @@ As in the unix shell `~` is now used to identify the home directory.
**Files and Directories**
Add command to create a new empty directory within the project using the command
`files:create-directory`.
In addition a control-click on a project directory will prompt the user to create
a new directory inside the directory pointed.
Add command to create a new empty directory within the project using the
command `files:create-directory`.
In addition a control-click on a project directory will prompt the user to
create a new directory inside the directory pointed.
**New welcome screen**
@ -315,51 +723,56 @@ Show 'Lite XL' instead of 'lite' and the version number.
**Various fixes and improvements**
A few quirks previously with some of the new features have been fixed for a better user experience.
A few quirks previously with some of the new features have been fixed for a
better user experience.
### 1.14
## [1.14] - 2020-12-13
**Project Management**
Add a new command, Core: Change Project Folder, to change project directory by staying on the same window.
All the current opened documents will be closed.
Add a new command, Core: Change Project Folder, to change project directory by
staying on the same window. All the current opened documents will be closed.
The new command is associated with the keyboard combination ctrl+shit+c.
A similar command is also added, Core: Open Project Folder, with key binding ctrl+shift+o.
It will open the chosen folder in a new window.
A similar command is also added, Core: Open Project Folder, with key binding
ctrl+shift+o. It will open the chosen folder in a new window.
In addition Lite XL will now remember the recently used projects across different sessions.
When invoked without arguments it will now open the project more recently used.
If a directory is specified it will behave like before and open the directory indicated as an argument.
In addition Lite XL will now remember the recently used projects across
different sessions. When invoked without arguments it will now open the project
more recently used. If a directory is specified it will behave like before and
open the directory indicated as an argument.
**Restart command**
A Core: Restart command is added to restart the editor without leaving the current window.
Very convenient when modifying the Lua code for the editor itself.
A Core: Restart command is added to restart the editor without leaving the
current window. Very convenient when modifying the Lua code for the editor
itself.
**User's setting auto-reload**
When saving the user configuration, the user's module, the changes will be automatically applied to the
current instance.
When saving the user configuration, the user's module, the changes will be
automatically applied to the current instance.
**Bundle community provided colors schemes**
Included now in the release files the colors schemes from github.com/rxi/lite-colors.
Included now in the release files the colors schemes from
github.com/rxi/lite-colors.
**Usability improvements**
Improve left and right scrolling of text to behave like other editors and improves text selection with mouse.
Improve left and right scrolling of text to behave like other editors and
improves text selection with mouse.
**Fixes**
Correct font's rendering for full hinting mode when using subpixel antialiasing.
### 1.13
## [1.13] - 2020-12-06
**Rendering options for fonts**
When loading fonts with the function renderer.font.load some rendering options can
be optionally specified:
When loading fonts with the function renderer.font.load some rendering options
can be optionally specified:
- antialiasing: grayscale or subpixel
- hinting: none, slight or full
@ -368,36 +781,39 @@ See data/core/style.lua for the details about its utilisation.
The default remains antialiasing subpixel and hinting slight to reproduce the
behavior of previous versions.
The option grayscale with full hinting is specially interesting for crisp font rendering
without color artifacts.
The option grayscale with full hinting is specially interesting for crisp font
rendering without color artifacts.
**Unix-like install directories**
Use unix-like install directories for the executable and for the data directory.
The executable will be placed under $prefix/bin and the data folder will be
$prefix/share/lite-xl.
The folder $prefix is not hard-coded in the binary but is determined at runtime
as the directory such as the executable is inside $prefix/bin.
If no such $prefix exist it will fall back to the old behavior and use the "data"
folder from the executable directory.
In addtion to the `EXEDIR` global variable an additional variable is exposed, `DATADIR`,
to point to the data directory.
If no such $prefix exist it will fall back to the old behavior and use the
"data" folder from the executable directory.
The old behavior using the "data" directory can be still selected at compile time
using the "portable" option. The released Windows package will use the "data"
directory as before.
In addtion to the `EXEDIR` global variable an additional variable is exposed,
`DATADIR`, to point to the data directory.
The old behavior using the "data" directory can be still selected at compile
time using the "portable" option. The released Windows package will use the
"data" directory as before.
**Configuration stored into the user's home directory**
Now the Lite XL user's configuration will be stored in the user's home directory under
".config/lite-xl".
The home directory is determined using the "HOME" environment variable except on Windows
wher "USERPROFILE" is used instead.
Now the Lite XL user's configuration will be stored in the user's home directory
under .config/lite-xl".
The home directory is determined using the "HOME" environment variable except
on Windows wher "USERPROFILE" is used instead.
A new global variable `USERDIR` is exposed to point to the user's directory.
### 1.11
## [1.11] - 2020-07-05
- include changes from rxi's Lite 1.11
- fix behavior of tab to indent multiple lines
@ -405,11 +821,36 @@ A new global variable `USERDIR` is exposed to point to the user's directory.
- limit project scan to a maximum number of files to limit memory usage
- list recently visited files when using "Find File" command
### 1.08
## [1.08] - 2020-06-14
- Subpixel font rendering, removed gamma correction
- Avoid using CPU when the editor is idle
### 1.06
## [1.06] - 2020-05-31
- subpixel font rendering with gamma correction
[2.1.0]: https://github.com/lite-xl/lite-xl/releases/tag/v2.1.0
[2.0.5]: https://github.com/lite-xl/lite-xl/releases/tag/v2.0.5
[2.0.4]: https://github.com/lite-xl/lite-xl/releases/tag/v2.0.4
[2.0.3]: https://github.com/lite-xl/lite-xl/releases/tag/v2.0.3
[2.0.2]: https://github.com/lite-xl/lite-xl/releases/tag/v2.0.2
[2.0.1]: https://github.com/lite-xl/lite-xl/releases/tag/v2.0.1
[2.0]: https://github.com/lite-xl/lite-xl/releases/tag/v2.0.0
[1.16.11]: https://github.com/lite-xl/lite-xl/releases/tag/v1.16.11
[1.16.10]: https://github.com/lite-xl/lite-xl/releases/tag/v1.16.10
[1.16.9]: https://github.com/lite-xl/lite-xl/releases/tag/v1.16.9
[1.16.8]: https://github.com/lite-xl/lite-xl/releases/tag/v1.16.8
[1.16.7]: https://github.com/lite-xl/lite-xl/releases/tag/v1.16.7
[1.16.6]: https://github.com/lite-xl/lite-xl/releases/tag/v1.16.6
[1.16.5]: https://github.com/lite-xl/lite-xl/releases/tag/v1.16.5
[1.16.4]: https://github.com/lite-xl/lite-xl/releases/tag/v1.16.4
[1.16.2]: https://github.com/lite-xl/lite-xl/releases/tag/v1.16.2-lite-xl
[1.16.1]: https://github.com/lite-xl/lite-xl/releases/tag/v1.16.1-lite-xl
[1.16]: https://github.com/lite-xl/lite-xl/releases/tag/v1.16.0-lite-xl
[1.15]: https://github.com/lite-xl/lite-xl/releases/tag/v1.15-lite-xl
[1.14]: https://github.com/lite-xl/lite-xl/releases/tag/v1.14-lite-xl
[1.13]: https://github.com/lite-xl/lite-xl/releases/tag/v1.13-lite-xl
[1.11]: https://github.com/lite-xl/lite-xl/releases/tag/v1.11-lite-xl
[1.08]: https://github.com/lite-xl/lite-xl/releases/tag/v1.08-subpixel
[1.06]: https://github.com/lite-xl/lite-xl/releases/tag/1.06-subpixel-rc1

46
data/colors/default.lua Normal file
View File

@ -0,0 +1,46 @@
local style = require "core.style"
local common = require "core.common"
style.background = { common.color "#2e2e32" } -- Docview
style.background2 = { common.color "#252529" } -- Treeview
style.background3 = { common.color "#252529" } -- Command view
style.text = { common.color "#97979c" }
style.caret = { common.color "#93DDFA" }
style.accent = { common.color "#e1e1e6" }
-- style.dim - text color for nonactive tabs, tabs divider, prefix in log and
-- search result, hotkeys for context menu and command view
style.dim = { common.color "#525257" }
style.divider = { common.color "#202024" } -- Line between nodes
style.selection = { common.color "#48484f" }
style.line_number = { common.color "#525259" }
style.line_number2 = { common.color "#83838f" } -- With cursor
style.line_highlight = { common.color "#343438" }
style.scrollbar = { common.color "#414146" }
style.scrollbar2 = { common.color "#4b4b52" } -- Hovered
style.scrollbar_track = { common.color "#252529" }
style.nagbar = { common.color "#FF0000" }
style.nagbar_text = { common.color "#FFFFFF" }
style.nagbar_dim = { common.color "rgba(0, 0, 0, 0.45)" }
style.drag_overlay = { common.color "rgba(255,255,255,0.1)" }
style.drag_overlay_tab = { common.color "#93DDFA" }
style.good = { common.color "#72b886" }
style.warn = { common.color "#FFA94D" }
style.error = { common.color "#FF3333" }
style.modified = { common.color "#1c7c9c" }
style.syntax["normal"] = { common.color "#e1e1e6" }
style.syntax["symbol"] = { common.color "#e1e1e6" }
style.syntax["comment"] = { common.color "#676b6f" }
style.syntax["keyword"] = { common.color "#E58AC9" } -- local function end if case
style.syntax["keyword2"] = { common.color "#F77483" } -- self int float
style.syntax["number"] = { common.color "#FFA94D" }
style.syntax["literal"] = { common.color "#FFA94D" } -- true false nil
style.syntax["string"] = { common.color "#f7c95c" }
style.syntax["operator"] = { common.color "#93DDFA" } -- = + - / < >
style.syntax["function"] = { common.color "#93DDFA" }
style.log["INFO"] = { icon = "i", color = style.text }
style.log["WARN"] = { icon = "!", color = style.warn }
style.log["ERROR"] = { icon = "!", color = style.error }
return style

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

@ -6,17 +6,48 @@ command.map = {}
local always_true = function() return true end
function command.add(predicate, map)
---Used iternally by command.add, statusview, and contextmenu to generate a
---function with a condition to evaluate returning the boolean result of this
---evaluation.
---
---If a string predicate is given it is treated as a require import that should
---return a valid object which is checked against the current active view,
---eg: "core.docview" will match any view that inherits from DocView. Appending
---a `!` at the end of the string means we want to match the given object
---from the import strcitly eg: "core.docview!" only DocView is matched.
---A function that returns a boolean can be used instead to perform a custom
---evaluation, setting to nil means always evaluates to true.
---
---@param predicate string | table | function
---@return function
function command.generate_predicate(predicate)
predicate = predicate or always_true
local strict = false
if type(predicate) == "string" then
if predicate:match("!$") then
strict = true
predicate = predicate:gsub("!$", "")
end
predicate = require(predicate)
end
if type(predicate) == "table" then
local class = predicate
predicate = function() return core.active_view:is(class) end
if not strict then
predicate = function(...) return core.active_view:extends(class), core.active_view, ... end
else
predicate = function(...) return core.active_view:is(class), core.active_view, ... end
end
end
return predicate
end
function command.add(predicate, map)
predicate = command.generate_predicate(predicate)
for name, fn in pairs(map) do
assert(not command.map[name], "command already exists: " .. name)
if command.map[name] then
core.log_quiet("Replacing existing command \"%s\"", name)
end
command.map[name] = { predicate = predicate, perform = fn }
end
end
@ -33,8 +64,12 @@ end
function command.get_all_valid()
local res = {}
local memoized_predicates = {}
for name, cmd in pairs(command.map) do
if cmd.predicate() then
if memoized_predicates[cmd.predicate] == nil then
memoized_predicates[cmd.predicate] = cmd.predicate()
end
if memoized_predicates[cmd.predicate] then
table.insert(res, name)
end
end
@ -47,8 +82,16 @@ end
local function perform(name, ...)
local cmd = command.map[name]
if cmd and cmd.predicate(...) then
cmd.perform(...)
if not cmd then return false end
local res = { cmd.predicate(...) }
if table.remove(res, 1) then
if #res > 0 then
-- send values returned from predicate
cmd.perform(table.unpack(res))
else
-- send original parameters
cmd.perform(...)
end
return true
end
return false
@ -64,7 +107,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

@ -2,23 +2,23 @@ local core = require "core"
local command = require "core.command"
command.add("core.commandview", {
["command:submit"] = function()
core.active_view:submit()
["command:submit"] = function(active_view)
active_view:submit()
end,
["command:complete"] = function()
core.active_view:complete()
["command:complete"] = function(active_view)
active_view:complete()
end,
["command:escape"] = function()
core.active_view:exit()
["command:escape"] = function(active_view)
active_view:exit()
end,
["command:select-previous"] = function()
core.active_view:move_suggestion_idx(1)
["command:select-previous"] = function(active_view)
active_view:move_suggestion_idx(1)
end,
["command:select-next"] = function()
core.active_view:move_suggestion_idx(-1)
["command:select-next"] = function(active_view)
active_view:move_suggestion_idx(-1)
end,
})

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, {
@ -38,36 +48,42 @@ command.add(nil, {
end,
["core:reload-module"] = function()
core.command_view:enter("Reload Module", function(text, item)
local text = item and item.text or text
core.reload_module(text)
core.log("Reloaded module %q", text)
end, function(text)
local items = {}
for name in pairs(package.loaded) do
table.insert(items, name)
core.command_view:enter("Reload Module", {
submit = function(text, item)
local text = item and item.text or text
core.reload_module(text)
core.log("Reloaded module %q", text)
end,
suggest = function(text)
local items = {}
for name in pairs(package.loaded) do
table.insert(items, name)
end
return common.fuzzy_match(items, text)
end
return common.fuzzy_match(items, text)
end)
})
end,
["core:find-command"] = function()
local commands = command.get_all_valid()
core.command_view:enter("Do Command", function(text, item)
if item then
command.perform(item.command)
core.command_view:enter("Do Command", {
submit = function(text, item)
if item then
command.perform(item.command)
end
end,
suggest = function(text)
local res = common.fuzzy_match(commands, text)
for i, name in ipairs(res) do
res[i] = {
text = command.prettify_name(name),
info = keymap.get_binding(name),
command = name,
}
end
return res
end
end, function(text)
local res = common.fuzzy_match(commands, text)
for i, name in ipairs(res) do
res[i] = {
text = command.prettify_name(name),
info = keymap.get_binding(name),
command = name,
}
end
return res
end)
})
end,
["core:find-file"] = function()
@ -81,56 +97,72 @@ command.add(nil, {
table.insert(files, common.home_encode(path .. item.filename))
end
end
core.command_view:enter("Open File From Project", function(text, item)
text = item and item.text or text
core.root_view:open_doc(core.open_doc(common.home_expand(text)))
end, function(text)
return common.fuzzy_match_with_recents(files, core.visited_files, text)
end)
core.command_view:enter("Open File From Project", {
submit = function(text, item)
text = item and item.text or text
core.root_view:open_doc(core.open_doc(common.home_expand(text)))
end,
suggest = function(text)
return common.fuzzy_match_with_recents(files, core.visited_files, text)
end
})
end,
["core:new-doc"] = function()
core.root_view:open_doc(core.open_doc())
end,
["core:new-named-doc"] = function()
core.command_view:enter("File name", {
submit = function(text)
core.root_view:open_doc(core.open_doc(text))
end
})
end,
["core:open-file"] = function()
local view = core.active_view
local text
if view.doc and view.doc.abs_filename then
local dirname, filename = view.doc.abs_filename:match("(.*)[/\\](.+)$")
if dirname then
dirname = core.normalize_to_project_dir(dirname)
local text = dirname == core.project_dir and "" or common.home_encode(dirname) .. PATHSEP
core.command_view:set_text(text)
text = dirname == core.project_dir and "" or common.home_encode(dirname) .. PATHSEP
end
end
core.command_view:enter("Open File", function(text)
local filename = system.absolute_path(common.home_expand(text))
core.root_view:open_doc(core.open_doc(filename))
end, function (text)
return common.home_encode_list(common.path_suggest(common.home_expand(text)))
end, nil, function(text)
local filename = common.home_expand(text)
local path_stat, err = system.get_file_info(filename)
if err then
if err:find("No such file", 1, true) then
-- check if the containing directory exists
local dirname = common.dirname(filename)
local dir_stat = dirname and system.get_file_info(dirname)
if not dirname or (dir_stat and dir_stat.type == 'dir') then
core.command_view:enter("Open File", {
text = text,
submit = function(text)
local filename = system.absolute_path(common.home_expand(text))
core.root_view:open_doc(core.open_doc(filename))
end,
suggest = function (text)
return common.home_encode_list(common.path_suggest(common.home_expand(text)))
end,
validate = function(text)
local filename = common.home_expand(text)
local path_stat, err = system.get_file_info(filename)
if err then
if err:find("No such file", 1, true) then
-- check if the containing directory exists
local dirname = common.dirname(filename)
local dir_stat = dirname and system.get_file_info(dirname)
if not dirname or (dir_stat and dir_stat.type == 'dir') then
return true
end
end
core.error("Cannot open file %s: %s", text, err)
elseif path_stat.type == 'dir' then
core.error("Cannot open %s, is a folder", text)
else
return true
end
end
core.error("Cannot open file %s: %s", text, err)
elseif path_stat.type == 'dir' then
core.error("Cannot open %s, is a folder", text)
else
return true
end
end)
end,
})
end,
["core:open-log"] = function()
local node = core.root_view:get_active_node()
local node = core.root_view:get_active_node_default()
node:add_view(LogView())
end,
@ -141,62 +173,79 @@ 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)
local text
if dirname then
core.command_view:set_text(common.home_encode(dirname))
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)
return
end
core.confirm_close_docs(core.docs, core.open_folder_project, text)
end, suggest_directory)
core.command_view:enter("Change Project Folder", {
text = text,
submit = 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
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 = suggest_directory
})
end,
["core:open-project-folder"] = function()
local dirname = common.dirname(core.project_dir)
local text
if dirname then
core.command_view:set_text(common.home_encode(dirname))
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)
return
end
system.exec(string.format("%q %q", EXEFILE, text))
end, suggest_directory)
core.command_view:enter("Open Project", {
text = text,
submit = 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
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 = suggest_directory
})
end,
["core:add-directory"] = function()
core.command_view:enter("Add Directory", function(text)
text = common.home_expand(text)
local path_stat, err = system.get_file_info(text)
if not path_stat then
core.error("cannot open %q: %s", text, err)
return
elseif path_stat.type ~= 'dir' then
core.error("%q is not a directory", text)
return
end
core.add_project_directory(system.absolute_path(text))
end, suggest_directory)
core.command_view:enter("Add Directory", {
submit = function(text)
text = common.home_expand(text)
local path_stat, err = system.get_file_info(text)
if not path_stat then
core.error("cannot open %q: %s", text, err)
return
elseif path_stat.type ~= 'dir' then
core.error("%q is not a directory", text)
return
end
core.add_project_directory(system.absolute_path(text))
end,
suggest = suggest_directory
})
end,
["core:remove-directory"] = function()
@ -205,14 +254,17 @@ command.add(nil, {
for i = n, 2, -1 do
dir_list[n - i + 1] = core.project_directories[i].name
end
core.command_view:enter("Remove Directory", function(text, item)
text = common.home_expand(item and item.text or text)
if not core.remove_project_directory(text) then
core.error("No directory %q to be removed", text)
core.command_view:enter("Remove Directory", {
submit = function(text, item)
text = common.home_expand(item and item.text or text)
if not core.remove_project_directory(text) then
core.error("No directory %q to be removed", text)
end
end,
suggest = function(text)
text = common.home_expand(text)
return common.home_encode_list(common.dir_list_suggest(text, dir_list))
end
end, function(text)
text = common.home_expand(text)
return common.home_encode_list(common.dir_list_suggest(text, dir_list))
end)
})
end,
})

View File

@ -3,30 +3,25 @@ local command = require "core.command"
local common = require "core.common"
command.add("core.nagview", {
["dialog:previous-entry"] = function()
local v = core.active_view
["dialog:previous-entry"] = function(v)
local hover = v.hovered_item or 1
v:change_hovered(hover == 1 and #v.options or hover - 1)
end,
["dialog:next-entry"] = function()
local v = core.active_view
["dialog:next-entry"] = function(v)
local hover = v.hovered_item or 1
v:change_hovered(hover == #v.options and 1 or hover + 1)
end,
["dialog:select-yes"] = function()
local v = core.active_view
["dialog:select-yes"] = function(v)
if v ~= core.nag_view then return end
v:change_hovered(common.find_index(v.options, "default_yes"))
command.perform "dialog:select"
end,
["dialog:select-no"] = function()
local v = core.active_view
["dialog:select-no"] = function(v)
if v ~= core.nag_view then return end
v:change_hovered(common.find_index(v.options, "default_no"))
command.perform "dialog:select"
end,
["dialog:select"] = function()
local v = core.active_view
["dialog:select"] = function(v)
if v.hovered_item then
v.on_selected(v.options[v.hovered_item])
v:next()

View File

@ -47,19 +47,34 @@ 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)
elseif #doc().lines == 1 then
doc():remove(line1, 1, line1, math.huge)
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
@ -74,17 +89,100 @@ local function split_cursor(direction)
core.blink_reset()
end
local function set_cursor(x, y, snap_type)
local line, col = dv():resolve_screen_position(x, y)
doc():set_selection(line, col, line, col)
local function set_cursor(dv, x, y, snap_type)
local line, col = dv:resolve_screen_position(x, y)
dv.doc:set_selection(line, col, line, col)
if snap_type == "word" or snap_type == "lines" then
command.perform("doc:select-" .. snap_type)
end
dv().mouse_selecting = { line, col, snap_type }
dv.mouse_selecting = { line, col, 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(dv)
local line, col = dv.doc:get_selection()
dv.doc:set_selection(line, col)
end,
["doc:cut"] = function()
cut_or_copy(true)
end,
@ -93,219 +191,231 @@ 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()
["doc:undo"] = function(dv)
dv.doc:undo()
end,
["doc:redo"] = function()
doc():redo()
["doc:redo"] = function(dv)
dv.doc:redo()
end,
["doc:paste"] = function()
["doc:paste"] = function(dv)
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
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)
local value, whole_line
for idx, line1, col1, line2, col2 in dv.doc:get_selections() do
if #core.cursor_clipboard_whole_line == (#dv.doc.selections/4) then
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
dv.doc:insert(line1, 1, value:gsub("\r", ""))
if col1 == 1 then
dv.doc:move_to_cursor(idx, #value)
end
else
dv.doc:text_input(value:gsub("\r", ""), idx)
end
end
end,
["doc:newline"] = function()
for idx, line, col in doc():get_selections(false, true) do
local indent = doc().lines[line]:match("^[\t ]*")
["doc:newline"] = function(dv)
for idx, line, col in dv.doc:get_selections(false, true) do
local indent = dv.doc.lines[line]:match("^[\t ]*")
if col <= #indent then
indent = indent:sub(#indent + 2 - col)
end
doc():text_input("\n" .. indent, idx)
end
end,
["doc:newline-below"] = function()
for idx, line in doc():get_selections(false, true) do
local indent = doc().lines[line]:match("^[\t ]*")
doc():insert(line, math.huge, "\n" .. indent)
doc():set_selections(idx, line + 1, math.huge)
end
end,
["doc:newline-above"] = function()
for idx, line in doc():get_selections(false, true) do
local indent = doc().lines[line]:match("^[\t ]*")
doc():insert(line, 1, indent .. "\n")
doc():set_selections(idx, line, math.huge)
end
end,
["doc:delete"] = function()
for idx, line1, col1, line2, col2 in doc():get_selections() do
if line1 == line2 and col1 == col2 and doc().lines[line1]:find("^%s*$", col1) then
doc():remove(line1, col1, line1, math.huge)
-- Remove current line if it contains only whitespace
if dv.doc.lines[line]:match("^%s+$") then
dv.doc:remove(line, 1, line, math.huge)
end
doc():delete_to_cursor(idx, translate.next_char)
dv.doc:text_input("\n" .. indent, idx)
end
end,
["doc:backspace"] = function()
local _, indent_size = doc():get_indent_info()
for idx, line1, col1, line2, col2 in doc():get_selections() do
["doc:newline-below"] = function(dv)
for idx, line in dv.doc:get_selections(false, true) do
local indent = dv.doc.lines[line]:match("^[\t ]*")
dv.doc:insert(line, math.huge, "\n" .. indent)
dv.doc:set_selections(idx, line + 1, math.huge)
end
end,
["doc:newline-above"] = function(dv)
for idx, line in dv.doc:get_selections(false, true) do
local indent = dv.doc.lines[line]:match("^[\t ]*")
dv.doc:insert(line, 1, indent .. "\n")
dv.doc:set_selections(idx, line, math.huge)
end
end,
["doc:delete"] = function(dv)
for idx, line1, col1, line2, col2 in dv.doc:get_selections() do
if line1 == line2 and col1 == col2 and dv.doc.lines[line1]:find("^%s*$", col1) then
dv.doc:remove(line1, col1, line1, math.huge)
end
dv.doc:delete_to_cursor(idx, translate.next_char)
end
end,
["doc:backspace"] = function(dv)
local _, indent_size = dv.doc:get_indent_info()
for idx, line1, col1, line2, col2 in dv.doc:get_selections() do
if line1 == line2 and col1 == col2 then
local text = doc():get_text(line1, 1, line1, col1)
local text = dv.doc:get_text(line1, 1, line1, col1)
if #text >= indent_size and text:find("^ *$") then
doc():delete_to_cursor(idx, 0, -indent_size)
dv.doc:delete_to_cursor(idx, 0, -indent_size)
return
end
end
doc():delete_to_cursor(idx, translate.previous_char)
dv.doc:delete_to_cursor(idx, translate.previous_char)
end
end,
["doc:select-all"] = function()
doc():set_selection(1, 1, math.huge, math.huge)
["doc:select-all"] = function(dv)
dv.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 = #dv.doc.lines
dv.last_col2 = #dv.doc.lines[#dv.doc.lines]
end,
["doc:select-lines"] = function()
for idx, line1, _, line2 in doc():get_selections(true) do
["doc:select-lines"] = function(dv)
for idx, line1, _, line2 in dv.doc:get_selections(true) do
append_line_if_last_line(line2)
doc():set_selections(idx, line1, 1, line2 + 1, 1)
dv.doc:set_selections(idx, line1, 1, line2 + 1, 1)
end
end,
["doc:select-word"] = function()
for idx, line1, col1 in doc():get_selections(true) do
local line1, col1 = translate.start_of_word(doc(), line1, col1)
local line2, col2 = translate.end_of_word(doc(), line1, col1)
doc():set_selections(idx, line2, col2, line1, col1)
["doc:select-word"] = function(dv)
for idx, line1, col1 in dv.doc:get_selections(true) do
local line1, col1 = translate.start_of_word(dv.doc, line1, col1)
local line2, col2 = translate.end_of_word(dv.doc, line1, col1)
dv.doc:set_selections(idx, line2, col2, line1, col1)
end
end,
["doc:join-lines"] = function()
for idx, line1, col1, line2, col2 in doc():get_selections(true) do
["doc:join-lines"] = function(dv)
for idx, line1, col1, line2, col2 in dv.doc:get_selections(true) do
if line1 == line2 then line2 = line2 + 1 end
local text = doc():get_text(line1, 1, line2, math.huge)
local text = dv.doc:get_text(line1, 1, line2, math.huge)
text = text:gsub("(.-)\n[\t ]*", function(x)
return x:find("^%s*$") and x or x .. " "
end)
doc():insert(line1, 1, text)
doc():remove(line1, #text + 1, line2, math.huge)
dv.doc:insert(line1, 1, text)
dv.doc:remove(line1, #text + 1, line2, math.huge)
if line1 ~= line2 or col1 ~= col2 then
doc():set_selections(idx, line1, math.huge)
dv.doc:set_selections(idx, line1, math.huge)
end
end
end,
["doc:indent"] = function()
["doc:indent"] = function(dv)
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
local l1, c1, l2, c2 = doc():indent_text(false, line1, col1, line2, col2)
local l1, c1, l2, c2 = dv.doc:indent_text(false, line1, col1, line2, col2)
if l1 then
doc():set_selections(idx, l1, c1, l2, c2)
dv.doc:set_selections(idx, l1, c1, l2, c2)
end
end
end,
["doc:unindent"] = function()
["doc:unindent"] = function(dv)
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
local l1, c1, l2, c2 = doc():indent_text(true, line1, col1, line2, col2)
local l1, c1, l2, c2 = dv.doc:indent_text(true, line1, col1, line2, col2)
if l1 then
doc():set_selections(idx, l1, c1, l2, c2)
dv.doc:set_selections(idx, l1, c1, l2, c2)
end
end
end,
["doc:duplicate-lines"] = function()
["doc:duplicate-lines"] = function(dv)
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
append_line_if_last_line(line2)
local text = doc():get_text(line1, 1, line2 + 1, 1)
doc():insert(line2 + 1, 1, text)
dv.doc:insert(line2 + 1, 1, text)
local n = line2 - line1 + 1
doc():set_selections(idx, line1 + n, col1, line2 + n, col2)
dv.doc:set_selections(idx, line1 + n, col1, line2 + n, col2)
end
end,
["doc:delete-lines"] = function()
["doc:delete-lines"] = function(dv)
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
append_line_if_last_line(line2)
doc():remove(line1, 1, line2 + 1, 1)
doc():set_selections(idx, line1, col1)
dv.doc:remove(line1, 1, line2 + 1, 1)
dv.doc:set_selections(idx, line1, col1)
end
end,
["doc:move-lines-up"] = function()
["doc:move-lines-up"] = function(dv)
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
append_line_if_last_line(line2)
if line1 > 1 then
local text = doc().lines[line1 - 1]
doc():insert(line2 + 1, 1, text)
doc():remove(line1 - 1, 1, line1, 1)
doc():set_selections(idx, line1 - 1, col1, line2 - 1, col2)
dv.doc:insert(line2 + 1, 1, text)
dv.doc:remove(line1 - 1, 1, line1, 1)
dv.doc:set_selections(idx, line1 - 1, col1, line2 - 1, col2)
end
end
end,
["doc:move-lines-down"] = function()
["doc:move-lines-down"] = function(dv)
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
append_line_if_last_line(line2 + 1)
if line2 < #doc().lines then
local text = doc().lines[line2 + 1]
doc():remove(line2 + 1, 1, line2 + 2, 1)
doc():insert(line1, 1, text)
doc():set_selections(idx, line1 + 1, col1, line2 + 1, col2)
if line2 < #dv.doc.lines then
local text = dv.doc.lines[line2 + 1]
dv.doc:remove(line2 + 1, 1, line2 + 2, 1)
dv.doc:insert(line1, 1, text)
dv.doc:set_selections(idx, line1 + 1, col1, line2 + 1, col2)
end
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(dv)
local comment = dv.doc.syntax.block_comment
if not comment then
if dv.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 = #dv.doc.lines[line2]
end
dv.doc:set_selections(idx, block_comment(comment, line1, col1, line2, col2))
end
end,
["doc:toggle-line-comments"] = function(dv)
local comment = dv.doc.syntax.comment or dv.doc.syntax.block_comment
if comment then
for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do
dv.doc:set_selections(idx, line_comment(comment, line1, col1, line2, col2))
end
end
end,
["doc:upper-case"] = function()
doc():replace(string.upper)
["doc:upper-case"] = function(dv)
dv.doc:replace(string.uupper)
end,
["doc:lower-case"] = function()
doc():replace(string.lower)
["doc:lower-case"] = function(dv)
dv.doc:replace(string.ulower)
end,
["doc:go-to-line"] = function()
local dv = dv()
["doc:go-to-line"] = function(dv)
local items
local function init_items()
if items then return end
@ -317,165 +427,195 @@ local commands = {
end
end
core.command_view:enter("Go To Line", function(text, item)
local line = item and item.line or tonumber(text)
if not line then
core.error("Invalid line number or unmatched string")
return
core.command_view:enter("Go To Line", {
submit = function(text, item)
local line = item and item.line or tonumber(text)
if not line then
core.error("Invalid line number or unmatched string")
return
end
dv.doc:set_selection(line, 1 )
dv:scroll_to_line(line, true)
end,
suggest = function(text)
if not text:find("^%d*$") then
init_items()
return common.fuzzy_match(items, text)
end
end
dv.doc:set_selection(line, 1 )
dv:scroll_to_line(line, true)
end, function(text)
if not text:find("^%d*$") then
init_items()
return common.fuzzy_match(items, text)
end
end)
})
end,
["doc:toggle-line-ending"] = function()
doc().crlf = not doc().crlf
["doc:toggle-line-ending"] = function(dv)
dv.doc.crlf = not dv.doc.crlf
end,
["doc:save-as"] = function()
["doc:save-as"] = function(dv)
local last_doc = core.last_active_view and core.last_active_view.doc
if doc().filename then
core.command_view:set_text(doc().filename)
local text
if dv.doc.filename then
text = dv.doc.filename
elseif last_doc and last_doc.filename then
local dirname, filename = core.last_active_view.doc.abs_filename:match("(.*)[/\\](.+)$")
core.command_view:set_text(core.normalize_to_project_dir(dirname) .. PATHSEP)
text = core.normalize_to_project_dir(dirname) .. PATHSEP
end
core.command_view:enter("Save As", function(filename)
save(common.home_expand(filename))
end, function (text)
return common.home_encode_list(common.path_suggest(common.home_expand(text)))
end)
core.command_view:enter("Save As", {
text = text,
submit = function(filename)
save(common.home_expand(filename))
end,
suggest = function (text)
return common.home_encode_list(common.path_suggest(common.home_expand(text)))
end
})
end,
["doc:save"] = function()
if doc().filename then
["doc:save"] = function(dv)
if dv.doc.filename then
save()
else
command.perform("doc:save-as")
end
end,
["file:rename"] = function()
local old_filename = doc().filename
["doc:reload"] = function(dv)
dv.doc:reload()
end,
["file:rename"] = function(dv)
local old_filename = dv.doc.filename
if not old_filename then
core.error("Cannot rename unsaved doc")
return
end
core.command_view:set_text(old_filename)
core.command_view:enter("Rename", function(filename)
save(common.home_expand(filename))
core.log("Renamed \"%s\" to \"%s\"", old_filename, filename)
if filename ~= old_filename then
os.remove(old_filename)
core.command_view:enter("Rename", {
text = old_filename,
submit = function(filename)
save(common.home_expand(filename))
core.log("Renamed \"%s\" to \"%s\"", old_filename, filename)
if filename ~= old_filename then
os.remove(old_filename)
end
end,
suggest = function (text)
return common.home_encode_list(common.path_suggest(common.home_expand(text)))
end
end, function (text)
return common.home_encode_list(common.path_suggest(common.home_expand(text)))
end)
})
end,
["file:delete"] = function()
local filename = doc().abs_filename
["file:delete"] = function(dv)
local filename = dv.doc.abs_filename
if not filename then
core.error("Cannot remove unsaved doc")
return
end
for i,docview in ipairs(core.get_views_referencing_doc(doc())) do
for i,docview in ipairs(core.get_views_referencing_doc(dv.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(dv, 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")
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")
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 line2, col2 = dv:resolve_screen_position(x, y)
dv.mouse_selecting = { line1, col1, nil }
dv.doc:set_selection(line2, col2, line1, col1)
end,
["doc:create-cursor-previous-line"] = function()
["doc:create-cursor-previous-line"] = function(dv)
split_cursor(-1)
doc():merge_cursors()
dv.doc:merge_cursors()
end,
["doc:create-cursor-next-line"] = function()
["doc:create-cursor-next-line"] = function(dv)
split_cursor(1)
doc():merge_cursors()
dv.doc:merge_cursors()
end
}
command.add(function(x, y)
if x == nil or y == nil or not core.active_view:is(DocView) then return false end
local dv = core.active_view
local x1,y1,x2,y2 = dv.position.x, dv.position.y, dv.position.x + dv.size.x, dv.position.y + dv.size.y
return x >= x1 + dv:get_gutter_width() and x < x2 and y >= y1 and y < y2, dv, x, y
end, {
["doc:set-cursor"] = function(dv, x, y)
set_cursor(dv, x, y, "set")
end,
["doc:set-cursor-word"] = function(dv, x, y)
set_cursor(dv, x, y, "word")
end,
["doc:set-cursor-line"] = function(dv, x, y, clicks)
set_cursor(dv, x, y, "lines")
end,
["doc:split-cursor"] = function(dv, x, y, clicks)
local line, col = dv:resolve_screen_position(x, y)
local removal_target = nil
for idx, line1, col1 in dv.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
dv.doc:remove_selection(removal_target)
else
dv.doc:add_selection(line, col, line, col)
end
dv.mouse_selecting = { line, col, "set" }
end
})
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(dv) dv.doc:move_to(obj[name:gsub("-", "_")], dv) end
commands["doc:select-to-" .. name] = function(dv) dv.doc:select_to(obj[name:gsub("-", "_")], dv) end
commands["doc:delete-to-" .. name] = function(dv) dv.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
commands["doc:move-to-previous-char"] = function(dv)
for idx, line1, col1, line2, col2 in dv.doc:get_selections(true) do
if line1 ~= line2 or col1 ~= col2 then
doc():set_selections(idx, line1, col1)
dv.doc:set_selections(idx, line1, col1)
else
dv.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
commands["doc:move-to-next-char"] = function(dv)
for idx, line1, col1, line2, col2 in dv.doc:get_selections(true) do
if line1 ~= line2 or col1 ~= col2 then
doc():set_selections(idx, line2, col2)
dv.doc:set_selections(idx, line2, col2)
else
dv.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

@ -4,11 +4,13 @@ local common = require "core.common"
command.add(nil, {
["files:create-directory"] = function()
core.command_view:enter("New directory name", function(text)
local success, err, path = common.mkdirp(text)
if not success then
core.error("cannot create directory %q: %s", path, err)
core.command_view:enter("New directory name", {
submit = function(text)
local success, err, path = common.mkdirp(text)
if not success then
core.error("cannot create directory %q: %s", path, err)
end
end
end)
})
end,
})

View File

@ -46,9 +46,12 @@ end
local function insert_unique(t, v)
local n = #t
for i = 1, n do
if t[i] == v then return end
if t[i] == v then
table.remove(t, i)
break
end
end
t[n + 1] = v
table.insert(t, 1, v)
end
@ -58,58 +61,76 @@ local function find(label, search_fn)
local text = last_view.doc:get_text(table.unpack(last_sel))
found_expression = false
core.command_view:set_text(text, true)
core.status_view:show_tooltip(get_find_tooltip())
core.command_view:set_hidden_suggestions()
core.command_view:enter(label, function(text, item)
insert_unique(core.previous_find, text)
core.status_view:remove_tooltip()
if found_expression then
core.command_view:enter(label, {
text = text,
select_text = true,
show_suggestions = false,
submit = function(text, item)
insert_unique(core.previous_find, text)
core.status_view:remove_tooltip()
if found_expression then
last_fn, last_text = search_fn, text
else
core.error("Couldn't find %q", text)
last_view.doc:set_selection(table.unpack(last_sel))
last_view:scroll_to_make_visible(table.unpack(last_sel))
end
end,
suggest = function(text)
update_preview(last_sel, search_fn, text)
last_fn, last_text = search_fn, text
else
core.error("Couldn't find %q", text)
last_view.doc:set_selection(table.unpack(last_sel))
last_view:scroll_to_make_visible(table.unpack(last_sel))
return core.previous_find
end,
cancel = function(explicit)
core.status_view:remove_tooltip()
if explicit then
last_view.doc:set_selection(table.unpack(last_sel))
last_view:scroll_to_make_visible(table.unpack(last_sel))
end
end
end, function(text)
update_preview(last_sel, search_fn, text)
last_fn, last_text = search_fn, text
return core.previous_find
end, function(explicit)
core.status_view:remove_tooltip()
if explicit then
last_view.doc:set_selection(table.unpack(last_sel))
last_view:scroll_to_make_visible(table.unpack(last_sel))
end
end)
})
end
local function replace(kind, default, fn)
core.command_view:set_text(default, true)
core.status_view:show_tooltip(get_find_tooltip())
core.command_view:set_hidden_suggestions()
core.command_view:enter("Find To Replace " .. kind, function(old)
insert_unique(core.previous_find, old)
core.command_view:set_text(old, true)
core.command_view:enter("Find To Replace " .. kind, {
text = default,
select_text = true,
show_suggestions = false,
submit = function(old)
insert_unique(core.previous_find, old)
local s = string.format("Replace %s %q With", kind, old)
core.command_view:set_hidden_suggestions()
core.command_view:enter(s, function(new)
local s = string.format("Replace %s %q With", kind, old)
core.command_view:enter(s, {
text = old,
select_text = true,
show_suggestions = false,
submit = function(new)
core.status_view:remove_tooltip()
insert_unique(core.previous_replace, new)
local results = doc():replace(function(text)
return fn(text, old, new)
end)
local n = 0
for _,v in pairs(results) do
n = n + v
end
core.log("Replaced %d instance(s) of %s %q with %q", n, kind, old, new)
end,
suggest = function() return core.previous_replace end,
cancel = function()
core.status_view:remove_tooltip()
end
})
end,
suggest = function() return core.previous_find end,
cancel = function()
core.status_view:remove_tooltip()
insert_unique(core.previous_replace, new)
local n = doc():replace(function(text)
return fn(text, old, new)
end)
core.log("Replaced %d instance(s) of %s %q with %q", n, kind, old, new)
end, function() return core.previous_replace end, function()
core.status_view:remove_tooltip()
end)
end, function() return core.previous_find end, function()
core.status_view:remove_tooltip()
end)
end
})
end
local function has_selection()
@ -179,7 +200,7 @@ command.add(has_unique_selection, {
["find-replace:select-add-all"] = function() select_add_next(true) end
})
command.add("core.docview", {
command.add("core.docview!", {
["find-replace:find"] = function()
find("Find Text", function(doc, line, col, text, case_sensitive, find_regex, find_reverse)
local opt = { wrap = true, no_case = not case_sensitive, regex = find_regex, reverse = find_reverse }

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

@ -7,13 +7,11 @@ local config = require "core.config"
local t = {
["root:close"] = function()
local node = core.root_view:get_active_node()
["root:close"] = function(node)
node:close_active_view(core.root_view.root_node)
end,
["root:close-or-quit"] = function()
local node = core.root_view:get_active_node()
["root:close-or-quit"] = function(node)
if node and (not node:is_empty() or not node.is_primary_node) then
node:close_active_view(core.root_view.root_node)
else
@ -30,25 +28,22 @@ 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()
["root:switch-to-previous-tab"] = function(node)
local idx = node:get_view_idx(core.active_view)
idx = idx - 1
if idx < 1 then idx = #node.views end
node:set_active_view(node.views[idx])
end,
["root:switch-to-next-tab"] = function()
local node = core.root_view:get_active_node()
["root:switch-to-next-tab"] = function(node)
local idx = node:get_view_idx(core.active_view)
idx = idx + 1
if idx > #node.views then idx = 1 end
node:set_active_view(node.views[idx])
end,
["root:move-tab-left"] = function()
local node = core.root_view:get_active_node()
["root:move-tab-left"] = function(node)
local idx = node:get_view_idx(core.active_view)
if idx > 1 then
table.remove(node.views, idx)
@ -56,24 +51,21 @@ local t = {
end
end,
["root:move-tab-right"] = function()
local node = core.root_view:get_active_node()
["root:move-tab-right"] = function(node)
local idx = node:get_view_idx(core.active_view)
if idx < #node.views then
table.remove(node.views, idx)
table.insert(node.views, idx + 1, core.active_view)
end
end,
["root:shrink"] = function()
local node = core.root_view:get_active_node()
["root:shrink"] = function(node)
local parent = node:get_parent_node(core.root_view.root_node)
local n = (parent.a == node) and -0.1 or 0.1
parent.divider = common.clamp(parent.divider + n, 0.1, 0.9)
end,
["root:grow"] = function()
local node = core.root_view:get_active_node()
["root:grow"] = function(node)
local parent = node:get_parent_node(core.root_view.root_node)
local n = (parent.a == node) and 0.1 or -0.1
parent.divider = common.clamp(parent.divider + n, 0.1, 0.9)
@ -82,8 +74,7 @@ local t = {
for i = 1, 9 do
t["root:switch-to-tab-" .. i] = function()
local node = core.root_view:get_active_node()
t["root:switch-to-tab-" .. i] = function(node)
local view = node.views[i]
if view then
node:set_active_view(view)
@ -93,8 +84,7 @@ end
for _, dir in ipairs { "left", "right", "up", "down" } do
t["root:split-" .. dir] = function()
local node = core.root_view:get_active_node()
t["root:split-" .. dir] = function(node)
local av = node.active_view
node:split(dir)
if av:is(DocView) then
@ -102,8 +92,7 @@ for _, dir in ipairs { "left", "right", "up", "down" } do
end
end
t["root:switch-to-" .. dir] = function()
local node = core.root_view:get_active_node()
t["root:switch-to-" .. dir] = function(node)
local x, y
if dir == "left" or dir == "right" then
y = node.position.y + node.size.y / 2
@ -123,7 +112,7 @@ end
command.add(function()
local node = core.root_view:get_active_node()
local sx, sy = node:get_locked_size()
return not sx and not sy
return not sx and not sy, node
end, t)
command.add(nil, {

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", {
submit = function(text, item)
core.status_view:hide_items(item.name)
end,
suggest = status_view_get_items
})
end,
["status-bar:show-item"] = function()
core.command_view:enter("Status bar item to show", {
submit = function(text, item)
core.status_view:show_items(item.name)
end,
suggest = status_view_get_items
})
end,
["status-bar:reset-items"] = function()
core.status_view:show_items()
end,
})

View File

@ -6,13 +6,16 @@ local DocView = require "core.docview"
local View = require "core.view"
---@class core.commandview.input : core.doc
---@field super core.doc
local SingleLineDoc = Doc:extend()
function SingleLineDoc:insert(line, col, text)
SingleLineDoc.super.insert(self, line, col, text:gsub("\n", ""))
end
---@class core.commandview : core.docview
---@field super core.docview
local CommandView = DocView:extend()
CommandView.context = "application"
@ -21,11 +24,26 @@ local max_suggestions = 10
local noop = function() end
---@class core.commandview.state
---@field submit function
---@field suggest function
---@field cancel function
---@field validate function
---@field text string
---@field select_text boolean
---@field show_suggestions boolean
---@field typeahead boolean
---@field wrap boolean
local default_state = {
submit = noop,
suggest = noop,
cancel = noop,
validate = function() return true end
validate = function() return true end,
text = "",
select_text = false,
show_suggestions = true,
typeahead = true,
wrap = true,
}
@ -34,8 +52,8 @@ function CommandView:new()
self.suggestion_idx = 1
self.suggestions = {}
self.suggestions_height = 0
self.show_suggestions = true
self.last_change_id = 0
self.last_text = ""
self.gutter_width = 0
self.gutter_text_brightness = 0
self.selection_offset = 0
@ -46,8 +64,10 @@ function CommandView:new()
end
---@deprecated
function CommandView:set_hidden_suggestions()
self.show_suggestions = false
core.warn("Using deprecated function CommandView:set_hidden_suggestions")
self.state.show_suggestions = false
end
@ -56,8 +76,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
@ -80,6 +100,7 @@ end
function CommandView:set_text(text, select)
self.last_text = text
self.doc:remove(1, 1, math.huge, math.huge)
self.doc:text_input(text)
if select then
@ -89,9 +110,18 @@ end
function CommandView:move_suggestion_idx(dir)
if self.show_suggestions then
local function overflow_suggestion_idx(n, count)
if count == 0 then return 0 end
if self.state.wrap then
return (n - 1) % count + 1
else
return common.clamp(n, 1, count)
end
end
if self.state.show_suggestions then
local n = self.suggestion_idx + dir
self.suggestion_idx = common.clamp(n, 1, #self.suggestions)
self.suggestion_idx = overflow_suggestion_idx(n, #self.suggestions)
self:complete()
self.last_change_id = self.doc:get_change_id()
else
@ -102,7 +132,7 @@ function CommandView:move_suggestion_idx(dir)
if n == 0 and self.save_suggestion then
self:set_text(self.save_suggestion)
else
self.suggestion_idx = common.clamp(n, 1, #self.suggestions)
self.suggestion_idx = overflow_suggestion_idx(n, #self.suggestions)
self:complete()
end
else
@ -132,21 +162,53 @@ function CommandView:submit()
end
end
function CommandView:enter(text, submit, suggest, cancel, validate)
---@param label string
---@varargs any
---@overload fun(label:string, options: core.commandview.state)
function CommandView:enter(label, ...)
if self.state ~= default_state then
return
end
self.state = {
submit = submit or noop,
suggest = suggest or noop,
cancel = cancel or noop,
validate = validate or function() return true end
}
local options = select(1, ...)
if type(options) ~= "table" then
core.warn("Using CommandView:enter in a deprecated way")
local submit, suggest, cancel, validate = ...
options = {
submit = submit,
suggest = suggest,
cancel = cancel,
validate = validate,
}
end
-- Support deprecated CommandView:set_hidden_suggestions
-- Remove this when set_hidden_suggestions is not supported anymore
if options.show_suggestions == nil then
options.show_suggestions = self.state.show_suggestions
end
self.state = common.merge(default_state, options)
-- We need to keep the text entered with CommandView:set_text to
-- maintain compatibility with deprecated usage, but still allow
-- overwriting with options.text
local old_text = self:get_text()
if old_text ~= "" then
core.warn("Using deprecated function CommandView:set_text")
end
if options.text or options.select_text then
local text = options.text or old_text
self:set_text(text, self.state.select_text)
end
-- Replace with a simple
-- self:set_text(self.state.text, self.state.select_text)
-- once old usage is removed
core.set_active_view(self)
self:update_suggestions()
self.gutter_text_brightness = 100
self.label = text .. ": "
self.label = label .. ": "
end
@ -159,8 +221,13 @@ function CommandView:exit(submitted, inexplicit)
self.doc:reset()
self.suggestions = {}
if not submitted then cancel(not inexplicit) end
self.show_suggestions = true
self.save_suggestion = nil
self.last_text = ""
end
function CommandView:get_line_height()
return math.floor(self:get_font():get_height() * 1.2)
end
@ -198,35 +265,45 @@ function CommandView:update()
-- update suggestions if text has changed
if self.last_change_id ~= self.doc:get_change_id() then
self:update_suggestions()
if self.state.typeahead and self.suggestions[self.suggestion_idx] then
local current_text = self:get_text()
local suggested_text = self.suggestions[self.suggestion_idx].text or ""
if #self.last_text < #current_text and
string.find(suggested_text, current_text, 1, true) == 1 then
self:set_text(suggested_text)
self.doc:set_selection(1, #current_text + 1, 1, math.huge)
end
self.last_text = current_text
end
self.last_change_id = self.doc:get_change_id()
end
-- update gutter text color brightness
self:move_towards("gutter_text_brightness", 0, 0.1)
self:move_towards("gutter_text_brightness", 0, 0.1, "commandview")
-- update gutter width
local dest = self:get_font():get_width(self.label) + style.padding.x
if self.size.y <= 0 then
self.gutter_width = dest
else
self:move_towards("gutter_width", dest)
self:move_towards("gutter_width", dest, nil, "commandview")
end
-- update suggestions box height
local lh = self:get_suggestion_line_height()
local dest = self.show_suggestions and math.min(#self.suggestions, max_suggestions) * lh or 0
self:move_towards("suggestions_height", dest)
local dest = self.state.show_suggestions and math.min(#self.suggestions, max_suggestions) * lh or 0
self:move_towards("suggestions_height", dest, nil, "commandview")
-- update suggestion cursor offset
local dest = math.min(self.suggestion_idx, max_suggestions) * self:get_suggestion_line_height()
self:move_towards("selection_offset", dest)
self:move_towards("selection_offset", dest, nil, "commandview")
-- update size based on whether this is the active_view
local dest = 0
if self == core.active_view then
dest = style.font:get_height() + style.padding.y * 2
end
self:move_towards(self.size, "y", dest)
self:move_towards(self.size, "y", dest, nil, "commandview")
end
@ -243,6 +320,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
@ -262,20 +340,20 @@ local function draw_suggestions_box(self)
end
-- draw suggestion text
local suggestion_offset = math.max(self.suggestion_idx - max_suggestions, 0)
local offset = math.max(self.suggestion_idx - max_suggestions, 0)
local last = math.min(offset + max_suggestions, #self.suggestions)
core.push_clip_rect(rx, ry, rw, rh)
local i = 1 + suggestion_offset
while i <= #self.suggestions do
local first = 1 + offset
for i=first, last do
local item = self.suggestions[i]
local color = (i == self.suggestion_idx) and style.accent or style.text
local y = self.position.y - (i - suggestion_offset) * lh - dh
local y = self.position.y - (i - offset) * lh - dh
common.draw_text(self:get_font(), color, item.text, nil, x, y, 0, lh)
if item.info then
local w = self.size.x - x - style.padding.x
common.draw_text(self:get_font(), style.dim, item.info, "right", x, y, w, lh)
end
i = i + 1
end
core.pop_clip_rect()
end
@ -283,7 +361,7 @@ end
function CommandView:draw()
CommandView.super.draw(self)
if self.show_suggestions then
if self.state.show_suggestions then
core.root_view:defer_draw(draw_suggestions_box, self)
end
end

View File

@ -16,6 +16,21 @@ function common.clamp(n, lo, hi)
end
function common.merge(a, b)
a = type(a) == "table" and a or {}
local t = {}
for k, v in pairs(a) do
t[k] = v
end
if b and type(b) == "table" 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
@ -41,7 +56,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
@ -57,7 +72,7 @@ function common.color(str)
r = (f() or 0)
g = (f() or 0)
b = (f() or 0)
a = (f() or 1) * 0xff
a = (f() or 1) * 0xff
else
error(string.format("bad color string '%s'", str))
end
@ -131,9 +146,29 @@ function common.fuzzy_match_with_recents(haystack, recents, needle)
end
function common.path_suggest(text)
function common.path_suggest(text, root)
if root and root:sub(-1) ~= PATHSEP then
root = root .. PATHSEP
end
local path, name = text:match("^(.-)([^:/\\]*)$")
local files = system.list_dir(path == "" and "." or path) or {}
local clean_dotslash = false
-- ignore root if path is absolute
local is_absolute = common.is_absolute_path(text)
if not is_absolute then
if path == "" then
path = root or "."
clean_dotslash = not root
else
path = (root or "") .. path
end
end
-- Only in Windows allow using both styles of PATHSEP
if (PATHSEP == "\\" and not string.match(path:sub(-1), "[\\/]")) or
(PATHSEP ~= "\\" and path:sub(-1) ~= PATHSEP) then
path = path .. PATHSEP
end
local files = system.list_dir(path) or {}
local res = {}
for _, file in ipairs(files) do
file = path .. file
@ -142,6 +177,19 @@ function common.path_suggest(text)
if info.type == "dir" then
file = file .. PATHSEP
end
if root then
-- remove root part from file path
local s, e = file:find(root, nil, true)
if s == 1 then
file = file:sub(e + 1)
end
elseif clean_dotslash then
-- remove added dot slash
local s, e = file:find("." .. PATHSEP, nil, true)
if s == 1 then
file = file:sub(e + 1)
end
end
if file:lower():find(text:lower(), nil, true) == 1 then
table.insert(res, file)
end
@ -213,19 +261,58 @@ function common.bench(name, fn, ...)
end
function common.serialize(val)
local function serialize(val, pretty, indent_str, escape, sort, limit, level)
local space = pretty and " " or ""
local indent = pretty and string.rep(indent_str, level) or ""
local newline = pretty and "\n" or ""
if type(val) == "string" then
return string.format("%q", val)
local out = string.format("%q", val)
if escape then
out = string.gsub(out, "\\\n", "\\n")
out = string.gsub(out, "\\7", "\\a")
out = string.gsub(out, "\\8", "\\b")
out = string.gsub(out, "\\9", "\\t")
out = string.gsub(out, "\\11", "\\v")
out = string.gsub(out, "\\12", "\\f")
out = string.gsub(out, "\\13", "\\r")
end
return out
elseif type(val) == "table" then
-- early exit
if level >= limit then return tostring(val) end
local next_indent = pretty and (indent .. indent_str) or ""
local t = {}
for k, v in pairs(val) do
table.insert(t, "[" .. common.serialize(k) .. "]=" .. common.serialize(v))
table.insert(t,
next_indent .. "[" ..
serialize(k, pretty, indent_str, escape, sort, limit, level + 1) ..
"]" .. space .. "=" .. space .. serialize(v, pretty, indent_str, escape, sort, limit, level + 1))
end
return "{" .. table.concat(t, ",") .. "}"
if #t == 0 then return "{}" end
if sort then table.sort(t) end
return "{" .. newline .. table.concat(t, "," .. newline) .. newline .. indent .. "}"
end
return tostring(val)
end
-- Serialize `val` into a parsable string.
-- Available options
-- * pretty: enable pretty printing
-- * indent_str: indent to use (" " by default)
-- * escape: use normal escape characters instead of the ones used by string.format("%q", ...)
-- * sort: sort the keys inside tables
-- * limit: limit how deep to serialize
-- * initial_indent: the initial indentation level
function common.serialize(val, opts)
opts = opts or {}
local indent_str = opts.indent_str or " "
local initial_indent = opts.initial_indent or 0
local indent = opts.pretty and string.rep(indent_str, initial_indent) or ""
local limit = (opts.limit or math.huge) + initial_indent
return indent .. serialize(val, opts.pretty, indent_str,
opts.escape, opts.sort, limit, initial_indent)
end
function common.basename(path)
-- a path should never end by / or \ except if it is '/' (unix root) or
@ -287,11 +374,11 @@ end
-- absolute path without . or .. elements.
-- This function exists because on Windows the drive letter returned
-- by system.absolute_path is sometimes with a lower case and sometimes
-- with an upper case to we normalize to upper case.
-- with an upper case so we normalize to upper case.
function common.normalize_volume(filename)
if not filename then return end
if PATHSEP == '\\' then
local drive, rem = filename:match('^([a-zA-Z]:\\)(.*)')
local drive, rem = filename:match('^([a-zA-Z]:\\)(.-)'..PATHSEP..'?$')
if drive then
return drive:upper() .. rem
end
@ -340,6 +427,11 @@ function common.normalize_path(filename)
end
function common.is_absolute_path(path)
return path:sub(1, 1) == PATHSEP or path:match("^(%a):\\") or path:match('^(%w*):')
end
function common.path_belongs_to(filename, path)
return string.find(filename, path .. PATHSEP, 1, true) == 1
end
@ -439,5 +531,6 @@ function common.rm(path, recursively)
return true
end
return common

View File

@ -1,26 +1,37 @@
local config = {}
config.fps = 60
config.max_log_items = 80
config.max_log_items = 800
config.message_timeout = 5
config.mouse_wheel_scroll = 50 * SCALE
config.animate_drag_scroll = false
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.disabled_transitions = {
scroll = false,
commandview = false,
contextmenu = false,
logview = false,
nagbar = false,
tabs = false,
tab_drag = false,
statusbar = false,
}
config.animation_rate = 1.0
config.blink_period = 0.8
config.disable_blink = false
@ -29,12 +40,20 @@ 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
return config

View File

@ -5,11 +5,13 @@ local config = require "core.config"
local keymap = require "core.keymap"
local style = require "core.style"
local Object = require "core.object"
local View = require "core.view"
local border_width = 1
local divider_width = 1
local DIVIDER = {}
---@class core.contextmenu : core.object
local ContextMenu = Object:extend()
ContextMenu.DIVIDER = DIVIDER
@ -20,6 +22,7 @@ function ContextMenu:new()
self.selected = -1
self.height = 0
self.position = { x = 0, y = 0 }
self.current_scale = SCALE
end
local function get_item_size(item)
@ -37,18 +40,10 @@ local function get_item_size(item)
return lw, lh
end
function ContextMenu:register(predicate, items)
if type(predicate) == "string" then
predicate = require(predicate)
end
if type(predicate) == "table" then
local class = predicate
predicate = function() return core.active_view:is(class) end
end
local width, height = 0, 0 --precalculate the size of context menu
for i, item in ipairs(items) do
if item ~= DIVIDER then
local function update_items_size(items, update_binding)
local width, height = 0, 0
for _, item in ipairs(items) do
if update_binding and item ~= DIVIDER then
item.info = keymap.get_binding(item.command)
end
local lw, lh = get_item_size(item)
@ -57,6 +52,11 @@ function ContextMenu:register(predicate, items)
end
width = width + style.padding.x * 2
items.width, items.height = width, height
end
function ContextMenu:register(predicate, items)
predicate = command.generate_predicate(predicate)
update_items_size(items, true)
table.insert(self.itemset, { predicate = predicate, items = items })
end
@ -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,53 +139,73 @@ function ContextMenu:on_selected(item)
end
end
function ContextMenu:on_mouse_pressed(button, x, y, clicks)
local selected = (self.items or {})[self.selected]
local caught = false
local function change_value(value, change)
return value + change
end
self:hide()
if button == "left" then
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)
caught = true
end
end
end
if button == "right" then
caught = self:show(x, y)
function ContextMenu:on_mouse_pressed(button, px, py, clicks)
local caught = false
if self.show_context_menu then
if button == "left" then
local selected = self:get_item_selected()
if selected then
self:on_selected(selected)
end
end
self:hide()
caught = true
else
if button == "right" then
caught = self:show(px, py)
end
end
return caught
end
-- copied from core.docview
function ContextMenu:move_towards(t, k, dest, rate)
if type(t) ~= "table" then
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
t[k] = dest
else
rate = rate or 0.5
if config.fps ~= 60 or config.animation_rate ~= 1 then
local dt = 60 / config.fps
rate = 1 - common.clamp(1 - rate, 1e-8, 1 - 1e-8)^(config.animation_rate * dt)
end
t[k] = common.lerp(val, dest, rate)
end
if val ~= dest then
core.redraw = true
end
end
ContextMenu.move_towards = View.move_towards
function ContextMenu:update()
if self.show_context_menu then
self:move_towards("height", self.items.height)
self:move_towards("height", self.items.height, nil, "contextmenu")
end
end
function ContextMenu:draw()
if not self.show_context_menu then return end
if self.current_scale ~= SCALE then
update_items_size(self.items)
for _, set in ipairs(self.itemset) do
update_items_size(set.items)
end
self.current_scale = SCALE
end
core.root_view:defer_draw(self.draw_context_menu, self)
end

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

@ -0,0 +1,232 @@
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(),
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.
-- Can also be called on individual files, though this should be used sparingly,
-- so as not to run into system limits (like in the autoreload plugin).
function dirwatch:watch(directory, bool)
if bool == false then return self:unwatch(directory) end
local info = system.get_file_info(directory)
if not info then return end
if not self.watched[directory] and not self.scanned[directory] then
if PLATFORM == "Windows" then
if info.type ~= "dir" then return self:scan(directory) end
if not self.windows_watch_top or directory:find(self.windows_watch_top, 1, true) ~= 1 then
-- 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
end
end
self.windows_watch_count = self.windows_watch_count + 1
self.watched[directory] = true
else
local value = self.monitor:watch(directory)
-- 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)
local had_change = false
self.monitor:check(function(id)
had_change = true
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 info = system.get_file_info(directory)
local new_modified = info and info.modified
if old_modified ~= new_modified then
change_callback(directory)
had_change = true
self.scanned[directory] = new_modified
end
end
if system.get_time() - start_time > (scan_time or 0.01) then
coroutine.yield(wait_time or 0.01)
start_time = system.get_time()
end
end
return had_change
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
if n ~= nil then
entries_count = n
end
else
recurse_complete = false
end
end
table.sort(files, compare_file)
for _, f in ipairs(files) do
table.insert(t, f)
end
return t, recurse_complete, entries_count
end
return dirwatch

View File

@ -22,13 +22,21 @@ function Highlighter:new(doc)
else
local max = math.min(self.first_invalid_line + 40, self.max_wanted_line)
local retokenized_from
for i = self.first_invalid_line, max do
local state = (i > 1) and self.lines[i - 1].state
local line = self.lines[i]
if not (line and line.init_state == state and line.text == self.doc.lines[i]) then
retokenized_from = retokenized_from or i
self.lines[i] = self:tokenize_line(i, state)
elseif retokenized_from then
self:update_notify(retokenized_from, i - retokenized_from - 1)
retokenized_from = nil
end
end
if retokenized_from then
self:update_notify(retokenized_from, max - retokenized_from)
end
self.first_invalid_line = max + 1
core.redraw = true
@ -71,6 +79,10 @@ function Highlighter:remove_notify(line, n)
common.splice(self.lines, line, n)
end
function Highlighter:update_notify(line, n)
-- plugins can hook here to be notified that lines have been retokenized
end
function Highlighter:tokenize_line(idx, state)
local res = {}
@ -87,6 +99,7 @@ function Highlighter:get_line(idx)
local prev = self.lines[idx - 1]
line = self:tokenize_line(idx, prev and prev.state)
self.lines[idx] = line
self:update_notify(idx, 0)
end
self.max_wanted_line = math.max(self.max_wanted_line, idx)
return line

View File

@ -5,7 +5,7 @@ local syntax = require "core.syntax"
local config = require "core.config"
local common = require "core.common"
---@class core.doc : core.object
local Doc = Object:extend()
@ -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
@ -80,11 +80,23 @@ function Doc:load(filename)
end
function Doc:reload()
if self.filename then
local sel = { self:get_selection() }
self:load(self.filename)
self:clean()
self:set_selection(table.unpack(sel))
end
end
function Doc:save(filename, abs_filename)
if not filename then
assert(self.filename, "no filename set to default to")
filename = self.filename
abs_filename = self.abs_filename
else
assert(self.filename or abs_filename, "calling save on unnamed doc without absolute path")
end
local fp = assert( io.open(filename, "wb") )
for _, line in ipairs(self.lines) do
@ -94,7 +106,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
@ -135,8 +146,8 @@ end
-- curors can never swap positions; only merge or split, or change their position in cursor
-- order.
function Doc:get_selection(sort)
local idx, line1, col1, line2, col2 = self:get_selections(sort)({ self.selections, sort }, 0)
return line1, col1, line2, col2, sort
local idx, line1, col1, line2, col2, swap = self:get_selections(sort)({ self.selections, sort }, 0)
return line1, col1, line2, col2, swap
end
function Doc:get_selection_text(limit)
@ -172,9 +183,9 @@ end
local function sort_positions(line1, col1, line2, col2)
if line1 > line2 or line1 == line2 and col1 > col2 then
return line2, col2, line1, col1
return line2, col2, line1, col1, true
end
return line1, col1, line2, col2
return line1, col1, line2, col2, false
end
function Doc:set_selections(idx, line1, col1, line2, col2, swap, rm)
@ -197,8 +208,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 +225,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 +371,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 +403,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
@ -443,7 +458,7 @@ end
function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
local old_text = self:get_text(line1, col1, line2, col2)
local new_text, n = fn(old_text)
local new_text, res = fn(old_text)
if old_text ~= new_text then
self:insert(line2, col2, new_text)
self:remove(line1, col1, line2, col2)
@ -452,22 +467,22 @@ function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
self:set_selections(idx, line1, col1, line2, col2)
end
end
return n
return res
end
function Doc:replace(fn)
local has_selection, n = false, 0
local has_selection, results = false, { }
for idx, line1, col1, line2, col2 in self:get_selections(true) do
if line1 ~= line2 or col1 ~= col2 then
n = n + self:replace_cursor(idx, line1, col1, line2, col2, fn)
if line1 ~= line2 or col1 ~= col2 then
results[idx] = self:replace_cursor(idx, line1, col1, line2, col2, fn)
has_selection = true
end
end
if not has_selection then
self:set_selection(table.unpack(self.selections))
n = n + self:replace_cursor(1, 1, 1, #self.lines, #self.lines[#self.lines], fn)
results[1] = self:replace_cursor(1, 1, 1, #self.lines, #self.lines[#self.lines], fn)
end
return n
return results
end
@ -548,10 +563,12 @@ function Doc:indent_text(unindent, line1, col1, line2, col2)
if unindent or has_selection or in_beginning_whitespace then
local l1d, l2d = #self.lines[line1], #self.lines[line2]
for line = line1, line2 do
local e, rnded = self:get_line_indent(self.lines[line], unindent)
self:remove(line, 1, line, (e or 0) + 1)
self:insert(line, 1,
unindent and rnded:sub(1, #rnded - #text) or rnded .. text)
if not has_selection or #self.lines[line] > 1 then -- don't indent empty lines in a selection
local e, rnded = self:get_line_indent(self.lines[line], unindent)
self:remove(line, 1, line, (e or 0) + 1)
self:insert(line, 1,
unindent and rnded:sub(1, #rnded - #text) or rnded .. text)
end
end
l1d, l2d = #self.lines[line1] - l1d, #self.lines[line2] - l2d
if (unindent or in_beginning_whitespace) and not has_selection then

View File

@ -6,7 +6,8 @@ local keymap = require "core.keymap"
local translate = require "core.doc.translate"
local View = require "core.view"
---@class core.docview : core.view
---@field super core.view
local DocView = View:extend()
DocView.context = "session"
@ -29,6 +30,9 @@ DocView.translate = {
end,
["next_page"] = function(doc, line, col, dv)
if line == #doc.lines then
return #doc.lines, #doc.lines[line]
end
local min, max = dv:get_visible_line_range()
return line + (max - min), 1
end,
@ -62,19 +66,22 @@ end
function DocView:try_close(do_close)
if self.doc:is_dirty()
and #core.get_views_referencing_doc(self.doc) == 1 then
core.command_view:enter("Unsaved Changes; Confirm Close", function(_, item)
if item.text:match("^[cC]") then
do_close()
elseif item.text:match("^[sS]") then
self.doc:save()
do_close()
core.command_view:enter("Unsaved Changes; Confirm Close", {
submit = function(_, item)
if item.text:match("^[cC]") then
do_close()
elseif item.text:match("^[sS]") then
self.doc:save()
do_close()
end
end,
suggest = function(text)
local items = {}
if not text:find("^[^cC]") then table.insert(items, "Close Without Saving") end
if not text:find("^[^sS]") then table.insert(items, "Save And Close") end
return items
end
end, function(text)
local items = {}
if not text:find("^[^cC]") then table.insert(items, "Close Without Saving") end
if not text:find("^[^sS]") then table.insert(items, "Save And Close") end
return items
end)
})
else
do_close()
end
@ -121,14 +128,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 +209,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 +220,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,11 +236,10 @@ 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, ...)
if self:scrollbar_overlaps_point(x, y) or self.dragging_scrollbar then
if self.hovered_scrollbar_track or self.dragging_scrollbar then
self.cursor = "arrow"
else
self.cursor = "ibeam"
@ -271,8 +282,8 @@ function DocView:mouse_selection(doc, snap_type, line1, col1, line2, col2)
end
function DocView:on_mouse_released(button)
DocView.super.on_mouse_released(self, button)
function DocView:on_mouse_released(...)
DocView.super.on_mouse_released(self, ...)
self.mouse_selecting = nil
end
@ -284,13 +295,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 +326,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 +342,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 +380,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 +409,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 +427,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 +436,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

@ -2,14 +2,26 @@ local style = require "core.style"
local keymap = require "core.keymap"
local View = require "core.view"
---@class core.emptyview : core.view
---@field super core.view
local EmptyView = View:extend()
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

@ -1,9 +1,25 @@
local core = require "core"
local command = require "core.command"
local config = require "core.config"
local keymap = {}
---@alias keymap.shortcut string
---@alias keymap.command string
---@alias keymap.modkey string
---@alias keymap.pressed boolean
---@alias keymap.map table<keymap.shortcut,keymap.command|keymap.command[]>
---@alias keymap.rmap table<keymap.command, keymap.shortcut|keymap.shortcut[]>
---Pressed status of mod keys.
---@type table<keymap.modkey, keymap.pressed>
keymap.modkeys = {}
---List of commands assigned to a shortcut been the key of the map the shortcut.
---@type keymap.map
keymap.map = {}
---List of shortcuts assigned to a command been the key of the map the command.
---@type keymap.rmap
keymap.reverse_map = {}
local macos = PLATFORM == "Mac OS X"
@ -12,25 +28,88 @@ local mos = PLATFORM == "MORPHOS"
-- Thanks to mathewmariani, taken from his lite-macos github repository.
local modkeys_os = require("core.modkeys-" .. (macos and "macos" or os4 and "os4" or mos and "mos" or "generic"))
---@type table<keymap.modkey, keymap.modkey>
local modkey_map = modkeys_os.map
---@type keymap.modkey[]
local modkeys = modkeys_os.keys
local function key_to_stroke(k)
---Generates a stroke sequence including currently pressed mod keys.
---@param key string
---@return string
local function key_to_stroke(key)
local stroke = ""
for _, mk in ipairs(modkeys) do
if keymap.modkeys[mk] then
stroke = stroke .. mk .. "+"
end
end
return stroke .. k
return stroke .. key
end
---Remove the given value from an array associated to a key in a table.
---@param tbl table<string, string> The table containing the key
---@param k string The key containing the array
---@param v? string The value to remove from the array
local function remove_only(tbl, k, v)
if tbl[k] then
if v then
local j = 0
for i=1, #tbl[k] do
while tbl[k][i + j] == v do
j = j + 1
end
tbl[k][i] = tbl[k][i + j]
end
else
tbl[k] = nil
end
end
end
---Removes from a keymap.map the bindings that are already registered.
---@param map keymap.map
local function remove_duplicates(map)
for stroke, commands in pairs(map) do
if type(commands) == "string" or type(commands) == "function" then
commands = { commands }
end
if keymap.map[stroke] then
for _, registered_cmd in ipairs(keymap.map[stroke]) do
local j = 0
for i=1, #commands do
while commands[i + j] == registered_cmd do
j = j + 1
end
commands[i] = commands[i + j]
end
end
end
if #commands < 1 then
map[stroke] = nil
else
map[stroke] = commands
end
end
end
---Add bindings by replacing commands that were previously assigned to a shortcut.
---@param map keymap.map
function keymap.add_direct(map)
for stroke, commands in pairs(map) do
if type(commands) == "string" then
if type(commands) == "string" or type(commands) == "function" then
commands = { commands }
end
if keymap.map[stroke] then
for _, cmd in ipairs(keymap.map[stroke]) do
remove_only(keymap.reverse_map, cmd, stroke)
end
end
keymap.map[stroke] = commands
for _, cmd in ipairs(commands) do
keymap.reverse_map[cmd] = keymap.reverse_map[cmd] or {}
@ -39,15 +118,23 @@ function keymap.add_direct(map)
end
end
---Adds bindings by appending commands to already registered shortcut or by
---replacing currently assigned commands if overwrite is specified.
---@param map keymap.map
---@param overwrite? boolean
function keymap.add(map, overwrite)
remove_duplicates(map)
for stroke, commands in pairs(map) do
if macos then
stroke = stroke:gsub("%f[%a]ctrl%f[%A]", "cmd")
end
if type(commands) == "string" then
commands = { commands }
end
if overwrite then
if keymap.map[stroke] then
for _, cmd in ipairs(keymap.map[stroke]) do
remove_only(keymap.reverse_map, cmd, stroke)
end
end
keymap.map[stroke] = commands
else
keymap.map[stroke] = keymap.map[stroke] or {}
@ -63,35 +150,34 @@ function keymap.add(map, overwrite)
end
local function remove_only(tbl, k, v)
for key, values in pairs(tbl) do
if key == k then
if v then
for i, value in ipairs(values) do
if value == v then
table.remove(values, i)
end
end
else
tbl[key] = nil
end
break
end
end
end
function keymap.unbind(key, cmd)
remove_only(keymap.map, key, cmd)
remove_only(keymap.reverse_map, cmd, key)
---Unregisters the given shortcut and associated command.
---@param shortcut string
---@param cmd string
function keymap.unbind(shortcut, cmd)
remove_only(keymap.map, shortcut, cmd)
remove_only(keymap.reverse_map, cmd, shortcut)
end
---Returns all the shortcuts associated to a command unpacked for easy assignment.
---@param cmd string
---@return ...
function keymap.get_binding(cmd)
return table.unpack(keymap.reverse_map[cmd] or {})
end
---Returns all the shortcuts associated to a command packed in a table.
---@param cmd string
---@return table<integer, string> | nil shortcuts
function keymap.get_bindings(cmd)
return keymap.reverse_map[cmd]
end
--------------------------------------------------------------------------------
-- Events listening
--------------------------------------------------------------------------------
function keymap.on_key_pressed(k, ...)
local mk = modkey_map[k]
if mk then
@ -102,10 +188,19 @@ function keymap.on_key_pressed(k, ...)
end
else
local stroke = key_to_stroke(k)
local commands, performed = keymap.map[stroke]
local commands, performed = keymap.map[stroke], false
if commands then
for _, cmd in ipairs(commands) do
performed = command.perform(cmd, ...)
if type(cmd) == "function" then
local ok, res = core.try(cmd, ...)
if ok then
performed = not (res == false)
else
performed = true
end
else
performed = command.perform(cmd, ...)
end
if performed then break end
end
return performed
@ -135,6 +230,9 @@ function keymap.on_key_released(k)
end
--------------------------------------------------------------------------------
-- Register default bindings
--------------------------------------------------------------------------------
if macos then
local keymap_macos = require("core.keymap-macos")
keymap_macos(keymap)
@ -148,7 +246,7 @@ keymap.add_direct {
["ctrl+n"] = "core:new-doc",
["ctrl+shift+c"] = "core:change-project-folder",
["ctrl+shift+o"] = "core:open-project-folder",
["ctrl+shift+r"] = "core:restart",
["ctrl+alt+r"] = "core:restart",
["alt+return"] = "core:toggle-fullscreen",
["f11"] = "core:toggle-fullscreen",
@ -217,6 +315,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,7 @@
local core = require "core"
local common = require "core.common"
local config = require "core.config"
local keymap = require "core.keymap"
local style = require "core.style"
local View = require "core.view"
@ -36,12 +38,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 +82,43 @@ 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
if px >= x and py >= y and px < x + w and py < y + h then
hovered = true
self.hovered_item = item
break
end
function LogView:get_scrollable_size()
local _, y_off = self:get_content_offset()
local last_y, last_h = 0, 0
for i, item, x, y, w, h in self:each_item() do
last_y, last_h = y, h
end
if not hovered then self.hovered_item = nil end
if not config.scroll_past_end then
return last_y + last_h - y_off + style.padding.y
end
return last_y + self.size.y - y_off
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)
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
index = i
selected = item
break
end
end
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
@ -109,13 +132,13 @@ function LogView:update()
local expanding = self.expanding[1]
if expanding then
self:move_towards(expanding, "current", expanding.target)
self:move_towards(expanding, "current", expanding.target, nil, "logview")
if expanding.current == expanding.target then
table.remove(self.expanding, 1)
end
end
self:move_towards("yoffset", 0)
self:move_towards("yoffset", 0, nil, "logview")
LogView.super.update(self)
end
@ -131,41 +154,62 @@ 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
x = x + style.padding.x
local iw = math.max(
style.icon_font:get_width(style.log.ERROR.icon),
style.icon_font:get_width(style.log.INFO.icon)
)
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
local tw = style.font:get_width(datestr)
for _, item, x, y, w, h in self:each_item() do
if y + h >= self.position.y and y <= self.position.y + self.size.y then
core.push_clip_rect(x, y, w, h)
x = x + 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())
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
if is_expanded(item) then
y = y + common.round(style.padding.y / 2)
_, y = draw_text_multiline(style.font, item.text, x, y, style.text)
-- timestamps are always 15% of the width
local time = os.date(nil, item.time)
common.draw_text(style.font, style.dim, time, "left", x, y, tw, lh)
x = x + tw + style.padding.x
local at = "at " .. common.home_encode(item.at)
_, y = common.draw_text(style.font, style.dim, at, "left", x, y, w, lh)
w = w - (x - self:get_content_offset())
if item.info then
_, y = draw_text_multiline(style.font, item.info, x, y, style.dim)
if is_expanded(item) then
y = y + common.round(style.padding.y / 2)
_, y = draw_text_multiline(style.font, item.text, x, y, style.text)
local at = "at " .. common.home_encode(item.at)
_, y = common.draw_text(style.font, style.dim, at, "left", x, y, w, lh)
if item.info then
_, y = draw_text_multiline(style.font, item.info, x, y, style.dim)
end
else
local line, has_newline = string.match(item.text, "([^\n]+)(\n?)")
if has_newline ~= "" then
line = line .. " ..."
end
_, y = common.draw_text(style.font, style.text, line, "left", x, y, w, lh)
end
else
local line, has_newline = string.match(item.text, "([^\n]+)(\n?)")
if has_newline ~= "" then
line = line .. " ..."
end
_, y = common.draw_text(style.font, style.text, line, "left", x, y, w, lh)
core.pop_clip_rect()
end
end
LogView.super.draw_scrollbar(self)
end

View File

@ -11,13 +11,19 @@ local UNDERLINE_MARGIN = common.round(1 * SCALE)
local noop = function() end
---@class core.nagview : core.view
---@field super core.view
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 +52,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 +87,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 +100,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 +111,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 +167,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(), nil, "nagbar")
self:move_towards(self, "underline_progress", 1, nil, "nagbar")
else
self:move_towards(self, "show_height", 0, nil, "nagbar")
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 +232,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 +253,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 +287,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

@ -6,6 +6,7 @@ local Object = require "core.object"
local EmptyView = require "core.emptyview"
local View = require "core.view"
---@class core.node : core.object
local Node = Object:extend()
function Node:new(type)
@ -51,6 +52,15 @@ function Node:on_mouse_released(...)
end
function Node:on_mouse_left()
if self.type == "leaf" then
self.active_view:on_mouse_left()
else
self:propagate("on_mouse_left")
end
end
function Node:consume(node)
for k, _ in pairs(self) do self[k] = nil end
for k, v in pairs(node) do self[k] = v end
@ -160,8 +170,12 @@ end
function Node:set_active_view(view)
assert(self.type == "leaf", "Tried to set active view on non-leaf node")
local last_active_view = self.active_view
self.active_view = view
core.set_active_view(view)
if last_active_view and last_active_view ~= view then
last_active_view:on_mouse_left()
end
end
@ -260,8 +274,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
@ -468,59 +482,67 @@ function Node:update()
end
self:tab_hovered_update(self.hovered.x, self.hovered.y)
local tab_width = self:target_tab_width()
self:move_towards("tab_shift", tab_width * (self.tab_offset - 1))
self:move_towards("tab_width", tab_width)
self:move_towards("tab_shift", tab_width * (self.tab_offset - 1), nil, "tabs")
self:move_towards("tab_width", tab_width, nil, "tabs")
else
self.a:update()
self.b: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 +569,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 +710,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

@ -1,11 +1,12 @@
---@class core.object
---@field super core.object
local Object = {}
Object.__index = Object
---Can be overrided by child objects to implement a constructor.
function Object:new() end
function Object:new()
end
---@return core.object
function Object:extend()
local cls = {}
for k, v in pairs(self) do
@ -19,8 +20,17 @@ function Object:extend()
return cls
end
---Check if the object is strictly of the given type.
---@param T any
---@return boolean
function Object:is(T)
return getmetatable(self) == T
end
---Check if the object inherits from the given type.
---@param T any
---@return boolean
function Object:extends(T)
local mt = getmetatable(self)
while mt do
if mt == T then
@ -31,12 +41,14 @@ function Object:is(T)
return false
end
---Metamethod to get a string representation of an object.
---@return string
function Object:__tostring()
return "Object"
end
---Methamethod to allow using the object call as a constructor.
---@return core.object
function Object:__call(...)
local obj = setmetatable({}, self)
obj:new(...)

View File

@ -5,8 +5,9 @@ regex.__index = function(table, key) return regex[key]; end
regex.match = function(pattern_string, string, offset, options)
local pattern = type(pattern_string) == "table" and
pattern_string or regex.compile(pattern_string)
local s, e = regex.cmatch(pattern, string, offset or 1, options or 0)
return s, e and e - 1
local res = { regex.cmatch(pattern, string, offset or 1, options or 0) }
res[2] = res[2] and res[2] - 1
return table.unpack(res)
end
-- Will iterate back through any UTF-8 bytes so that we don't replace bits

View File

@ -5,7 +5,10 @@ local Node = require "core.node"
local View = require "core.view"
local DocView = require "core.docview"
---@class core.rootview : core.view
---@field super core.view
---@field root_node core.node
---@field mouse core.view.position
local RootView = View:extend()
function RootView:new()
@ -29,11 +32,15 @@ function RootView:defer_draw(fn, ...)
end
---@return core.node
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
---@return core.node
local function get_primary_node(node)
if node.is_primary_node then
return node
@ -44,8 +51,10 @@ local function get_primary_node(node)
end
---@return core.node
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.")
@ -56,11 +65,14 @@ function RootView:get_active_node_default()
end
---@return core.node
function RootView:get_primary_node()
return get_primary_node(self.root_node)
end
---@param node core.node
---@return core.node
local function select_next_primary_node(node)
if node.is_primary_node then return end
if node.type ~= "leaf" then
@ -74,11 +86,14 @@ local function select_next_primary_node(node)
end
---@return core.node
function RootView:select_next_primary_node()
return select_next_primary_node(self.root_node)
end
---@param doc core.doc
---@return core.docview
function RootView:open_doc(doc)
local node = self:get_active_node_default()
for i, view in ipairs(node.views) do
@ -95,17 +110,27 @@ function RootView:open_doc(doc)
end
---@param keep_active boolean
function RootView:close_all_docviews(keep_active)
self.root_node:close_all_docviews(keep_active)
end
-- Function to intercept mouse pressed events on the active view.
-- Do nothing by default.
---Function to intercept mouse pressed events on the active view.
---Do nothing by default.
---@param button core.view.mousebutton
---@param x number
---@param y number
---@param clicks integer
function RootView.on_view_mouse_pressed(button, x, y, clicks)
end
---@param button core.view.mousebutton
---@param x number
---@param y number
---@param clicks integer
---@return boolean
function RootView:on_mouse_pressed(button, x, y, clicks)
local div = self.root_node:get_divider_overlapping_point(x, y)
local node = self.root_node:get_child_overlapping_point(x, y)
@ -159,6 +184,9 @@ function RootView:set_show_overlay(overlay, status)
end
---@param button core.view.mousebutton
---@param x number
---@param y number
function RootView:on_mouse_released(button, x, y, ...)
if self.dragged_divider then
self.dragged_divider = nil
@ -217,6 +245,10 @@ local function resize_child_node(node, axis, value, delta)
end
---@param x number
---@param y number
---@param dx number
---@param dy number
function RootView:on_mouse_moved(x, y, dx, dy)
if core.active_view == core.nag_view then
core.request_cursor("arrow")
@ -253,8 +285,13 @@ function RootView:on_mouse_moved(x, y, dx, dy)
self.root_node:on_mouse_moved(x, y, dx, dy)
local last_overlapping_node = self.overlapping_node
self.overlapping_node = self.root_node:get_child_overlapping_point(x, y)
if last_overlapping_node and last_overlapping_node ~= self.overlapping_node then
last_overlapping_node:on_mouse_left()
end
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 +306,23 @@ function RootView:on_mouse_moved(x, y, dx, dy)
end
function RootView:on_mouse_left()
if self.overlapping_node then
self.overlapping_node:on_mouse_left()
end
end
---@param filename string
---@param x number
---@param y number
---@return boolean
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)
@ -288,12 +342,12 @@ end
function RootView:interpolate_drag_overlay(overlay)
self:move_towards(overlay, "x", overlay.to.x)
self:move_towards(overlay, "y", overlay.to.y)
self:move_towards(overlay, "w", overlay.to.w)
self:move_towards(overlay, "h", overlay.to.h)
self:move_towards(overlay, "x", overlay.to.x, nil, "tab_drag")
self:move_towards(overlay, "y", overlay.to.y, nil, "tab_drag")
self:move_towards(overlay, "w", overlay.to.w, nil, "tab_drag")
self:move_towards(overlay, "h", overlay.to.h, nil, "tab_drag")
self:move_towards(overlay, "opacity", overlay.visible and 100 or 0)
self:move_towards(overlay, "opacity", overlay.visible and 100 or 0, nil, "tab_drag")
overlay.color[4] = overlay.base_color[4] * overlay.opacity / 100
end
@ -381,8 +435,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 = "2.0.3r3"
MOD_VERSION = "2"
VERSION = "2.1.0r1"
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,16 +12,26 @@ 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 .. '/?.lua;'
package.path = DATADIR .. '/?/init.lua;' .. package.path
package.path = USERDIR .. '/?.lua;' .. package.path
package.path = USERDIR .. '/?/init.lua;' .. package.path
local dynamic_suffix = PLATFORM == "Mac OS X" and 'lib' or (PLATFORM == "Windows" and 'dll' or 'so')
package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ";" .. USERDIR .. '/?.' .. dynamic_suffix
local suffix = PLATFORM == "Mac OS X" and 'lib' or (PLATFORM == "Windows" and 'dll' or 'so')
package.cpath =
USERDIR .. '/?.' .. ARCH .. "." .. suffix .. ";" ..
USERDIR .. '/?/init.' .. ARCH .. "." .. suffix .. ";" ..
USERDIR .. '/?.' .. suffix .. ";" ..
USERDIR .. '/?/init.' .. suffix .. ";" ..
DATADIR .. '/?.' .. ARCH .. "." .. suffix .. ";" ..
DATADIR .. '/?/init.' .. ARCH .. "." .. suffix .. ";" ..
DATADIR .. '/?.' .. suffix .. ";" ..
DATADIR .. '/?/init.' .. suffix .. ";"
package.native_plugins = {}
package.searchers = { package.searchers[1], package.searchers[2], function(modname)
local path = package.searchpath(modname, package.cpath)
@ -32,3 +42,16 @@ end }
table.pack = table.pack or pack or function(...) return {...} end
table.unpack = table.unpack or unpack
bit32 = bit32 or require "core.bit"
require "core.utf8string"
-- Because AppImages change the working directory before running the executable,
-- we need to change it back to the original one.
-- https://github.com/AppImage/AppImageKit/issues/172
-- https://github.com/AppImage/AppImageKit/pull/191
local appimage_owd = os.getenv("OWD")
if os.getenv("APPIMAGE") and appimage_owd then
system.chdir(appimage_owd)
end

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@ local style = {}
style.padding = { x = common.round(14 * SCALE), y = common.round(7 * SCALE) }
style.divider_size = common.round(1 * SCALE)
style.scrollbar_size = common.round(4 * SCALE)
style.expanded_scrollbar_size = common.round(12 * SCALE)
style.caret_width = common.round(2 * SCALE)
style.tab_width = common.round(170 * SCALE)
@ -27,43 +28,7 @@ style.icon_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 16 * SCALE,
style.icon_big_font = style.icon_font:copy(23 * SCALE)
style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 15 * SCALE)
style.background = { common.color "#2e2e32" } -- Docview
style.background2 = { common.color "#252529" } -- Treeview
style.background3 = { common.color "#252529" } -- Command view
style.text = { common.color "#97979c" }
style.caret = { common.color "#93DDFA" }
style.accent = { common.color "#e1e1e6" }
-- style.dim - text color for nonactive tabs, tabs divider, prefix in log and
-- search result, hotkeys for context menu and command view
style.dim = { common.color "#525257" }
style.divider = { common.color "#202024" } -- Line between nodes
style.selection = { common.color "#48484f" }
style.line_number = { common.color "#525259" }
style.line_number2 = { common.color "#83838f" } -- With cursor
style.line_highlight = { common.color "#343438" }
style.scrollbar = { common.color "#414146" }
style.scrollbar2 = { common.color "#4b4b52" } -- Hovered
style.nagbar = { common.color "#FF0000" }
style.nagbar_text = { common.color "#FFFFFF" }
style.nagbar_dim = { common.color "rgba(0, 0, 0, 0.45)" }
style.drag_overlay = { common.color "rgba(255,255,255,0.1)" }
style.drag_overlay_tab = { common.color "#93DDFA" }
style.good = { common.color "#72b886" }
style.warn = { common.color "#FFA94D" }
style.error = { common.color "#FF3333" }
style.modified = { common.color "#1c7c9c" }
style.syntax = {}
style.syntax["normal"] = { common.color "#e1e1e6" }
style.syntax["symbol"] = { common.color "#e1e1e6" }
style.syntax["comment"] = { common.color "#676b6f" }
style.syntax["keyword"] = { common.color "#E58AC9" } -- local function end if case
style.syntax["keyword2"] = { common.color "#F77483" } -- self int float
style.syntax["number"] = { common.color "#FFA94D" }
style.syntax["literal"] = { common.color "#FFA94D" } -- true false nil
style.syntax["string"] = { common.color "#f7c95c" }
style.syntax["operator"] = { common.color "#93DDFA" } -- = + - / < >
style.syntax["function"] = { common.color "#93DDFA" }
-- This can be used to override fonts per syntax group.
-- The syntax highlighter will take existing values from this table and
@ -72,5 +37,7 @@ 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 = {}
return style

View File

@ -7,6 +7,24 @@ local plain_text_syntax = { name = "Plain Text", patterns = {}, symbols = {} }
function syntax.add(t)
if type(t.space_handling) ~= "boolean" then t.space_handling = true end
if t.patterns then
-- the rule %s+ gives us a performance gain for the tokenizer in lines with
-- long amounts of consecutive spaces, can be disabled by plugins where it
-- causes conflicts by declaring the table property: space_handling = false
if t.space_handling then
table.insert(t.patterns, { pattern = "%s+", type = "normal" })
end
-- this rule gives us additional performance gain by matching every word
-- that was not matched by the syntax patterns as a single token, preventing
-- the tokenizer from iterating over each character individually which is a
-- lot slower since iteration occurs in lua instead of C and adding to that
-- it will also try to match every pattern to a single char (same as spaces)
table.insert(t.patterns, { pattern = "%w+%f[%s]", type = "normal" })
end
table.insert(syntax.items, t)
end

View File

@ -17,6 +17,8 @@ local title_commands = {
{symbol = "X", action = function() core.quit() end},
}
---@class core.titleview : core.view
---@field super core.view
local TitleView = View:extend()
local function title_view_height()

View File

@ -1,12 +1,15 @@
local core = require "core"
local syntax = require "core.syntax"
local common = require "core.common"
local tokenizer = {}
local bad_patterns = {}
local function push_token(t, type, text)
type = type or "normal"
local prev_type = t[#t-1]
local prev_text = t[#t]
if prev_type and (prev_type == type or prev_text:find("^%s*$")) then
if prev_type and (prev_type == type or prev_text:ufind("^%s*$")) then
t[#t-1] = type
t[#t] = prev_text .. text
else
@ -38,12 +41,12 @@ local function push_tokens(t, syn, pattern, full_text, find_results)
local fin = find_results[i + 1] - 1
local type = pattern.type[i - 2]
-- ↑ (i - 2) to convert from [3; n] to [1; n]
local text = full_text:sub(start, fin)
local text = full_text:usub(start, fin)
push_token(t, syn.symbols[text] or type, text)
end
else
local start, fin = find_results[1], find_results[2]
local text = full_text:sub(start, fin)
local text = full_text:usub(start, fin)
push_token(t, syn.symbols[text] or pattern.type, text)
end
end
@ -52,12 +55,12 @@ end
-- State is a 32-bit number that is four separate bytes, illustrating how many
-- differnet delimiters we have open, and which subsyntaxes we have active.
-- At most, there are 3 subsyntaxes active at the same time. Beyond that,
-- does not support further highlighting.
-- does not support further highlighting.
-- You can think of it as a maximum 4 integer (0-255) stack. It always has
-- 1 integer in it. Calling `push_subsyntax` increases the stack depth. Calling
-- `pop_subsyntax` decreases it. The integers represent the index of a pattern
-- that we're following in the syntax. The top of the stack can be any valid
-- that we're following in the syntax. The top of the stack can be any valid
-- pattern index, any integer lower in the stack must represent a pattern that
-- specifies a subsyntax.
@ -92,6 +95,19 @@ local function retrieve_syntax_state(incoming_syntax, state)
return current_syntax, subsyntax_info, current_pattern_idx, current_level
end
local function report_bad_pattern(log_fn, syntax, pattern_idx, msg, ...)
if not bad_patterns[syntax] then
bad_patterns[syntax] = { }
end
if bad_patterns[syntax][pattern_idx] then return end
bad_patterns[syntax][pattern_idx] = true
log_fn("Malformed pattern #%d in %s language plugin. " .. msg,
pattern_idx, syntax.name or "unnamed", ...)
end
---@param incoming_syntax table
---@param text string
---@param state integer
function tokenizer.tokenize(incoming_syntax, text, state)
local res = {}
local i = 1
@ -102,22 +118,22 @@ function tokenizer.tokenize(incoming_syntax, text, state)
state = state or 0
-- incoming_syntax : the parent syntax of the file.
-- state : a 32-bit number representing syntax state (see above)
-- state : a 32-bit number representing syntax state (see above)
-- current_syntax : the syntax we're currently in.
-- subsyntax_info : info about the delimiters of this subsyntax.
-- current_pattern_idx: the index of the pattern we're on for this syntax.
-- current_level : how many subsyntaxes deep we are.
local current_syntax, subsyntax_info, current_pattern_idx, current_level =
retrieve_syntax_state(incoming_syntax, state)
-- Should be used to set the state variable. Don't modify it directly.
local function set_subsyntax_pattern_idx(pattern_idx)
current_pattern_idx = pattern_idx
state = bit32.replace(state, pattern_idx, current_level*8, 8)
end
local function push_subsyntax(entering_syntax, pattern_idx)
set_subsyntax_pattern_idx(pattern_idx)
current_level = current_level + 1
@ -126,127 +142,182 @@ function tokenizer.tokenize(incoming_syntax, text, state)
entering_syntax.syntax or syntax.get(entering_syntax.syntax)
current_pattern_idx = 0
end
local function pop_subsyntax()
set_subsyntax_pattern_idx(0)
current_level = current_level - 1
set_subsyntax_pattern_idx(0)
current_syntax, subsyntax_info, current_pattern_idx, current_level =
current_syntax, subsyntax_info, current_pattern_idx, current_level =
retrieve_syntax_state(incoming_syntax, state)
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:umatch("^%^") 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:usub(2)
else
p.pattern = p.pattern and code:usub(2)
p.regex = p.regex and code:usub(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
-- 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
-- If the pattern contained '^', allow matching only the whole line
if p.whole_line[p_idx] and next > 1 then
return
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) }
if res[1] and close and target[3] then
local count = 0
for i = res[1] - 1, 1, -1 do
if text:byte(i) ~= target[3]:byte() then break end
count = count + 1
res = p.pattern and { text:ufind((at_start or p.whole_line[p_idx]) and "^" .. code or code, next) }
or { regex.match(code, text, text:ucharpos(next), (at_start or p.whole_line[p_idx]) and regex.ANCHORED or 0) }
if p.regex and #res > 0 then -- set correct utf8 len for regex result
local char_pos_1 = string.ulen(text:sub(1, res[1]))
local char_pos_2 = char_pos_1 + string.ulen(text:sub(res[1], res[2])) - 1
-- `regex.match` returns group results as a series of `begin, end`
-- we only want `begin`s
if #res >= 3 then
res[3] = char_pos_1 + string.ulen(text:sub(res[1], res[3])) - 1
end
for i=1,(#res-3) do
local curr = i + 3
local from = i * 2 + 3
if from < #res then
res[curr] = char_pos_1 + string.ulen(text:sub(res[1], res[from])) - 1
else
res[curr] = nil
end
end
res[1] = char_pos_1
res[2] = char_pos_2
end
if res[1] and target[3] then
-- Check to see if the escaped character is there,
-- and if it is not itself escaped.
if count % 2 == 0 then break end
local count = 0
for i = res[1] - 1, 1, -1 do
if text:ubyte(i) ~= target[3]:ubyte() then break end
count = count + 1
end
if count % 2 == 0 then
-- The match is not escaped, so confirm it
break
elseif not close then
-- The *open* match is escaped, so avoid it
return
end
end
until not res[1] or not close or not target[3]
return table.unpack(res)
end
while i <= #text do
-- continue trying to match the end pattern of a pair if we have a state set
if current_pattern_idx > 0 then
local p = current_syntax.patterns[current_pattern_idx]
local s, e = find_text(text, p, i, false, true)
local cont = true
-- If we're in subsyntax mode, always check to see if we end our syntax
-- first, before the found delimeter, as ending the subsyntax takes
-- precedence over ending the delimiter in the subsyntax.
if subsyntax_info then
local ss, se = find_text(text, subsyntax_info, i, false, true)
-- If we find that we end the subsyntax before the
-- delimiter, push the token, and signal we shouldn't
-- treat the bit after as a token to be normally parsed
-- (as it's the syntax delimiter).
if ss and (s == nil or ss < s) then
push_token(res, p.type, text:sub(i, ss - 1))
i = ss
cont = false
local text_len = text:ulen()
if text_len ~= nil then
while i <= text_len do
-- continue trying to match the end pattern of a pair if we have a state set
if current_pattern_idx > 0 then
local p = current_syntax.patterns[current_pattern_idx]
local s, e = find_text(text, p, i, false, true)
local cont = true
-- If we're in subsyntax mode, always check to see if we end our syntax
-- first, before the found delimeter, as ending the subsyntax takes
-- precedence over ending the delimiter in the subsyntax.
if subsyntax_info then
local ss, se = find_text(text, subsyntax_info, i, false, true)
-- If we find that we end the subsyntax before the
-- delimiter, push the token, and signal we shouldn't
-- treat the bit after as a token to be normally parsed
-- (as it's the syntax delimiter).
if ss and (s == nil or ss < s) then
push_token(res, p.type, text:usub(i, ss - 1))
i = ss
cont = false
end
end
-- If we don't have any concerns about syntax delimiters,
-- continue on as normal.
if cont then
if s then
push_token(res, p.type, text:usub(i, e))
set_subsyntax_pattern_idx(0)
i = e + 1
else
push_token(res, p.type, text:usub(i))
break
end
end
end
-- If we don't have any concerns about syntax delimiters,
-- continue on as normal.
if cont then
-- General end of syntax check. Applies in the case where
-- we're ending early in the middle of a delimiter, or
-- just normally, upon finding a token.
if subsyntax_info then
local s, e = find_text(text, subsyntax_info, i, true, true)
if s then
push_token(res, p.type, text:sub(i, e))
set_subsyntax_pattern_idx(0)
push_token(res, subsyntax_info.type, text:usub(i, e))
-- On finding unescaped delimiter, pop it.
pop_subsyntax()
i = e + 1
else
push_token(res, p.type, text:sub(i))
end
end
-- find matching pattern
local matched = false
for n, p in ipairs(current_syntax.patterns) do
local find_results = { find_text(text, p, i, true, false) }
if find_results[1] then
local type_is_table = type(p.type) == "table"
local n_types = type_is_table and #p.type or 1
if #find_results == 2 and type_is_table then
report_bad_pattern(core.warn, current_syntax, n,
"Token type is a table, but a string was expected.")
p.type = p.type[1]
elseif #find_results - 1 > n_types then
report_bad_pattern(core.error, current_syntax, n,
"Not enough token types: got %d needed %d.", n_types, #find_results - 1)
elseif #find_results - 1 < n_types then
report_bad_pattern(core.warn, current_syntax, n,
"Too many token types: got %d needed %d.", n_types, #find_results - 1)
end
-- matched pattern; make and add tokens
push_tokens(res, current_syntax, p, text, find_results)
-- update state if this was a start|end pattern pair
if type(p.pattern or p.regex) == "table" then
-- If we have a subsyntax, push that onto the subsyntax stack.
if p.syntax then
push_subsyntax(p, n)
else
set_subsyntax_pattern_idx(n)
end
end
-- move cursor past this token
i = find_results[2] + 1
matched = true
break
end
end
end
-- General end of syntax check. Applies in the case where
-- we're ending early in the middle of a delimiter, or
-- just normally, upon finding a token.
if subsyntax_info then
local s, e = find_text(text, subsyntax_info, i, true, true)
if s then
push_token(res, subsyntax_info.type, text:sub(i, e))
-- On finding unescaped delimiter, pop it.
pop_subsyntax()
i = e + 1
end
end
-- find matching pattern
local matched = false
for n, p in ipairs(current_syntax.patterns) do
local find_results = { find_text(text, p, i, true, false) }
if find_results[1] then
-- matched pattern; make and add tokens
push_tokens(res, current_syntax, p, text, find_results)
-- update state if this was a start|end pattern pair
if type(p.pattern or p.regex) == "table" then
-- If we have a subsyntax, push that onto the subsyntax stack.
if p.syntax then
push_subsyntax(p, n)
else
set_subsyntax_pattern_idx(n)
end
end
-- move cursor past this token
i = find_results[2] + 1
matched = true
break
-- consume character if we didn't match
if not matched then
push_token(res, "normal", text:usub(i, i))
i = i + 1
end
end
-- consume character if we didn't match
if not matched then
local n = 0
-- reach the next character
while text:byte(i + n + 1) and common.is_utf8_cont(text, i + n + 1) do
n = n + 1
end
push_token(res, "normal", text:sub(i, i + n))
i = i + n + 1
end
end
return res, state
end

32
data/core/utf8string.lua Normal file
View File

@ -0,0 +1,32 @@
--------------------------------------------------------------------------------
-- inject utf8 functions to strings
--------------------------------------------------------------------------------
local utf8 = require "utf8extra"
string.ubyte = utf8.byte
string.uchar = utf8.char
string.ufind = utf8.find
string.ugmatch = utf8.gmatch
string.ugsub = utf8.gsub
string.ulen = utf8.len
string.ulower = utf8.lower
string.umatch = utf8.match
string.ureverse = utf8.reverse
string.usub = utf8.sub
string.uupper = utf8.upper
string.uescape = utf8.escape
string.ucharpos = utf8.charpos
string.unext = utf8.next
string.uinsert = utf8.insert
string.uremove = utf8.remove
string.uwidth = utf8.width
string.uwidthindex = utf8.widthindex
string.utitle = utf8.title
string.ufold = utf8.fold
string.uncasecmp = utf8.ncasecmp
string.uoffset = utf8.offset
string.ucodepoint = utf8.codepoint
string.ucodes = utf8.codes

View File

@ -4,7 +4,51 @@ local style = require "core.style"
local common = require "core.common"
local Object = require "core.object"
---@class core.view.position
---@field x number
---@field y number
---@class core.view.scroll
---@field x number
---@field y number
---@field to core.view.position
---@class core.view.thumbtrack
---@field thumb number
---@field track number
---@class core.view.thumbtrackwidth
---@field thumb number
---@field track number
---@field to core.view.thumbtrack
---@class core.view.scrollbar
---@field x core.view.thumbtrack
---@field y core.view.thumbtrack
---@field w core.view.thumbtrackwidth
---@field h core.view.thumbtrack
---@class core.view.increment
---@field value number
---@field to number
---@alias core.view.cursor "'arrow'" | "'ibeam'" | "'sizeh'" | "'sizev'" | "'hand'"
---@alias core.view.mousebutton "'left'" | "'right'"
---@alias core.view.context "'application'" | "'session'"
---Base view.
---@class core.view : core.object
---@field context core.view.context
---@field super core.object
---@field position core.view.position
---@field size core.view.position
---@field scroll core.view.scroll
---@field cursor core.view.cursor
---@field scrollable boolean
---@field scrollbar core.view.scrollbar
---@field scrollbar_alpha core.view.increment
local View = Object:extend()
-- context can be "application" or "session". The instance of objects
@ -18,14 +62,22 @@ function View:new()
self.scroll = { x = 0, y = 0, to = { x = 0, y = 0 } }
self.cursor = "arrow"
self.scrollable = false
self.scrollbar = {
x = { thumb = 0, track = 0 },
y = { thumb = 0, track = 0 },
w = { thumb = 0, track = 0, to = { thumb = 0, track = 0 } },
h = { thumb = 0, track = 0 },
}
self.scrollbar_alpha = { value = 0, to = 0 }
end
function View:move_towards(t, k, dest, rate)
function View:move_towards(t, k, dest, rate, name)
if type(t) ~= "table" then
return self:move_towards(self, t, k, dest, rate)
return self:move_towards(self, t, k, dest, rate, name)
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 or config.disabled_transitions[name] then
t[k] = dest
else
rate = rate or 0.5
@ -35,7 +87,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
@ -46,62 +98,146 @@ function View:try_close(do_close)
end
---@return string
function View:get_name()
return "---"
end
---@return number
function View:get_scrollable_size()
return math.huge
end
---@return number x
---@return number y
---@return number width
---@return number height
function View:get_scrollbar_track_rect()
local sz = self:get_scrollable_size()
if sz <= self.size.y or sz == math.huge then
return 0, 0, 0, 0
end
local width = style.scrollbar_size
if self.hovered_scrollbar_track or self.dragging_scrollbar then
width = style.expanded_scrollbar_size
end
return
self.position.x + self.size.x - width,
self.position.y,
width,
self.size.y
end
---@return number x
---@return number y
---@return number width
---@return number height
function View:get_scrollbar_rect()
local sz = self:get_scrollable_size()
if sz <= self.size.y or sz == math.huge then
return 0, 0, 0, 0
end
local h = math.max(20, self.size.y * self.size.y / sz)
local width = style.scrollbar_size
if self.hovered_scrollbar_track or self.dragging_scrollbar then
width = style.expanded_scrollbar_size
end
return
self.position.x + self.size.x - style.scrollbar_size,
self.position.x + self.size.x - width,
self.position.y + self.scroll.y * (self.size.y - h) / (sz - self.size.y),
style.scrollbar_size,
width,
h
end
---@param x number
---@param y number
---@return boolean
function View:scrollbar_overlaps_point(x, y)
local sx, sy, sw, sh = self:get_scrollbar_rect()
return x >= sx - sw * 3 and x < sx + sw and y >= sy and y < sy + sh
return x >= sx - style.scrollbar_size * 3 and x < sx + sw and y > sy and y <= sy + sh
end
---@param x number
---@param y number
---@return boolean
function View:scrollbar_track_overlaps_point(x, y)
local sx, sy, sw, sh = self:get_scrollbar_track_rect()
return x >= sx - style.scrollbar_size * 3 and x < sx + sw and y > sy and y <= sy + sh
end
---@param button core.view.mousebutton
---@param x number
---@param y number
---@param clicks integer
---return boolean
function View:on_mouse_pressed(button, x, y, clicks)
if self:scrollbar_overlaps_point(x, y) then
self.dragging_scrollbar = true
if self:scrollbar_track_overlaps_point(x, y) then
if self:scrollbar_overlaps_point(x, y) then
self.dragging_scrollbar = true
else
local _, _, _, sh = self:get_scrollbar_rect()
local ly = (y - self.position.y) - sh / 2
local pct = common.clamp(ly / self.size.y, 0, 100)
self.scroll.to.y = self:get_scrollable_size() * pct
end
return true
end
end
---@param button core.view.mousebutton
---@param x number
---@param y number
function View:on_mouse_released(button, x, y)
self.dragging_scrollbar = false
end
---@param x number
---@param y number
---@param dx number
---@param dy number
function View:on_mouse_moved(x, y, dx, dy)
if self.dragging_scrollbar then
local delta = self:get_scrollable_size() / self.size.y * dy
self.scroll.to.y = self.scroll.to.y + delta
if not config.animate_drag_scroll then
self:clamp_scroll_position()
self.scroll.y = self.scroll.to.y
end
end
self.hovered_scrollbar = self:scrollbar_overlaps_point(x, y)
self.hovered_scrollbar_track = self.hovered_scrollbar or self:scrollbar_track_overlaps_point(x, y)
end
function View:on_mouse_left()
self.hovered_scrollbar = false
self.hovered_scrollbar_track = false
end
---@param filename string
---@param x number
---@param y number
---@return boolean
function View:on_file_dropped(filename, x, y)
return false
end
---@param text string
function View:on_text_input(text)
-- no-op
end
---@param y number
---@return boolean
function View:on_mouse_wheel(y)
end
@ -113,6 +249,8 @@ function View:get_content_bounds()
end
---@return number x
---@return number y
function View:get_content_offset()
local x = common.round(self.position.x - self.scroll.x)
local y = common.round(self.position.y - self.scroll.y)
@ -126,13 +264,37 @@ function View:clamp_scroll_position()
end
function View:update()
self:clamp_scroll_position()
self:move_towards(self.scroll, "x", self.scroll.to.x, 0.3)
self:move_towards(self.scroll, "y", self.scroll.to.y, 0.3)
function View:update_scrollbar()
local x, y, w, h = self:get_scrollbar_rect()
self.scrollbar.w.to.thumb = w
self:move_towards(self.scrollbar.w, "thumb", self.scrollbar.w.to.thumb, 0.3, "scroll")
self.scrollbar.x.thumb = x + w - self.scrollbar.w.thumb
self.scrollbar.y.thumb = y
self.scrollbar.h.thumb = h
local x, y, w, h = self:get_scrollbar_track_rect()
self.scrollbar.w.to.track = w
self:move_towards(self.scrollbar.w, "track", self.scrollbar.w.to.track, 0.3, "scroll")
self.scrollbar.x.track = x + w - self.scrollbar.w.track
self.scrollbar.y.track = y
self.scrollbar.h.track = h
-- we use 100 for a smoother transition
self.scrollbar_alpha.to = (self.hovered_scrollbar_track or self.dragging_scrollbar) and 100 or 0
self:move_towards(self.scrollbar_alpha, "value", self.scrollbar_alpha.to, 0.3, "scroll")
end
function View:update()
self:clamp_scroll_position()
self:move_towards(self.scroll, "x", self.scroll.to.x, 0.3, "scroll")
self:move_towards(self.scroll, "y", self.scroll.to.y, 0.3, "scroll")
self:update_scrollbar()
end
---@param color renderer.color
function View:draw_background(color)
local x, y = self.position.x, self.position.y
local w, h = self.size.x, self.size.y
@ -140,11 +302,29 @@ function View:draw_background(color)
end
function View:draw_scrollbar()
local x, y, w, h = self:get_scrollbar_rect()
function View:draw_scrollbar_track()
if not (self.hovered_scrollbar_track or self.dragging_scrollbar)
and self.scrollbar_alpha.value == 0 then
return
end
local color = { table.unpack(style.scrollbar_track) }
color[4] = color[4] * self.scrollbar_alpha.value / 100
renderer.draw_rect(self.scrollbar.x.track, self.scrollbar.y.track,
self.scrollbar.w.track, self.scrollbar.h.track, color)
end
function View:draw_scrollbar_thumb()
local highlight = self.hovered_scrollbar or self.dragging_scrollbar
local color = highlight and style.scrollbar2 or style.scrollbar
renderer.draw_rect(x, y, w, h, color)
renderer.draw_rect(self.scrollbar.x.thumb, self.scrollbar.y.thumb,
self.scrollbar.w.thumb, self.scrollbar.h.thumb, color)
end
function View:draw_scrollbar()
self:draw_scrollbar_track()
self:draw_scrollbar_thumb()
end

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local core = require "core"
local common = require "core.common"
local config = require "core.config"
@ -10,14 +10,66 @@ 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,
-- The config specification used by gui generators
config_spec = {
name = "Autocomplete",
{
label = "Minimum Length",
description = "Amount of characters that need to be written for autocomplete to popup.",
path = "min_len",
type = "number",
default = 3,
min = 1,
max = 5
},
{
label = "Maximum Height",
description = "The maximum amount of visible items.",
path = "max_height",
type = "number",
default = 6,
min = 1,
max = 20
},
{
label = "Maximum Suggestions",
description = "The maximum amount of scrollable items.",
path = "max_suggestions",
type = "number",
default = 100,
min = 10,
max = 10000
},
{
label = "Maximum Symbols",
description = "Maximum amount of symbols to cache per document.",
path = "max_symbols",
type = "number",
default = 4000,
min = 1000,
max = 10000
},
{
label = "Description Font Size",
description = "Font size of the description box.",
path = "desc_font_size",
type = "number",
default = 12,
min = 8
}
}
}, config.plugins.autocomplete)
local autocomplete = {}
@ -33,7 +85,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 +95,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 +109,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,26 +119,43 @@ 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" })
local function get_syntax_symbols(symbols, doc)
if doc.syntax then
for sym in pairs(doc.syntax.symbols) do
symbols[sym] = true
end
end
end
local function get_symbols(doc)
if doc.disable_symbols then return {} end
local i = 1
local s = {}
get_syntax_symbols(s, doc)
if doc.disable_symbols then return s end
local i = 1
local symbols_count = 0
while i < #doc.lines do
while i <= #doc.lines do
for sym in doc.lines[i]:gmatch(config.symbol_pattern) do
if not s[sym] then
symbols_count = symbols_count + 1
if symbols_count > max_symbols then
s = nil
doc.disable_symbols = true
local filename_message
if doc.filename then
filename_message = "document " .. doc.filename
else
filename_message = "unnamed document"
end
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.")
"Too many symbols in "..filename_message..
": stopping auto-complete for this document according to "..
"config.plugins.autocomplete.max_symbols."
)
collectgarbage('collect')
return {}
end
@ -132,6 +202,7 @@ core.add_thread(function()
for _, doc in ipairs(core.docs) do
if not cache_is_valid(doc) then
valid = false
break
end
end
end
@ -159,16 +230,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 +260,7 @@ local function update_suggestions()
j = j + 1
end
end
suggestions_idx = 1
end
local function get_partial_symbol()
@ -209,7 +271,7 @@ local function get_partial_symbol()
end
local function get_active_view()
if getmetatable(core.active_view) == DocView then
if core.active_view:is(DocView) then
return core.active_view
end
end
@ -220,8 +282,7 @@ local function get_suggestions_rect(av)
end
local line, col = av.doc:get_selection()
local x, y = av:get_line_screen_position(line)
x = x + av:get_col_x_offset(line, col - #partial)
local x, y = av:get_line_screen_position(line, col - #partial)
y = y + av:get_line_height() + style.padding.y
local font = av:get_font()
local th = font:get_height()
@ -249,6 +310,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 +322,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 +422,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 +462,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)
@ -480,17 +621,26 @@ end
-- Commands
--
local function predicate()
return get_active_view() and #suggestions > 0
local active_docview = get_active_view()
return active_docview and #suggestions > 0, active_docview
end
command.add(predicate, {
["autocomplete:complete"] = function()
local doc = core.active_view.doc
["autocomplete:complete"] = function(dv)
local doc = dv.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
local current_partial = get_partial_symbol()
doc:insert(line, col, text)
doc:remove(line, col, line, col - #current_partial)
doc:set_selection(line, col + #text - #current_partial)
end
reset_suggestions()
end,

View File

@ -1,44 +1,110 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local core = require "core"
local config = require "core.config"
local style = require "core.style"
local Doc = require "core.doc"
local Node = require "core.node"
local common = require "core.common"
local dirwatch = require "core.dirwatch"
config.plugins.autoreload = common.merge({
always_show_nagview = false,
config_spec = {
name = "Autoreload",
{
label = "Always Show Nagview",
description = "Alerts you if an opened file changes externally even if you haven't modified it.",
path = "always_show_nagview",
type = "toggle",
default = false
}
}
}, config.plugins.autoreload)
local watch = dirwatch.new()
local times = setmetatable({}, { __mode = "k" })
local visible = setmetatable({}, { __mode = "k" })
local function get_project_doc_watch(doc)
for i, v in ipairs(core.project_directories) do
if doc.abs_filename:find(v.name, 1, true) == 1 then return v.watch end
end
return watch
end
local function update_time(doc)
local info = system.get_file_info(doc.filename)
times[doc] = info.modified
times[doc] = system.get_file_info(doc.filename).modified
end
local function reload_doc(doc)
local fp = io.open(doc.filename, "r")
local text = fp:read("*a")
fp:close()
local sel = { doc:get_selection() }
doc:remove(1, 1, math.huge, math.huge)
doc:insert(1, 1, text:gsub("\r", ""):gsub("\n$", ""))
doc:set_selection(table.unpack(sel))
doc:reload()
update_time(doc)
doc:clean()
core.redraw = true
core.log_quiet("Auto-reloaded doc \"%s\"", doc.filename)
end
local on_modify = core.on_dirmonitor_modify
core.on_dirmonitor_modify = function(dir, filepath)
local abs_filename = dir.name .. PATHSEP .. filepath
for _, doc in ipairs(core.docs) do
local info = system.get_file_info(doc.filename or "")
if doc.abs_filename == abs_filename and info and times[doc] ~= info.modified then
reload_doc(doc)
break
end
local function check_prompt_reload(doc)
if doc and doc.deferred_reload then
core.nag_view:show("File Changed", doc.filename .. " has changed. Reload this file?", {
{ font = style.font, text = "Yes", default_yes = true },
{ font = style.font, text = "No" , default_no = true }
}, function(item)
if item.text == "Yes" then reload_doc(doc) end
doc.deferred_reload = false
end)
end
on_modify(dir, filepath)
end
local function doc_changes_visiblity(doc, visibility)
if doc and visible[doc] ~= visibility and doc.abs_filename then
visible[doc] = visibility
if visibility then check_prompt_reload(doc) end
get_project_doc_watch(doc):watch(doc.abs_filename, visibility)
end
end
local on_check = dirwatch.check
function dirwatch:check(change_callback, ...)
on_check(self, function(dir)
for _, doc in ipairs(core.docs) do
if doc.abs_filename and (dir == common.dirname(doc.abs_filename) or dir == doc.abs_filename) then
local info = system.get_file_info(doc.filename or "")
if info and times[doc] ~= info.modified then
if not doc:is_dirty() and not config.plugins.autoreload.always_show_nagview then
reload_doc(doc)
else
doc.deferred_reload = true
if doc == core.active_view.doc then check_prompt_reload(doc) end
end
end
end
end
change_callback(dir)
end, ...)
end
local core_set_active_view = core.set_active_view
function core.set_active_view(view)
core_set_active_view(view)
doc_changes_visiblity(view.doc, true)
end
local node_set_active_view = Node.set_active_view
function Node:set_active_view(view)
if self.active_view then doc_changes_visiblity(self.active_view.doc, false) end
node_set_active_view(self, view)
doc_changes_visiblity(self.active_view.doc, true)
end
core.add_thread(function()
while true do
-- because we already hook this function above; we only
-- need to check the file.
watch:check(function() end)
coroutine.yield(0.05)
end
end)
-- patch `Doc.save|load` to store modified time
local load = Doc.load
local save = Doc.save
@ -51,6 +117,8 @@ end
Doc.save = function(self, ...)
local res = save(self, ...)
-- if starting with an unsaved document with a filename.
if not times[self] then get_project_doc_watch(self):watch(self.abs_filename, true) end
update_time(self)
return res
end

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local core = require "core"
local command = require "core.command"
local keymap = require "core.keymap"
@ -32,9 +32,9 @@ function RootView:draw(...)
menu:draw()
end
command.add(nil, {
["context:show"] = function()
menu:show(core.active_view.position.x, core.active_view.position.y)
command.add("core.docview!", {
["context:show"] = function(dv)
menu:show(dv.position.x, dv.position.y)
end
})
@ -42,23 +42,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
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,55 +291,54 @@ 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
local function set_indent_type_command()
core.command_view:enter(
"Specify indent style for this file",
function(value) -- submit
local doc = core.active_view.doc
local function set_indent_type_command(dv)
core.command_view:enter("Specify indent style for this file", {
submit = function(value)
local doc = dv.doc
value = value:lower()
set_indent_type(doc, value == "tabs" and "hard" or "soft")
end,
function(text) -- suggest
suggest = function(text)
return common.fuzzy_match({"tabs", "spaces"}, text)
end,
nil, -- cancel
function(text) -- validate
validate = function(text)
local t = text:lower()
return t == "tabs" or t == "spaces"
end
)
})
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
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))
local doc = core.active_view.doc
local function set_indent_size_command(dv)
core.command_view:enter("Specify indent size for current file", {
submit = function(value)
value = math.floor(tonumber(value))
local doc = dv.doc
set_indent_size(doc, value)
end,
nil, -- suggest
nil, -- cancel
function(value) -- validate
local value = tonumber(value)
validate = function(value)
value = tonumber(value)
return value ~= nil and value >= 1
end
)
})
end
@ -187,20 +347,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,36 +1,304 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local style = require "core.style"
local DocView = require "core.docview"
local common = require "core.common"
local config = require "core.config"
local Highlighter = require "core.doc.highlighter"
config.plugins.drawwhitespace = common.merge({
enabled = true,
show_leading = true,
show_trailing = true,
show_middle = true,
show_middle_min = 1,
color = style.syntax.whitespace or style.syntax.comment,
leading_color = nil,
middle_color = nil,
trailing_color = nil,
substitutions = {
{
char = " ",
sub = "·",
-- You can put any of the previous options here too.
-- For example:
-- show_middle_min = 2,
-- show_leading = false,
},
{
char = "\t",
sub = "»",
},
},
config_spec = {
name = "Draw Whitespace",
{
label = "Enabled",
description = "Disable or enable the drawing of white spaces.",
path = "enabled",
type = "toggle",
default = true
},
{
label = "Show Leading",
description = "Draw whitespaces starting at the beginning of a line.",
path = "show_leading",
type = "toggle",
default = true,
},
{
label = "Show Middle",
description = "Draw whitespaces on the middle of a line.",
path = "show_middle",
type = "toggle",
default = true,
},
{
label = "Show Trailing",
description = "Draw whitespaces on the end of a line.",
path = "show_trailing",
type = "toggle",
default = true,
},
{
label = "Show Trailing as Error",
description = "Uses an error square to spot them easily, requires 'Show Trailing' enabled.",
path = "show_trailing_error",
type = "toggle",
default = false,
on_apply = function(enabled)
local found = nil
local substitutions = config.plugins.drawwhitespace.substitutions
for i, sub in ipairs(substitutions) do
if sub.trailing_error then
found = i
end
end
if found == nil and enabled then
table.insert(substitutions, {
char = " ",
sub = "",
show_leading = false,
show_middle = false,
show_trailing = true,
trailing_color = style.error,
trailing_error = true
})
elseif found ~= nil and not enabled then
table.remove(substitutions, found)
end
end
}
}
}, config.plugins.drawwhitespace)
local ws_cache
local cached_settings
local function reset_cache()
ws_cache = setmetatable({}, { __mode = "k" })
local settings = config.plugins.drawwhitespace
cached_settings = {
show_leading = settings.show_leading,
show_trailing = settings.show_trailing,
show_middle = settings.show_middle,
show_middle_min = settings.show_middle_min,
color = settings.color,
leading_color = settings.leading_color,
middle_color = settings.middle_color,
trailing_color = settings.trailing_color,
substitutions = settings.substitutions,
}
end
reset_cache()
local function reset_cache_if_needed()
local settings = config.plugins.drawwhitespace
if
not ws_cache or
cached_settings.show_leading ~= settings.show_leading
or cached_settings.show_trailing ~= settings.show_trailing
or cached_settings.show_middle ~= settings.show_middle
or cached_settings.show_middle_min ~= settings.show_middle_min
or cached_settings.color ~= settings.color
or cached_settings.leading_color ~= settings.leading_color
or cached_settings.middle_color ~= settings.middle_color
or cached_settings.trailing_color ~= settings.trailing_color
-- we assume that the entire table changes
or cached_settings.substitutions ~= settings.substitutions
then
reset_cache()
end
end
-- Move cache to make space for new lines
local prev_insert_notify = Highlighter.insert_notify
function Highlighter:insert_notify(line, n, ...)
prev_insert_notify(self, line, n, ...)
if not ws_cache[self] then
ws_cache[self] = {}
end
local to = math.min(line + n, #self.doc.lines)
for i=#self.doc.lines+n,to,-1 do
ws_cache[self][i] = ws_cache[self][i - n]
end
for i=line,to do
ws_cache[self][i] = nil
end
end
-- Close the cache gap created by removed lines
local prev_remove_notify = Highlighter.remove_notify
function Highlighter:remove_notify(line, n, ...)
prev_remove_notify(self, line, n, ...)
if not ws_cache[self] then
ws_cache[self] = {}
end
local to = math.max(line + n, #self.doc.lines)
for i=line,to do
ws_cache[self][i] = ws_cache[self][i + n]
end
end
-- Remove changed lines from the cache
local prev_update_notify = Highlighter.update_notify
function Highlighter:update_notify(line, n, ...)
prev_update_notify(self, line, n, ...)
if not ws_cache[self] then
ws_cache[self] = {}
end
for i=line,line+n do
ws_cache[self][i] = nil
end
end
local function get_option(substitution, option)
if substitution[option] == nil then
return config.plugins.drawwhitespace[option]
end
return substitution[option]
end
local draw_line_text = DocView.draw_line_text
function DocView:draw_line_text(idx, x, y)
if
not config.plugins.drawwhitespace.enabled
or
getmetatable(self) ~= DocView
then
return draw_line_text(self, idx, x, y)
end
local font = (self:get_font() or style.syntax_fonts["whitespace"] or style.syntax_fonts["comment"])
local color = style.syntax.whitespace or style.syntax.comment
local ty = y + self:get_line_text_y_offset()
local tx
local text, offset, s, e = self.doc.lines[idx], 1
local font_size = font:get_size()
local _, indent_size = self.doc:get_indent_info()
reset_cache_if_needed()
if
not ws_cache[self.doc.highlighter]
or ws_cache[self.doc.highlighter].font ~= font
or ws_cache[self.doc.highlighter].font_size ~= font_size
or ws_cache[self.doc.highlighter].indent_size ~= indent_size
then
ws_cache[self.doc.highlighter] =
setmetatable(
{ font = font, font_size = font_size, indent_size = indent_size },
{ __mode = "k" }
)
end
if not ws_cache[self.doc.highlighter][idx] then -- need to cache line
local cache = {}
local tx
local text = self.doc.lines[idx]
for _, substitution in pairs(config.plugins.drawwhitespace.substitutions) do
local char = substitution.char
local sub = substitution.sub
local offset = 1
local show_leading = get_option(substitution, "show_leading")
local show_middle = get_option(substitution, "show_middle")
local show_trailing = get_option(substitution, "show_trailing")
local show_middle_min = get_option(substitution, "show_middle_min")
local base_color = get_option(substitution, "color")
local leading_color = get_option(substitution, "leading_color") or base_color
local middle_color = get_option(substitution, "middle_color") or base_color
local trailing_color = get_option(substitution, "trailing_color") or base_color
local pattern = char.."+"
while true do
local s, e = text:find(pattern, offset)
if not s then break end
tx = self:get_col_x_offset(idx, s)
local color = base_color
local draw = false
if e == #text - 1 then
draw = show_trailing
color = trailing_color
elseif s == 1 then
draw = show_leading
color = leading_color
else
draw = show_middle and (e - s + 1 >= show_middle_min)
color = middle_color
end
if draw then
local last_cache_idx = #cache
-- We need to draw tabs one at a time because they might have a
-- different size than the substituting character.
-- This also applies to any other char if we use non-monospace fonts
-- but we ignore this case for now.
if char == "\t" then
for i = s,e do
tx = self:get_col_x_offset(idx, i)
cache[last_cache_idx + 1] = sub
cache[last_cache_idx + 2] = tx
cache[last_cache_idx + 3] = font:get_width(sub)
cache[last_cache_idx + 4] = color
last_cache_idx = last_cache_idx + 4
end
else
cache[last_cache_idx + 1] = string.rep(sub, e - s + 1)
cache[last_cache_idx + 2] = tx
cache[last_cache_idx + 3] = font:get_width(cache[last_cache_idx + 1])
cache[last_cache_idx + 4] = color
end
end
offset = e + 1
end
end
ws_cache[self.doc.highlighter][idx] = cache
end
-- draw from cache
local x1, _, x2, _ = self:get_content_bounds()
local _offset = self:get_x_offset_col(idx, x1)
offset = _offset
while true do
s, e = text:find(" +", offset)
if not s then break end
tx = self:get_col_x_offset(idx, s) + x
renderer.draw_text(font, string.rep("·", e - s + 1), tx, ty, color)
if tx > x + x2 then break end
offset = e + 1
x1 = x1 + x
x2 = x2 + x
local ty = y + self:get_line_text_y_offset()
local cache = ws_cache[self.doc.highlighter][idx]
for i=1,#cache,4 do
local sub = cache[i]
local tx = cache[i + 1] + x
local tw = cache[i + 2]
local color = cache[i + 3]
if tx + tw >= x1 then
tx = renderer.draw_text(font, sub, tx, ty, color)
end
if tx > x2 then break end
end
offset = _offset
while true do
s, e = text:find("\t", offset)
if not s then break end
tx = self:get_col_x_offset(idx, s) + x
renderer.draw_text(font, "»", tx, ty, color)
if tx > x + x2 then break end
offset = e + 1
end
draw_line_text(self, idx, x, y)
return draw_line_text(self, idx, x, y)
end

View File

@ -1,12 +1,13 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
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" },
{ pattern = { "/%*", "%*/" }, type = "comment" },
{ pattern = { '"', '"', '\\' }, type = "string" },
{ pattern = { "'", "'", '\\' }, type = "string" },
@ -14,12 +15,64 @@ syntax.add {
{ pattern = "%d+[%d%.eE]*f?", type = "number" },
{ pattern = "%.?%d+f?", type = "number" },
{ pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" },
{ pattern = "##", type = "operator" },
{ pattern = "struct%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{ pattern = "union%s()[%a_][%w_]*", type = {"keyword", "keyword2"} },
-- static declarations
{ pattern = "static()%s+()inline",
type = { "keyword", "normal", "keyword" }
},
{ pattern = "static()%s+()const",
type = { "keyword", "normal", "keyword" }
},
{ pattern = "static()%s+()[%a_][%w_]*",
type = { "keyword", "normal", "literal" }
},
-- match function type declarations
{ pattern = "[%a_][%w_]*()%*+()%s+()[%a_][%w_]*%f[%(]",
type = { "literal", "operator", "normal", "function" }
},
{ pattern = "[%a_][%w_]*()%s+()%*+()[%a_][%w_]*%f[%(]",
type = { "literal", "normal", "operator", "function" }
},
{ pattern = "[%a_][%w_]*()%s+()[%a_][%w_]*%f[%(]",
type = { "literal", "normal", "function" }
},
-- match variable type declarations
{ pattern = "[%a_][%w_]*()%*+()%s+()[%a_][%w_]*",
type = { "literal", "operator", "normal", "normal" }
},
{ pattern = "[%a_][%w_]*()%s+()%*+()[%a_][%w_]*",
type = { "literal", "normal", "operator", "normal" }
},
{ pattern = "[%a_][%w_]*()%s+()[%a_][%w_]*()%s*()[;,%[%)]",
type = { "literal", "normal", "normal", "normal", "normal" }
},
{ pattern = "[%a_][%w_]*()%s+()[%a_][%w_]*()%s*()=",
type = { "literal", "normal", "normal", "normal", "operator" }
},
{ pattern = "[%a_][%w_]*()&()%s+()[%a_][%w_]*",
type = { "literal", "operator", "normal", "normal" }
},
{ pattern = "[%a_][%w_]*()%s+()&()[%a_][%w_]*",
type = { "literal", "normal", "operator", "normal" }
},
-- 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" },
-- all other functions
{ pattern = "[%a_][%w_]*%f[(]", type = "function" },
-- Macros
{ pattern = "^%s*#%s*define%s+()[%a_][%a%d_]*",
type = { "keyword", "symbol" }
},
{ pattern = "#%s*include%s()<.->", type = {"keyword", "string"} },
{ pattern = "%f[#]#%s*[%a_][%w_]*", type = "keyword" },
-- Everything else to make the tokenizer work properly
{ pattern = "[%a_][%w_]*", type = "symbol" },
{ pattern = "#include%s()<.->", type = {"keyword", "string"} },
{ pattern = "#[%a_][%w_]*", type = "keyword" },
},
symbols = {
["if"] = "keyword",
@ -44,6 +97,8 @@ syntax.add {
["case"] = "keyword",
["default"] = "keyword",
["auto"] = "keyword",
["struct"] = "keyword",
["union"] = "keyword",
["void"] = "keyword2",
["int"] = "keyword2",
["short"] = "keyword2",
@ -60,6 +115,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
local syntax = require "core.syntax"
syntax.add {
@ -10,28 +8,101 @@ 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 = "//.*", 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 = "##", 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" },
-- static declarations
{ pattern = "static()%s+()inline",
type = { "keyword", "normal", "keyword" }
},
{ pattern = "static()%s+()const",
type = { "keyword", "normal", "keyword" }
},
{ pattern = "static()%s+()[%a_][%w_]*",
type = { "keyword", "normal", "literal" }
},
-- match method type declarations
{ pattern = "[%a_][%w_]*()%s*()%**()%s*()[%a_][%w_]*()%s*()::",
type = {
"literal", "normal", "operator", "normal",
"literal", "normal", "operator"
}
},
-- match function type declarations
{ pattern = "[%a_][%w_]*()%*+()%s+()[%a_][%w_]*%f[%(]",
type = { "literal", "operator", "normal", "function" }
},
{ pattern = "[%a_][%w_]*()%s+()%*+()[%a_][%w_]*%f[%(]",
type = { "literal", "normal", "operator", "function" }
},
{ pattern = "[%a_][%w_]*()%s+()[%a_][%w_]*%f[%(]",
type = { "literal", "normal", "function" }
},
-- match variable type declarations
{ pattern = "[%a_][%w_]*()%*+()%s+()[%a_][%w_]*",
type = { "literal", "operator", "normal", "normal" }
},
{ pattern = "[%a_][%w_]*()%s+()%*+()[%a_][%w_]*",
type = { "literal", "normal", "operator", "normal" }
},
{ pattern = "[%a_][%w_]*()%s+()[%a_][%w_]*()%s*()[;,%[%)]",
type = { "literal", "normal", "normal", "normal", "normal" }
},
{ pattern = "[%a_][%w_]*()%s+()[%a_][%w_]*()%s*()=",
type = { "literal", "normal", "normal", "normal", "operator" }
},
{ pattern = "[%a_][%w_]*()&()%s+()[%a_][%w_]*",
type = { "literal", "operator", "normal", "normal" }
},
{ pattern = "[%a_][%w_]*()%s+()&()[%a_][%w_]*",
type = { "literal", "normal", "operator", "normal" }
},
-- Match scope operator element access
{ pattern = "[%a_][%w_]*()%s*()::",
type = { "literal", "normal", "operator" }
},
-- 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" },
-- all other functions
{ pattern = "[%a_][%w_]*%f[(]", type = "function" },
-- Macros
{ pattern = "^%s*#%s*define%s+()[%a_][%a%d_]*",
type = { "keyword", "symbol" }
},
{ pattern = "#%s*include%s+()<.->",
type = { "keyword", "string" }
},
{ pattern = "%f[#]#%s*[%a_][%w_]*", type = "keyword" },
-- 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 +110,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 +125,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 +137,6 @@ syntax.add {
["co_yield"] = "keyword",
["decltype"] = "keyword",
["delete"] = "keyword",
["export"] = "keyword",
["friend"] = "keyword",
["typeid"] = "keyword",
["typename"] = "keyword",
@ -71,6 +144,7 @@ syntax.add {
["override"] = "keyword",
["virtual"] = "keyword",
["using"] = "keyword",
["namespace"] = "keyword",
["new"] = "keyword",
["noexcept"] = "keyword",
["if"] = "keyword",
@ -84,6 +158,8 @@ syntax.add {
["continue"] = "keyword",
["return"] = "keyword",
["goto"] = "keyword",
["struct"] = "keyword",
["union"] = "keyword",
["typedef"] = "keyword",
["enum"] = "keyword",
["extern"] = "keyword",
@ -95,7 +171,6 @@ syntax.add {
["case"] = "keyword",
["default"] = "keyword",
["auto"] = "keyword",
["const"] = "keyword",
["void"] = "keyword2",
["int"] = "keyword2",
["short"] = "keyword2",
@ -105,12 +180,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 +199,5 @@ syntax.add {
["#warning"] = "keyword",
["#error"] = "keyword",
["#pragma"] = "keyword",
},
},
}

View File

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

View File

@ -1,31 +1,23 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local syntax = require "core.syntax"
syntax.add {
name = "HTML",
files = { "%.html?$" },
block_comment = { "<!--", "-->" },
patterns = {
{
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]>"
{
pattern = {
"<%s*[sS][cC][rR][iI][pP][tT]%f[%s>].->",
"<%s*/%s*[sS][cC][rR][iI][pP][tT]%s*>"
},
syntax = ".js",
type = "function"
},
{
pattern = {
"<%s*[sS][tT][yY][lL][eE][^>]*>",
"<%s*/%s*[sS][tT][yY][lL][eE]%s*>"
{
pattern = {
"<%s*[sS][tT][yY][lL][eE]%f[%s>].->",
"<%s*/%s*[sS][tT][yY][lL][eE]%s*>"
},
syntax = ".css",
type = "function"

View File

@ -1,12 +1,13 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local syntax = require "core.syntax"
syntax.add {
name = "JavaScript",
files = { "%.js$", "%.json$", "%.cson$" },
files = { "%.js$", "%.json$", "%.cson$", "%.mjs$", "%.cjs$" },
comment = "//",
block_comment = { "/*", "*/" },
patterns = {
{ pattern = "//.-\n", type = "comment" },
{ pattern = "//.*", type = "comment" },
{ pattern = { "/%*", "%*/" }, type = "comment" },
{ pattern = { '/[^= ]', '/', '\\' },type = "string" },
{ pattern = { '"', '"', '\\' }, type = "string" },

View File

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

View File

@ -1,56 +1,235 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local syntax = require "core.syntax"
local style = require "core.style"
local core = require "core"
local initial_color = style.syntax["keyword2"]
-- Add 3 type of font styles for use on markdown files
for _, attr in pairs({"bold", "italic", "bold_italic"}) do
local attributes = {}
if attr ~= "bold_italic" then
attributes[attr] = true
else
attributes["bold"] = true
attributes["italic"] = true
end
style.syntax_fonts["markdown_"..attr] = style.code_font:copy(
style.code_font:get_size(),
attributes
)
-- also add a color for it
style.syntax["markdown_"..attr] = style.syntax["keyword2"]
end
local in_squares_match = "^%[%]"
local in_parenthesis_match = "^%(%)"
syntax.add {
name = "Markdown",
files = { "%.md$", "%.markdown$" },
block_comment = { "<!--", "-->" },
space_handling = false, -- turn off this feature to handle it our selfs
patterns = {
{ pattern = "\\.", type = "normal" },
{ pattern = { "<!%-%-", "%-%->" }, type = "comment" },
---- Place patterns that require spaces at start to optimize matching speed
---- and apply the %s+ optimization immediately afterwards
-- 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" },
-- alternative bold italic formats
{ pattern = { "%s___", "___%f[%s]" }, type = "markdown_bold_italic" },
{ pattern = { "%s__", "__%f[%s]" }, type = "markdown_bold" },
{ pattern = { "%s_[%S]", "_%f[%s]" }, type = "markdown_italic" },
-- reference links
{
pattern = "^%s*%[%^()["..in_squares_match.."]+()%]: ",
type = { "function", "number", "function" }
},
{
pattern = "^%s*%[%^?()["..in_squares_match.."]+()%]:%s+.*",
type = { "function", "number", "function" }
},
-- optimization
{ pattern = "%s+", type = "normal" },
---- 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
-- math
{ pattern = { "%$%$", "%$%$", "\\" }, type = "string", syntax = ".tex"},
{ regex = { "\\$", [[\$|(?=\\*\n)]], "\\" }, type = "string", syntax = ".tex"},
-- 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 = { "```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 = "^%-%-%-+$" , type = "comment" },
{ pattern = "^%*%*%*+$", type = "comment" },
{ pattern = "^___+$", type = "comment" },
-- 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" },
-- heading with custom id
{
pattern = "^#+%s[%w%s%p]+(){()#[%w%-]+()}",
type = { "keyword", "function", "string", "function" }
},
-- headings
{ pattern = "^#+%s.+$", 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 = "!?%[%^?()["..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" },
-- optimize consecutive dashes used in tables
{ pattern = "%-+", type = "normal" },
},
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
local syntax = require "core.syntax"
syntax.add {
@ -6,9 +6,14 @@ syntax.add {
files = { "%.py$", "%.pyw$", "%.rpy$" },
headers = "^#!.*[ /]python",
comment = "#",
block_comment = { '"""', '"""' },
patterns = {
{ pattern = { "#", "\n" }, type = "comment" },
{ pattern = "#.*", type = "comment" },
{ pattern = { '^%s*"""', '"""' }, type = "comment" },
{ pattern = '[uUrR]%f["]', type = "keyword" },
{ pattern = "class%s+()[%a_][%w_]*", type = {"keyword", "keyword2"} },
{ pattern = { '[ruU]?"""', '"""'; '\\' }, type = "string" },
{ pattern = { "[ruU]?'''", "'''", '\\' }, type = "string" },
{ pattern = { '[ruU]?"', '"', '\\' }, type = "string" },
{ pattern = { "[ruU]?'", "'", '\\' }, type = "string" },
{ pattern = "0x[%da-fA-F]+", type = "number" },
@ -28,6 +33,8 @@ syntax.add {
["lambda"] = "keyword",
["try"] = "keyword",
["def"] = "keyword",
["async"] = "keyword",
["await"] = "keyword",
["from"] = "keyword",
["nonlocal"] = "keyword",
["while"] = "keyword",
@ -40,6 +47,8 @@ syntax.add {
["if"] = "keyword",
["or"] = "keyword",
["else"] = "keyword",
["match"] = "keyword",
["case"] = "keyword",
["import"] = "keyword",
["pass"] = "keyword",
["break"] = "keyword",

View File

@ -1,10 +1,11 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
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,21 +1,115 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local common = require "core.common"
local command = require "core.command"
local config = require "core.config"
local style = require "core.style"
local DocView = require "core.docview"
local CommandView = require "core.commandview"
local draw_overlay = DocView.draw_overlay
config.plugins.lineguide = common.merge({
enabled = false,
width = 2,
rulers = {
-- 80,
-- 100,
-- 120,
config.line_limit
},
-- The config specification used by gui generators
config_spec = {
name = "Line Guide",
{
label = "Enabled",
description = "Disable or enable drawing of the line guide.",
path = "enabled",
type = "toggle",
default = true
},
{
label = "Width",
description = "Width in pixels of the line guide.",
path = "width",
type = "number",
default = 2,
min = 1
},
{
label = "Ruler Positions",
description = "The different column numbers for the line guides to draw.",
path = "rulers",
type = "list_strings",
default = { tostring(config.line_limit) or "80" },
get_value = function(rulers)
if type(rulers) == "table" then
local new_rulers = {}
for _, ruler in ipairs(rulers) do
table.insert(new_rulers, tostring(ruler))
end
return new_rulers
else
return { tostring(config.line_limit) }
end
end,
set_value = function(rulers)
local new_rulers = {}
for _, ruler in ipairs(rulers) do
local number = tonumber(ruler)
if number then
table.insert(new_rulers, number)
end
end
if #new_rulers == 0 then
table.insert(new_rulers, config.line_limit)
end
return new_rulers
end
}
}
}, config.plugins.lineguide)
function DocView:draw_overlay(...)
if not self:is(CommandView) then
local offset = self:get_font():get_width("n") * config.line_limit
local x = self:get_line_screen_position(1) + offset
local y = self.position.y
local w = math.ceil(SCALE * 1)
local h = self.size.y
local color = style.guide or style.selection
renderer.draw_rect(x, y, w, h, color)
local function get_ruler(v)
local result = nil
if type(v) == 'number' then
result = { columns = v }
elseif type(v) == 'table' then
result = v
end
draw_overlay(self, ...)
return result
end
local draw_overlay = DocView.draw_overlay
function DocView:draw_overlay(...)
draw_overlay(self, ...)
if
type(config.plugins.lineguide) == "table"
and
config.plugins.lineguide.enabled
and
not self:is(CommandView)
then
local line_x = self:get_line_screen_position(1)
local character_width = self:get_font():get_width("n")
local ruler_width = config.plugins.lineguide.width
local ruler_color = style.guide or style.selection
for k,v in ipairs(config.plugins.lineguide.rulers) do
local ruler = get_ruler(v)
if ruler then
local x = line_x + (character_width * ruler.columns)
local y = self.position.y
local w = ruler_width
local h = self.size.y
renderer.draw_rect(x, y, w, h, ruler.color or ruler_color)
end
end
end
end
command.add(nil, {
["lineguide:toggle"] = function()
config.plugins.lineguide.enabled = not config.plugins.lineguide.enabled
end
})

View File

@ -0,0 +1,581 @@
-- mod-version:3 --priority:10
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 = common.merge({
-- 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. Can be a function.
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 = false,
-- Requires tokenization
require_tokenization = false,
-- The config specification used by gui generators
config_spec = {
name = "Line Wrapping",
{
label = "Mode",
description = "The type of wrapping to perform.",
path = "mode",
type = "selection",
default = "letter",
values = {
{"Letters", "letter"},
{"Words", "word"}
}
},
{
label = "Guide",
description = "Whether or not to draw a guide.",
path = "guide",
type = "toggle",
default = true
},
{
label = "Indent",
description = "Whether or not to follow the indentation of wrapped line.",
path = "indent",
type = "toggle",
default = true
},
{
label = "Enable by Default",
description = "Whether or not to enable wrapping by default when opening files.",
path = "enable_by_default",
type = "toggle",
default = false
},
{
label = "Require Tokenization",
description = "Use tokenization when applying wrapping.",
path = "require_tokenization",
type = "toggle",
default = false
}
}
}, config.plugins.linewrapping)
local LineWrapping = {}
-- Optimzation function. The tokenizer is relatively slow (at present), and
-- so if we don't need to run it, should be run sparingly.
local function spew_tokens(doc, line) if line < math.huge then return math.huge, "normal", doc.lines[line] end end
local function get_tokens(doc, line)
if config.plugins.linewrapping.require_tokenization then
return doc.highlighter:each_token(line)
end
return spew_tokens, doc, line
end
-- 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, nil, 0, 0
local splits = { 1 }
for idx, type, text in get_tokens(doc, line) do
local font = style.syntax_fonts[type] or default_font
if idx == 1 or idx == math.huge 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" and last_space then
table.insert(splits, last_space + 1)
xoffset = w + begin_width + (xoffset - last_width)
else
table.insert(splits, i)
xoffset = w + begin_width
end
last_space = nil
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 = (type(config.plugins.linewrapping.width_override) == "function" and config.plugins.linewrapping.width_override(docview))
or config.plugins.linewrapping.width_override or (docview.size.x - docview:get_gutter_width() - w)
if (not docview.wrapped_settings or docview.wrapped_settings.width == nil or width ~= docview.wrapped_settings.width) then
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 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
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
local core = require "core"
local common = require "core.common"
local keymap = require "core.keymap"
@ -11,11 +11,11 @@ local ResultsView = View:extend()
ResultsView.context = "session"
function ResultsView:new(text, fn)
function ResultsView:new(path, text, fn)
ResultsView.super.new(self)
self.scrollable = true
self.brightness = 0
self:begin_search(text, fn)
self:begin_search(path, text, fn)
end
@ -45,8 +45,8 @@ local function find_all_matches_in_file(t, filename, fn)
end
function ResultsView:begin_search(text, fn)
self.search_args = { text, fn }
function ResultsView:begin_search(path, text, fn)
self.search_args = { path, text, fn }
self.results = {}
self.last_file_idx = 1
self.query = text
@ -56,9 +56,9 @@ function ResultsView:begin_search(text, fn)
core.add_thread(function()
local i = 1
for dir_name, file in core.get_project_files() do
if file.type == "file" then
local path = (dir_name == core.project_dir and "" or (dir_name .. PATHSEP))
find_all_matches_in_file(self.results, path .. file.filename, fn)
if file.type == "file" and (not path or (dir_name .. "/" .. file.filename):find(path, 1, true) == 1) then
local truncated_path = (dir_name == core.project_dir and "" or (dir_name .. PATHSEP))
find_all_matches_in_file(self.results, truncated_path .. file.filename, fn)
end
self.last_file_idx = i
i = i + 1
@ -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
@ -219,41 +219,72 @@ function ResultsView:draw()
end
local function begin_search(text, fn)
local function begin_search(path, text, fn)
if text == "" then
core.error("Expected non-empty string")
return
end
local rv = ResultsView(text, fn)
local rv = ResultsView(path, text, fn)
core.root_view:get_active_node_default():add_view(rv)
end
local function get_selected_text()
local view = core.active_view
local doc = (view and view.doc) and view.doc or nil
if doc then
return doc:get_text(table.unpack({ doc:get_selection() }))
end
end
local function normalize_path(path)
if not path then return nil end
path = common.normalize_path(path)
for i, project_dir in ipairs(core.project_directories) do
if common.path_belongs_to(path, project_dir.name) then
return project_dir.item.filename .. PATHSEP .. common.relative_path(project_dir.name, path)
end
end
return path
end
command.add(nil, {
["project-search:find"] = function()
core.command_view:enter("Find Text In Project", function(text)
text = text:lower()
begin_search(text, function(line_text)
return line_text:lower():find(text, nil, true)
end)
end)
["project-search:find"] = function(path)
core.command_view:enter("Find Text In " .. (normalize_path(path) or "Project"), {
text = get_selected_text(),
select_text = true,
submit = function(text)
text = text:lower()
begin_search(path, text, function(line_text)
return line_text:lower():find(text, nil, true)
end)
end
})
end,
["project-search:find-regex"] = function()
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)
end)
end)
["project-search:find-regex"] = function(path)
core.command_view:enter("Find Regex In " .. (normalize_path(path) or "Project"), {
submit = function(text)
local re = regex.compile(text, "i")
begin_search(path, text, function(line_text)
return regex.cmatch(re, line_text)
end)
end
})
end,
["project-search:fuzzy-find"] = function()
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
end)
end)
["project-search:fuzzy-find"] = function(path)
core.command_view:enter("Fuzzy Find Text In " .. (normalize_path(path) or "Project"), {
text = get_selected_text(),
select_text = true,
submit = function(text)
begin_search(path, text, function(line_text)
return common.fuzzy_match(line_text, text) and 1
end)
end
})
end,
})
@ -278,22 +309,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
local core = require "core"
local command = require "core.command"
local keymap = require "core.keymap"
@ -19,8 +19,8 @@ end
command.add("core.docview", {
["quote:quote"] = function()
core.active_view.doc:replace(function(text)
["quote:quote"] = function(dv)
dv.doc:replace(function(text)
return '"' .. text:gsub("[\0-\31\\\"]", replace) .. '"'
end)
end,

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local core = require "core"
local config = require "core.config"
local command = require "core.command"
@ -25,8 +25,8 @@ end
command.add("core.docview", {
["reflow:reflow"] = function()
local doc = core.active_view.doc
["reflow:reflow"] = function(dv)
local doc = dv.doc
doc:replace(function(text)
local prefix_set = "[^%w\n%[%](){}`'\"]*"

View File

@ -1,17 +1,20 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local core = require "core"
local common = require "core.common"
local command = require "core.command"
local config = require "core.config"
local keymap = require "core.keymap"
local style = require "core.style"
local RootView = require "core.rootview"
local CommandView = require "core.commandview"
config.plugins.scale = {
config.plugins.scale = common.merge({
-- The method used to apply the scaling: "code", "ui"
mode = "code",
-- Default scale applied at startup.
default_scale = "autodetect",
-- Allow using CTRL + MouseWheel for changing the scale.
use_mousewheel = true
}
}, config.plugins.scale)
local scale_steps = 0.05
@ -44,18 +47,14 @@ local function set_scale(scale)
style.tab_width = style.tab_width * s
for _, name in ipairs {"font", "big_font", "icon_font", "icon_big_font", "code_font"} do
style[name] = renderer.font.copy(style[name], s * style[name]:get_size())
style[name]:set_size(s * style[name]:get_size())
end
else
style.code_font = renderer.font.copy(style.code_font, s * style.code_font:get_size())
style.code_font:set_size(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]:set_size(s * font:get_size())
end
-- restore scroll positions
@ -83,6 +82,75 @@ local function dec_scale()
set_scale(current_scale - scale_steps)
end
if default_scale ~= config.plugins.scale.default_scale then
if type(config.plugins.scale.default_scale) == "number" then
set_scale(config.plugins.scale.default_scale)
end
end
-- The config specification used by gui generators
config.plugins.scale.config_spec = {
name = "Scale",
{
label = "Mode",
description = "The method used to apply the scaling.",
path = "mode",
type = "selection",
default = "code",
values = {
{"Everything", "ui"},
{"Code Only", "code"}
}
},
{
label = "Default Scale",
description = "The scaling factor applied to lite-xl.",
path = "default_scale",
type = "selection",
default = "autodetect",
values = {
{"Autodetect", "autodetect"},
{"80%", 0.80},
{"90%", 0.90},
{"100%", 1.00},
{"110%", 1.10},
{"120%", 1.20},
{"125%", 1.25},
{"130%", 1.30},
{"140%", 1.40},
{"150%", 1.50},
{"175%", 1.75},
{"200%", 2.00},
{"250%", 2.50},
{"300%", 3.00}
},
on_apply = function(value)
if type(value) == "string" then value = default_scale end
if value ~= current_scale then
set_scale(value)
end
end
},
{
label = "Use MouseWheel",
description = "Allow using CTRL + MouseWheel for changing the scale.",
path = "use_mousewheel",
type = "toggle",
default = true,
on_apply = function(enabled)
if enabled then
keymap.add {
["ctrl+wheelup"] = "scale:increase",
["ctrl+wheeldown"] = "scale:decrease"
}
else
keymap.unbind("ctrl+wheelup", "scale:increase")
keymap.unbind("ctrl+wheeldown", "scale:decrease")
end
end
}
}
command.add(nil, {
["scale:reset" ] = function() res_scale() end,
@ -93,11 +161,16 @@ command.add(nil, {
keymap.add {
["ctrl+0"] = "scale:reset",
["ctrl+-"] = "scale:decrease",
["ctrl+="] = "scale:increase",
["ctrl+wheelup"] = "scale:increase",
["ctrl+wheeldown"] = "scale:decrease"
["ctrl+="] = "scale:increase"
}
if config.plugins.scale.use_mousewheel then
keymap.add {
["ctrl+wheelup"] = "scale:increase",
["ctrl+wheeldown"] = "scale:decrease"
}
end
return {
["set"] = set_scale,
["get"] = get_scale,

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local core = require "core"
local command = require "core.command"
local translate = require "core.doc.translate"
@ -41,21 +41,23 @@ end
command.add("core.docview", {
["tabularize:tabularize"] = function()
core.command_view:enter("Tabularize On Delimiter", function(delim)
if delim == "" then delim = " " end
["tabularize:tabularize"] = function(dv)
core.command_view:enter("Tabularize On Delimiter", {
submit = function(delim)
if delim == "" then delim = " " end
local doc = core.active_view.doc
local line1, col1, line2, col2, swap = doc:get_selection(true)
line1, col1 = doc:position_offset(line1, col1, translate.start_of_line)
line2, col2 = doc:position_offset(line2, col2, translate.end_of_line)
doc:set_selection(line1, col1, line2, col2, swap)
local doc = dv.doc
local line1, col1, line2, col2, swap = doc:get_selection(true)
line1, col1 = doc:position_offset(line1, col1, translate.start_of_line)
line2, col2 = doc:position_offset(line2, col2, translate.end_of_line)
doc:set_selection(line1, col1, line2, col2, swap)
doc:replace(function(text)
local lines = gmatch_to_array(text, "[^\n]*\n?")
tabularize_lines(lines, delim)
return table.concat(lines)
end)
end)
doc:replace(function(text)
local lines = gmatch_to_array(text, "[^\n]*\n?")
tabularize_lines(lines, delim)
return table.concat(lines)
end)
end
})
end,
})

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local core = require "core"
local common = require "core.common"
local command = require "core.command"
@ -7,31 +7,26 @@ local View = require "core.view"
local ToolbarView = View:extend()
local toolbar_commands = {
{symbol = "f", command = "core:new-doc"},
{symbol = "D", command = "core:open-file"},
{symbol = "S", command = "doc:save"},
{symbol = "L", command = "core:find-file"},
{symbol = "B", command = "core:find-command"},
{symbol = "P", command = "core:open-user-module"},
}
local function toolbar_height()
return style.icon_big_font:get_height() + style.padding.y * 2
end
function ToolbarView:new()
ToolbarView.super.new(self)
self.visible = true
self.init_size = true
self.tooltip = false
self.toolbar_font = style.icon_big_font
self.toolbar_commands = {
{symbol = "f", command = "core:new-doc"},
{symbol = "D", command = "core:open-file"},
{symbol = "S", command = "doc:save"},
{symbol = "L", command = "core:find-file"},
{symbol = "B", command = "core:find-command"},
{symbol = "P", command = "core:open-user-module"},
}
end
function ToolbarView:update()
local dest_size = self.visible and toolbar_height() or 0
local dest_size = self.visible and (self.toolbar_font:get_height() + style.padding.y * 2) or 0
if self.init_size then
self.size.y = dest_size
self.init_size = nil
@ -46,19 +41,24 @@ function ToolbarView:toggle_visible()
self.visible = not self.visible
end
function ToolbarView:get_icon_width()
local max_width = 0
for i,v in ipairs(self.toolbar_commands) do max_width = math.max(max_width, self.toolbar_font:get_width(v.symbol)) end
return max_width
end
function ToolbarView:each_item()
local icon_h, icon_w = style.icon_big_font:get_height(), style.icon_big_font:get_width("D")
local icon_h, icon_w = self.toolbar_font:get_height(), self:get_icon_width()
local toolbar_spacing = icon_w / 2
local ox, oy = self:get_content_offset()
local index = 0
local iter = function()
index = index + 1
if index <= #toolbar_commands then
if index <= #self.toolbar_commands then
local dx = style.padding.x + (icon_w + toolbar_spacing) * (index - 1)
local dy = style.padding.y
if dx + icon_w > self.size.x then return end
return toolbar_commands[index], ox + dx, oy + dy, icon_w, icon_h
return self.toolbar_commands[index], ox + dx, oy + dy, icon_w, icon_h
end
end
return iter
@ -66,9 +66,9 @@ end
function ToolbarView:get_min_width()
local icon_w = style.icon_big_font:get_width("D")
local icon_w = self:get_icon_width()
local space = icon_w / 2
return 2 * style.padding.x + (icon_w + space) * #toolbar_commands - space
return 2 * style.padding.x + (icon_w + space) * #self.toolbar_commands - space
end
@ -76,19 +76,20 @@ function ToolbarView:draw()
self:draw_background(style.background2)
for item, x, y, w, h in self:each_item() do
local color = item == self.hovered_item and style.text or style.dim
common.draw_text(style.icon_big_font, color, item.symbol, nil, x, y, 0, h)
local color = item == self.hovered_item and command.is_valid(item.command) and style.text or style.dim
common.draw_text(self.toolbar_font, color, item.symbol, nil, x, y, 0, h)
end
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
if self.hovered_item and command.is_valid(self.hovered_item.command) 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
local core = require "core"
local common = require "core.common"
local command = require "core.command"
@ -8,9 +8,13 @@ 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({
-- 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 +43,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 +101,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
@ -98,7 +109,7 @@ end
function TreeView:get_name()
return "Project"
return nil
end
@ -142,34 +153,51 @@ function TreeView:each_item()
count_lines = count_lines + 1
y = y + h
local i = 1
while i <= #dir.files and dir_cached.expanded do
local item = dir.files[i]
local cached = self:get_cached(dir, item, dir.name)
if dir.files then -- if consumed max sys file descriptors this can be nil
while i <= #dir.files and dir_cached.expanded do
local item = dir.files[i]
local cached = self:get_cached(dir, item, dir.name)
coroutine.yield(cached, ox, y, w, h)
count_lines = count_lines + 1
y = y + h
i = i + 1
coroutine.yield(cached, ox, y, w, h)
count_lines = count_lines + 1
y = y + h
i = i + 1
if not cached.expanded then
if cached.skip then
i = cached.skip
else
local depth = cached.depth
while i <= #dir.files do
if get_depth(dir.files[i].filename) <= depth then break end
i = i + 1
if not cached.expanded then
if cached.skip then
i = cached.skip
else
local depth = cached.depth
while i <= #dir.files do
if get_depth(dir.files[i].filename) <= depth then break end
i = i + 1
end
cached.skip = i
end
cached.skip = i
end
end
end -- while files
end -- while files
end
end -- for directories
self.count_lines = count_lines
end)
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
@ -203,50 +237,6 @@ function TreeView:on_mouse_moved(px, py, ...)
end
local function create_directory_in(item)
local path = item.abs_filename
core.command_view:enter("Create directory in " .. path, function(text)
local dirname = path .. PATHSEP .. text
local success, err = system.mkdir(dirname)
if not success then
core.error("cannot create directory %q: %s", dirname, err)
end
item.expanded = true
end)
end
function TreeView:on_mouse_pressed(button, x, y, clicks)
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 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
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)
end
return true
end
function TreeView:update()
-- update width
local dest = self.visible and self.target_size or 0
@ -254,12 +244,14 @@ function TreeView:update()
self.size.x = dest
self.init_size = false
else
self:move_towards(self.size, "x", dest)
self:move_towards(self.size, "x", dest, nil, "treeview")
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)
self:move_towards(self.tooltip, "alpha", tooltip_alpha, tooltip_alpha_rate, "treeview")
else
self.tooltip.alpha = 0
end
@ -267,6 +259,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 +349,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 +369,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,23 +379,86 @@ 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:get_item(item, where)
local last_item, last_x, last_y, last_w, last_h
local stop = false
for it, x, y, w, h in self:each_item() do
if not item and where >= 0 then
return it, x, y, w, h
end
if item == it then
if where < 0 and last_item then
break
elseif where == 0 or (where < 0 and not last_item) then
return it, x, y, w, h
end
stop = true
elseif stop then
item = it
return it, x, y, w, h
end
last_item, last_x, last_y, last_w, last_h = it, x, y, w, h
end
return last_item, last_x, last_y, last_w, last_h
end
function TreeView:get_next(item)
return self:get_item(item, 1)
end
function TreeView:get_previous(item)
return self:get_item(item, -1)
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.depth == 0 and "" or item.filename, item.expanded)
end
end
end
-- init
local view = TreeView()
local node = core.root_view:get_active_node()
local treeview_node = node:split("left", view, {x = true}, true)
view.node = node:split("left", view, {x = true}, true)
-- The toolbarview plugin is special because it is plugged inside
-- a treeview pane which is itelf provided in a plugin.
@ -400,12 +467,12 @@ local treeview_node = node:split("left", view, {x = true}, true)
-- plugin module that plug itself in the active node but it is plugged here
-- in the treeview node.
local toolbar_view = nil
local toolbar_plugin, ToolbarView = core.try(require, "plugins.toolbarview")
local toolbar_plugin, ToolbarView = pcall(require, "plugins.toolbarview")
if config.plugins.toolbarview ~= false and toolbar_plugin then
toolbar_view = ToolbarView()
treeview_node:split("down", toolbar_view, {y = true})
view.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,7 +513,22 @@ 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)
for _,dir in pairs(core.project_directories) do
if dir.name == path then
return true
end
end
return false
end
local function is_primary_project_folder(path)
return core.project_dir == path
end
@ -476,65 +558,143 @@ menu:register(
}
)
menu:register(
function()
return view.hovered_item
and not is_primary_project_folder(view.hovered_item.abs_filename)
and is_project_folder(view.hovered_item.abs_filename)
end,
{
{ text = "Remove directory", command = "treeview:remove-project-directory" },
}
)
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
})
command.add(TreeView, {
["treeview:next"] = function()
local item, _, item_y = view:get_next(view.selected_item)
view:set_selection(item, item_y)
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 .. "/")
["treeview:previous"] = function()
local item, _, item_y = view:get_previous(view.selected_item)
view:set_selection(item, 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
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:delete"] = function()
local filename = view.hovered_item.abs_filename
local relfilename = view.hovered_item.filename
["treeview:deselect"] = function()
view.selected_item = nil
end,
["treeview:select"] = function()
view:set_selection(view.hovered_item)
end,
["treeview:select-and-open"] = function()
if view.hovered_item then
view:set_selection(view.hovered_item)
command.perform "treeview:open"
end
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()
local item = view.selected_item
if not item or item.type ~= "dir" then return end
if item.expanded then
local next_item, _, next_y = view:get_next(item)
if next_item.depth > item.depth then
view:set_selection(next_item, next_y)
end
else
view:toggle_expand(true)
end
end,
})
local function treeitem() return view.hovered_item or view.selected_item end
command.add(
function()
local item = treeitem()
return item ~= 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
), item
end, {
["treeview:delete"] = function(item)
local filename = item.abs_filename
local relfilename = item.filename
if item.dir_name ~= core.project_dir then
-- add secondary project dirs names to the file path to show
relfilename = common.basename(item.dir_name) .. PATHSEP .. relfilename
end
local file_info = system.get_file_info(filename)
local file_type = file_info.type == "dir" and "Directory" or "File"
-- Ask before deleting
@ -568,22 +728,177 @@ command.add(function() return view.hovered_item ~= nil end, {
end
end
)
end
})
command.add(function()
if not (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) then return end
if core.root_view.overlapping_node.active_view ~= view then return end
local item = treeitem()
return item ~= nil, item
end, {
["treeview:rename"] = function(item)
local old_filename = item.filename
local old_abs_filename = item.abs_filename
core.command_view:enter("Rename", {
text = old_filename,
submit = function(filename)
local abs_filename = filename
if not common.is_absolute_path(filename) then
abs_filename = item.dir_name .. PATHSEP .. filename
end
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,
suggest = function(text)
return common.path_suggest(text, item.dir_name)
end
})
end,
["treeview:open-in-system"] = function()
local hovered_item = view.hovered_item
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
system.exec(string.format("xdg-open %q", hovered_item.abs_filename))
["treeview:new-file"] = function(item)
local text
if not is_project_folder(item.abs_filename) then
text = item.filename .. PATHSEP
end
core.command_view:enter("Filename", {
text = text,
submit = function(filename)
local doc_filename = item.dir_name .. PATHSEP .. filename
core.log(doc_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,
suggest = function(text)
return common.path_suggest(text, item.dir_name)
end
})
end,
["treeview:new-folder"] = function(item)
local text
if not is_project_folder(item.abs_filename) then
text = item.filename .. PATHSEP
end
core.command_view:enter("Folder Name", {
text = text,
submit = function(filename)
local dir_path = item.dir_name .. PATHSEP .. filename
common.mkdirp(dir_path)
core.log("Created %s", dir_path)
end,
suggest = function(text)
return common.path_suggest(text, item.dir_name)
end
})
end,
["treeview:open-in-system"] = function(item)
if PLATFORM == "Windows" then
system.exec(string.format("start \"\" %q", item.abs_filename))
elseif string.find(PLATFORM, "Mac") or PLATFORM == "MorphOS" then
system.exec(string.format("open %q", item.abs_filename))
elseif PLATFORM == "Linux" or string.find(PLATFORM, "BSD") then
system.exec(string.format("xdg-open %q", item.abs_filename))
elseif PLATFORM == "AmigaOS 4" then
system.exec(string.format("WBRUN %q SHOW=all VIEWBY=name", item.abs_filename))
end
end
})
local projectsearch = pcall(require, "plugins.projectsearch")
if projectsearch then
menu:register(function()
return view.hovered_item and view.hovered_item.type == "dir"
end, {
{ text = "Find in directory", command = "treeview:search-in-directory" }
})
command.add(function()
return view.hovered_item and view.hovered_item.type == "dir"
end, {
["treeview:search-in-directory"] = function(item)
command.perform("project-search:find", view.hovered_item.abs_filename)
end
})
end
command.add(function()
local item = treeitem()
return item
and not is_primary_project_folder(item.abs_filename)
and is_project_folder(item.abs_filename), item
end, {
["treeview:remove-project-directory"] = function(item)
core.remove_project_directory(item.dir_name)
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",
["lclick"] = "treeview:select-and-open",
["mclick"] = "treeview:select",
["ctrl+lclick"] = "treeview:new-folder"
}
-- The config specification used by gui generators
config.plugins.treeview.config_spec = {
name = "Treeview",
{
label = "Size",
description = "Default treeview width.",
path = "size",
type = "number",
default = toolbar_view and math.ceil(toolbar_view:get_min_width() / SCALE)
or 200 * SCALE,
min = toolbar_view and toolbar_view:get_min_width() / SCALE
or 200 * SCALE,
get_value = function(value)
return value / SCALE
end,
set_value = function(value)
return value * SCALE
end,
on_apply = function(value)
view:set_target_size("x", math.max(
value, toolbar_view and toolbar_view:get_min_width() or 200 * SCALE
))
end
},
{
label = "Hide on Startup",
description = "Show or hide the treeview on startup.",
path = "visible",
type = "toggle",
default = false,
on_apply = function(value)
view.visible = not value
end
}
}
-- 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
local core = require "core"
local command = require "core.command"
local Doc = require "core.doc"
@ -24,8 +24,8 @@ end
command.add("core.docview", {
["trim-whitespace:trim-trailing-whitespace"] = function()
trim_trailing_whitespace(core.active_view.doc)
["trim-whitespace:trim-trailing-whitespace"] = function(dv)
trim_trailing_whitespace(dv.doc)
end,
})

View File

@ -1,4 +1,4 @@
-- mod-version:2 -- lite-xl 2.0
-- mod-version:3
local core = require "core"
local common = require "core.common"
local DocView = require "core.docview"
@ -117,7 +117,7 @@ local function load_view(t)
-- cannot be read.
if dv and dv.doc then
dv.doc:set_selection(table.unpack(t.selection))
dv.last_line, dv.last_col = dv.doc:get_selection()
dv.last_line1, dv.last_col1, dv.last_line2, dv.last_col2 = dv.doc:get_selection()
dv.scroll.x, dv.scroll.to.x = t.scroll.x, t.scroll.x
dv.scroll.y, dv.scroll.to.y = t.scroll.y, t.scroll.y
end

View File

@ -4,6 +4,11 @@
---@type table<integer, string>
ARGS = {}
---The current platform tuple used for native modules loading,
---for example: "x86_64-linux", "x86_64-darwin", "x86_64-windows", etc...
---@type string
ARCH = "Architecture-OperatingSystem"
---The current operating system.
---@type string | "'Windows'" | "'Mac OS X'" | "'Linux'" | "'iOS'" | "'Android'"
PLATFORM = "Operating System"

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

@ -6,7 +6,9 @@
renderer = {}
---
---Represents a color used by the rendering functions.
---Array of bytes that represents a color used by the rendering functions.
---Note: indexes for rgba are numerical 1 = r, 2 = g, 3 = b, 4 = a but for
---documentation purposes the letters r, g, b, a were used.
---@class renderer.color
---@field public r number Red
---@field public g number Green
@ -17,11 +19,13 @@ renderer.color = {}
---
---Represent options that affect a font's rendering.
---@class renderer.fontoptions
---@field public antialiasing "'grayscale'" | "'subpixel'"
---@field public antialiasing "'none'" | "'grayscale'" | "'subpixel'"
---@field public hinting "'slight'" | "'none'" | '"full"'
-- @field public bold boolean
-- @field public italic boolean
-- @field public underline boolean
-- @field public smoothing boolean
-- @field public strikethrough boolean
renderer.fontoptions = {}
---
@ -33,18 +37,29 @@ renderer.font = {}
---
---@param path string
---@param size number
---@param options renderer.fontoptions
---@param options? renderer.fontoptions
---
---@return renderer.font
function renderer.font.load(path, size, options) end
---
---Combines an array of fonts into a single one for broader charset support,
---the order of the list determines the fonts precedence when retrieving
---a symbol from it.
---
---@param fonts renderer.font[]
---
---@return renderer.font
function renderer.font.group(fonts) end
---
---Clones a font object into a new one.
---
---@param size? number Optional new size for cloned font.
---@param options? renderer.fontoptions
---
---@return renderer.font
function renderer.font:copy(size) end
function renderer.font:copy(size, options) end
---
---Set the amount of characters that represent a tab.
@ -81,23 +96,11 @@ function renderer.font:get_size() end
function renderer.font:set_size(size) end
---
---Assistive functionality to replace characters in a
---rendered text with other characters.
---@class renderer.replacements
renderer.replacements = {}
---Get the current path of the font as a string if a single font or as an
---array of strings if a group font.
---
---Create a new character replacements object.
---
---@return renderer.replacements
function renderer.replacements.new() end
---
---Add to internal map a character to character replacement.
---
---@param original_char string Should be a single character like '\t'
---@param replacement_char string Should be a single character like '»'
function renderer.replacements:add(original_char, replacement_char) end
---@return string | table<integer, string>
function renderer.font:get_path() end
---
---Toggles drawing debugging rectangles on the currently rendered sections
@ -141,29 +144,13 @@ function renderer.set_clip_rect(x, y, width, height) end
function renderer.draw_rect(x, y, width, height, color) end
---
---Draw text.
---Draw text and return the x coordinate where the text finished drawing.
---
---@param font renderer.font
---@param text string
---@param x number
---@param y number
---@param color renderer.color
---@param replace renderer.replacements
---@param color_replace renderer.color
---
---@return number x_subpixel
function renderer.draw_text(font, text, x, y, color, replace, color_replace) end
---
---Draw text at subpixel level.
---
---@param font renderer.font
---@param text string
---@param x number
---@param y number
---@param color renderer.color
---@param replace renderer.replacements
---@param color_replace renderer.color
---
---@return number x_subpixel
function renderer.draw_text_subpixel(font, text, x, y, color, replace, color_replace) end
---@return number x
function renderer.draw_text(font, text, x, y, color) end

165
docs/api/string.lua Normal file
View File

@ -0,0 +1,165 @@
---@meta
---UTF-8 equivalent of string.byte
---@param s string
---@param i? integer
---@param j? integer
---@return integer
---@return ...
function string.ubyte(s, i, j) end
---UTF-8 equivalent of string.char
---@param byte integer
---@param ... integer
---@return string
---@return ...
function string.uchar(byte, ...) end
---UTF-8 equivalent of string.find
---@param s string
---@param pattern string
---@param init? integer
---@param plain? boolean
---@return integer start
---@return integer end
---@return ... captured
function string.ufind(s, pattern, init, plain) end
---UTF-8 equivalent of string.gmatch
---@param s string
---@param pattern string
---@param init? integer
---@return fun():string, ...
function string.ugmatch(s, pattern, init) end
---UTF-8 equivalent of string.gsub
---@param s string
---@param pattern string
---@param repl string|table|function
---@param n integer
---@return string
---@return integer count
function string.ugsub(s, pattern, repl, n) end
---UTF-8 equivalent of string.len
---@param s string
---@return integer
function string.ulen(s) end
---UTF-8 equivalent of string.lower
---@param s string
---@return string
function string.ulower(s) end
---UTF-8 equivalent of string.match
---@param s string
---@param pattern string
---@param init? integer
---@return string | number captured
function string.umatch(s, pattern, init) end
---UTF-8 equivalent of string.reverse
---@param s string
---@return string
function string.ureverse(s) end
---UTF-8 equivalent of string.sub
---@param s string
---@param i integer
---@param j? integer
---@return string
function string.usub(s, i, j) end
---UTF-8 equivalent of string.upper
---@param s string
---@return string
function string.uupper(s) end
---Equivalent to utf8.escape()
---@param s string
---@return string utf8_string
function string.uescape(s) end
---Equivalent to utf8.charpos()
---@param s string
---@param charpos? integer
---@param index? integer
---@return integer charpos
---@return integer codepoint
function string.ucharpos(s, charpos, index) end
---Equivalent to utf8.next()
---@param s string
---@param charpos? integer
---@param index? integer
---@return integer charpos
---@return integer codepoint
function string.unext(s, charpos, index) end
---Equivalent to utf8.insert()
---@param s string
---@param idx? integer
---@param substring string
---return string new_string
function string.uinsert(s, idx, substring) end
---Equivalent to utf8.remove()
---@param s string
---@param start? integer
---@param stop? integer
---return string new_string
function string.uremove(s, start, stop) end
---Equivalent to utf8.width()
---@param s string
---@param ambi_is_double? boolean
---@param default_width? integer
---@return integer width
function string.uwidth(s, ambi_is_double, default_width) end
---Equivalent to utf8.widthindex()
---@param s string
---@param location integer
---@param ambi_is_double? boolean
---@param default_width? integer
---@return integer idx
---@return integer offset
---@return integer width
function string.uwidthindex(s, location, ambi_is_double, default_width) end
---Equivalent to utf8.title()
---@param s string
---return string new_string
function string.utitle(s) end
---Equivalent to utf8.fold()
---@param s string
---return string new_string
function string.ufold(s) end
---Equivalent to utf8.ncasecmp()
---@param a string
---@param b string
---@return integer result
function string.uncasecmp(a, b) end
---Equivalent to utf8.offset()
---@param s string
---@param n integer
---@param i? integer
---@return integer position_in_bytes
function string.uoffset(s, n, i) end
---Equivalent to utf8.codepoint()
---@param s string
---@param i? integer
---@param j? integer
---@return integer code
---@return ...
function string.ucodepoint(s, i, j) end
---Equivalent to utf8.codes()
---@param s string
---@return fun():integer, integer
function string.ucodes(s) end

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()

191
docs/api/utf8extra.lua Normal file
View File

@ -0,0 +1,191 @@
---@meta
---Additional utf8 support not provided by lua.
---@class utf8extra
utf8extra = {}
---UTF-8 equivalent of string.byte
---@param s string
---@param i? integer
---@param j? integer
---@return integer
---@return ...
function utf8extra.byte(s, i, j) end
---UTF-8 equivalent of string.char
---@param byte integer
---@param ... integer
---@return string
---@return ...
function utf8extra.char(byte, ...) end
---UTF-8 equivalent of string.find
---@param s string
---@param pattern string
---@param init? integer
---@param plain? boolean
---@return integer start
---@return integer end
---@return ... captured
function utf8extra.find(s, pattern, init, plain) end
---UTF-8 equivalent of string.gmatch
---@param s string
---@param pattern string
---@param init? integer
---@return fun():string, ...
function utf8extra.gmatch(s, pattern, init) end
---UTF-8 equivalent of string.gsub
---@param s string
---@param pattern string
---@param repl string|table|function
---@param n integer
---@return string
---@return integer count
function utf8extra.gsub(s, pattern, repl, n) end
---UTF-8 equivalent of string.len
---@param s string
---@return integer
function utf8extra.len(s) end
---UTF-8 equivalent of string.lower
---@param s string
---@return string
function utf8extra.lower(s) end
---UTF-8 equivalent of string.match
---@param s string
---@param pattern string
---@param init? integer
---@return string | number captured
function utf8extra.match(s, pattern, init) end
---UTF-8 equivalent of string.reverse
---@param s string
---@return string
function utf8extra.reverse(s) end
---UTF-8 equivalent of string.sub
---@param s string
---@param i integer
---@param j? integer
---@return string
function utf8extra.sub(s, i, j) end
---UTF-8 equivalent of string.upper
---@param s string
---@return string
function utf8extra.upper(s) end
---Escape a str to UTF-8 format string. It support several escape format:
---* %ddd - which ddd is a decimal number at any length: change Unicode code point to UTF-8 format.
---* %{ddd} - same as %nnn but has bracket around.
---* %uddd - same as %ddd, u stands Unicode
---* %u{ddd} - same as %{ddd}
---* %xhhh - hexadigit version of %ddd
---* %x{hhh} same as %xhhh.
---* %? - '?' stands for any other character: escape this character.
---Example:
---```lua
---local u = utf8.escape
---print(u"%123%u123%{123}%u{123}%xABC%x{ABC}")
---print(u"%%123%?%d%%u")
---```
---@param s string
---@return string utf8_string
function utf8extra.escape(s) end
---Convert UTF-8 position to byte offset. if only index is given, return byte
---offset of this UTF-8 char index. if both charpos and index is given, a new
---charpos will be calculated, by add/subtract UTF-8 char index to current
---charpos. in all cases, it returns a new char position, and code point
---(a number) at this position.
---@param s string
---@param charpos? integer
---@param index? integer
---@return integer charpos
---@return integer codepoint
function utf8extra.charpos(s, charpos, index) end
---Iterate though the UTF-8 string s. If only s is given, it can used as a iterator:
---```lua
--- for pos, code in utf8.next, "utf8-string" do
--- -- ...
--- end
---````
---If only charpos is given, return the next byte offset of in string. if
---charpos and index is given, a new charpos will be calculated, by add/subtract
---UTF-8 char offset to current charpos. in all case, it return a new char
---position (in bytes), and code point (a number) at this position.
---@param s string
---@param charpos? integer
---@param index? integer
---@return integer charpos
---@return integer codepoint
function utf8extra.next(s, charpos, index) end
---Insert a substring to s. If idx is given, insert substring before char at
---this index, otherwise substring will concat to s. idx can be negative.
---@param s string
---@param idx? integer
---@param substring string
---return string new_string
function utf8extra.insert(s, idx, substring) end
---Delete a substring in s. If neither start nor stop is given, delete the last
---UTF-8 char in s, otherwise delete char from start to end of s. if stop is
---given, delete char from start to stop (include start and stop). start and
---stop can be negative.
---@param s string
---@param start? integer
---@param stop? integer
---return string new_string
function utf8extra.remove(s, start, stop) end
---Calculate the width of UTF-8 string s. if ambi_is_double is given, the
---ambiguous width character's width is 2, otherwise it's 1. fullwidth/doublewidth
---character's width is 2, and other character's width is 1. if default_width is
---given, it will be the width of unprintable character, used display a
---non-character mark for these characters. if s is a code point, return the
---width of this code point.
---@param s string
---@param ambi_is_double? boolean
---@param default_width? integer
---@return integer width
function utf8extra.width(s, ambi_is_double, default_width) end
---Return the character index at given location in string s. this is a reverse
---operation of utf8.width(). this function returns a index of location, and a
---offset in UTF-8 encoding. e.g. if cursor is at the second column (middle)
---of the wide char, offset will be 2. the width of character at idx is
---returned, also.
---@param s string
---@param location integer
---@param ambi_is_double? boolean
---@param default_width? integer
---@return integer idx
---@return integer offset
---@return integer width
function utf8extra.widthindex(s, location, ambi_is_double, default_width) end
---Convert UTF-8 string s to title-case, used to compare by ignore case. if s
---is a number, it's treat as a code point and return a convert code point
---(number). utf8.lower/utf8.pper has the same extension.
---@param s string
---return string new_string
function utf8extra.title(s) end
---Convert UTF-8 string s to folded case, used to compare by ignore case. if s
---is a number, it's treat as a code point and return a convert code point
---(number). utf8.lower/utf8.pper has the same extension.
---@param s string
---return string new_string
function utf8extra.fold(s) end
---Compare a and b without case, -1 means a < b, 0 means a == b and 1 means a > b.
---@param a string
---@param b string
---@return integer result
function utf8extra.ncasecmp(a, b) end

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.1.0',
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,84 @@ 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']
default_fallback_options = ['warning_level=0', 'werror=false']
# Lua has no official .pc file
# so distros come up with their own names
lua_names = [
'lua5.4', # Debian
'lua-5.4', # FreeBSD
'lua', # Fedora
]
foreach lua : lua_names
last_lua = (lua == lua_names[-1] or get_option('wrap_mode') == 'forcefallback')
lua_dep = dependency(lua, fallback: last_lua ? ['lua', 'lua_dep'] : [], required : last_lua,
version: '>= 5.4',
default_options: default_fallback_options + ['default_library=static', 'line_editing=false', 'interpreter=false']
)
if lua_dep.found()
break
endif
endforeach
pcre2_dep = dependency('libpcre2-8', fallback: ['pcre2', 'libpcre2_8'],
default_options: default_fallback_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_fallback_options + ['default_library=static', 'zlib=disabled', 'bzip2=disabled', 'png=disabled', 'harfbuzz=disabled', 'brotli=disabled']
)
sdl_options = ['default_library=static']
# we explicitly need these
sdl_options += 'use_loadso=enabled'
sdl_options += 'prefer_dlopen=true'
sdl_options += 'use_video=enabled'
sdl_options += 'use_atomic=enabled'
sdl_options += 'use_threads=enabled'
sdl_options += 'use_timers=enabled'
# investigate if this is truly needed
# Do not remove before https://github.com/libsdl-org/SDL/issues/5413 is released
sdl_options += 'use_events=enabled'
if host_machine.system() == 'darwin' or host_machine.system() == 'windows'
sdl_options += 'use_video_x11=disabled'
sdl_options += 'use_video_wayland=disabled'
else
sdl_options += 'use_render=enabled'
sdl_options += 'use_video_x11=auto'
sdl_options += 'use_video_wayland=auto'
endif
# we leave this up to what the host system has except on windows
if host_machine.system() != 'windows'
sdl_options += 'use_video_opengl=auto'
sdl_options += 'use_video_openglesv2=auto'
else
sdl_options += 'use_video_opengl=disabled'
sdl_options += 'use_video_openglesv2=disabled'
endif
# we don't need these
sdl_options += 'test=false'
sdl_options += 'use_sensor=disabled'
sdl_options += 'use_haptic=disabled'
sdl_options += 'use_audio=disabled'
sdl_options += 'use_cpuinfo=disabled'
sdl_options += 'use_joystick=disabled'
sdl_options += 'use_video_vulkan=disabled'
sdl_options += 'use_video_offscreen=disabled'
sdl_options += 'use_power=disabled'
sdl_dep = dependency('sdl2', fallback: ['sdl2', 'sdl2_dep'],
default_options: default_fallback_options + sdl_options
)
lite_deps = [lua_dep, sdl_dep, freetype_dep, pcre2_dep, libm, libdl]
endif
#===============================================================================
# Install Configuration
@ -94,21 +187,20 @@ endif
install_data('licenses/licenses.md', install_dir : lite_docdir)
install_subdir('data' / 'core' , install_dir : lite_datadir, exclude_files : 'start.lua')
install_subdir('docs/api' , install_dir : lite_datadir, strip_directory: true)
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

@ -6,6 +6,7 @@
<name>Lite XL</name>
<summary>A lightweight text editor written in Lua</summary>
<content_rating type="oars-1.0" />
<launchable type="desktop-id">org.lite_xl.lite_xl.desktop</launchable>
<description>
<p>
@ -17,11 +18,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

@ -0,0 +1,195 @@
diff -ruN lua-5.4.3/meson.build newlua/meson.build
--- lua-5.4.3/meson.build 2022-05-29 21:04:17.850449500 +0800
+++ newlua/meson.build 2022-06-10 19:23:55.685139800 +0800
@@ -82,6 +82,7 @@
'src/lutf8lib.c',
'src/lvm.c',
'src/lzio.c',
+ 'src/utf8_wrappers.c',
dependencies: lua_lib_deps,
override_options: project_options,
implicit_include_directories: false,
Binary files lua-5.4.3/src/lua54.dll and newlua/src/lua54.dll differ
diff -ruN lua-5.4.3/src/luaconf.h newlua/src/luaconf.h
--- lua-5.4.3/src/luaconf.h 2021-03-15 21:32:52.000000000 +0800
+++ newlua/src/luaconf.h 2022-06-10 19:15:03.014745300 +0800
@@ -786,5 +786,15 @@
+#if defined(lua_c) || defined(luac_c) || (defined(LUA_LIB) && \
+ (defined(lauxlib_c) || defined(liolib_c) || \
+ defined(loadlib_c) || defined(loslib_c)))
+#include "utf8_wrappers.h"
+#endif
+
+
+
+
+
#endif
diff -ruN lua-5.4.3/src/Makefile newlua/src/Makefile
--- lua-5.4.3/src/Makefile 2021-02-10 02:47:17.000000000 +0800
+++ newlua/src/Makefile 2022-06-10 19:22:45.267931400 +0800
@@ -33,7 +33,7 @@
PLATS= guess aix bsd c89 freebsd generic linux linux-readline macosx mingw posix solaris
LUA_A= liblua.a
-CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o
+CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o utf8_wrappers.o
LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)
diff -ruN lua-5.4.3/src/utf8_wrappers.c newlua/src/utf8_wrappers.c
--- lua-5.4.3/src/utf8_wrappers.c 1970-01-01 07:30:00.000000000 +0730
+++ newlua/src/utf8_wrappers.c 2022-06-10 19:13:11.904613300 +0800
@@ -0,0 +1,101 @@
+/**
+ * Wrappers to provide Unicode (UTF-8) support on Windows.
+ *
+ * Copyright (c) 2018 Peter Wu <peter@lekensteyn.nl>
+ * SPDX-License-Identifier: (GPL-2.0-or-later OR MIT)
+ */
+
+#ifdef _WIN32
+#include <windows.h> /* for MultiByteToWideChar */
+#include <wchar.h> /* for _wrename */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+// Set a high limit in case long paths are enabled.
+#define MAX_PATH_SIZE 4096
+#define MAX_MODE_SIZE 128
+// cmd.exe argument length is reportedly limited to 8192.
+#define MAX_CMD_SIZE 8192
+
+FILE *fopen_utf8(const char *pathname, const char *mode) {
+ wchar_t pathname_w[MAX_PATH_SIZE];
+ wchar_t mode_w[MAX_MODE_SIZE];
+ if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, pathname, -1, pathname_w, MAX_PATH_SIZE) ||
+ !MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, mode_w, MAX_MODE_SIZE)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return _wfopen(pathname_w, mode_w);
+}
+
+FILE *freopen_utf8(const char *pathname, const char *mode, FILE *stream) {
+ wchar_t pathname_w[MAX_PATH_SIZE];
+ wchar_t mode_w[MAX_MODE_SIZE];
+ if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, pathname, -1, pathname_w, MAX_PATH_SIZE) ||
+ !MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, mode_w, MAX_MODE_SIZE)) {
+ // Close stream as documented for the error case.
+ fclose(stream);
+ errno = EINVAL;
+ return NULL;
+ }
+ return _wfreopen(pathname_w, mode_w, stream);
+}
+
+int remove_utf8(const char *pathname) {
+ wchar_t pathname_w[MAX_PATH_SIZE];
+ if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, pathname, -1, pathname_w, MAX_PATH_SIZE)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return _wremove(pathname_w);
+}
+
+int rename_utf8(const char *oldpath, const char *newpath) {
+ wchar_t oldpath_w[MAX_PATH_SIZE];
+ wchar_t newpath_w[MAX_PATH_SIZE];
+ if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, oldpath, -1, oldpath_w, MAX_PATH_SIZE) ||
+ !MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, newpath, -1, newpath_w, MAX_PATH_SIZE)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return _wrename(oldpath_w, newpath_w);
+}
+
+FILE *popen_utf8(const char *command, const char *mode) {
+ wchar_t command_w[MAX_CMD_SIZE];
+ wchar_t mode_w[MAX_MODE_SIZE];
+ if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, command, -1, command_w, MAX_CMD_SIZE) ||
+ !MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, mode_w, MAX_MODE_SIZE)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return _wpopen(command_w, mode_w);
+}
+
+int system_utf8(const char *command) {
+ wchar_t command_w[MAX_CMD_SIZE];
+ if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, command, -1, command_w, MAX_CMD_SIZE)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return _wsystem(command_w);
+}
+
+DWORD GetModuleFileNameA_utf8(HMODULE hModule, LPSTR lpFilename, DWORD nSize) {
+ wchar_t filename_w[MAX_PATH + 1];
+ if (!GetModuleFileNameW(hModule, filename_w, MAX_PATH + 1)) {
+ return 0;
+ }
+ return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, filename_w, -1, lpFilename, nSize, NULL, NULL);
+}
+
+HMODULE LoadLibraryExA_utf8(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) {
+ wchar_t pathname_w[MAX_PATH_SIZE];
+ if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, lpLibFileName, -1, pathname_w, MAX_PATH_SIZE)) {
+ SetLastError(ERROR_INVALID_NAME);
+ return NULL;
+ }
+ return LoadLibraryExW(pathname_w, hFile, dwFlags);
+}
+#endif
diff -ruN lua-5.4.3/src/utf8_wrappers.h newlua/src/utf8_wrappers.h
--- lua-5.4.3/src/utf8_wrappers.h 1970-01-01 07:30:00.000000000 +0730
+++ newlua/src/utf8_wrappers.h 2022-06-10 19:22:53.554879400 +0800
@@ -0,0 +1,42 @@
+/**
+ * Wrappers to provide Unicode (UTF-8) support on Windows.
+ *
+ * Copyright (c) 2018 Peter Wu <peter@lekensteyn.nl>
+ * SPDX-License-Identifier: (GPL-2.0-or-later OR MIT)
+ */
+
+#ifdef _WIN32
+
+#if defined(loadlib_c) || defined(lauxlib_c) || defined(liolib_c) || defined(luac_c)
+#include <stdio.h> /* for loadlib_c */
+FILE *fopen_utf8(const char *pathname, const char *mode);
+#define fopen fopen_utf8
+#endif
+
+#ifdef lauxlib_c
+FILE *freopen_utf8(const char *pathname, const char *mode, FILE *stream);
+#define freopen freopen_utf8
+#endif
+
+#ifdef liolib_c
+FILE *popen_utf8(const char *command, const char *mode);
+#define _popen popen_utf8
+#endif
+
+#ifdef loslib_c
+int remove_utf8(const char *pathname);
+int rename_utf8(const char *oldpath, const char *newpath);
+int system_utf8(const char *command);
+#define remove remove_utf8
+#define rename rename_utf8
+#define system system_utf8
+#endif
+
+#ifdef loadlib_c
+#include <windows.h>
+DWORD GetModuleFileNameA_utf8(HMODULE hModule, LPSTR lpFilename, DWORD nSize);
+HMODULE LoadLibraryExA_utf8(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
+#define GetModuleFileNameA GetModuleFileNameA_utf8
+#define LoadLibraryExA LoadLibraryExA_utf8
+#endif
+#endif

View File

@ -1,5 +1,5 @@
#!/bin/env bash
set -ex
set -e
if [ ! -e "src/api/api.h" ]; then
echo "Please run this script from the root directory of Lite XL."
@ -8,6 +8,13 @@ fi
source scripts/common.sh
ARCH="$(uname -m)"
BUILD_DIR="$(get_default_build_dir)"
RUN_BUILD=true
STATIC_BUILD=false
ADDONS=false
BUILD_TYPE="debug"
show_help(){
echo
echo "Usage: $0 <OPTIONS>"
@ -16,22 +23,21 @@ show_help(){
echo
echo "-h --help Show this help and exits."
echo "-b --builddir DIRNAME Sets the name of the build dir (no path)."
echo " Default: 'build'."
echo " Default: '${BUILD_DIR}'."
echo " --debug Debug this script."
echo "-n --nobuild Skips the build step, use existing files."
echo "-s --static Specify if building using static libraries"
echo " by using lhelper tool."
echo "-s --static Specify if building using static libraries."
echo "-v --version VERSION Specify a version, non whitespace separated string."
echo "-a --addons Install 3rd party addons."
echo "-r --release Compile in release mode."
echo
}
ARCH="$(uname -m)"
BUILD_DIR="$(get_default_build_dir)"
RUN_BUILD=true
STATIC_BUILD=false
initial_arg_count=$#
for i in "$@"; do
case $i in
-h|--belp)
-h|--help)
show_help
exit 0
;;
@ -40,10 +46,22 @@ for i in "$@"; do
shift
shift
;;
-a|--addons)
ADDONS=true
shift
;;
--debug)
set -x
shift
;;
-n|--nobuild)
RUN_BUILD=false
shift
;;
-r|--release)
BUILD_TYPE="release"
shift
;;
-s|--static)
STATIC_BUILD=true
shift
@ -59,25 +77,19 @@ for i in "$@"; do
esac
done
# TODO: Versioning using git
#if [[ -z $VERSION && -d .git ]]; then
# VERSION=$(git describe --tags --long | sed 's/^v//; s/\([^-]*-g\)/r\1/; s/-/./g')
#fi
if [[ -n $1 ]]; then
# show help if no valid argument was found
if [ $initial_arg_count -eq $# ]; then
show_help
exit 1
fi
setup_appimagetool() {
if ! which appimagetool > /dev/null ; then
if [ ! -e appimagetool ]; then
if ! wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${ARCH}.AppImage" ; then
echo "Could not download the appimagetool for the arch '${ARCH}'."
exit 1
else
chmod 0755 appimagetool
fi
if [ ! -e appimagetool ]; then
if ! wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${ARCH}.AppImage" ; then
echo "Could not download the appimagetool for the arch '${ARCH}'."
exit 1
else
chmod 0755 appimagetool
fi
fi
}
@ -104,7 +116,14 @@ build_litexl() {
echo "Build lite-xl..."
sleep 1
meson setup --buildtype=release --prefix /usr ${BUILD_DIR}
if [[ $STATIC_BUILD == false ]]; then
meson setup --buildtype=$BUILD_TYPE --prefix=/usr ${BUILD_DIR}
else
meson setup --wrap-mode=forcefallback \
--buildtype=$BUILD_TYPE \
--prefix=/usr \
${BUILD_DIR}
fi
meson compile -C ${BUILD_DIR}
}
@ -121,6 +140,11 @@ generate_appimage() {
cp resources/icons/lite-xl.svg LiteXL.AppDir/
cp resources/linux/org.lite_xl.lite_xl.desktop LiteXL.AppDir/
if [[ $ADDONS == true ]]; then
addons_download "${BUILD_DIR}"
addons_install "${BUILD_DIR}" "LiteXL.AppDir/usr/share/lite-xl"
fi
if [[ $STATIC_BUILD == false ]]; then
echo "Copying libraries..."
@ -153,6 +177,10 @@ generate_appimage() {
version="-$VERSION"
fi
if [[ $ADDONS == true ]]; then
version="${version}-addons"
fi
./appimagetool LiteXL.AppDir LiteXL${version}-${ARCH}.AppImage
}

View File

@ -22,19 +22,25 @@ show_help() {
echo "-B --bundle Create an App bundle (macOS only)"
echo "-P --portable Create a portable binary package."
echo "-O --pgo Use profile guided optimizations (pgo)."
echo "-U --windows-lua-utf Use the UTF8 patch for Lua."
echo " macOS: disabled when used with --bundle,"
echo " Windows: Implicit being the only option."
echo "-r --release Compile in release mode."
echo
}
main() {
local platform="$(get_platform_name)"
local build_dir="$(get_default_build_dir)"
local build_type="debug"
local prefix=/
local force_fallback
local bundle
local portable
local pgo
local patch_lua
local lua_subproject_path
for i in "$@"; do
case $i in
@ -76,6 +82,14 @@ main() {
pgo="-Db_pgo=generate"
shift
;;
-U|--windows-lua-utf)
patch_lua="true"
shift
;;
-r|--release)
build_type="release"
shift
;;
*)
# unknown option
;;
@ -95,7 +109,7 @@ main() {
rm -rf "${build_dir}"
CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS meson setup \
--buildtype=release \
--buildtype=$build_type \
--prefix "$prefix" \
$force_fallback \
$bundle \
@ -103,6 +117,11 @@ main() {
$pgo \
"${build_dir}"
lua_subproject_path=$(echo subprojects/lua-*/)
if [[ $patch_lua == "true" ]] && [[ ! -z $force_fallback ]] && [[ -d $lua_subproject_path ]]; then
patch -d $lua_subproject_path -p1 --forward < resources/windows/001-lua-unicode.diff
fi
meson compile -C "${build_dir}"
if [ ! -z ${pgo+x} ]; then

View File

@ -2,6 +2,64 @@
set -e
addons_download() {
local build_dir="$1"
if [[ -d "${build_dir}/third/data/colors" ]]; then
echo "Warning: found previous addons installation, skipping."
echo " addons path: ${build_dir}/third/data/colors"
return 0
fi
# Download third party color themes
curl --insecure \
-L "https://github.com/lite-xl/lite-xl-colors/archive/master.zip" \
-o "${build_dir}/lite-xl-colors.zip"
mkdir -p "${build_dir}/third/data/colors"
unzip "${build_dir}/lite-xl-colors.zip" -d "${build_dir}"
mv "${build_dir}/lite-xl-colors-master/colors" "${build_dir}/third/data"
rm -rf "${build_dir}/lite-xl-colors-master"
# Download widgets library
curl --insecure \
-L "https://github.com/lite-xl/lite-xl-widgets/archive/master.zip" \
-o "${build_dir}/lite-xl-widgets.zip"
unzip "${build_dir}/lite-xl-widgets.zip" -d "${build_dir}"
mv "${build_dir}/lite-xl-widgets-master" "${build_dir}/third/data/widget"
# Downlaod thirdparty plugins
curl --insecure \
-L "https://github.com/lite-xl/lite-xl-plugins/archive/2.1.zip" \
-o "${build_dir}/lite-xl-plugins.zip"
unzip "${build_dir}/lite-xl-plugins.zip" -d "${build_dir}"
mv "${build_dir}/lite-xl-plugins-2.1/plugins" "${build_dir}/third/data"
rm -rf "${build_dir}/lite-xl-plugins-2.1"
}
# Addons installation: some distributions forbid external downloads
# so make it as optional module.
addons_install() {
local build_dir="$1"
local data_dir="$2"
for module_name in colors widget; do
cp -r "${build_dir}/third/data/$module_name" "${data_dir}"
done
mkdir -p "${data_dir}/plugins"
for plugin_name in settings open_ext; do
cp -r "${build_dir}/third/data/plugins/${plugin_name}.lua" \
"${data_dir}/plugins/"
done
cp "${build_dir}/third/data/plugins/"language_* \
"${data_dir}/plugins/"
}
get_platform_name() {
if [[ "$OSTYPE" == "msys" ]]; then
echo "windows"
@ -14,9 +72,23 @@ get_platform_name() {
fi
}
get_platform_arch() {
platform=$(get_platform_name)
arch=$(uname -m)
if [[ $MSYSTEM != "" ]]; then
if [[ $MSYSTEM == "MINGW64" ]]; then
arch=x86_64
else
arch=i686
fi
fi
echo "$arch"
}
get_default_build_dir() {
platform=$(get_platform_name)
echo "build-$platform-$(uname -m)"
arch=$(get_platform_arch)
echo "build-$platform-$arch"
}
if [[ $(get_platform_name) == "UNSUPPORTED-OS" ]]; then

View File

@ -15,15 +15,29 @@ show_help() {
echo
echo "-b --builddir DIRNAME Sets the name of the build directory (not path)."
echo " Default: '$(get_default_build_dir)'."
echo "-v --version VERSION Sets the version on the package name."
echo "-a --addons Tell the script we are packaging an install with addons."
echo " --debug Debug this script."
echo
}
main() {
local build_dir=$(get_default_build_dir)
local addons=false
local arch
local arch_file
local version
local output
if [[ $MSYSTEM == "MINGW64" ]]; then arch=x64; else arch=Win32; fi
if [[ $MSYSTEM == "MINGW64" ]]; then
arch=x64
arch_file=x86_64
else
arch=i686;
arch_file=i686
fi
initial_arg_count=$#
for i in "$@"; do
case $i in
@ -31,11 +45,20 @@ main() {
show_help
exit 0
;;
-a|--addons)
addons=true
shift
;;
-b|--builddir)
build_dir="$2"
shift
shift
;;
-v|--version)
if [[ -n $2 ]]; then version="-$2"; fi
shift
shift
;;
--debug)
set -x
shift
@ -46,19 +69,19 @@ main() {
esac
done
if [[ -n $1 ]]; then
# show help if no valid argument was found
if [ $initial_arg_count -eq $# ]; then
show_help
exit 1
fi
# Copy MinGW libraries dependencies.
# MSYS2 ldd command seems to be only 64bit, so use ntldd
# see https://github.com/msys2/MINGW-packages/issues/4164
local mingwLibsDir="${build_dir}/mingwLibs$arch"
mkdir -p "$mingwLibsDir"
ntldd -R "${build_dir}/src/lite-xl.exe" | grep mingw | awk '{print $3}' | sed 's#\\#/#g' | xargs -I '{}' cp -v '{}' $mingwLibsDir
if [[ $addons == true ]]; then
version="${version}-addons"
fi
"/c/Program Files (x86)/Inno Setup 6/ISCC.exe" -dARCH=$arch "${build_dir}/scripts/innosetup.iss"
output="LiteXL${version}-${arch_file}-setup"
"/c/Program Files (x86)/Inno Setup 6/ISCC.exe" -dARCH=$arch //F"${output}" "${build_dir}/scripts/innosetup.iss"
pushd "${build_dir}/scripts"; mv LiteXL*.exe "./../../"; popd
}

View File

@ -48,7 +48,7 @@ main() {
if [[ $lhelper == true ]]; then
sudo apt-get install -qq ninja-build
else
sudo apt-get install -qq ninja-build libsdl2-dev libfreetype6
sudo apt-get install -qq libfuse2 ninja-build wayland-protocols libsdl2-dev libfreetype6
fi
pip3 install meson
elif [[ "$OSTYPE" == "darwin"* ]]; then
@ -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

@ -51,25 +51,23 @@ main() {
pushd lhelper; bash install "${lhelper_prefix}"; popd
if [[ "$OSTYPE" == "darwin"* ]]; then
CC=clang CXX=clang++ lhelper create lite-xl -n
CC=clang CXX=clang++ lhelper create build
else
lhelper create lite-xl -n
lhelper create lite-xl build
fi
fi
# Not using $(lhelper activate lite-xl) to support CI
source "$(lhelper env-source lite-xl)"
lhelper install freetype2
lhelper install sdl2 2.0.14-wait-event-timeout-1
lhelper install pcre2
source "$(lhelper env-source build)"
# Help MSYS2 to find the SDL2 include and lib directories to avoid errors
# during build and linking when using lhelper.
if [[ "$OSTYPE" == "msys" ]]; then
CFLAGS=-I${LHELPER_ENV_PREFIX}/include/SDL2
LDFLAGS=-L${LHELPER_ENV_PREFIX}/lib
fi
# Francesco: not sure why this is needed. I have never observed the problem when
# building on window.
# if [[ "$OSTYPE" == "msys" ]]; then
# CFLAGS=-I${LHELPER_ENV_PREFIX}/include/SDL2
# LDFLAGS=-L${LHELPER_ENV_PREFIX}/lib
# fi
}
main
main "$@"

View File

@ -20,44 +20,19 @@ show_help() {
echo "-h --help Show this help and exit."
echo "-p --prefix PREFIX Install directory prefix. Default: '/'."
echo "-v --version VERSION Sets the version on the package name."
echo " --addons Install 3rd party addons (currently Lite XL colors)."
echo "-a --addons Install 3rd party addons."
echo " --debug Debug this script."
echo "-A --appimage Create an AppImage (Linux only)."
echo "-B --binary Create a normal / portable package or macOS bundle,"
echo " depending on how the build was configured. (Default.)"
echo "-D --dmg Create a DMG disk image with AppDMG (macOS only)."
echo "-I --innosetup Create a InnoSetup package (Windows only)."
echo "-r --release Strip debugging symbols."
echo "-S --source Create a source code package,"
echo " including subprojects dependencies."
echo
}
# Addons installation: some distributions forbid external downloads
# so make it as optional module.
install_addons() {
local build_dir="$1"
local data_dir="$2"
if [[ -d "${build_dir}/third/data/colors" ]]; then
echo "Warning: found previous colors addons installation, skipping."
return 0
fi
# Copy third party color themes
curl --insecure \
-L "https://github.com/lite-xl/lite-xl-colors/archive/master.zip" \
-o "${build_dir}/lite-xl-colors.zip"
mkdir -p "${build_dir}/third/data/colors"
unzip "${build_dir}/lite-xl-colors.zip" -d "${build_dir}"
mv "${build_dir}/lite-xl-colors-master/colors" "${build_dir}/third/data"
rm -rf "${build_dir}/lite-xl-colors-master"
for module_name in colors; do
cp -r "${build_dir}/third/data/$module_name" "${data_dir}"
done
}
source_package() {
local build_dir=build-src
local package_name=$1
@ -85,7 +60,7 @@ source_package() {
}
main() {
local arch="$(uname -m)"
local arch="$(get_platform_arch)"
local platform="$(get_platform_name)"
local build_dir="$(get_default_build_dir)"
local dest_dir=lite-xl
@ -96,8 +71,12 @@ main() {
local binary=false
local dmg=false
local innosetup=false
local release=false
local source=false
# store the current flags to easily pass them to appimage script
local flags="$@"
for i in "$@"; do
case $i in
-b|--builddir)
@ -152,11 +131,15 @@ main() {
fi
shift
;;
-r|--release)
release=true
shift
;;
-S|--source)
source=true
shift
;;
--addons)
-a|--addons)
addons=true
shift
;;
@ -170,6 +153,10 @@ main() {
esac
done
if [[ $addons == true ]]; then
version="$version-addons"
fi
if [[ -n $1 ]]; then show_help; exit 1; fi
# The source package doesn't require a previous build,
@ -190,6 +177,7 @@ main() {
local data_dir="$(pwd)/${dest_dir}/data"
local exe_file="$(pwd)/${dest_dir}/lite-xl"
local package_name=lite-xl$version-$platform-$arch
local bundle=false
local portable=false
@ -202,6 +190,14 @@ main() {
if [[ $platform == "windows" ]]; then
exe_file="${exe_file}.exe"
stripcmd="strip --strip-all"
# Copy MinGW libraries dependencies.
# MSYS2 ldd command seems to be only 64bit, so use ntldd
# see https://github.com/msys2/MINGW-packages/issues/4164
ntldd -R "${exe_file}" \
| grep mingw \
| awk '{print $3}' \
| sed 's#\\#/#g' \
| xargs -I '{}' cp -v '{}' "$(pwd)/${dest_dir}/"
else
# Windows archive is always portable
package_name+="-portable"
@ -216,18 +212,21 @@ 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
mkdir -p "${data_dir}"
if [[ $addons == true ]]; then install_addons "${build_dir}" "${data_dir}"; fi
if [[ $addons == true ]]; then
addons_download "${build_dir}"
addons_install "${build_dir}" "${data_dir}"
fi
# TODO: use --skip-subprojects when 0.58.0 will be available on supported
# distributions to avoid subprojects' include and lib directories to be copied.
@ -238,8 +237,11 @@ main() {
find . -type d -empty -delete
popd
$stripcmd "${exe_file}"
if [[ $release == true ]]; then
$stripcmd "${exe_file}"
fi
echo "Creating a compressed archive ${package_name}"
if [[ $binary == true ]]; then
rm -f "${package_name}".tar.gz
rm -f "${package_name}".zip
@ -251,9 +253,15 @@ main() {
fi
fi
if [[ $appimage == true ]]; then source scripts/appimage.sh; fi
if [[ $bundle == true && $dmg == true ]]; then source scripts/appdmg.sh "${package_name}"; fi
if [[ $innosetup == true ]]; then source scripts/innosetup/innosetup.sh -b "${build_dir}"; fi
if [[ $appimage == true ]]; then
source scripts/appimage.sh $flags --static
fi
if [[ $bundle == true && $dmg == true ]]; then
source scripts/appdmg.sh "${package_name}"
fi
if [[ $innosetup == true ]]; then
source scripts/innosetup/innosetup.sh $flags
fi
}
main "$@"

View File

@ -1,19 +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_dirmonitor(lua_State* L);
int luaopen_utf8extra(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 },
{ "utf8extra", luaopen_utf8extra },
{ 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))

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