Compare commits

...

340 Commits

Author SHA1 Message Date
Jakub Panek c9e4c33948 chore: were spellcecked nauw (#3887)
CI / Formatting and spellchecking (push) Has been cancelled
CI / Clippy on macos-latest (push) Has been cancelled
CI / Clippy on ubuntu-latest (push) Has been cancelled
CI / Clippy on windows-latest (push) Has been cancelled
CI / Rust on macos-latest (push) Has been cancelled
CI / Rust on ubuntu-latest (push) Has been cancelled
CI / Rust on windows-latest (push) Has been cancelled
using crate-ci/typos
2026-04-03 19:56:12 +02:00
Бог311_ 15f2f19bfd fix: remove duplicate issue ref and add missing fixes for v0.4.6 (#3877)
Remove duplicate 3821 entry and add missing 3818 (editor tab
selectability/flickering) and 3819 (cursor style inconsistencies)
which were fixed in v0.4.6 per CHANGELOG.md but absent from the
AppStream metainfo.
2026-04-03 17:31:20 +02:00
Jakub Panek 0349ba04ca ci: drop permissions where not needed, use commit hash for actions 2026-03-26 23:43:35 +01:00
Jakub Panek b7ef6f9884 ci: remove rust 1.94 workaround 2026-03-26 19:13:04 +01:00
Jakub Panek 30cfb663aa fix: update floem for gpu fallback, add crash notif on unix 2026-03-16 08:30:59 +01:00
Jakub Panek 80a0e0ddd0 ci: update actions, use separate cargo profile, upload builds 2026-03-14 04:26:04 +01:00
Jakub Panek f8326d8b34 ci: fix rust 1.94 OpenOptionsExt windows bug
https://github.com/rust-lang/rust/issues/153486
2026-03-12 03:07:21 +01:00
Jakub Panek bc83f0f89a gitignore: add tar/gz 2026-03-12 01:10:57 +01:00
Jakub Panek 4ae2437e74 ci: remove fedora 41, add fedora 43 2026-03-12 01:10:57 +01:00
Jakub Panek 8ce7c354e4 fix: use downlevel defaults for device limits
fixes https://github.com/lapce/lapce/issues/3848
2026-03-12 01:10:57 +01:00
Jakub Panek b3017c17d9 fix: trim whitespace from settings inputs
Prevents from having invalid config values because someone clicks Enter key
2026-03-12 01:10:57 +01:00
Jakub Panek 2c17bd535f fix: context menu in editor state reset after pointerup event
fixes https://github.com/lapce/lapce/issues/3810
2026-03-12 01:10:57 +01:00
Jakub Panek 6cefd27acc ci: update to macos-15 2026-03-12 01:10:57 +01:00
Jakub Panek 28df4a3499 chore: format code 2026-03-12 01:10:57 +01:00
Dongdong Zhou ef488ca4a5 code clean up 2026-02-27 15:20:11 +00:00
Jakub Panek b012cef466 release: v0.4.6
Release / tagname (push) Has been cancelled
Release / deb (ubuntu, plucky) (push) Has been cancelled
Release / rpm (fedora, 41) (push) Has been cancelled
Release / rpm (fedora, 42) (push) Has been cancelled
Release / lapce-proxy (alpine, ) (push) Has been cancelled
Release / macos (push) Has been cancelled
Release / publish (push) Has been cancelled
Release / windows (push) Has been cancelled
Release / linux (push) Has been cancelled
Release / deb (debian, bookworm) (push) Has been cancelled
Release / deb (debian, bullseye) (push) Has been cancelled
Release / deb (ubuntu, jammy) (push) Has been cancelled
Release / deb (ubuntu, noble) (push) Has been cancelled
2026-01-21 20:44:40 +01:00
Jakub Panek d92e8a8a78 fix: update window scale when it changes via settings
fixes https://github.com/lapce/lapce/issues/3832
2026-01-14 17:26:43 +01:00
Wes 59ce6df700 language.rs: json, add har (#3853) 2026-01-03 04:17:25 +01:00
Jakub Panek a886d68d0b docs: remove mutable.ai
does not exist anymore
2025-12-10 20:00:58 +01:00
Arthur Cosentino 5aef5e6178 Fix "diff" view scrolling issues (#3822)
* Fix editor pointer events blocked by diff overlay

* Allow mouse wheel scrolling when hovering over diff_show_more_section_view

* Fix diff_show_more_section_view positioning with wrapped lines

* Fix diff rendering skipping by only visual lines and not real lines

* Scroll sliding when hovering over diff section header

* Improve performance when skipping over lines in diffs

* Update changelog

* Update CHANGELOG.md

Co-authored-by: Ryan Brue <ryanbrue.dev@gmail.com>

---------

Co-authored-by: Ryan Brue <ryanbrue.dev@gmail.com>
2025-11-29 11:28:24 +01:00
Jakub Panek 78871fe727 ci: re-add vendor tarball 2025-11-29 11:15:25 +01:00
Dongdong Zhou 1d5ee125f4 clippy fix 2025-11-25 20:36:34 +00:00
Arthur Cosentino 707f94d4b7 Fix editor tab selection and flickering during reordering (#3818)
* Fix flickering when reordering editor tabs

* Fix editor tabs not selectable while appearing selectable

* Update change log
2025-10-31 16:31:23 +01:00
Arthur Cosentino c426503c25 CursorStyle::Pointer consistency (#3819) 2025-10-31 16:28:31 +01:00
Jakub Panek fdfeebbf6a ci: remove ubuntu oracular, add plucky (#3808)
* ci: remove ubuntu oracular, add plucky

* ci: verbose output for notarize-action
2025-10-04 15:14:23 +02:00
Jakub Panek 9cdbcf6bdf ci: run ci on workflow change 2025-09-10 09:46:18 +02:00
Jakub Panek dc3ea80094 docs: fix rust version
fixes #3574
2025-09-07 21:46:06 +02:00
Jakub Panek d36246f803 fix: metainfo releases dates 2025-09-06 00:48:06 +02:00
Jakub Panek 8f98d8eb97 fix: metainfo screenshot url 2025-09-05 20:18:33 +02:00
Jakub Panek d61511609d fix: metainfo wrong tag 2025-09-05 19:44:46 +02:00
Jakub Panek d979e6914e release: v0.4.5
Release / tagname (push) Has been cancelled
Release / windows (push) Has been cancelled
Release / linux (push) Has been cancelled
Release / deb (debian, bookworm) (push) Has been cancelled
Release / deb (debian, bullseye) (push) Has been cancelled
Release / deb (ubuntu, jammy) (push) Has been cancelled
Release / deb (ubuntu, noble) (push) Has been cancelled
Release / deb (ubuntu, oracular) (push) Has been cancelled
Release / rpm (fedora, 41) (push) Has been cancelled
Release / rpm (fedora, 42) (push) Has been cancelled
Release / lapce-proxy (alpine, ) (push) Has been cancelled
Release / macos (push) Has been cancelled
Release / publish (push) Has been cancelled
2025-09-05 18:15:14 +02:00
Jakub Panek 4fcd496511 chore: bump dependencies, try to remove winapi 2025-09-05 03:13:50 +02:00
Jakub Panek d41b4d52a8 github: split dependencies into more groups 2025-09-03 00:53:49 +02:00
Jakub Panek 9467bcea20 chore: bump floem
fixes https://github.com/lapce/lapce/issues/3795
2025-09-03 00:33:03 +02:00
Jakub Panek 107a310063 buffer: decouple kotlinbuildscript to own id
closes https://github.com/lapce/lapce/pull/3784
2025-09-03 00:32:57 +02:00
Reim-developer f543ba0d97 Fix: Add Kotlin Build Script to 'SyntaxProperties' 2025-09-03 00:21:28 +02:00
Jakub Panek 314e74fcfe release: v0.4.4
Release / tagname (push) Has been cancelled
Release / deb (ubuntu, noble) (push) Has been cancelled
Release / deb (ubuntu, oracular) (push) Has been cancelled
Release / rpm (fedora, 41) (push) Has been cancelled
Release / rpm (fedora, 42) (push) Has been cancelled
Release / lapce-proxy (alpine, ) (push) Has been cancelled
Release / windows (push) Has been cancelled
Release / linux (push) Has been cancelled
Release / deb (debian, bookworm) (push) Has been cancelled
Release / deb (debian, bullseye) (push) Has been cancelled
Release / deb (ubuntu, jammy) (push) Has been cancelled
Release / macos (push) Has been cancelled
Release / publish (push) Has been cancelled
2025-08-30 07:01:02 +02:00
Jakub Panek b52433e567 fix: mismatched_lifetime_syntaxes 2025-08-30 05:56:59 +02:00
Jakub Panek ba703dd4aa chore: bump floem
fixes https://github.com/lapce/lapce/issues/3786
2025-08-30 05:56:46 +02:00
Jakub Panek adaf8302b1 ci: re-enable vendor, add workaround for dpi crate (#3779) 2025-06-28 21:53:10 +02:00
Jakub Panek 9531f60fd4 ci: update linux distros (#3778) 2025-06-28 20:50:57 +02:00
Dongdong Zhou 8bfcc8456d bump to v0.4.3
Release / tagname (push) Has been cancelled
Release / windows (push) Has been cancelled
Release / linux (push) Has been cancelled
Release / deb (debian, bookworm) (push) Has been cancelled
Release / deb (debian, bullseye) (push) Has been cancelled
Release / deb (ubuntu, focal) (push) Has been cancelled
Release / deb (ubuntu, jammy) (push) Has been cancelled
Release / deb (ubuntu, noble) (push) Has been cancelled
Release / deb (ubuntu, oracular) (push) Has been cancelled
Release / rpm (fedora, 39) (push) Has been cancelled
Release / rpm (fedora, 40) (push) Has been cancelled
Release / lapce-proxy (alpine, 3-20) (push) Has been cancelled
Release / macos (push) Has been cancelled
Release / publish (push) Has been cancelled
2025-06-26 21:06:31 +01:00
Integral f3994aa939 feat: update file dialog titles for better clarity (#3774) 2025-06-20 21:01:06 +01:00
Dongdong Zhou a289ebd687 update dependency 2025-06-19 21:42:04 +01:00
Hanif Ariffin 8d50fdadd1 Fix compilation with Rust 2024 Edition (#3637)
Signed-off-by: Hanif Ariffin <hanif.ariffin.4326@gmail.com>
2025-06-19 08:52:17 +01:00
sT331h0rs3 ce6d273f39 Use Diff syntax for files with .diff/.patch extensions by default. (#3770) 2025-06-06 22:58:22 +02:00
Gorg Worgington eb83cee172 Set pointer_events_none on empty view (#3764) 2025-05-20 09:18:42 +01:00
Jakub Panek 1a6bd6d571 fix: git2 linking on windows (#3765) 2025-05-18 03:20:30 +02:00
Gorg Worgington 4878ea3a97 Fix pointer events getting intercepted (#3762) 2025-05-14 14:42:34 +01:00
Dongdong Zhou 24d01303ca disable vendor for now
https://github.com/rust-lang/cargo/issues/10310
2025-05-03 21:30:37 +01:00
Dongdong Zhou ae539020c9 Upstream winit (#3622)
* in progress

* in progress

* bump floem

* in progress

* fix paint_text

* bump floem

* clippy fix

* change some icons

* clear diff left/right editor from editors
2025-05-02 22:33:51 +01:00
Marcin Kolenda 5d93b62654 Docs improvement (minor) (#3666)
* Remove UD from awk usage in 'make help'

The AWK field separator defined by the regular expression `:.*?##` has undefined behavior. According to the GNU Awk documentation the '?' symbol must be preceded by a character specifier.

See: https://www.gnu.org/software/gawk/manual/gawk.html#Regexp-Operators-in-awk cf. paragraph starting with the 'According to the POSIX specification...'.

* Makefile header comment for macOS context

* Improve description

 - `lapce.spec` file is no longer mentioning druid UI toolkit.
 - improve punctuation in descriptions.
2025-05-02 15:24:55 +01:00
Dongdong Zhou 02a870c165 cargo fmt 2025-04-30 20:42:42 +01:00
Alan Lu 1d6ba03b08 only register lapce dir in PATH if not already present (#3648)
* only register lapce dir in PATH if not already present

* reverted name change of register_lapce_path
2025-04-30 20:33:55 +01:00
Tokito b411efbf46 fix! bash Syntax highlighting (#3636)
- added sh to bash Syntax highlighting
- sh extension is what most bash scripts use
2025-04-30 20:29:36 +01:00
yan4rust 115bc731f5 Focus window when open file in a existing window (#3643)
* focus window when open doc

* Delete unnecessary calls

---------

Co-authored-by: yuan4rust <yuan4rust@gmail.com>
2025-03-20 10:36:28 +00:00
Jakub Panek c6959764c6 reduce dependabot prs 2025-02-25 05:06:18 +00:00
Jakub Panek f1f6a651de update openssl 2025-02-25 05:03:09 +00:00
Jakub Panek bfdf7cb1c1 update num_cpus 2025-02-25 05:00:34 +00:00
Jakub Panek e62abcc3e2 Fix clippy 2025-02-09 13:07:15 +01:00
Jakub Panek 13d1cb4d87 Add OutOfMemory error, add error source for unknown 2025-02-09 13:04:09 +01:00
Jakub Panek 795ea0d317 fix clippy 2025-01-17 12:51:30 +01:00
Jakub Panek 7100aa459e remove ubuntu lunar/mantic, add oracular 2024-11-24 02:15:51 +01:00
Jakub Panek 387a24b070 remove ubuntu lunar/mantic, add oracular, add fedora 41 2024-11-21 12:46:21 +01:00
Jakub Panek a2aedb0cd3 fix newlines 2024-11-21 12:36:24 +01:00
Jakub Panek 6ef86351fc fix: hide process window when sourcing shell env (#3577)
Fixes #3571
2024-10-24 11:36:18 +02:00
Jakub Panek ef22ae0c6d cargo: link statically vcruntime 2024-10-24 10:54:28 +02:00
ifengqi 3a40e6b729 cancel to get folding range (#3576) 2024-10-23 21:20:28 +01:00
Dongdong Zhou 2b4fb8eb2c update devcontainer config 2024-10-22 20:48:49 +01:00
Dongdong Zhou 915a8eb6c1 Update README.md 2024-10-18 21:21:13 +01:00
Dongdong Zhou 9100b94167 Update README.md 2024-10-18 21:15:29 +01:00
Dongdong Zhou 2ac5de944c Update README.md 2024-10-18 21:13:52 +01:00
Dongdong Zhou c47bb53809 Update README.md 2024-10-18 18:56:43 +01:00
ifengqi 02ed2fe2cf Temporarily cancel the click event and set Unfold to default status, because being folded will cause a scrolling problem. (#3572) 2024-10-18 09:11:46 +01:00
Dongdong Zhou 10c17453c2 add devcontainer 2024-10-16 19:08:41 +01:00
ifengqi bbe8b79569 Support LSP commands to display CodeLens in the latest Rust Analyzer. (#3565) 2024-10-11 14:39:35 +01:00
ifengqi c826f94ec1 Display the workspace file and path in the File Explorer panel. (#3563)
* Display the workspace file and path in the `File Explorer` panel.

* Fix folder display when the workspace is null
2024-10-11 14:38:47 +01:00
ifengqi 3c73763b2e The setting value should update when the settings file changes. (#3564) 2024-10-11 14:37:21 +01:00
Dongdong Zhou 2268ae14c4 Update README.md 2024-10-03 20:49:05 +01:00
Dongdong Zhou 8e12e0664f Update README.md 2024-10-03 20:48:12 +01:00
ifengqi f82d2f80e4 Update the inlay hint after receiving the notification. (#3562) 2024-10-02 14:05:14 +01:00
ifengqi 061c986c85 Update the inlay hint after receiving the notification. (#3561) 2024-10-02 13:56:49 +01:00
ifengqi 44a9bf7fb5 Sort and filter Reference (#3558) 2024-10-02 13:56:02 +01:00
ifengqi 34c316e47a Update the inlay hint after receiving the notification. (#3560) 2024-10-02 13:55:47 +01:00
ifengqi cde488fb89 Only render the styling without displaying messages, provided that Enable ErrorLens is enabled (#3556) 2024-10-01 10:40:09 +01:00
ifengqi dc9fa918d0 display folding icon (#3552) 2024-10-01 10:38:48 +01:00
ifengqi 74729f8862 fix debug name (#3557) 2024-10-01 10:37:10 +01:00
ifengqi f81d162c47 add the file symbol to document symbol panel (#3547) 2024-09-30 15:41:49 +01:00
ifengqi a5b7accf61 Implement the foldingRange request in the LSP (#3544) 2024-09-30 15:35:15 +01:00
ifengqi ca0e689a06 change GoToLocation to JumpToLocation (#3539) 2024-09-30 15:34:53 +01:00
ifengqi db8ac2cdb6 Handle the file deletion event. (#3541) 2024-09-25 09:55:04 +01:00
ifengqi d75ce05ba9 Launch DAP with environment variables. (#3540) 2024-09-25 09:53:11 +01:00
ifengqi a99ce3e626 Format the document before auto-saving. (#3527)
* Format the document before auto-saving.

* check format_on_save

* 1. Verify rev-matching
2024-09-24 10:33:00 +01:00
ifengqi ea9b7a9c9c 1. Fix the PathBuf value in file monitoring events in the Windows environment (#3538) 2024-09-24 09:30:16 +01:00
ifengqi b6d11b39dc Navigate to the location when clicking the sticky header. (#3532) 2024-09-23 08:49:28 +01:00
ifengqi b12d2b8a02 1. Automatically jump to the first position. (#3530)
2. Jump only when triggered by changes in the search string, and limit the jump to one time.
2024-09-23 08:46:19 +01:00
ifengqi d2627c1240 change GoToLocation to JumpToLocation (#3528) 2024-09-20 13:59:14 +01:00
ifengqi a31277fd78 Add "Stop" and "Restart" commands to the right-click menu in the terminal. (#3526) 2024-09-20 10:31:50 +01:00
ifengqi d88fc896d7 1.fix typo (#3523) 2024-09-19 13:20:59 +01:00
ifengqi 4e7d353f44 Fix the issue where the width is too short. (#3522) 2024-09-19 13:20:32 +01:00
ifengqi d5017ff859 Convenient operations: Right-click to copy the message and double-click to close it (#3521) 2024-09-19 11:53:35 +01:00
ifengqi 7dc7d0707d Implement the LSP $/cancelRequest notification (#3519) 2024-09-19 11:49:07 +01:00
ifengqi e1a409f77c Optimize the display of references (#3518) 2024-09-19 11:48:44 +01:00
ifengqi 44b546c419 1. Add the core setting auto_reload_plugin: Enable auto-reload for the plugin when its configuration changes (#3517) 2024-09-19 11:48:32 +01:00
ifengqi 18b1a70d02 1. Sending the 'UpdatePluginConfigs' proxy notification when the plugin's configuration changes. (#3516)
2. add some logs
2024-09-18 09:47:13 +01:00
ifengqi cfb7d204da Add thread names for easier tracking and troubleshooting. (#3510) 2024-09-18 09:46:24 +01:00
ifengqi b0ea4481b1 Optimize the display of implementations (#3509) 2024-09-18 09:43:57 +01:00
ifengqi e69729efa1 Add the 'references_resolve' method to the proxy. (#3507) 2024-09-16 10:52:28 +01:00
ifengqi 0115151aa2 Reimplement the functionality to capture terminal output. (#3506) 2024-09-16 10:48:34 +01:00
ifengqi 2773c5c9ff Fix the issue where commands without a keymap would disappear. (#3504) 2024-09-16 10:38:54 +01:00
ifengqi a84ea98837 1. reload the configuration when restarting a task from palette (#3501) 2024-09-16 10:34:38 +01:00
ifengqi ce154cedf4 Right click (#3496)
* Add a right-click command "Add Run Debug Config" to quickly add a configuration to the run.toml file

* Add a right-click command "Add Run Debug Config" to quickly add a configuration to the run.toml file

* add some commands
2024-09-16 10:33:57 +01:00
ifengqi 2b8bc95a6e 1. Add the editor setting disable_single_click: Disable single-click … (#3494)
* 1. Add the editor setting disable_single_click: Disable single-click to open files.

* rename
2024-09-16 10:33:10 +01:00
ifengqi 351cfa6cbc 1. Add the field 'config_source' to 'RunDebugConfig' to indicate where the configuration is from. (#3500)
2. Add the field  'main_split' to'TerminalPanelData'
2024-09-13 11:18:55 +01:00
ifengqi eafaa138b1 Fix loading the shell environment in Windows. (#3499) 2024-09-13 11:17:45 +01:00
Paolo Melchiorre 513be4377d Update release.yml (#3495)
Activate Ubuntu 24.04 LTS (noble) build
2024-09-13 11:07:25 +01:00
ifengqi b09ed0af01 optimize some logs (#3497) 2024-09-12 15:07:34 +01:00
ifengqi b8772caefc 1. show message "Debugger not found. Please install the appropriate plugin." (#3493) 2024-09-12 09:37:18 +01:00
ifengqi 001d766a39 1. Preliminary implementation of the "Go to Implementation" panel (#3492) 2024-09-11 16:10:17 +01:00
ifengqi a2869a4916 1. Add the editor setting completion_width. (#3491) 2024-09-11 13:31:25 +01:00
ifengqi 690339fe5e 1. Preliminary implementation of the "Go to Implementation" panel (#3490) 2024-09-11 09:39:50 +01:00
ifengqi debfbc4d22 1. Add the editor setting completion_item_show_detail. (#3489)
2. Add some LSP logs.
2024-09-11 09:37:56 +01:00
ifengqi 29e7c3e1f0 1. Add a default position. (#3486)
2. Adjust some logs.
2024-09-10 15:40:06 +01:00
ifengqi d3c87bfc1c 1. Preliminary implementation of the "Find References" panel (#3484) 2024-09-10 10:20:29 +01:00
ifengqi 0e55cc4ce8 Implement the LSP implementation method. (#3483) 2024-09-09 10:57:01 +01:00
ifengqi 88028d5af2 When running a task, display the terminal(debug) panel (#3481) 2024-09-09 10:56:45 +01:00
ifengqi fc021a6b19 Supports tracing the output of the task. (#3480)
* Supports tracing the output of the task.

* cargo fmt
2024-09-09 10:55:40 +01:00
Dongdong Zhou 6f629148ff some improvements on palette help and file 2024-09-07 21:57:37 +01:00
ifengqi ad3c7e0bfc add some error logging (#3478) 2024-09-06 18:13:21 +01:00
ifengqi f6ff3aac4e 1. When receiving a DapStopped event, display the Debug panel. (#3476) 2024-09-06 15:17:51 +01:00
ifengqi dcc179e399 Set the Palette to default to the Help Palette. (#3463)
* 1. Set the Palette to default to the Help Palette.

* Optimize the default item.
2024-09-06 13:53:52 +01:00
ifengqi ef65fa614a 1. add error logging (#3474) 2024-09-05 16:38:26 +01:00
ifengqi 0403fb2bcc 1. When closing a debug task, a stop request should be sent to the DAP (Debug Adapter Protocol). (#3473)
2. add some log
2024-09-05 16:38:04 +01:00
ifengqi 35c5ff451a 1. add error log for lapce-rpc (#3470) 2024-09-05 10:51:37 +01:00
ifengqi 3ff7ba9ca8 1. add error log for lapce-core (#3469) 2024-09-05 10:51:22 +01:00
ifengqi a239243287 1. add another error log for lapce-app (#3468) 2024-09-05 10:51:09 +01:00
ifengqi 2815e17cc4 1. add some error log for lapce-app (#3467) 2024-09-05 10:50:33 +01:00
ifengqi 81333eb375 add error log (#3466) 2024-09-05 10:50:19 +01:00
Dongdong Zhou 840b915c4b only modifiers keys can't repeat 2024-09-03 15:26:45 +01:00
ifengqi 0adf2b3c96 1. Add repeat field to filter out long press (#3462)
2. Add SystemTime to filter out excessively long intervals.
2024-09-01 22:50:40 +01:00
ifengqi 3125034892 Closing a terminal tab should also close the underlying terminal. (#3460)
* Closing a terminal tab should also close the underlying terminal.

* The current task will only be executed if the preceding task is successful.
2024-09-01 22:41:27 +01:00
ifengqi 31178fdd8c implement "Open in GitHub" command (#3440)
* implement "Open in GitHub" command

* rename
2024-09-01 22:32:32 +01:00
ifengqi f690ea7dee Ensure the line height is not too small. (#3459) 2024-09-01 22:31:27 +01:00
ifengqi c88a8a0a78 support codeLens/resolve of lsp (#3458) 2024-08-29 09:50:33 +01:00
ifengqi a983f0bddf The double-click event restriction only takes effect at the top of the terminal. (#3442) 2024-08-27 20:31:37 +01:00
ifengqi 5f07fc3334 about dap: (#3456)
1. add some log
2. add seq field to response msg
3. support runInTerminal in Windows
4. Changed ConfigurationDone to an asynchronous request
2024-08-27 15:57:54 +01:00
Dongdong Zhou 76ed075cd9 bump version to 0.4.2
Release / tagname (push) Has been cancelled
Release / windows (push) Has been cancelled
Release / linux (push) Has been cancelled
Release / deb (debian, bookworm) (push) Has been cancelled
Release / deb (debian, bullseye) (push) Has been cancelled
Release / deb (ubuntu, focal) (push) Has been cancelled
Release / deb (ubuntu, jammy) (push) Has been cancelled
Release / deb (ubuntu, lunar) (push) Has been cancelled
Release / deb (ubuntu, mantic) (push) Has been cancelled
Release / rpm (fedora, 39) (push) Has been cancelled
Release / rpm (fedora, 40) (push) Has been cancelled
Release / lapce-proxy (alpine, 3-20) (push) Has been cancelled
Release / macos (push) Has been cancelled
Release / publish (push) Has been cancelled
2024-08-24 19:44:43 +01:00
Dongdong Zhou b618c25e1d update CHANGELOG 2024-08-24 19:04:16 +01:00
Dongdong Zhou 8047100baf only cancel completion popup if scrolled vertically 2024-08-23 21:20:15 +01:00
Dongdong Zhou 15958294e2 bump floem to fix panic 2024-08-22 20:31:09 +01:00
Dongdong Zhou 30ebe951d5 bump to latest floem 2024-08-21 21:05:01 +01:00
Dongdong Zhou 9ca4a99a9e some style changes 2024-08-14 20:30:46 +01:00
ifengqi 1f22a05026 add "Reveal in File Explorer" to right_click of editor (#3437) 2024-08-14 20:17:33 +01:00
ifengqi 81f3838249 By default, open the parent directory. (#3439) 2024-08-14 17:35:52 +01:00
Dongdong Zhou 5f58268c4a reveal file in explorer centered 2024-08-13 20:07:52 +01:00
ifengqi 74faab555f Implement a selection visual effect in the File Explorer. (#3434)
Co-authored-by: Dongdong Zhou <dzhou121@gmail.com>
2024-08-13 19:46:55 +01:00
Dongdong Zhou e95283f51a A few style tweaks 2024-08-13 19:40:04 +01:00
Dongdong Zhou 3dd37e703b scroll style fix 2024-08-12 23:03:48 +01:00
Dongdong Zhou b9062c01a7 make file explorer horizontal scrollable 2024-08-12 22:57:44 +01:00
Dongdong Zhou 06c5174555 update CHANGELOG 2024-08-12 22:24:31 +01:00
Dongdong Zhou 8efe4bbdef change some panel icons 2024-08-12 22:22:15 +01:00
ifengqi 0bbb901a3b implement document symbol (#3425) 2024-08-12 21:48:12 +01:00
ifengqi bb3709dfab Implement 'Go To Location' functionality in the Diff editor. (#3426) 2024-08-12 21:45:20 +01:00
ifengqi 082f63ec82 Filter out code-len without commands. (#3427) 2024-08-12 21:43:51 +01:00
Dongdong Zhou 8d63ee769b implement on screen find 2024-08-09 21:49:49 +01:00
Dongdong Zhou be4b2686cb update changelog 2024-08-09 20:13:18 +01:00
Dongdong Zhou 48d09de958 Fix pointer down on window message 2024-08-09 20:12:40 +01:00
ifengqi 08222a7d6f Implement feature "Run in Terminal" (#3419)
* Implement feature "Run in Terminal"

* call other method to get select text
2024-08-09 16:25:25 +01:00
ifengqi e45e292bf0 Avoid overflow. (#3420) 2024-08-09 14:58:41 +01:00
Dongdong Zhou 7c3405c147 just language configurations 2024-08-08 19:22:09 +01:00
Dongdong Zhou 36bebb6d15 add markup syntax color for markdown 2024-08-08 19:02:28 +01:00
Dongdong Zhou cab5ddeac8 relase 0.4.1
Release / tagname (push) Has been cancelled
Release / windows (push) Has been cancelled
Release / linux (push) Has been cancelled
Release / deb (debian, bookworm) (push) Has been cancelled
Release / deb (debian, bullseye) (push) Has been cancelled
Release / deb (ubuntu, focal) (push) Has been cancelled
Release / deb (ubuntu, jammy) (push) Has been cancelled
Release / deb (ubuntu, lunar) (push) Has been cancelled
Release / deb (ubuntu, mantic) (push) Has been cancelled
Release / rpm (fedora, 39) (push) Has been cancelled
Release / rpm (fedora, 40) (push) Has been cancelled
Release / lapce-proxy (alpine, 3-20) (push) Has been cancelled
Release / macos (push) Has been cancelled
Release / publish (push) Has been cancelled
2024-08-07 23:07:24 +01:00
Dongdong Zhou ca40d8cb1e Update CHANGELOG.md 2024-08-07 23:02:27 +01:00
Dongdong Zhou c5c349380c panel order default fix 2024-08-07 22:23:27 +01:00
Dongdong Zhou 8fc6fd59ae after reset highlight configs
reset the syntax in the existing docs
2024-08-07 21:47:28 +01:00
Dongdong Zhou 2639cc9f2d clippy fix 2024-08-07 21:19:21 +01:00
Dongdong Zhou 103206f50a editor gutter refactor
Use code actions popup for CodeLens
2024-08-07 21:17:27 +01:00
ifengqi d9e0411763 Implement the drag-and-drop to open file feature. (#3415) 2024-08-07 12:33:57 +01:00
Dongdong Zhou 8661de659d bump floem 2024-08-06 23:33:25 +01:00
ifengqi 3a78eaa1ce Add error message display for plugins and LSP server. (#3414) 2024-08-06 13:54:50 +01:00
ifengqi 69914369c6 Add proxy and retry count to all HTTPS GET requests. (#3413) 2024-08-06 13:53:19 +01:00
ifengqi e00d5ca751 show call hierarchy and jump to location (#3403)
* show call hierarchy and jump to location

* Remove the redundant code.

* merge master

* Optimize some code.
2024-08-05 22:36:48 +01:00
ifengqi 41a40a1a06 load_available_volts: 1. Add proxy and retry count. (#3401)
2. Display error message after request failure.
2024-08-05 14:51:44 +01:00
ifengqi 9dec920916 Optimize "Reveal in File Tree". (#3404) 2024-08-05 14:45:12 +01:00
ifengqi 14f67a7e2a Optimize double click Terminal. (#3405) 2024-08-05 14:43:21 +01:00
Dongdong Zhou 0f1aeddfc2 tree sitter nightly version detection change 2024-08-03 13:44:53 +01:00
Jakub Panek a23f8aee7e update deps, fix msrv and clippy lints
update semver

update bytemuck

update serde_json

fix msrv

fixes https://github.com/lapce/lapce/issues/3399

fix clippy lints
2024-08-02 21:18:07 +02:00
Jakub Panek 16ebd02775 fix: upgrade locale_config to non-panic version (#3395) 2024-08-02 06:42:38 +02:00
Dongdong Zhou 9c5e480ce8 fix crash 2024-08-01 21:29:47 +01:00
Jakub Panek 87a517a1cd add more languages, cleanup code (#3396)
* add more languages, cleanup code

* replace inline indents declarations with comptime

* remove newlines

* rename (lapce) "code lens" to "code glance"
2024-08-01 18:07:27 +01:00
ifengqi 7a616d5553 show code lens (#3387)
* to display code lens

* to display code lens

* run code lens

* Call the appropriate method
2024-08-01 16:53:39 +01:00
ifengqi 41aaca3f5b 1. editor: Right-click add command "Show Call Hierarchy" (#3389)
* 1. editor: Right-click add command "Show Call Hierarchy"
2. fixed warnings in clippy report

* Remove the right-click menu "Show Call Hierarchy"
2024-07-29 11:48:42 +01:00
ifengqi 98ebe57583 support to click hint (#3388) 2024-07-27 01:17:21 +02:00
Jared Moulton 1d2372daa2 Improve editor tab header (#3322) 2024-07-25 16:40:49 +01:00
ifengqi 7d3830797b support rust-analyzer's notification "experimental/serverStatus". (#3381) 2024-07-25 16:34:59 +02:00
Jakub Panek fef57e8258 show file status for scratch documents
fixes https://github.com/lapce/lapce/issues/3385
2024-07-25 16:30:10 +02:00
Dongdong Zhou 20fa316f2c fix git commit char length in version 2024-07-22 20:42:26 +01:00
ifengqi 8a610b0442 Fix head's path (#3380) 2024-07-22 18:24:25 +02:00
Jakub Panek ea999c653a update version matching 2024-07-21 16:59:27 +02:00
Jakub Panek a6c21dc5c1 fix lapce-proxy build (#3379) 2024-07-21 15:24:27 +02:00
Dongdong Zhou bd2cfc510c fix terminal on mac keeps fires PTY_READ_WRITE_TOKEN 2024-07-21 11:09:45 +01:00
ifengqi 930487449c Avoid receiving the terminal disconnection event before the terminal has finished reading, which leads to incorrect display. (#3355) 2024-07-21 10:49:11 +01:00
Dongdong Zhou 2ed1f6c68b update wgpu 2024-07-21 10:40:52 +01:00
ifengqi 8f149d28f8 editor: Right-click add command "Select in File Explorer" (#3375)
* editor: Right-click add command "Select in File Explorer"

* rename command

* rename function
2024-07-21 09:56:35 +01:00
ifengqi b83823122c get code lens and store in doc (#3370) 2024-07-21 09:39:05 +01:00
Dongdong Zhou ecea551209 fix release.yml publish 2024-07-21 09:26:05 +01:00
Jakub Panek 86e1837232 fix proxy download for nightly, fix linux updater 2024-07-21 01:34:10 +02:00
Jakub Panek 94c83124ab fix: dont use git2 for linux, since it fails when cross-compiling 2024-07-21 00:38:39 +02:00
Jakub Panek 80fed3987d fix: update wgpu{,-hal} to mitigate issues on Windows 2024-07-20 01:28:30 +02:00
ifengqi b40d8a68d4 terminal: double-click to maximize bottom (#3376) 2024-07-19 17:34:54 +02:00
ifengqi d9d7d1772e Terminal ctrl + click to jump: Support path contains '-' and toml file (#3367) 2024-07-16 17:01:59 +01:00
Jakub Panek 348bd17b46 ci: disable aarch64 proxy 2024-07-16 15:17:28 +02:00
ifengqi 6ffa4bb84b Terminal: right-click to add "clear all" function (#3368)
* Terminal: right-click to add "clear all" function

* format imports

* format imports
2024-07-16 09:50:34 +01:00
Jakub Panek 8236725896 add open grammars/queries directory command 2024-07-14 09:26:24 +02:00
Jakub Panek af42f186ed fix grammars fetching, log errors for queries, add zstd
log errors when reading queries

add error context to grammars fetching

fix grammars fetching, allow zstd archives
2024-07-13 09:30:08 +02:00
Jakub Panek e104f1db03 update dependencies (#3366) 2024-07-12 07:08:27 +02:00
Jakub Panek 9f120f9016 re-implement grammar/queries update system (#3341) 2024-07-12 06:03:12 +02:00
Dongdong Zhou 0d0b08d0c8 fix terminal key handling 2024-07-11 22:50:22 +01:00
ifengqi 07e7203962 1. terminal: The default number of jump columns is 0 (#3358) 2024-07-08 14:09:35 +01:00
ifengqi 7f1172246d terminal: added support for Alt+ some character input (#3357) 2024-07-08 10:08:31 +02:00
ifengqi 11e4428c83 1. add right-click function for the tab of editor (#3346) 2024-06-30 14:05:10 +02:00
ifengqi 28302a43ec Terminal file jump: change the judgment point to Ctrl + left mouse click. (#3342)
* 1. Terminal file jump: change the judgment point to Ctrl + left mouse click.
2. Optimize some code according to clippy.

* 1. Fix the issue where the actual Point are incorrect due to the scroll bar.

* 1. Adjust the actual number of lines and characters.
2024-06-28 11:22:06 +01:00
dependabot[bot] db55cbe3bb chore(deps): bump open from 5.1.2 to 5.1.4
Bumps [open](https://github.com/Byron/open-rs) from 5.1.2 to 5.1.4.
- [Release notes](https://github.com/Byron/open-rs/releases)
- [Changelog](https://github.com/Byron/open-rs/blob/main/changelog.md)
- [Commits](https://github.com/Byron/open-rs/compare/v5.1.2...v5.1.4)

---
updated-dependencies:
- dependency-name: open
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-24 17:50:34 +00:00
dependabot[bot] 0541f1cd8d chore(deps): bump tar from 0.4.40 to 0.4.41
Bumps [tar](https://github.com/alexcrichton/tar-rs) from 0.4.40 to 0.4.41.
- [Commits](https://github.com/alexcrichton/tar-rs/compare/0.4.40...0.4.41)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-24 17:34:13 +00:00
dependabot[bot] 67f8f82aaf chore(deps): bump flate2 from 1.0.28 to 1.0.30
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.0.28 to 1.0.30.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.0.28...1.0.30)

---
updated-dependencies:
- dependency-name: flate2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-24 17:33:29 +00:00
dependabot[bot] 3133098db3 chore(deps): bump git2 from 0.18.2 to 0.19.0
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.18.2 to 0.19.0.
- [Changelog](https://github.com/rust-lang/git2-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-0.18.2...git2-0.19.0)

---
updated-dependencies:
- dependency-name: git2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-24 17:27:17 +00:00
dependabot[bot] 51c7647d5a chore(deps): bump unicode-width from 0.1.12 to 0.1.13
Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.1.12 to 0.1.13.
- [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.1.12...v0.1.13)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-24 17:25:55 +00:00
dependabot[bot] 05ac2561ad chore(deps): bump regex from 1.10.4 to 1.10.5
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.4 to 1.10.5.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.10.4...1.10.5)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-24 17:25:39 +00:00
Jakub Panek dd1dce96ef format imports 2024-06-24 11:47:39 +02:00
Jakub Panek ffdbb1faca update time
fixes #3336
2024-06-22 23:21:42 +02:00
Dongdong Zhou f5fddebb5d update tree-sitter 2024-06-20 22:36:11 +01:00
Dongdong Zhou f258bb782c handle keypress properly when failing back to windowtab 2024-06-14 22:20:42 +01:00
Dongdong Zhou dc9a24d8e7 update linux dockerfile 2024-06-14 08:52:48 +01:00
Dongdong Zhou 28988c8fa2 load tree sitter quries from folder 2024-06-13 22:32:11 +01:00
Dongdong Zhou caab22d091 Fix lanaguage from path 2024-06-12 22:20:30 +01:00
Dongdong Zhou 73a1c79d12 code action lightbulb position fix 2024-06-12 19:51:48 +01:00
Jakub Panek 45b5bad59e fix environment loading, add windows support, add show env command (#3326) 2024-06-12 12:36:28 +02:00
Long0x0 67696bd586 fix progress view overflow (#3324)
The position of the middle buttons is not fixed when the progress messages are too long. `flex-wrap` is used to wrap them outside the view.
2024-06-12 10:11:37 +01:00
Jakub Panek d4c562fcae fix file language detection from path
detection was broken due to Option's returning early on
file_stem() and extension()
2024-06-12 10:13:31 +02:00
Dongdong Zhou 890438883e fix panel picker drag 2024-06-11 19:59:12 +01:00
Dongdong Zhou e99621d971 fix file explorer 2024-06-11 19:47:16 +01:00
Dongdong Zhou 35b60fd50f remove println 2024-06-11 19:31:16 +01:00
Dongdong Zhou 139ce92411 bump floem to fix clear_focus
fix #3320
2024-06-11 19:30:26 +01:00
Jared Moulton 2768da1d88 Make editor tab separator height configurable (#3313) 2024-06-10 15:23:45 +02:00
Jakub Panek c6d39eca4b disable selection on editor tabs (#3309) 2024-06-10 13:24:21 +02:00
Jared Moulton a0316a1107 Remove containers (#3312) 2024-06-09 19:21:19 +02:00
Dongdong Zhou 7c9099c4b3 bump floem to fix emoji in tinyskia renderer
fix #2781
2024-06-08 20:15:01 +01:00
Dongdong Zhou 6e5f1aa4fa stop pointerdown event propogation for keyboard shortcut picker 2024-06-08 16:47:58 +01:00
Jared Moulton c4af55ee7c Add debug names, trim extra containers (#3310) 2024-06-08 11:55:56 +02:00
Jakub Panek fcc62ca7a1 fix CI paths 2024-06-08 11:21:39 +02:00
Dongdong Zhou 901adddcc9 paint cursor caret after indent guide 2024-06-07 19:48:36 +01:00
Dongdong Zhou f6e0e2023d reorder editor cursor painting order
fix #3271
2024-06-06 22:03:10 +01:00
Jakub Panek a9c1739ca4 implement gotodefinition on cmd/ctrl+click
currently hardcoded to specific mouse bind + modifier
fixes #2801
2024-06-06 14:13:52 +02:00
Jakub Panek 4d4c098abc use macos-14, use xcode 15.4, retain artefacts for 3 days 2024-06-05 23:11:09 +02:00
Jakub Panek 9b01cb192a switch to macos-12 2024-06-04 23:19:11 +02:00
Jakub Panek a28eca8787 ci: run only on code changes 2024-06-04 19:59:02 +02:00
Jakub Panek fd1794889a use macos-13 for release 2024-06-04 11:51:22 +02:00
Jakub Panek e5212b90b4 fix: separate binary and package in dockerfile 2024-06-04 03:29:42 +02:00
Jakub Panek 45ecb8de84 remove coverage 2024-06-04 02:51:32 +02:00
Jakub Panek 527dc1d83a clean-up ci jobs, fix codecov 2024-06-04 02:03:17 +02:00
Jakub Panek 9008913c65 switch to upstream for import-codesign-certs action 2024-06-04 01:53:59 +02:00
Jakub Panek 41b7ea4436 use macos-14 for release (#3303) 2024-06-04 01:51:50 +02:00
Jakub Panek 7d2aed5edb update structdesc, format code 2024-06-04 01:00:03 +02:00
Jakub Panek faf8c9ba92 save scale configuration
Saves scale configuration in settings to restore at startup
Related to #1458
2024-06-04 00:06:42 +02:00
Jakub Panek 68243b57f5 fix error_modal import 2024-06-03 20:32:21 +02:00
Jakub Panek 6b5f008a88 fix status to align with viewport gutter line count
fixes #3272
2024-06-03 19:43:33 +02:00
Jakub Panek e17ff9c1ef fix scoping for error_modal 2024-06-03 18:19:54 +02:00
Jakub Panek 965ad662f6 use logical key for numpad key location
fixes #3238
2024-06-03 18:10:48 +02:00
Jakub Panek bb28f43eed log issues when not running with -w, return with exitcode 1, cleanup tracing 2024-06-03 18:09:49 +02:00
Jakub Panek 20d5f9f799 derive std::fmt::Debug for most structures 2024-06-03 18:09:33 +02:00
Jakub Panek a57dd5e0a7 update floem 2024-06-03 17:35:59 +02:00
Christopher Bayliss c0fc11ec96 cargo-deny: init (#3295) 2024-06-02 20:51:14 +02:00
Jakub Panek 8b1fb7a2ff format code 2024-06-02 15:34:18 +02:00
Jakub Panek 02a4b1dc37 vendor dejavu fonts
fixes #3285
fixes #3235
fixes #2748
2024-06-02 15:28:22 +02:00
Jakub Panek 682ed93f23 remove unused dependencies (#3296) 2024-06-02 11:45:03 +02:00
Jakub Panek 4af4244397 format code 2024-06-02 10:10:07 +02:00
Jakub Panek 44a54e2b39 fixup! upgrade softbuffer, wayland-* 2024-06-01 21:19:20 +02:00
Jakub Panek 6d7dc612ff upgrade softbuffer, wayland-* 2024-06-01 18:15:30 +02:00
Jakub Panek 32f3cd8f8c upgrade tempfile 2024-06-01 18:12:53 +02:00
Jakub Panek bfb0358c53 upgrade include_dir 2024-06-01 18:10:51 +02:00
Jakub Panek 90a6378e60 remove patch version lock for ripgrep crates 2024-06-01 17:50:16 +02:00
Jakub Panek f467fd8a81 update grep-regex 2024-06-01 17:49:16 +02:00
Jakub Panek 552b57e67c update grep-searcher 2024-06-01 17:47:56 +02:00
Jakub Panek 28f9d75133 update fontconfig-parser 2024-06-01 17:44:25 +02:00
Jakub Panek 6f34384174 update blocking 2024-06-01 17:42:18 +02:00
Jakub Panek b2bf83cce8 update hyper, tokio, etc.; remove duplicate socket2 2024-06-01 16:31:00 +02:00
Jakub Panek a5e22c8af3 add placeholder for ssh input 2024-06-01 15:56:38 +02:00
Jakub Panek 7abee828f0 upgrade stretto and wg 2024-06-01 11:14:12 +02:00
Jakub Panek dade20f478 ignore newline in extension search, add placeholder 2024-06-01 11:13:21 +02:00
Jakub Panek 9a04401a47 upgrade pulldown-cmark to 0.11 2024-06-01 11:13:21 +02:00
Jakub Panek d3b3e5fb90 update lsp-types patch comment 2024-06-01 08:02:56 +02:00
Jakub Panek f2355aa0b8 decode percent-encoded URLs, move path_from_url to own module
fixes #2932
2024-05-29 22:52:42 +02:00
Jakub Panek 21192b6ec1 fix formatting 2024-05-29 21:13:33 +02:00
Jakub Panek f2ab022735 fix parsing URLs for LSPs that send it URL encoded
Add unhandled encoding of drive letter

Fixes #2954
Fixes #1942
2024-05-29 21:06:42 +02:00
Jakub Panek 4bc49aa8db fix: update schannel
fixes #3290
closes #3291
2024-05-29 14:56:08 +02:00
dependabot[bot] 9ed68160e1 chore(deps): bump serde_json from 1.0.107 to 1.0.117
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.107 to 1.0.117.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.107...v1.0.117)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-29 12:49:57 +00:00
dependabot[bot] 65a99d5945 chore(deps): bump parking_lot from 0.12.1 to 0.12.3
Bumps [parking_lot](https://github.com/Amanieu/parking_lot) from 0.12.1 to 0.12.3.
- [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Amanieu/parking_lot/compare/0.12.1...0.12.3)

---
updated-dependencies:
- dependency-name: parking_lot
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-29 12:49:29 +00:00
dependabot[bot] 3251d3d05c chore(deps): bump libc from 0.2.153 to 0.2.155
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.153 to 0.2.155.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.153...0.2.155)

---
updated-dependencies:
- dependency-name: libc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-29 12:47:40 +00:00
dependabot[bot] 3ee00cebb8 chore(deps): bump thiserror from 1.0.58 to 1.0.61
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.58 to 1.0.61.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.58...1.0.61)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-29 12:47:30 +00:00
dependabot[bot] 2e601e50ab chore(deps): bump chrono from 0.4.34 to 0.4.38
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.34 to 0.4.38.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.34...v0.4.38)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-29 12:46:41 +00:00
Jakub Panek af72732768 fix launching default terminal profile when showing panel
fixes #2375
2024-05-28 09:36:27 +02:00
Jakub Panek 6a64a5331e remove unnecessary "Settings" suffix, hide terminal profiles from settings 2024-05-26 21:01:17 +02:00
Dongdong Zhou 9d016feb26 bump floem 2024-05-21 21:54:07 +01:00
dependabot[bot] 11f36a7038 chore(deps): bump pulldown-cmark from 0.10.2 to 0.10.3 (#3223)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-11 12:50:04 +02:00
dependabot[bot] 0ddc216245 chore(deps): bump unicode-width from 0.1.11 to 0.1.12 (#3226)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-11 01:39:53 +02:00
dependabot[bot] 0ff10e346f chore(deps): bump bytemuck from 1.14.3 to 1.15.0 (#3187)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-11 01:08:53 +02:00
Dongdong Zhou 5263087173 Update README.md 2024-05-10 21:46:34 +01:00
Dongdong Zhou 3f69ff4c18 stop palette scroll to bubble up 2024-05-10 21:39:44 +01:00
Jakub Panek c604f513ae fix: reimplement path un/install for macos (#3255)
Fixes #3254
2024-05-10 15:23:34 +02:00
Dongdong Zhou c9f5f8fd20 proxy gzip overwrite file 2024-05-09 22:46:36 +01:00
Dongdong Zhou 985b07cca2 bump floem 2024-05-08 23:01:42 +01:00
Jakub Panek 09065bb7af fix: remove tree-sitter-yaml
fixes #3251
2024-05-07 22:07:45 +02:00
Jakub Panek 1004b7fbf3 fix: use default profile when spawning terminal panel
fixes #3244
2024-05-05 23:58:47 +02:00
Jakub Panek cbca98e108 fix clippy lints (#3237) 2024-05-02 17:39:17 +02:00
Jakub Panek 75cfa069aa add additional logging for environment loading 2024-05-01 21:43:25 +02:00
Jakub Panek 3dc59aeb27 fix: remove unsupported highlight for cpp 2024-05-01 15:44:30 +02:00
Dongdong Zhou 43bdb124e3 hover doesn't propagate wheel event
Fix #3219
2024-04-29 21:24:19 +01:00
dependabot[bot] 6fd83c49bc chore(deps): bump hashbrown from 0.14.3 to 0.14.5
Bumps [hashbrown](https://github.com/rust-lang/hashbrown) from 0.14.3 to 0.14.5.
- [Changelog](https://github.com/rust-lang/hashbrown/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/hashbrown/compare/v0.14.3...v0.14.5)

---
updated-dependencies:
- dependency-name: hashbrown
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 18:19:07 +00:00
Jakub Panek e9a87c0b01 fix: use windows subsystem to prevent console running
fixes #2813
2024-04-29 19:43:37 +02:00
Jakub Panek 8985acd81b fix: log error with path when it cannot be parsed to Url 2024-04-28 12:40:43 +02:00
Dongdong Zhou 0bc1c97575 rename preview_editor_width to palette_width 2024-04-27 22:36:50 +01:00
Jakub Panek 36627a93ec ci: fix fedora glob 2024-04-26 11:13:59 +02:00
Jakub Panek 42b219e642 add fedora builds (#3204) 2024-04-26 09:23:48 +02:00
MinusGix b066d164d3 Fallback to default theme if key is wrong (#3203) 2024-04-25 22:17:25 +02:00
Dongdong Zhou d390b5870c Add cursor affinity for diagnostic phantom text
Fix #3202
2024-04-25 14:16:58 +01:00
Jakub Panek f49ff95eee docs: add pkgconf just in case 2024-04-25 11:38:21 +02:00
Dongdong Zhou 8531d7abe9 bump version to 0.4.0
Release / tagname (push) Has been cancelled
Release / windows (push) Has been cancelled
Release / linux (push) Has been cancelled
Release / deb (debian, bookworm) (push) Has been cancelled
Release / deb (debian, bullseye) (push) Has been cancelled
Release / deb (ubuntu, focal) (push) Has been cancelled
Release / deb (ubuntu, jammy) (push) Has been cancelled
Release / deb (ubuntu, lunar) (push) Has been cancelled
Release / deb (ubuntu, mantic) (push) Has been cancelled
Release / Build lapce-proxy for aarch64 (push) Has been cancelled
Release / Build lapce-proxy for x86_64 (push) Has been cancelled
Release / macos (push) Has been cancelled
Release / publish (push) Has been cancelled
2024-04-24 21:32:06 +01:00
Dongdong Zhou 7c99eca99e update changelog 2024-04-24 21:27:45 +01:00
Dongdong Zhou 49f4f50dfd make code action and completion clickable 2024-04-24 21:16:07 +01:00
Jakub Panek ab52dbcd57 docs: use openssl system lib
fixes #3198
2024-04-24 15:17:47 +02:00
Dongdong Zhou a234ae75e8 update screenshot 2024-04-23 21:27:05 +01:00
Dongdong Zhou abd6688fc5 Focus key bindings input when open
Fix #3174
2024-04-23 21:25:03 +01:00
Dongdong Zhou e2ec4234b7 reimplement settings dropdown
fix #2904
2024-04-23 21:22:13 +01:00
dependabot[bot] cc90d1e0bc chore(deps): bump structdesc from a6a2b8a to 47d8201
Bumps [structdesc](https://github.com/lapce/structdesc) from `a6a2b8a` to `47d8201`.
- [Commits](https://github.com/lapce/structdesc/compare/a6a2b8ab65d43475238f889736c4aa5dfff4500a...47d8201fb13240166f4d842c718d63c1e24f0236)

---
updated-dependencies:
- dependency-name: structdesc
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-23 03:09:20 +00:00
Jakub Panek 7ebdf7f5bb fix fmt 2024-04-23 03:38:26 +02:00
Jakub Panek 468d416704 log target lapce_core=debug, try to unload library on failure and log the error 2024-04-23 03:11:53 +02:00
Dongdong Zhou 46f29719dd restore unsaved buffer 2024-04-22 22:20:58 +01:00
dependabot[bot] d94668cea5 chore(deps): bump smallvec from 1.13.1 to 1.13.2
Bumps [smallvec](https://github.com/servo/rust-smallvec) from 1.13.1 to 1.13.2.
- [Release notes](https://github.com/servo/rust-smallvec/releases)
- [Commits](https://github.com/servo/rust-smallvec/compare/v1.13.1...v1.13.2)

---
updated-dependencies:
- dependency-name: smallvec
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-22 18:12:07 +00:00
Dongdong Zhou 78b90a4ccd Update README.md 2024-04-20 21:14:34 +01:00
Dongdong Zhou 9fb61d0a72 remember panel section open status 2024-04-19 22:20:52 +01:00
231 changed files with 14737 additions and 11721 deletions
+8
View File
@@ -0,0 +1,8 @@
# Link runtime statically so Visual C++ Redist is not required
[target.'cfg(all(windows, target_env = "msvc"))']
rustflags = ["-C", "target-feature=+crt-static"]
[profile.ci]
inherits = "dev"
opt-level = 0
debug = false
+10
View File
@@ -0,0 +1,10 @@
{
"image": "ghcr.io/lapce/lapdev-devcontainer-lapce",
"forwardPorts": [6080],
"portsAttributes": {
"6080": {
"label": "VNC",
}
}
}
+9 -1
View File
@@ -1 +1,9 @@
/target
.github
.vscode
.lapce
docs
extra/linux/docker
extra/macos
extra/windows
target
lapce.spec
+47 -7
View File
@@ -1,12 +1,52 @@
version: 2
updates:
- package-ecosystem: 'github-actions'
directory: '/'
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: 'weekly'
- package-ecosystem: 'cargo'
directory: '/'
interval: "monthly"
groups:
all-dependencies:
patterns:
- "*"
- package-ecosystem: "cargo"
directory: "/"
open-pull-requests-limit: 10
schedule:
interval: 'weekly'
interval: "monthly"
groups:
all-dependencies:
patterns:
- "*"
exclude-patterns:
- "wasmtime*"
- "wasi-common"
- "wasi-experimental-http-wasmtime"
- "tracing*"
- "strum*"
- "tree-sitter"
- "floem*"
wasmtime:
patterns:
- "wasmtime*"
- "wasi-common"
- "wasi-experimental-http-wasmtime"
toml:
patterns:
- "toml"
- "toml_edit"
strum:
patterns:
- "strum"
- "strum_macros"
tracing:
patterns:
- "tracing*"
floem:
patterns:
- "floem*"
tree-sitter:
patterns:
- "tree-sitter"
windows:
patterns:
- "windows*"
+40 -39
View File
@@ -4,6 +4,12 @@ on:
- master
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- Cargo.toml
- Cargo.lock
- lapce-**
- .github/workflows/ci.yml
workflow_dispatch:
name: CI
@@ -15,71 +21,63 @@ env:
CARGO_TERM_COLOR: always
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
permissions: {}
jobs:
build:
name: Rust on ${{ matrix.os }} (${{ join(matrix.features, ',') }})
name: Rust on ${{ matrix.os }}
if: github.event.pull_request.draft == false
needs: [fmt, clippy]
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, macos-latest ]
include:
- os: windows-latest
features: []
- os: windows-latest
features:
- lapce-app/portable
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Install dependencies on Ubuntu
if: startsWith(matrix.os, 'ubuntu')
run: sudo make ubuntu-deps
- name: Update toolchain & add llvm-tools
- name: Update toolchain
run: |
rustup update
rustup component add llvm-tools-preview
rustup update --no-self-update
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4
- name: Fetch dependencies
run: cargo fetch --locked
- name: Build
if: ${{ join(matrix.features, ',') == '' }}
run: cargo build --frozen
run: cargo build --profile ci --frozen
- name: Build
if: ${{ join(matrix.features, ',') != '' }}
run: cargo build --frozen --features ${{ join(matrix.features, ',') }}
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f
with:
name: lapce-${{ matrix.os }}
path: |
target/ci/lapce*
retention-days: 1
- name: Free space
- name: Build as portable
if: startsWith(matrix.os, 'windows')
run: cargo build --profile ci --frozen --features lapce-app/portable
- name: Free space on Windows
if: startsWith(matrix.os, 'windows')
run: cargo clean
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Run tests and coverage
if: ${{ toJSON(matrix.features) == '[]' }}
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
- name: Run doc tests
run: cargo test --doc --workspace
- uses: codecov/codecov-action@v4
with:
files: lcov.info
run: cargo test --profile ci --doc --workspace
fmt:
name: Rustfmt
name: Formatting and spellchecking
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Update toolchain & add rustfmt
run: |
@@ -89,19 +87,22 @@ jobs:
- name: Run rustfmt
run: cargo fmt --all --check
- name: Spell Check Repo
uses: crate-ci/typos@02ea592e44b3a53c302f697cddca7641cd051c3d
clippy:
name: Clippy on ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ]
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Update toolchain & add clippy
run: |
rustup update
rustup update --no-self-update
rustup component add clippy
- name: Install dependencies on Ubuntu
@@ -109,10 +110,10 @@ jobs:
run: sudo make ubuntu-deps
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4
- name: Fetch dependencies
run: cargo fetch --locked
- name: Run clippy
run: cargo clippy
run: cargo clippy --profile ci
+79 -96
View File
@@ -24,6 +24,8 @@ env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_TERM_COLOR: always
permissions: {}
jobs:
tagname:
runs-on: ubuntu-latest
@@ -53,16 +55,16 @@ jobs:
needs: tagname
env:
RELEASE_TAG_NAME: ${{ needs.tagname.outputs.tag_name }}
defaults:
run:
shell: bash
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Update rust
run: rustup update
run: rustup update --no-self-update
- name: Fetch dependencies
run: cargo fetch --locked
@@ -95,7 +97,7 @@ jobs:
Start-Sleep -Seconds 10
$compressor.close()
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f
with:
name: lapce-windows
path: |
@@ -109,8 +111,10 @@ jobs:
needs: tagname
env:
RELEASE_TAG_NAME: ${{ needs.tagname.outputs.tag_name }}
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Build deb packages
run: |
docker buildx create --driver=docker-container --use
@@ -137,7 +141,7 @@ jobs:
mv ./vendor-config.toml ./vendor/
tar -zcf vendor.tar.gz ./vendor/
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f
with:
name: lapce-linux
path: |
@@ -150,6 +154,8 @@ jobs:
needs: tagname
env:
RELEASE_TAG_NAME: ${{ needs.tagname.outputs.tag_name }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
@@ -158,136 +164,111 @@ jobs:
os-version: bookworm
- os-name: debian
os-version: bullseye
- os-name: ubuntu
os-version: focal
- os-name: ubuntu
os-version: jammy
- os-name: ubuntu
os-version: lunar
os-version: noble
- os-name: ubuntu
os-version: mantic
# - os-name: ubuntu
# os-version: noble
os-version: plucky
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Build deb packages
run: |
docker buildx create --driver=docker-container --use
docker buildx bake --pull ${{ matrix.os-name }}-${{ matrix.os-version }}-package
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f
with:
name: lapce-${{ matrix.os-name }}-${{ matrix.os-version }}
path: |
./target/linux_*/*
retention-days: 1
linux-musl:
name: Build lapce-proxy for ${{ matrix.platform }}
rpm:
runs-on: ubuntu-latest
needs: tagname
env:
RELEASE_TAG_NAME: ${{ needs.tagname.outputs.tag_name }}
CROSS_SYSROOT: /mnt/alpine-${{ matrix.platform }}
PACKAGES: >
zlib-static freetype-static fontconfig-static
libgit2-static libssh2-static openssl-libs-static
libssl3 gtk+3.0-dev http-parser-dev rustup
build-base openssl-dev git lld clang
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- triple: x86_64-unknown-linux-musl
platform: x86_64
- triple: aarch64-unknown-linux-musl
platform: aarch64
- os-name: fedora
os-version: 42
- os-name: fedora
os-version: 43
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Set up Alpine Linux for ${{ matrix.platform }} (target arch)
id: alpine-target
uses: jirutka/setup-alpine@v1
with:
arch: ${{ matrix.platform }}
branch: v3.18
packages: ${{ env.PACKAGES }}
shell-name: alpine-target.sh
- name: Set up Alpine Linux for x86_64 (build arch)
uses: jirutka/setup-alpine@v1
with:
arch: x86_64
branch: v3.18
packages: ${{ env.PACKAGES }}
volumes: ${{ steps.alpine-target.outputs.root-path }}:${{ env.CROSS_SYSROOT }}
shell-name: alpine.sh
- name: Install Rust stable toolchain via rustup
run: rustup-init --target ${{ matrix.triple }} --default-toolchain stable --profile minimal -y
shell: alpine.sh {0}
- name: Build ${{ matrix.triple }}
shell: alpine.sh {0}
env:
LIBZ_SYS_STATIC: 1
LIBSSH2_STATIC: 1
LIBGIT2_STATIC: 1
OPENSSL_STATIC: 1
OPENSSL_DIR: ${{ env.CROSS_SYSROOT }}/usr # static/dynamic lib workaround <3
OPENSSL_NO_VENDOR: 1 # don't even try to build without it on musl
PKG_CONFIG_ALL_STATIC: 1
PKG_CONFIG_LIBDIR: ${{ env.CROSS_SYSROOT }}/usr/lib/pkgconfig
RUSTFLAGS: -C target-feature=+crt-static -C linker=/usr/bin/ld.lld # link runtime static, use universal linker
CARGO_BUILD_TARGET: ${{ matrix.triple }}
CARGO_PROFILE_RELEASE_LTO: "true"
CARGO_PROFILE_RELEASE_STRIP: symbols # remove unneeded debug stuff
CARGO_PROFILE_RELEASE_OPT_LEVEL: "s" # optimise for size
CARGO_PROFILE_RELEASE_CODEGEN_UNITS: "1" # optimise each crate
SYSROOT: /dummy # workaround for https://github.com/rust-lang/pkg-config-rs/issues/102
CC: clang
- name: Build rpm packages
run: |
# Workaround for https://github.com/rust-lang/pkg-config-rs/issues/102
echo -e '#!/bin/sh\nPKG_CONFIG_SYSROOT_DIR=${{ env.CROSS_SYSROOT }} exec pkgconf "$@"' \
| install -m755 /dev/stdin pkg-config
export PKG_CONFIG="$(pwd)/pkg-config"
cargo fetch --locked
cargo build \
--frozen \
--verbose \
--release \
--bin lapce-proxy \
--manifest-path lapce-proxy/Cargo.toml
docker buildx create --driver=docker-container --use
docker buildx bake --pull ${{ matrix.os-name }}-${{ matrix.os-version }}-package
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f
with:
name: lapce-${{ matrix.os-name }}-${{ matrix.os-version }}
path: |
./target/*
retention-days: 1
lapce-proxy:
runs-on: ubuntu-latest
needs: tagname
env:
RELEASE_TAG_NAME: ${{ needs.tagname.outputs.tag_name }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- os-name: alpine
os-version: "" # uses latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a
- name: Build lapce-proxy binary
run: |
docker buildx create --driver=docker-container --use
docker buildx bake --pull ${{ matrix.os-name }}-${{ matrix.os-version }}
- name: Gzip
run: |
gzip -c ./target/${{ matrix.triple }}/release/lapce-proxy > ./lapce-proxy-linux-${{ matrix.platform }}.gz
gzip -c ./target/linux_amd64/lapce-proxy > ./lapce-proxy-linux-x86_64.gz
gzip -c ./target/linux_arm64/lapce-proxy > ./lapce-proxy-linux-aarch64.gz
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f
with:
name: lapce-proxy-linux-${{ matrix.platform }}
name: lapce-proxy-linux
path: |
./lapce-proxy-linux-*.gz
retention-days: 1
macos:
runs-on: macos-11
runs-on: macos-15
needs: tagname
env:
RELEASE_TAG_NAME: ${{ needs.tagname.outputs.tag_name }}
NOTARIZE_USERNAME: ${{ secrets.NOTARIZE_USERNAME }}
NOTARIZE_PASSWORD: ${{ secrets.NOTARIZE_PASSWORD }}
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Install ARM target
run: rustup update && rustup target add aarch64-apple-darwin
run: rustup update && rustup target add x86_64-apple-darwin
- name: Import Certificate
uses: lapce/import-codesign-certs@72dec84923586f8bef2bed09fdb4f9475c8f623d # use updated action, can be dropped once/if upstream is fixed
uses: Apple-Actions/import-codesign-certs@b610f78488812c1e56b20e6df63ec42d833f2d14
with:
p12-file-base64: ${{ secrets.MACOS_CERTIFICATE }}
p12-password: ${{ secrets.MACOS_CERTIFICATE_PWD }}
@@ -308,33 +289,34 @@ jobs:
gzip -c ./target/aarch64-apple-darwin/release-lto/lapce-proxy > ./target/release-lto/macos/lapce-proxy-darwin-aarch64.gz
- name: Notarize Release Build
uses: lando/notarize-action@v2
uses: lando/notarize-action@b5c3ef16cf2fbcf2af26dc58c90255ec242abeed
with:
product-path: "./target/release-lto/macos/Lapce-macos.dmg"
appstore-connect-username: ${{ secrets.NOTARIZE_USERNAME }}
appstore-connect-password: ${{ secrets.NOTARIZE_PASSWORD }}
appstore-connect-team-id: CYSGAZFR8D
primary-bundle-id: "io.lapce"
verbose: true
- name: "Staple Release Build"
uses: lapce/xcode-staple@062485d6eeafe841c18a412f012e80f49e23c517
with:
product-path: "./target/release-lto/macos/Lapce-macos.dmg"
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f
with:
name: lapce-macos
path: |
./target/release-lto/macos/lapce-proxy-darwin-*.gz
./target/release-lto/macos/Lapce-macos.dmg
retention-days: 1
retention-days: 3
publish:
needs:
- linux
- linux-musl
- lapce-proxy
- deb
- rpm
- windows
- macos
runs-on: ubuntu-latest
@@ -346,9 +328,9 @@ jobs:
steps:
# Must perform checkout first, since it deletes the target directory
# before running, and would therefore delete the downloaded artifacts
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
- if: github.event_name == 'workflow_dispatch'
run: echo "TAG_NAME=${{ github.event.inputs.tag_name }}" | tee -a $GITHUB_ENV
@@ -391,5 +373,6 @@ jobs:
lapce-linux/* \
lapce-debian*/*/* \
lapce-ubuntu*/*/* \
lapce-proxy-linux-*/* \
lapce-fedora*/* \
lapce-proxy-linux/* \
lapce-windows/*
+2
View File
@@ -10,3 +10,5 @@ vendor/
.DS_Store
*.patch
*.tar
*.gz
+121 -2
View File
@@ -3,6 +3,112 @@
## Unreleased
### Features/Changes
### Bug Fixes
## 0.4.6
### Features/Changes
- Add `.har` file extension as recognised JSON language (<https://github.com/lapce/lapce/pull/3853>)
- Remove builds for Ubuntu Oracular, add Ubuntu Plucky (<https://github.com/lapce/lapce/pull/3808>)
### Bug Fixes
- Fix mouse wheel scrolling when viewing diff (<https://github.com/lapce/lapce/issues/3821>)
- Fix editor tabs not selectable while appearing selectable (<https://github.com/lapce/lapce/pull/3818>)
- Fix flickering when reordering editor tabs (<https://github.com/lapce/lapce/pull/3818>)
- Fix diff viewer to scroll using mouse (<https://github.com/lapce/lapce/issues/3821>)
- Fix updating window scale when it changes via settings tab (<https://github.com/lapce/lapce/issues/3832>)
- Fix vendor tarball not being included in GitHub release
- Fix cursor style inconsistencies (<https://github.com/lapce/lapce/pull/3819>)
## 0.4.5
### Bug Fixes
- Fix missing/incorrect metadata for Linux packages using AppStream metainfo
- Fix incorrect version in macOS/Windows package metadata
- Fix rendering issues when using custom scale display (<https://github.com/lapce/lapce/issues/3795>)
## 0.4.4
### Features/Changes
- Update Fedora releases to use 41/42
- Update Alpine to latest (used for building portable lapce-proxy)
- Remove Ubuntu Focal (20.04)
### Bug Fixes
- Fix dropdown menu disappearing after releasing mouse button (<https://github.com/lapce/lapce/issues/3786>)
- Fix issue with cargo vendoring due to duplicate `dpi` crate
## 0.4.3
### Features/Changes
- Add Ubuntu 24.04 LTS (noble) build
- Use Diff syntax for files with .diff/.patch extensions by default.
- Update titles of file dialogs for better clarity
- Bump to Rust 2024 Edition
- Implement "Find References" panel
- Implement "Go To Implementation" panel
### Bug Fixes
- Fix pointer event not working on plugin panel
- Fix rename/new file editor in file explorer is too small
## 0.4.2
### Features/Changes
- Implement "Run in terminal"
- Implement document symbols in a panel
- Implement "Go To Location" functionality in the Diff editor.
- Implement on screen find which is similar to `f` in vim but for the whole screen.
- Make file explorer horizontal scrollable
- Implement "Reveal in system file explorer"
### Bug Fixes
- Fix markdown syntax highlighting
- Fix click issue on window error message
## 0.4.1
### Features/Changes
- Add fedora builds
- Finish tree sitter dynamic library support by downloading from https://github.com/lapce/tree-sitter-grammars
- Saves scale configuration in settings to restore at startup
- text in ui can be selected and copied
- add right click context menu for editor tab
- terminal: added support for Alt+ some character input
- Terminal: right-click to add "clear all" function
- terminal: double-click to maximize bottom
- initial lsp code lens support for rust-analyzer
- Context menu for "reveal file in explorer"
- support rust-analyzer's notification "experimental/serverStatus"
- jump to definition support in Inlay Hints
- "Show Call Hierarchy" support
- Add proxy support and retry to all HTTPS GET requests.
- Implement the drag-and-drop to open file/folder
### Bug Fixes
- [#3203](https://github.com/lapce/lapce/pull/3203): Fallback to default theme is theme key is not found
- use windows subsystem to prevent console running
- reimplement gotodefinition on cmd/ctrl+click
- fix crash on tinyskia renderer
- show file status for scratch documents
- fix crash due to locale_config
## 0.4.0
### Features/Changes
- [#2723](https://github.com/lapce/lapce/pull/2723): Line wrapping based on width (no column-based yet)
- [#1277](https://github.com/lapce/lapce/pull/1277): Error message prompted on missing git user.email and/or user.name
- [#2910](https://github.com/lapce/lapce/pull/2910): Files can be compared in the diff editor
@@ -15,24 +121,30 @@
- [#3086](https://github.com/lapce/lapce/pull/3086): Add folding to panels
- [#3095](https://github.com/lapce/lapce/pull/3095): Add option to hide the Open Editors section in the explorer
- [#3096](https://github.com/lapce/lapce/pull/3096): Add source control status coloring to file explorer
- [#2989](https://github.com/lapce/lapce/pull/2989): Add building packages for linux distros
- [#3123](https://github.com/lapce/lapce/pull/3123): Swap sled to plain text file for db
### Bug Fixes
- [#2779](https://github.com/lapce/lapce/pull/2779): Fix files detection on fresh git/VCS repository
- [#3031](https://github.com/lapce/lapce/pull/3031): Fix find not receiving inputs when clicked
- [#3142](https://github.com/lapce/lapce/pull/3142): Fix terminal default profile saving incorrectly
- [#3158](https://github.com/lapce/lapce/pull/3158): Fix ShowHover command for showing hover with keybind
- [#3119](https://github.com/lapce/lapce/pull/3119): Fix non US keyboard layout
## 0.3.1
### Features/Changes
### Bug Fixes
- [#2754](https://github.com/lapce/lapce/pull/2754): Don't mark nonexistent files as read only (fix saving new files)
- [#2819](https://github.com/lapce/lapce/issues/2819): `Save Without Formatting` doesn't save the file
## 0.3.0
### Features/Changes
- [#2190](https://github.com/lapce/lapce/pull/2190): Rewrite with Floem UI
- [#2425](https://github.com/lapce/lapce/pull/2425): Reimplement completion lens
- [#2498](https://github.com/lapce/lapce/pull/2498): Show Lapce as an option when doing "Open With..." on Linux
@@ -58,6 +170,7 @@
### Features/Changes
### Bug Fixes
- [#2209](https://github.com/lapce/lapce/pull/2209): Fix macOS crashes
- [#2228](https://github.com/lapce/lapce/pull/2228): Fix `.desktop` entry to properly associate with Lapce on Wayland
@@ -69,6 +182,7 @@
- [#1988](https://github.com/lapce/lapce/pull/1987): Replace modal status background with background/foreground theme keys
### Features/Changes
- [#1899](https://github.com/lapce/lapce/pull/1899): Improve sorting files with numbers
- [#1831](https://github.com/lapce/lapce/pull/1831): Plugin settings shown on right click
- [#1830](https://github.com/lapce/lapce/pull/1830): Adds Clojure language support
@@ -92,6 +206,7 @@
- ![image](https://user-images.githubusercontent.com/4404609/211232461-293e3b31-4e17-457e-825c-3018699a6fc2.png)
### Bug Fixes
- [#1911](https://github.com/lapce/lapce/pull/1911): Fix movement on selections with left/right arrow keys
- [#1939](https://github.com/lapce/lapce/pull/1939): Fix saving/editing newly saved-as files
- [#1971](https://github.com/lapce/lapce/pull/1971): Fix up/down movement on first/last line
@@ -109,6 +224,7 @@
- [#1726](https://github.com/lapce/lapce/pull/1726): Add more panel theme keys, apply hover first, then current item colour
### Features/Changes
- [#1791](https://github.com/lapce/lapce/pull/1791): Add highlighting for scope lines
- [#1767](https://github.com/lapce/lapce/pull/1767): Added CMake tree-sitter syntax highlighting
- [#1759](https://github.com/lapce/lapce/pull/1759): Update C tree-sitter and highlight queries
@@ -118,11 +234,12 @@
- [#1723](https://github.com/lapce/lapce/pull/1723): In the palette, display the keybind for a command adjacent to it
- [#1722](https://github.com/lapce/lapce/pull/1722): Add 'Save without Formatting'; Add option to disable formatting on autosave
- [#1741](https://github.com/lapce/lapce/pull/1741): Add syntax highlighting for glsl
- [#1756](https://github.com/lapce/lapce/pull/1756): Add support for ssh port with ```[user@]host[:port]```
- [#1756](https://github.com/lapce/lapce/pull/1756): Add support for ssh port with `[user@]host[:port]`
- [#1760](https://github.com/lapce/lapce/pull/1760): Add vim motions `cw`, `ce`, `cc`, `S`, and QOL modal bind `gf`
- [#1770](https://github.com/lapce/lapce/pull/1770): Add support for terminal tabs
### Bug Fixes
- [#1771](https://github.com/lapce/lapce/pull/1771): Update tree-sitter-bash
- [#1737](https://github.com/lapce/lapce/pull/1726): Fix an issue that plugins can't be upgraded
- [#1724](https://github.com/lapce/lapce/pull/1724): Files and hidden folders no longer will be considered when trying to open a plugin base folder
@@ -132,6 +249,7 @@
## 0.2.4
### Features/Changes
- [#1700](https://github.com/lapce/lapce/pull/1700): Add prisma syntax and highlighting
- [#1702](https://github.com/lapce/lapce/pull/1702): Improved svelte treesitter queries
- [#1690](https://github.com/lapce/lapce/pull/1690): Add codelens and sticky headers for Dart
@@ -139,6 +257,7 @@
- [#1715](https://github.com/lapce/lapce/pull/1715): Add support for requests from plugins
### Bug Fixes
- [#1710](https://github.com/lapce/lapce/pull/1710): Fix autosave trying to save scratch files
- [#1709](https://github.com/lapce/lapce/pull/1709): Fix search result ordering
- [#1708](https://github.com/lapce/lapce/pull/1708): Fix visual issue when search panel is placed to either side panel
@@ -199,7 +318,7 @@
- [#1459](https://github.com/lapce/lapce/pull/1459): Fix opening currently used logfile
- [#1505](https://github.com/lapce/lapce/pull/1505): Fix proxy download for hosts with curl without -Z flag
- [#1483](https://github.com/lapce/lapce/pull/1483): Fix showing the close icon for the first tab when opening multiple tab
- [#1477](https://github.com/lapce/lapce/pull/1477): Now use `esc` to close searchbar regarless of the current focus
- [#1477](https://github.com/lapce/lapce/pull/1477): Now use `esc` to close searchbar regardless of the current focus
- [#1507](https://github.com/lapce/lapce/pull/1507): Fixed a crash when scratch buffer is closed
- [#1547](https://github.com/lapce/lapce/pull/1547): Fix infinite cycle in workspace symbol search
- [#1628](https://github.com/lapce/lapce/pull/1541): Fix kts files not being recognized
Generated
+2037 -1399
View File
File diff suppressed because it is too large Load Diff
+33 -15
View File
@@ -23,9 +23,9 @@ path = "lapce-proxy/src/bin/lapce-proxy.rs"
members = ["lapce-app", "lapce-proxy", "lapce-rpc", "lapce-core"]
[workspace.package]
version = "0.3.1"
edition = "2021"
rust-version = "1.75"
version = "0.4.6"
edition = "2024"
rust-version = "1.87.0"
license = "Apache-2.0"
homepage = "https://lapce.dev"
authors = ["Dongdong Zhou <dzhou121@gmail.com>"]
@@ -38,29 +38,34 @@ clap = { version = "4.5.0", default-features = false, features = ["
crossbeam-channel = { version = "0.5.12" }
directories = { version = "4.0.1" }
flate2 = { version = "1.0" }
git2 = { version = "0.20.0", features = ["vendored-openssl"] }
globset = { version = "0.4.14" }
hashbrown = { version = "0.14.3", features = ["serde"] }
hashbrown = { version = "0.14.5", features = ["serde"] }
im = { version = "15.0.0", features = ["serde"] }
include_dir = { version = "0.6.2" }
include_dir = { version = "0.7" }
indexmap = { version = "2.0", features = ["serde"] }
interprocess = { version = "1.2.1" }
itertools = { version = "0.12.1" }
notify = { version = "5.2.0", features = ["serde"] }
once_cell = { version = "1.19" }
parking_lot = { version = "0.12.0" }
parking_lot = { version = "0.12.3" }
rayon = { version = "1.10.0" }
regex = { version = "1.10.4" }
regex = { version = "1.10.5" }
remain = { version = "0.2" }
semver = { version = "1.0" }
reqwest = { version = "0.11", features = ["blocking", "json", "socks"] }
serde = { version = "1.0" }
serde_json = { version = "1.0" }
smallvec = { version = "1.13.1" }
strum = { version = "0.26.2" }
strum_macros = { version = "0.26.2" }
smallvec = { version = "1.15.1" }
strum = { version = "0.27.1" }
strum_macros = { version = "0.27.1" }
tar = { version = "0.4" }
tempfile = { version = "3.10.1" }
thiserror = { version = "1.0" }
toml = { version = "*" }
toml_edit = { version = "0.20.2", features = ["serde"] }
url = { version = "2.5.0" }
zstd = { version = "0.11.2" } # follow same version wasmtime-cache in lockfile
lsp-types = { version = "0.95.1", features = ["proposed"] } # not following semver, so should be locked to patch version updates only
psp-types = { git = "https://github.com/lapce/psp-types", rev = "f7fea28f59e7b2d6faa1034a21679ad49b3524ad" }
@@ -71,14 +76,27 @@ lapce-core = { path = "./lapce-core" }
lapce-rpc = { path = "./lapce-rpc" }
lapce-proxy = { path = "./lapce-proxy" }
floem-editor-core = { git = "https://github.com/lapce/floem", rev = "1477931896d14a9dafbfc8159e7a52a9d8e1d8b8", features = ["serde"] }
# floem-editor-core = { path = "../workspaces/floem/editor-core/", features = ["serde"] }
[workspace.dependencies.floem]
# path = "../floem"
git = "https://github.com/lapce/floem"
rev = "31fa8f444c37f4c314f47d88c23ffdbc25f2ab53"
features = ["editor", "serde", "default-image-formats", "rfd-async-std"]
[workspace.dependencies.floem-editor-core]
# path = "../floem/editor-core/"
git = "https://github.com/lapce/floem"
rev = "31fa8f444c37f4c314f47d88c23ffdbc25f2ab53"
features = ["serde"]
[patch.crates-io]
# Temporarily patch lsp-types with a version that supports inline-completion
# Temporarily patch lsp-types with a version that adds message-type debug
lsp-types = { git = "https://github.com/lapce/lsp-types", rev = "feaa1e2ec80975c9dadd400a238ceacf071058e6" }
regalloc2 = { rev = "5d79e12d0a93b10fc181f4da409b4671dd365228", git = "https://github.com/bytecodealliance/regalloc2" }
# cargo vendor issue: https://github.com/rust-lang/cargo/issues/10310
# dpi comes from winit (source) and muda (crate)
dpi = { git = "https://github.com/rust-windowing/winit", rev = "ee245c569d65fdeacf705ee5eedb564508d10ebe" }
[workspace.dependencies.tracing]
git = "https://github.com/tokio-rs/tracing"
@@ -101,7 +119,8 @@ rev = "908cc432a5994f6e17c8f36e13c217dc40085704"
package = "tracing-appender"
[workspace.dependencies.alacritty_terminal]
version = "0.23.0"
git = "https://github.com/alacritty/alacritty"
rev = "cacdb5bb3b72bad2c729227537979d95af75978f"
[workspace.dependencies.windows-sys]
version = "0"
@@ -125,4 +144,3 @@ opt-level = 3
[profile.fastdev]
inherits = "dev"
+9 -1
View File
@@ -1,3 +1,11 @@
# This Makefile is intended *only* for building macOS binaries of Lapce.
# It uses macOS-specific tools like `lipo`, `codesign`, and `hdiutil`,
# and requires that a valid Apple Developer signing identity is installed
# and available in the system Keychain under the fingerprint set in
# CODESIGN_IDENTITY.
#
# See `docs/building-from-source.md`.
TARGET = lapce
CODESIGN_IDENTITY = FAC8FBEA99169DC1980731029648F110628D6A32
@@ -22,7 +30,7 @@ vpath $(DMG_NAME) $(APP_DIR)
all: help
help: ## Print this help message
@grep -E '^[a-zA-Z._-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
@grep -E '^[a-zA-Z._-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
ubuntu-deps:
apt-get update -y
+8 -2
View File
@@ -21,7 +21,7 @@
<br/>
Lapce (IPA: /læps/) is written in pure Rust with a UI in [Floem](https://github.com/lapce/floem). It is designed with [Rope Science](https://xi-editor.io/docs/rope_science_00.html) from the [Xi-Editor](https://github.com/xi-editor/xi-editor) which makes for lightning-fast computation, and leverages [Wgpu](https://github.com/gfx-rs/wgpu) for rendering. More information about the features of Lapce can be found on the [main website](https://lapce.dev) and user documentation can be found on [GitBook](https://docs.lapce.dev/).
Lapce (IPA: /læps/) is written in pure Rust, with a UI in [Floem](https://github.com/lapce/floem). It is designed with [Rope Science](https://xi-editor.io/docs/rope_science_00.html) from the [Xi-Editor](https://github.com/xi-editor/xi-editor), enabling lightning-fast computation, and leverages [wgpu](https://github.com/gfx-rs/wgpu) for rendering. More information about the features of Lapce can be found on the [main website](https://lapce.dev) and user documentation can be found on [GitBook](https://docs.lapce.dev/).
![](https://github.com/lapce/lapce/blob/master/extra/images/screenshot.png?raw=true)
@@ -29,7 +29,7 @@ Lapce (IPA: /læps/) is written in pure Rust with a UI in [Floem](https://github
* Built-in LSP ([Language Server Protocol](https://microsoft.github.io/language-server-protocol/)) support to give you intelligent code features such as: completion, diagnostics and code actions
* Modal editing support as first class citizen (Vim-like, and toggleable)
* Built-in remote development support inspired by [VSCode Remote Development](https://code.visualstudio.com/docs/remote/remote-overview). Enjoy the benefits of a "local" experience, and seamlessly gain the full power of a remote system.
* Built-in remote development support inspired by [VSCode Remote Development](https://code.visualstudio.com/docs/remote/remote-overview). Enjoy the benefits of a "local" experience, and seamlessly gain the full power of a remote system. We also have [Lapdev](https://lap.dev/) which can help manage your remote dev environments.
* Plugins can be written in programming languages that can compile to the [WASI](https://wasi.dev/) format (C, Rust, [AssemblyScript](https://www.assemblyscript.org/))
* Built-in terminal, so you can execute commands in your workspace, without leaving Lapce.
@@ -40,6 +40,12 @@ If you'd like to compile from source, you can find the [guide](docs/building-fro
## Contributing
<a href="https://ws.lap.dev/#https://github.com/lapce/lapce" target="_blank">
<img src="https://lap.dev/images/open-in-lapdev.svg?version=8" alt="Open in Lapdev">
</a>
[Lapdev](https://lap.dev/), developed by the Lapce team, is a cloud dev env service similar to GitHub Codespaces. By clicking the button above, you'll be taken to a fully set up Lapce dev env where you can browse the code and start developing. All dependencies are pre-installed, so you can get straight to code.
Guidelines for contributing to Lapce can be found in [`CONTRIBUTING.md`](CONTRIBUTING.md).
## Feedback & Contact
+17
View File
@@ -0,0 +1,17 @@
# https://github.com/crate-ci/typos/blob/02ea592e44b3a53c302f697cddca7641cd051c3d/docs/reference.md
[files]
[default]
extend-ignore-re = [
# Ignore lines that end with `# spellchecker:disable-line`
"(?Rm)^.*(#|//)\\s*spellchecker:disable-line$",
# Ignore the line after `# spellchecker:ignore-next-line`:
"(#|//)\\s*spellchecker:ignore-next-line\\n.*",
# Ignore blocks between `# spellchecker:off` and `# spellchecker:on`
"(?s)(#|//)\\s*spellchecker:off.*?\\n\\s*(#|//)\\s*spellchecker:on",
]
[default.extend-identifiers]
[default.extend-words]
+9 -1
View File
@@ -68,6 +68,14 @@ dim-text = "#5C6370"
"variable.other.member" = "$red"
"tag" = "$blue"
"markup.heading" = "$red"
"markup.bold" = "$orange"
"markup.italic" = "$orange"
"markup.list" = "$orange"
"markup.link.url" = "$blue"
"markup.link.label" = "$purple"
"markup.link.text" = "$purple"
"bracket.color.1" = "$blue"
"bracket.color.2" = "$yellow"
"bracket.color.3" = "$purple"
@@ -138,7 +146,7 @@ dim-text = "#5C6370"
"source_control.removed" = "#FF5266CC"
"source_control.modified" = "#0184BCCC"
"tooltip.background" = "#4B4D51"
"tooltip.background" = "$primary-background"
"tooltip.foreground" = "$text"
"palette.background" = "$secondary-background"
+5 -1
View File
@@ -36,6 +36,7 @@ name = "Lapce Codicons"
"keyboard" = "keyboard.svg"
"breadcrumb_separator" = "chevron-right.svg"
"symbol_color" = "symbol-color.svg"
"type_hierarchy" = "type-hierarchy.svg"
"window.close" = "chrome-close.svg"
"window.restore" = "chrome-restore.svg"
@@ -66,7 +67,7 @@ name = "Lapce Codicons"
"dropdown.arrow" = "chevron-down.svg"
"panel.fold-down" = "chevron-down.svg"
"panel.fold-up" = "chevron-up.svg"
"panel.fold-up" = "chevron-right.svg"
"location.forward" = "arrow-right.svg"
"location.backward" = "arrow-left.svg"
@@ -103,6 +104,9 @@ name = "Lapce Codicons"
"search.replace" = "replace.svg"
"search.replace_all" = "replace-all.svg"
"document_symbol" = "symbol-class.svg"
"references" = "references.svg"
"implementation" = "combine.svg"
"symbol_kind.array" = "symbol-array.svg"
"symbol_kind.boolean" = "symbol-boolean.svg"
"symbol_kind.class" = "symbol-class.svg"
+1 -1
View File
@@ -718,7 +718,7 @@ mode = "n"
[[keymaps]]
key = "space"
command = "toggle_code_lens"
command = "toggle_code_glance"
mode = "nv"
[[keymaps]]
+1 -6
View File
@@ -16,7 +16,7 @@ command = "palette.command"
[[keymaps]]
key = "meta+e"
command = "toggle_code_lens"
command = "toggle_code_glance"
mode = "i"
[[keymaps]]
@@ -292,11 +292,6 @@ command = "down"
when = "!list_focus"
mode = "i"
[[keymaps]]
key = "right"
command = "right"
mode = "inv"
[[keymaps]]
key = "meta+shift+o"
command = "palette.symbol"
+1 -1
View File
@@ -16,7 +16,7 @@ command = "palette.command"
[[keymaps]]
key = "ctrl+e"
command = "toggle_code_lens"
command = "toggle_code_glance"
mode = "i"
[[keymaps]]
+9 -1
View File
@@ -74,6 +74,14 @@ dim-text = "#A0A1A7"
"variable.other.member" = "$red"
"tag" = "$blue"
"markup.heading" = "$red"
"markup.bold" = "$orange"
"markup.italic" = "$orange"
"markup.list" = "$orange"
"markup.link.url" = "$blue"
"markup.link.label" = "$purple"
"markup.link.text" = "$purple"
"bracket.color.1" = "$blue"
"bracket.color.2" = "$yellow"
"bracket.color.3" = "$purple"
@@ -164,7 +172,7 @@ dim-text = "#A0A1A7"
"source_control.removed" = "#FF5266CC"
"source_control.modified" = "#0184BCCC"
"tooltip.background" = "#D6D6D6"
"tooltip.background" = "$primary-background"
"tooltip.foreground" = "$text"
"palette.background" = "$secondary-background"
+2 -1
View File
@@ -1,3 +1,4 @@
# The run config is used for both run mode and debug mode
[[configs]]
@@ -7,7 +8,7 @@ name = "task"
# the type of the debugger. If not set, it can't be debugged but can still be run
# type = "lldb"
# the program to run
# the program to run, e.g. "${workspace}\\target\\debug\\check.exe"
program = ""
# the program arguments, e.g. args = ["arg1", "arg2"], optional
+11 -4
View File
@@ -5,11 +5,13 @@ modal = false
color-theme = "Lapce Dark"
icon-theme = "Lapce Codicons"
custom-titlebar = true
file-explorer-double-click = false
auto-reload-plugin = false
[editor]
font-family = "Monospace"
font-family = "monospace"
font-size = 13
code-lens-font-size = 2
code-glance-font-size = 2
line-height = 1.5
smart-tab = true
tab-width = 4
@@ -21,7 +23,9 @@ wrap-style = "editor-width"
wrap-column = 80
wrap-width = 600 # px
sticky-header = true
completion-width = 600
completion-show-documentation = true
completion-item-show-detail = false
show-signature = true
signature-label-code-block = true
auto-closing-matching-pairs = true
@@ -39,6 +43,7 @@ enable-inlay-hints = true
inlay-hint-font-family = ""
inlay-hint-font-size = 0
enable-error-lens = true
only-render-error-styling = true
error-lens-end-of-line = true
error-lens-font-family = ""
error-lens-font-size = 0
@@ -77,19 +82,21 @@ default = {}
# [terminal.profiles.example]
# command = "cargo"
# arguments = ["run"]
# environemnt = { "KEY" = "VALUE" }
# environment = { "KEY" = "VALUE" }
# workdir = "/home/user"
[ui]
scale = +1.0
font-family = ""
font-size = 13
icon-size = 0
header-height = 36
status-height = 25
tab-min-width = 100
tab-separator-height = "Content"
scroll-width = 10
drop-shadow-width = 0
preview-editor-width = 500
palette-width = 500
hover-font-family = ""
hover-font-size = 0
trim-search-results-whitespace = true
+15
View File
@@ -0,0 +1,15 @@
[licenses]
# use SPDX short identifiers
allow = [
"Apache-2.0",
"Apache-2.0 WITH LLVM-exception",
"BSD-2-Clause",
"BSD-3-Clause",
"BSL-1.0",
"CC0-1.0",
"ISC",
"MIT",
"MPL-2.0",
"Unicode-DFS-2016",
"Zlib",
]
+33 -20
View File
@@ -1,5 +1,5 @@
variable "RUST_VERSION" {
default = "1.76"
default = "1"
}
variable "XX_VERSION" {
@@ -129,13 +129,12 @@ target "ubuntu" {
matrix = {
os_name = ["ubuntu"]
build = [
{ packages = null, platforms = null, type = "package", os_version = "bionic" }, # 18.04
{ packages = null, platforms = null, type = "package", os_version = "focal" }, # 20.04
{ packages = null, platforms = null, type = "package", os_version = "jammy" }, # 22.04
{ packages = null, platforms = null, type = "package", os_version = "kinetic" }, # 22.10
{ packages = null, platforms = null, type = "package", os_version = "lunar" }, # 23.04
{ packages = null, platforms = null, type = "package", os_version = "mantic" }, # 23.10
{ packages = null, platforms = null, type = "package", os_version = "noble" }, # 24.04
{ packages = null, platforms = null, type = "package", os_version = "bionic" }, # 18.04
{ packages = null, platforms = null, type = "package", os_version = "focal" }, # 20.04
{ packages = null, platforms = null, type = "package", os_version = "jammy" }, # 22.04
{ packages = null, platforms = null, type = "package", os_version = "noble" }, # 24.04
{ packages = null, platforms = null, type = "package", os_version = "oracular" }, # 24.10
{ packages = null, platforms = null, type = "package", os_version = "plucky" }, # 25.04
# static binary, it looks ugly to define the target this way
# but I don't have a better way to make it more friendly on CLI side without
# more terrible code-wise way to implement it
@@ -160,24 +159,27 @@ variable "RHEL_FAMILY_PACKAGES" {
}
target "fedora" {
inherits = ["package"]
name = "${os_name}-${build.os_version}"
dockerfile = "extra/linux/docker/${os_name}/Dockerfile"
inherits = [build.type]
name = "${name}-${build.version}-${build.type}"
dockerfile = "extra/linux/docker/${name}/Dockerfile"
args = {
XX_VERSION = "test"
DISTRIBUTION_NAME = os_name
DISTRIBUTION_VERSION = build.os_version
DISTRIBUTION_NAME = name
DISTRIBUTION_VERSION = build.version
DISTRIBUTION_PACKAGES = join(" ", coalesce(build.packages, RHEL_FAMILY_PACKAGES))
}
platforms = coalesce(build.platforms, platforms)
// platforms = coalesce(build.platforms, platforms)
platforms = ["linux/amd64"]
matrix = {
os_name = ["fedora"]
name = ["fedora"]
build = [
{ os_version = "39", packages = null, platforms = null },
{ os_version = "40", packages = null, platforms = null },
{ os_version = "41", packages = null, platforms = null },
{ os_version = "rawhide", packages = null, platforms = null },
{ packages = null, platforms = null, type = "package", version = "39" },
{ packages = null, platforms = null, type = "package", version = "40" },
{ packages = null, platforms = null, type = "package", version = "41" },
{ packages = null, platforms = null, type = "package", version = "42" },
{ packages = null, platforms = null, type = "package", version = "43" },
{ packages = null, platforms = null, type = "package", version = "rawhide" },
]
}
}
@@ -194,6 +196,7 @@ variable "APK_FAMILY_PACKAGES" {
"lld",
"build-base",
"rustup",
"openssl-dev",
"openssl-libs-static",
"libssh2-static",
"libgit2-static",
@@ -221,11 +224,21 @@ target "alpine" {
matrix = {
os_name = ["alpine"]
build = [
{ os_version = "", packages = null, platforms = null },
{ os_version = "3.22", packages = null, platforms = null },
{ os_version = "3.20", packages = null, platforms = null },
{ os_version = "3.18", packages = null, platforms = null },
]
}
}
target "cross-alpine" {
inherits = ["alpine-3-18", "cross-binary"]
inherits = ["alpine-3-20", "cross-binary"]
}
target "alpine-dev" {
inherits = ["alpine-3-20"]
target = "dev"
tags = ["lapce/lapce:dev"]
output = ["type=docker"]
}
+2 -2
View File
@@ -2,7 +2,7 @@
It is easy to build Lapce from source on a GNU/Linux distribution. Cargo handles the build process, all you need to do, is ensure the correct dependencies are installed.
1. Install the Rust compiler and Cargo using [`rustup.rs`](https://rustup.rs/). If you already have the toolchain, ensure you are using version 1.64 or higher.
1. Install the Rust compiler and Cargo using [`rustup.rs`](https://rustup.rs/). If you already have the toolchain, ensure you are using latest Rust version.
2. Install dependencies for your operating system:
@@ -12,7 +12,7 @@ sudo apt install clang libxkbcommon-x11-dev pkg-config libvulkan-dev libwayland-
```
#### Fedora
```sh
sudo dnf install clang libxkbcommon-x11-devel libxcb-devel vulkan-loader-devel wayland-devel perl-File-Compare perl-FindBin
sudo dnf install clang libxkbcommon-x11-devel libxcb-devel vulkan-loader-devel wayland-devel openssl-devel pkgconf
```
#### Void Linux
```sh
+8
View File
@@ -0,0 +1,8 @@
# Updating versioning for package managers and whatnot
- App metainfo: `extra/linux/dev.lapce.lapce.metainfo.xml`
- macOS plist (`CFBundleShortVersionString`): `extra/macos/Lapce.app/Contents/Info.plist`
- Rust: `Cargo.toml`
- Obviously changelog: `CHANGELOG.md`
- RPM spec: `lapce.spec`
- Windows wix spec (`<Product [...] Version=X.X.X>`): `extra/windows/wix/lapce.wxs`
Binary file not shown.
Binary file not shown.
+187
View File
@@ -0,0 +1,187 @@
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
Bitstream Vera Fonts Copyright
------------------------------
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
a trademark of Bitstream, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of the fonts accompanying this license ("Fonts") and associated
documentation files (the "Font Software"), to reproduce and distribute the
Font Software, including without limitation the rights to use, copy, merge,
publish, distribute, and/or sell copies of the Font Software, and to permit
persons to whom the Font Software is furnished to do so, subject to the
following conditions:
The above copyright and trademark notices and this permission notice shall
be included in all copies of one or more of the Font Software typefaces.
The Font Software may be modified, altered, or added to, and in particular
the designs of glyphs or characters in the Fonts may be modified and
additional glyphs or characters may be added to the Fonts, only if the fonts
are renamed to names not containing either the words "Bitstream" or the word
"Vera".
This License becomes null and void to the extent applicable to Fonts or Font
Software that has been modified and is distributed under the "Bitstream
Vera" names.
The Font Software may be sold as part of a larger software package but no
copy of one or more of the Font Software typefaces may be sold by itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
FONT SOFTWARE.
Except as contained in this notice, the names of Gnome, the Gnome
Foundation, and Bitstream Inc., shall not be used in advertising or
otherwise to promote the sale, use or other dealings in this Font Software
without prior written authorization from the Gnome Foundation or Bitstream
Inc., respectively. For further information, contact: fonts at gnome dot
org.
Arev Fonts Copyright
------------------------------
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining
a copy of the fonts accompanying this license ("Fonts") and
associated documentation files (the "Font Software"), to reproduce
and distribute the modifications to the Bitstream Vera Font Software,
including without limitation the rights to use, copy, merge, publish,
distribute, and/or sell copies of the Font Software, and to permit
persons to whom the Font Software is furnished to do so, subject to
the following conditions:
The above copyright and trademark notices and this permission notice
shall be included in all copies of one or more of the Font Software
typefaces.
The Font Software may be modified, altered, or added to, and in
particular the designs of glyphs or characters in the Fonts may be
modified and additional glyphs or characters may be added to the
Fonts, only if the fonts are renamed to names not containing either
the words "Tavmjong Bah" or the word "Arev".
This License becomes null and void to the extent applicable to Fonts
or Font Software that has been modified and is distributed under the
"Tavmjong Bah Arev" names.
The Font Software may be sold as part of a larger software package but
no copy of one or more of the Font Software typefaces may be sold by
itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Except as contained in this notice, the name of Tavmjong Bah shall not
be used in advertising or otherwise to promote the sale, use or other
dealings in this Font Software without prior written authorization
from Tavmjong Bah. For further information, contact: tavmjong @ free
. fr.
TeX Gyre DJV Math
-----------------
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski
(on behalf of TeX users groups) are in public domain.
Letters imported from Euler Fraktur from AMSfonts are (c) American
Mathematical Society (see below).
Bitstream Vera Fonts Copyright
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera
is a trademark of Bitstream, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of the fonts accompanying this license (“Fonts”) and associated
documentation
files (the “Font Software”), to reproduce and distribute the Font Software,
including without limitation the rights to use, copy, merge, publish,
distribute,
and/or sell copies of the Font Software, and to permit persons to whom
the Font Software is furnished to do so, subject to the following
conditions:
The above copyright and trademark notices and this permission notice
shall be
included in all copies of one or more of the Font Software typefaces.
The Font Software may be modified, altered, or added to, and in particular
the designs of glyphs or characters in the Fonts may be modified and
additional
glyphs or characters may be added to the Fonts, only if the fonts are
renamed
to names not containing either the words “Bitstream” or the word “Vera”.
This License becomes null and void to the extent applicable to Fonts or
Font Software
that has been modified and is distributed under the “Bitstream Vera”
names.
The Font Software may be sold as part of a larger software package but
no copy
of one or more of the Font Software typefaces may be sold by itself.
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
FOUNDATION
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL,
SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN
ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR
INABILITY TO USE
THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
Except as contained in this notice, the names of GNOME, the GNOME
Foundation,
and Bitstream Inc., shall not be used in advertising or otherwise to promote
the sale, use or other dealings in this Font Software without prior written
authorization from the GNOME Foundation or Bitstream Inc., respectively.
For further information, contact: fonts at gnome dot org.
AMSFonts (v. 2.2) copyright
The PostScript Type 1 implementation of the AMSFonts produced by and
previously distributed by Blue Sky Research and Y&Y, Inc. are now freely
available for general use. This has been accomplished through the
cooperation
of a consortium of scientific publishers with Blue Sky Research and Y&Y.
Members of this consortium include:
Elsevier Science IBM Corporation Society for Industrial and Applied
Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS)
In order to assure the authenticity of these fonts, copyright will be
held by
the American Mathematical Society. This is not meant to restrict in any way
the legitimate use of the fonts, such as (but not limited to) electronic
distribution of documents containing these fonts, inclusion of these fonts
into other public domain or commercial font collections or computer
applications, use of the outline data to create derivative fonts and/or
faces, etc. However, the AMS does require that the AMS copyright notice be
removed from any derivative versions of the fonts which have been altered in
any way. In addition, to ensure the fidelity of TeX documents using Computer
Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces,
has requested that any alterations which yield different font metrics be
given a different name.
$Id$
Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 KiB

After

Width:  |  Height:  |  Size: 944 KiB

+31 -3
View File
@@ -9,6 +9,10 @@
<url type="homepage">https://lapce.dev/</url>
<url type="bugtracker">https://github.com/lapce/lapce/issues</url>
<url type="help">https://docs.lapce.dev/</url>
<categories>
<category>IDE</category>
<category>Development</category>
</categories>
<description>
<p>
Lapce is an open source code editor written in Rust. By utilising native GUI and GPU rendering, and with the performance Rust provides, Lapce is one of the fastest code editors out there.
@@ -25,11 +29,35 @@
<content_rating type="oars-1.1" />
<launchable type="desktop-id">dev.lapce.lapce.desktop</launchable>
<screenshots>
<screenshot type="default">
<image>https://raw.githubusercontent.com/lapce/lapce/master/extra/images/screenshot.png</image>
<screenshot type="default" environment="plasma">
<caption>Lapce source project opened in Lapce with Rust source code file opened</caption>
<image type="source" width="1000" height="700">https://raw.githubusercontent.com/lapce/lapce/refs/tags/v0.4.5/extra/images/linux/code-file-with-highlight.png</image>
</screenshot>
</screenshots>
<releases>
<release version="0.3.1" date="2023-12-04"/>
<release version="0.4.6" date="2026-01-21">
<url type="details">https://github.com/lapce/lapce/releases/tag/v0.4.6</url>
<issues>
<issue url="https://github.com/lapce/lapce/issues/3821">#3821</issue>
<issue url="https://github.com/lapce/lapce/issues/3832">#3832</issue>
<issue url="https://github.com/lapce/lapce/pull/3818">#3818</issue>
<issue url="https://github.com/lapce/lapce/pull/3819">#3819</issue>
</issues>
</release>
<release version="0.4.5" date="2025-09-05">
<url type="details">https://github.com/lapce/lapce/releases/tag/v0.4.5</url>
<issues>
<issue url="https://github.com/lapce/lapce/issues/3795">#3795</issue>
</issues>
</release>
<release version="0.4.4" date="2025-08-30">
<url type="details">https://github.com/lapce/lapce/releases/tag/v0.4.4</url>
<issues>
<issue url="https://github.com/lapce/lapce/issues/3786">#3795</issue>
</issues>
</release>
<release version="0.4.3" date="2024-06-27">
<url type="details">https://github.com/lapce/lapce/releases/tag/v0.4.3</url>
</release>
</releases>
</component>
+29 -50
View File
@@ -10,22 +10,22 @@ COPY --from=xx / /
SHELL [ "/bin/ash", "-c" ]
ENV DEBIAN_FRONTEND=noninteractive
# install host dependencies
ARG DISTRIBUTION_PACKAGES
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=private \
--mount=type=cache,target=/var/lib/apt,sharing=private \
--mount=type=cache,target=/var/cache/apk,sharing=private \
--mount=type=cache,target=/etc/apk/cache,sharing=private \
<<EOF
apk upgrade -Ua
apk add --no-cache \
bash clang lld llvm file git sccache \
cmake pkgconf build-base abuild \
cmake pkgconf build-base abuild curl mold \
${DISTRIBUTION_PACKAGES}
EOF
SHELL [ "/bin/bash", "-c" ]
WORKDIR /source
COPY --link . .
@@ -48,8 +48,8 @@ EOF
ARG TARGETPLATFORM
ARG DISTRIBUTION_PACKAGES
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=private \
--mount=type=cache,target=/var/lib/apt,sharing=private \
--mount=type=cache,target=/var/cache/apk,sharing=private \
--mount=type=cache,target=/etc/apk/cache,sharing=private \
<<EOF
#!/usr/bin/env bash
set -euxo pipefail
@@ -70,21 +70,9 @@ ENV OUTPUT_DIR="${OUTPUT_DIR}"
ARG CARGO_BUILD_INCREMENTAL='false'
ENV CARGO_TARGET_DIR='/root/.cache/rust'
ENV CC='xx-clang'
ENV CXX='xx-clang++'
ENV OPENSSL_NO_VENDOR="1"
ENV ZSTD_SYS_USE_PKG_CONFIG="1"
ARG RELEASE_TAG_NAME
ENV RELEASE_TAG_NAME="${RELEASE_TAG_NAME}"
ENV LIBZ_SYS_STATIC="1"
ENV LIBSSH2_STATIC="1"
ENV LIBGIT2_STATIC="1"
ENV OPENSSL_STATIC="1"
ENV PKG_CONFIG_ALL_STATIC="1"
RUN \
--mount=type=cache,target=/cargo/git/db,sharing=locked \
--mount=type=cache,target=/cargo/registry/cache,sharing=locked \
@@ -97,48 +85,39 @@ set -euxo pipefail
xx-clang --setup-target-triple
xx-clang --wrap
export RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=/usr/bin/ld.lld"
if ! xx-info is-cross; then
export OPENSSL_DIR="/usr"
else
export OPENSSL_DIR="/$(xx-info triple)/usr"
fi
export LIBZ_SYS_STATIC="1"
export LIBSSH2_STATIC="1"
export LIBGIT2_STATIC="1"
export OPENSSL_STATIC="1"
export PKG_CONFIG_ALL_STATIC="1"
export OPENSSL_NO_VENDOR="1"
export ZSTD_SYS_USE_PKG_CONFIG="1"
export RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=mold -C target-feature=+crt-static"
export PKG_CONFIG="$(xx-clang --print-prog-name=pkg-config)"
export CARGO_BUILD_TARGET="$(xx-cargo --print-target-triple)"
xx-cargo build \
--verbose \
--frozen \
--package lapce-app \
--bin lapce-proxy \
--profile release-lto \
--no-default-features \
--features all-languages
--no-default-features
export RUSTFLAGS="${RUSTFLAGS} -C target-feature=+crt-static"
xx-cargo build
xx-verify "${CARGO_TARGET_DIR}"/"$(xx-cargo --print-target-triple)"/release-lto/lapce
xx-verify "${CARGO_TARGET_DIR}"/"$(xx-cargo --print-target-triple)"/release-lto/lapce-proxy
mkdir -p /target
mv -v "${CARGO_TARGET_DIR}"/"$(xx-cargo --print-target-triple)"/release-lto/lapce /target/
cargo pkgid | cut -d'#' -f2 | cut -d'@' -f2 | cut -d':' -f2 | tee /target/lapce.version
mv -v "${CARGO_TARGET_DIR}"/"$(xx-cargo --print-target-triple)"/release-lto/lapce-proxy /usr/local/bin/
EOF
WORKDIR /output
RUN <<EOF
#!/usr/bin/env bash
set -euxo pipefail
echo 'a'
EOF
FROM build-base AS dev
COPY . ./dev
FROM build-prep AS dev
FROM scratch AS binary
COPY --from=build /output/lapce .
COPY --from=build /usr/local/bin/lapce-proxy .
FROM scratch AS cross-binary
COPY --from=build /output/lapce .
# FROM scratch AS package
# COPY --from=build /output/*.deb .
# FROM scratch AS cross-package
# COPY --from=build /output/*.deb .
COPY --from=build /usr/local/bin/lapce-proxy .
+1 -2
View File
@@ -101,8 +101,7 @@ xx-cargo build \
--frozen \
--package lapce-app \
--profile release-lto \
--no-default-features \
--features all-languages
--no-default-features
xx-verify "${CARGO_TARGET_DIR}"/"$(xx-cargo --print-target-triple)"/release-lto/lapce
+70 -32
View File
@@ -80,9 +80,6 @@ FROM build-prep AS build
ARG PACKAGE_NAME
ENV PACKAGE_NAME="${PACKAGE_NAME}"
ARG OUTPUT_DIR="/output"
ENV OUTPUT_DIR="${OUTPUT_DIR}"
ARG CARGO_BUILD_INCREMENTAL='false'
ENV CC='xx-clang'
@@ -95,58 +92,99 @@ ARG RELEASE_TAG_NAME
ENV RELEASE_TAG_NAME="${RELEASE_TAG_NAME}"
RUN \
--mount=type=cache,target=/cargo/git/db,sharing=locked,readonly=true \
--mount=type=cache,target=/cargo/registry/cache,sharing=locked,readonly=true \
--mount=type=cache,target=/cargo/registry/index,sharing=locked,readonly=true \
--mount=type=cache,target=/cargo/git/db,readonly=true \
--mount=type=cache,target=/cargo/registry/cache,readonly=true \
--mount=type=cache,target=/cargo/registry/index,readonly=true \
--mount=type=cache,target=/root/.cache,sharing=private \
<<EOF
#!/usr/bin/env bash
set -euxo pipefail
LAPCE_VERSION=$(cargo pkgid | cut -d'#' -f2 | cut -d'@' -f2 | cut -d':' -f2)
case "${RELEASE_TAG_NAME}" in
nightly-*)
commit=$(echo "${RELEASE_TAG_NAME}" | cut -d'-' -f2)
RELEASE_TAG_NAME="${LAPCE_VERSION}+${commit}"
;;
debug|nightly)
date=$(date +%Y%m%d%H%M)
RELEASE_TAG_NAME="${LAPCE_VERSION}+${date}"
;;
*)
RELEASE_TAG_NAME="${RELEASE_TAG_NAME//v/}"
;;
esac
. /etc/os-release
export DISTRO_ID="$(echo $ID | tr - _)"
export DISTRO_VERSION="$(echo $VERSION_ID | tr - _)"
cat > lapce.spec <<EOL
Name: ${PACKAGE_NAME}
Version: ${RELEASE_TAG_NAME}
Release: 1.${DISTRO_ID}${DISTRO_VERSION}
Summary: Lightning-fast and Powerful Code Editor written in Rust
License: Apache-2.0
URL: https://github.com/lapce/lapce
Packager: Jakub Panek
%description
Lapce is written in pure Rust with a UI in Floem (which is also written in Rust).
It is designed with Rope Science from the Xi-Editor which makes for lightning-fast computation, and leverages OpenGL for rendering.
%build
xx-clang --setup-target-triple
xx-clang --wrap
export RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=/usr/bin/ld.lld"
export PKG_CONFIG="$(xx-clang --print-prog-name=pkg-config)"
export CARGO_BUILD_TARGET="$(xx-cargo --print-target-triple)"
rustup target add "$(xx-cargo --print-target-triple)"
export RELEASE_TAG_NAME="${RELEASE_TAG_NAME}"
xx-cargo build --profile release-lto --bin lapce --frozen
xx-verify "${CARGO_TARGET_DIR}"/"$(xx-cargo --print-target-triple)"/release-lto/lapce
mkdir -p /target
mv -v "${CARGO_TARGET_DIR}"/"$(xx-cargo --print-target-triple)"/release-lto/lapce /target/
%install
install -Dm755 "${CARGO_TARGET_DIR}"/"$(xx-cargo --print-target-triple)"/release-lto/lapce %{buildroot}%{_bindir}/lapce
install -Dm644 extra/linux/dev.lapce.lapce.desktop %{buildroot}/usr/share/applications/dev.lapce.lapce.desktop
install -Dm644 extra/linux/dev.lapce.lapce.metainfo.xml %{buildroot}/usr/share/metainfo/dev.lapce.lapce.metainfo.xml
install -Dm644 extra/images/logo.png %{buildroot}/usr/share/pixmaps/dev.lapce.lapce.png
%files
%license LICENSE*
%doc *.md
%{_bindir}/lapce
/usr/share/applications/dev.lapce.lapce.desktop
/usr/share/metainfo/dev.lapce.lapce.metainfo.xml
/usr/share/pixmaps/dev.lapce.lapce.png
%changelog
* Thu Apr 25 2024 Jakub Panek
- See GitHub for full changelog
EOL
/usr/bin/rpmbuild --build-in-place --noclean --noprep -bb --rmspec lapce.spec --verbose -D 'debug_package %{nil}'
ls -lahR $HOME/rpmbuild
EOF
WORKDIR /output
RUN <<EOF
RUN \
<<EOF
#!/usr/bin/env bash
set -euxo pipefail
export _PACKAGE_ARCHITECTURE=$(xx-info rhel-arch)
mkdir -v -p ${PACKAGE_NAME}/{etc,usr/bin}
cd ${PACKAGE_NAME}
mv /target/lapce ./usr/bin/
if [ "${PACKAGE_NAME}" = "lapce" ]; then
conflicts="lapce-nightly"
else
conflicts="lapce"
fi
if [ "${RELEASE_TAG_NAME}" = "nightly" ]; then
RELEASE_TAG_NAME="v0.0.1"
fi
mv -v $HOME/rpmbuild/RPMS/* /output/
EOF
FROM build-base AS dev
COPY . ./dev
FROM scratch AS binary
COPY --from=build /output/*.deb .
COPY --from=build /output/lapce .
FROM scratch AS cross-binary
COPY --from=build /output/*.deb .
COPY --from=build /output/lapce .
FROM scratch AS package
COPY --from=build /output/*.rpm .
FROM scratch AS cross-package
COPY --from=build /output/*.rpm .
+15 -17
View File
@@ -126,8 +126,7 @@ xx-cargo build \
--frozen \
--package lapce-app \
--profile release-lto \
--no-default-features \
--features all-languages
--no-default-features
xx-verify "${CARGO_TARGET_DIR}"/"$(xx-cargo --print-target-triple)"/release-lto/lapce
@@ -137,7 +136,18 @@ mv -v "${CARGO_TARGET_DIR}"/"$(xx-cargo --print-target-triple)"/release-lto/lapc
cargo pkgid | cut -d'#' -f2 | cut -d'@' -f2 | cut -d':' -f2 | tee /target/lapce.version
EOF
FROM build-base AS dev
COPY . ./dev
FROM scratch AS binary
COPY --from=build /target/lapce .
FROM scratch AS cross-binary
COPY --from=build /target/lapce .
FROM build AS package-prepare
WORKDIR /output
COPY --from=build /target /target
RUN <<EOF
#!/usr/bin/env bash
set -euxo pipefail
@@ -151,10 +161,7 @@ cp '/source/extra/linux/dev.lapce.lapce.desktop' './usr/share/applications/dev.l
cp '/source/extra/linux/dev.lapce.lapce.metainfo.xml' './usr/share/metainfo/dev.lapce.lapce.metainfo.xml'
cp '/source/extra/images/logo.png' './usr/share/pixmaps/dev.lapce.lapce.png'
cp '/target/lapce' './usr/bin/'
# single binary
cp /target/lapce /output/lapce
mv '/target/lapce' './usr/bin/'
if [[ "${PACKAGE_NAME}" == "lapce" ]]; then
conflicts="lapce-nightly"
@@ -199,17 +206,8 @@ mv debian DEBIAN
dpkg-deb --root-owner-group --build . "${OUTPUT_DIR}"/"${PACKAGE_NAME}.${ID}.${VERSION_CODENAME}.${_PACKAGE_ARCHITECTURE}.deb"
EOF
FROM build-base AS dev
COPY . ./dev
FROM scratch AS binary
COPY --from=build /output/lapce .
FROM scratch AS cross-binary
COPY --from=build /output/lapce .
FROM scratch AS package
COPY --from=build /output/*.deb .
COPY --from=package-prepare /output/*.deb .
FROM scratch AS cross-package
COPY --from=build /output/*.deb .
COPY --from=package-prepare /output/*.deb .
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.3.1</string>
<string>0.4.6</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
+1 -1
View File
@@ -91,7 +91,7 @@ else
fi
printf 'Decompressing gzip\n'
gzip -d "${lapce_dir}/lapce-proxy-${os_name}-${arch_name}.gz"
gzip -df "${lapce_dir}/lapce-proxy-${os_name}-${arch_name}.gz"
printf 'Renaming proxy \n'
mv -v "${lapce_dir}/lapce-proxy-${os_name}-${arch_name}" "${lapce_dir}/lapce"
+1 -1
View File
@@ -333,7 +333,7 @@
"drop-shadow-width": {
"type": "integer"
},
"preview-editor-width": {
"palette-width": {
"type": "integer"
},
"hover-font-family": {
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="windows-1252"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Name="Lapce" Id="*" UpgradeCode="9c09a374-1135-4782-959f-2dec376a1dfa" Language="1033" Codepage="1252" Version="0.3.1" Manufacturer="Lapce">
<Product Name="Lapce" Id="*" UpgradeCode="9c09a374-1135-4782-959f-2dec376a1dfa" Language="1033" Codepage="1252" Version="0.4.6" Manufacturer="Lapce">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine"/>
<MajorUpgrade AllowSameVersionUpgrades="yes" DowngradeErrorMessage="A newer version of [ProductName] is already installed."/>
<Icon Id="lapce.exe" SourceFile=".\extra\windows\lapce.ico"/>
+1
View File
@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 1l-.5.5v3l.5.5h3l.5-.5v-3L4.5 1h-3zM2 4V2h2v2H2zm-.5 2l-.5.5v3l.5.5h3l.5-.5v-3L4.5 6h-3zM2 9V7h2v2H2zm-1 2.5l.5-.5h3l.5.5v3l-.5.5h-3l-.5-.5v-3zm1 .5v2h2v-2H2zm10.5-7l-.5.5v6l.5.5h3l.5-.5v-6l-.5-.5h-3zM15 8h-2V6h2v2zm0 3h-2V9h2v2zM9.1 8H6v1h3.1l-1 1 .7.6 1.8-1.8v-.7L8.8 6.3l-.7.7 1 1z"/></svg>

After

Width:  |  Height:  |  Size: 450 B

+1
View File
@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M13.5 12h-1.793L10 10.293V6.5L9.5 6H8V4h.5l.5-.5v-2L8.5 1h-2l-.5.5v2l.5.5H7v2H5.5l-.5.5v3.793L3.293 12H1.5l-.5.5v2l.5.5h2l.5-.5v-1.793L5.707 11h3.586L11 12.707V14.5l.5.5h2l.5-.5v-2l-.5-.5zM7 2h1v1H7V2zM6 7h3v3H6V7zm-3 7H2v-1h1v1zm10 0h-1v-1h1v1z"/></svg>

After

Width:  |  Height:  |  Size: 366 B

+20 -40
View File
@@ -13,7 +13,6 @@ backtrace = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true }
crossbeam-channel = { workspace = true }
directories = { workspace = true }
flate2 = { workspace = true }
globset = { workspace = true }
im = { workspace = true }
@@ -34,10 +33,12 @@ reqwest = { workspace = true }
smallvec = { workspace = true }
strum = { workspace = true }
strum_macros = { workspace = true }
semver = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
regex = { workspace = true }
tar = { workspace = true }
tempfile = { workspace = true }
thiserror = { workspace = true }
toml = { workspace = true }
toml_edit = { workspace = true }
@@ -46,25 +47,21 @@ tracing-log = { workspace = true }
tracing-subscriber = { workspace = true }
tracing-appender = { workspace = true }
url = { workspace = true }
zstd = { workspace = true }
floem = { workspace = true }
pulldown-cmark = "0.10.2"
Inflector = "0.11.4"
open = "5.1.2"
unicode-width = "0.1.11"
nucleo = "0.5.0"
bytemuck = "1.14.3"
config = { version = "=0.13.4", default-features = false, features = ["toml"] }
structdesc = { git = "https://github.com/lapce/structdesc" }
base64 = "0.21.7"
sha2 = "0.10.8"
zip = { version = "0.6.6", default-features = false, features = ["deflate"] }
[dependencies.floem]
# path = "../../workspaces/floem"
git = "https://github.com/lapce/floem"
rev = "1477931896d14a9dafbfc8159e7a52a9d8e1d8b8"
default-features = true
features = ["editor", "serde", "default-image-formats", "rfd-async-std"]
pulldown-cmark = { version = "0.11.0" }
Inflector = { version = "0.11.4" }
open = { version = "5.1.4" }
unicode-width = { version = "0.1.13" }
nucleo = { version = "0.5.0" }
bytemuck = { version = "1.22.0" }
config = { version = "=0.13.4", default-features = false, features = ["toml"] }
structdesc = { git = "https://github.com/lapce/structdesc", rev = "bb56969f22fdb2c2d6c03f158fd4a2bdc983b659" }
base64 = { version = "0.21.7" }
sha2 = { version = "0.10.8" }
zip = { version = "0.6.6", default-features = false, features = ["deflate"] }
percent-encoding = { version = "2.3.1" }
[target.'cfg(target_os="macos")'.dependencies]
fs_extra = "1.2.0"
@@ -75,27 +72,10 @@ workspace = true
features = ["Win32_Foundation", "Win32_System_Threading", "Win32_UI_WindowsAndMessaging"]
[features]
default = ["all-languages", "updater"]
portable = ["lapce-core/portable"]
updater = []
# To build lapce with only some of the supported languages, for example:
#
# cargo build --no-default-features -p lapce-ui \
# --features lang-rust,lang-toml,lang-markdown,lang-json
#
all-languages = [
"lapce-core/lang-bash",
"lapce-core/lang-c",
"lapce-core/lang-cpp",
"lapce-core/lang-javascript",
"lapce-core/lang-json",
"lapce-core/lang-markdown",
"lapce-core/lang-python",
"lapce-core/lang-rust",
"lapce-core/lang-toml",
"lapce-core/lang-yaml",
]
default = ["updater", "vendored-fonts"]
portable = ["lapce-core/portable"]
updater = []
vendored-fonts = []
[dev-dependencies]
criterion = "0.5"
+7 -6
View File
@@ -1,9 +1,9 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use floem::{
cosmic_text::{Attrs, AttrsList, FamilyOwned, TextLayout, Wrap},
reactive::Scope,
text::{Attrs, AttrsList, FamilyOwned, TextLayout, Wrap},
views::editor::{
layout::TextLayoutLine,
phantom_text::PhantomTextLine,
@@ -28,7 +28,7 @@ struct TLProv<'a> {
font_family: Vec<FamilyOwned>,
has_multiline_phantom: bool,
}
impl<'a> TextLayoutProvider for TLProv<'a> {
impl TextLayoutProvider for TLProv<'_> {
fn text(&self) -> Rope {
self.text.clone()
}
@@ -70,7 +70,7 @@ impl<'a> TextLayoutProvider for TLProv<'a> {
let attrs = Attrs::new()
.family(&self.font_family)
.font_size(font_size as f32);
let mut attrs_list = AttrsList::new(attrs);
let mut attrs_list = AttrsList::new(attrs.clone());
// We don't do line styles, since they aren't relevant
@@ -79,7 +79,7 @@ impl<'a> TextLayoutProvider for TLProv<'a> {
let start = col + offset;
let end = start + size;
let mut attrs = attrs;
let mut attrs = attrs.clone();
if let Some(fg) = phantom.fg {
attrs = attrs.color(fg);
}
@@ -105,7 +105,7 @@ impl<'a> TextLayoutProvider for TLProv<'a> {
text_layout.set_size(px, f32::MAX);
}
}
text_layout.set_text(&line_content, attrs_list);
text_layout.set_text(&line_content, attrs_list, None);
// skip phantom text background styling because it doesn't shift positions
// skip severity styling
@@ -116,6 +116,7 @@ impl<'a> TextLayoutProvider for TLProv<'a> {
text: text_layout,
whitespaces: None,
indent: 0.0,
phantom_text: PhantomTextLine::default(),
})
}
+6 -5
View File
@@ -1,12 +1,12 @@
use std::rc::Rc;
use floem::{
View,
event::EventListener,
keyboard::Modifiers,
reactive::{RwSignal, Scope},
reactive::{RwSignal, Scope, SignalGet, SignalUpdate},
style::{CursorStyle, Display, Position},
view::View,
views::{container, label, stack, svg, Decorators},
views::{Decorators, container, label, stack, svg},
};
use lapce_core::{command::FocusCommand, meta::VERSION, mode::Mode};
@@ -28,7 +28,7 @@ impl AboutUri {
const CODICONS: &'static str = "https://github.com/microsoft/vscode-codicons";
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct AboutData {
pub visible: RwSignal<bool>,
pub focus: RwSignal<Focus>,
@@ -157,6 +157,7 @@ pub fn about_popup(window_tab_data: Rc<WindowTabData>) -> impl View {
))
.style(|s| s.flex_col().items_center())
})
.debug_name("About Popup")
}
fn exclusive_popup<V: View + 'static>(
@@ -206,7 +207,7 @@ fn exclusive_popup<V: View + 'static>(
config
.get()
.color(LapceColor::LAPCE_DROPDOWN_SHADOW)
.with_alpha_factor(0.5),
.multiply_alpha(0.5),
)
})
}
+7 -6
View File
@@ -1,19 +1,19 @@
use std::{
fmt,
rc::Rc,
sync::{atomic::AtomicU64, Arc},
sync::{Arc, atomic::AtomicU64},
};
use floem::{
View,
event::EventListener,
reactive::{ReadSignal, RwSignal, Scope},
reactive::{ReadSignal, RwSignal, Scope, SignalGet, SignalUpdate},
style::CursorStyle,
view::View,
views::{container, dyn_stack, label, stack, svg, Decorators},
views::{Decorators, container, dyn_stack, label, stack, svg},
};
use crate::{
config::{color::LapceColor, icon::LapceIcons, LapceConfig},
config::{LapceConfig, color::LapceColor, icon::LapceIcons},
window_tab::CommonData,
};
@@ -167,7 +167,8 @@ pub fn alert_box(alert_data: AlertBoxData) -> impl View {
config
.get()
.color(LapceColor::LAPCE_DROPDOWN_SHADOW)
.with_alpha_factor(0.5),
.multiply_alpha(0.5),
)
})
.debug_name("Alert Box")
}
+1053 -703
View File
File diff suppressed because it is too large Load Diff
+141
View File
@@ -0,0 +1,141 @@
use std::{
env,
fs::{self},
path::PathBuf,
};
use anyhow::{Context, Result, anyhow};
use lapce_core::directory::Directory;
use crate::{tracing::*, update::ReleaseInfo};
fn get_github_api(url: &str) -> Result<String> {
let user_agent = format!("Lapce/{}", lapce_core::meta::VERSION);
let resp = lapce_proxy::get_url(url, Some(user_agent.as_str()))?;
if !resp.status().is_success() {
return Err(anyhow!("get release info failed {}", resp.text()?));
}
Ok(resp.text()?)
}
pub fn find_grammar_release() -> Result<ReleaseInfo> {
let releases: Vec<ReleaseInfo> = serde_json::from_str(&get_github_api(
"https://api.github.com/repos/lapce/tree-sitter-grammars/releases?per_page=100",
).context("Failed to retrieve releases for tree-sitter-grammars")?)?;
use lapce_core::meta::{RELEASE, ReleaseType, VERSION};
let releases = releases
.into_iter()
.filter_map(|f| {
if matches!(RELEASE, ReleaseType::Debug | ReleaseType::Nightly) {
return Some(f);
}
let tag_name = if f.tag_name.starts_with('v') {
f.tag_name.trim_start_matches('v')
} else {
f.tag_name.as_str()
};
use std::cmp::Ordering;
use semver::Version;
let sv = Version::parse(tag_name).ok()?;
let version = Version::parse(VERSION).ok()?;
if matches!(sv.cmp_precedence(&version), Ordering::Equal) {
Some(f)
} else {
None
}
})
.collect::<Vec<_>>();
let Some(release) = releases.first() else {
return Err(anyhow!("Couldn't find any release"));
};
Ok(release.to_owned())
}
pub fn fetch_grammars(release: &ReleaseInfo) -> Result<bool> {
let dir = Directory::grammars_directory()
.ok_or_else(|| anyhow!("can't get grammars directory"))?;
let file_name = format!("grammars-{}-{}", env::consts::OS, env::consts::ARCH);
let updated = download_release(dir, release, &file_name)?;
trace!(TraceLevel::INFO, "Successfully downloaded grammars");
Ok(updated)
}
pub fn fetch_queries(release: &ReleaseInfo) -> Result<bool> {
let dir = Directory::queries_directory()
.ok_or_else(|| anyhow!("can't get queries directory"))?;
let file_name = "queries";
let updated = download_release(dir, release, file_name)?;
trace!(TraceLevel::INFO, "Successfully downloaded queries");
Ok(updated)
}
fn download_release(
dir: PathBuf,
release: &ReleaseInfo,
file_name: &str,
) -> Result<bool> {
if !dir.exists() {
fs::create_dir(&dir)?;
}
let current_version =
fs::read_to_string(dir.join("version")).unwrap_or_default();
let release_version = if release.tag_name == "nightly" {
format!("nightly-{}", &release.target_commitish[..7])
} else {
release.tag_name.clone()
};
if release_version == current_version {
return Ok(false);
}
for asset in &release.assets {
if asset.name.starts_with(file_name) {
let mut resp = lapce_proxy::get_url(&asset.browser_download_url, None)?;
if !resp.status().is_success() {
return Err(anyhow!("download file error {}", resp.text()?));
}
let file = tempfile::tempfile()?;
{
use std::io::{Seek, Write};
let file = &mut &file;
resp.copy_to(file)?;
file.flush()?;
file.rewind()?;
}
if asset.name.ends_with(".zip") {
let mut archive = zip::ZipArchive::new(file)?;
archive.extract(&dir)?;
} else if asset.name.ends_with(".tar.zst") {
let mut archive =
tar::Archive::new(zstd::stream::read::Decoder::new(file)?);
archive.unpack(&dir)?;
}
fs::write(dir.join("version"), &release_version)?;
}
}
Ok(true)
}
+153
View File
@@ -0,0 +1,153 @@
use lapce_core::directory::Directory;
use tracing::level_filters::LevelFilter;
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::{filter::Targets, reload::Handle};
use crate::tracing::*;
#[inline(always)]
pub(super) fn logging() -> (Handle<Targets>, Option<WorkerGuard>) {
use tracing_subscriber::{filter, fmt, prelude::*, reload};
let (log_file, guard) = match Directory::logs_directory()
.and_then(|dir| {
tracing_appender::rolling::Builder::new()
.max_log_files(10)
.rotation(tracing_appender::rolling::Rotation::DAILY)
.filename_prefix("lapce")
.filename_suffix("log")
.build(dir)
.ok()
})
.map(tracing_appender::non_blocking)
{
Some((log_file, guard)) => (Some(log_file), Some(guard)),
None => (None, None),
};
let log_file_filter_targets = filter::Targets::new()
.with_target("lapce_app", LevelFilter::DEBUG)
.with_target("lapce_proxy", LevelFilter::DEBUG)
.with_target("lapce_core", LevelFilter::DEBUG)
.with_default(LevelFilter::from_level(TraceLevel::INFO));
let (log_file_filter, reload_handle) =
reload::Subscriber::new(log_file_filter_targets);
let console_filter_targets = std::env::var("LAPCE_LOG")
.unwrap_or_default()
.parse::<filter::Targets>()
.unwrap_or_default();
let registry = tracing_subscriber::registry();
if let Some(log_file) = log_file {
let file_layer = tracing_subscriber::fmt::subscriber()
.with_ansi(false)
.with_writer(log_file)
.with_filter(log_file_filter);
registry
.with(file_layer)
.with(
fmt::Subscriber::default()
.with_line_number(true)
.with_target(true)
.with_thread_names(true)
.with_filter(console_filter_targets),
)
.init();
} else {
registry
.with(fmt::Subscriber::default().with_filter(console_filter_targets))
.init();
};
(reload_handle, guard)
}
pub(super) fn panic_hook() {
std::panic::set_hook(Box::new(move |info| {
let thread = std::thread::current();
let thread = thread.name().unwrap_or("main");
let backtrace = backtrace::Backtrace::new();
let payload = if let Some(s) = info.payload().downcast_ref::<&str>() {
s
} else {
"<unknown>"
};
match info.location() {
Some(loc) => {
trace!(
target: "lapce_app::panic_hook",
TraceLevel::ERROR,
"thread {thread} panicked at {} | file://./{}:{}:{}\n{:?}",
payload,
loc.file(), loc.line(), loc.column(),
backtrace,
);
}
None => {
trace!(
target: "lapce_app::panic_hook",
TraceLevel::ERROR,
"thread {thread} panicked at {}\n{:?}",
payload,
backtrace,
);
}
}
#[cfg(windows)]
error_modal("Error", &info.to_string());
#[cfg(unix)]
error_notification("Error", &info.to_string());
}))
}
#[cfg(windows)]
pub(super) fn error_modal(title: &str, msg: &str) -> i32 {
use std::{ffi::OsStr, iter::once, mem, os::windows::prelude::OsStrExt};
use windows::Win32::UI::WindowsAndMessaging::{
MB_ICONERROR, MB_SYSTEMMODAL, MessageBoxW,
};
let result: i32;
let title = OsStr::new(title)
.encode_wide()
.chain(once(0u16))
.collect::<Vec<u16>>();
let msg = OsStr::new(msg)
.encode_wide()
.chain(once(0u16))
.collect::<Vec<u16>>();
unsafe {
result = MessageBoxW(
mem::zeroed(),
msg.as_ptr(),
title.as_ptr(),
MB_ICONERROR | MB_SYSTEMMODAL,
);
}
result
}
#[cfg(unix)]
pub fn error_notification(title: &str, msg: &str) {
let res = std::process::Command::new("notify-send")
.args([
"-a",
"dev.lapce.lapce",
"-w",
"-n",
"dev.lapce.lapce",
"-c",
"error",
title,
msg,
])
.spawn();
}
+2
View File
@@ -1,3 +1,5 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use lapce_app::app;
pub fn main() {
+11 -11
View File
@@ -1,9 +1,9 @@
use std::{rc::Rc, sync::Arc};
use std::rc::Rc;
use floem::{
keyboard::Modifiers,
peniko::kurbo::Rect,
reactive::{RwSignal, Scope},
reactive::{RwSignal, Scope, SignalGet, SignalUpdate},
};
use lapce_core::{command::FocusCommand, mode::Mode, movement::Movement};
use lapce_rpc::plugin::PluginId;
@@ -11,7 +11,7 @@ use lsp_types::CodeActionOrCommand;
use crate::{
command::{CommandExecuted, CommandKind, InternalCommand},
keypress::{condition::Condition, KeyPressFocus},
keypress::{KeyPressFocus, condition::Condition},
window_tab::{CommonData, Focus},
};
@@ -21,7 +21,7 @@ pub enum CodeActionStatus {
Active,
}
#[derive(Clone, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub struct ScoredCodeActionItem {
pub item: CodeActionOrCommand,
pub plugin_id: PluginId,
@@ -38,7 +38,7 @@ impl ScoredCodeActionItem {
}
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct CodeActionData {
pub status: RwSignal<CodeActionStatus>,
pub active: RwSignal<usize>,
@@ -166,7 +166,8 @@ impl CodeActionData {
pub fn show(
&mut self,
code_actions: Arc<(PluginId, Vec<CodeActionOrCommand>)>,
plugin_id: PluginId,
code_actions: im::Vector<CodeActionOrCommand>,
offset: usize,
mouse_click: bool,
) {
@@ -176,11 +177,10 @@ impl CodeActionData {
self.mouse_click = mouse_click;
self.request_id += 1;
self.items = code_actions
.1
.iter()
.into_iter()
.map(|code_action| ScoredCodeActionItem {
item: code_action.clone(),
plugin_id: code_actions.0,
item: code_action,
plugin_id,
score: 0,
indices: Vec::new(),
})
@@ -194,7 +194,7 @@ impl CodeActionData {
self.common.focus.set(Focus::Workbench);
}
fn select(&self) {
pub fn select(&self) {
if let Some(item) = self.filtered_items.get(self.active.get_untracked()) {
self.common
.internal_command
+100
View File
@@ -0,0 +1,100 @@
use std::rc::Rc;
use lapce_rpc::dap_types::{ConfigSource, RunDebugConfig};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{command::InternalCommand, debug::RunDebugMode, window_tab::CommonData};
#[derive(Serialize, Deserialize)]
struct CargoArgs {
#[serde(rename = "cargoArgs")]
pub cargo_args: Vec<String>,
#[serde(rename = "cargoExtraArgs")]
pub cargo_extra_args: Vec<String>,
#[serde(rename = "executableArgs")]
pub executable_args: Vec<String>,
}
#[derive(Serialize, Deserialize)]
struct RustArgs {
pub args: CargoArgs,
pub kind: String,
pub label: String,
pub location: lsp_types::LocationLink,
}
#[derive(Clone)]
pub struct CodeLensData {
common: Rc<CommonData>,
}
impl CodeLensData {
pub fn new(common: Rc<CommonData>) -> Self {
Self { common }
}
pub fn run(&self, command: &str, args: Vec<Value>) {
match command {
"rust-analyzer.runSingle" | "rust-analyzer.debugSingle" => {
let mode = if command == "rust-analyzer.runSingle" {
RunDebugMode::Run
} else {
RunDebugMode::Debug
};
if let Some(config) = self.get_rust_command_config(&args, mode) {
self.common
.internal_command
.send(InternalCommand::RunAndDebug { mode, config });
}
}
_ => {
tracing::debug!("todo {:}", command);
}
}
}
fn get_rust_command_config(
&self,
args: &[Value],
mode: RunDebugMode,
) -> Option<RunDebugConfig> {
if let Some(args) = args.first() {
let Ok(mut cargo_args) =
serde_json::from_value::<RustArgs>(args.clone())
else {
tracing::error!("serde error");
return None;
};
cargo_args
.args
.cargo_args
.extend(cargo_args.args.cargo_extra_args);
if !cargo_args.args.executable_args.is_empty() {
cargo_args.args.cargo_args.push("--".to_string());
cargo_args
.args
.cargo_args
.extend(cargo_args.args.executable_args);
}
Some(RunDebugConfig {
ty: None,
name: cargo_args.label,
program: cargo_args.kind,
args: Some(cargo_args.args.cargo_args),
cwd: None,
env: None,
prelaunch: None,
debug_command: None,
dap_id: Default::default(),
tracing_output: mode == RunDebugMode::Debug,
config_source: ConfigSource::CodeLens,
})
} else {
tracing::error!("no args");
None
}
}
}
+92 -7
View File
@@ -1,7 +1,9 @@
use std::{path::PathBuf, rc::Rc, sync::Arc};
use std::{path::PathBuf, rc::Rc};
pub use floem::views::editor::command::CommandExecuted;
use floem::{
keyboard::Modifiers, peniko::kurbo::Vec2, views::editor::command::Command,
ViewId, keyboard::Modifiers, peniko::kurbo::Vec2,
views::editor::command::Command,
};
use indexmap::IndexMap;
use lapce_core::command::{
@@ -17,7 +19,7 @@ use lapce_rpc::{
use lsp_types::{CodeActionOrCommand, Position, WorkspaceEdit};
use serde_json::Value;
use strum::{EnumMessage, IntoEnumIterator};
use strum_macros::{Display, EnumIter, EnumMessage, EnumString, IntoStaticStr};
use strum_macros::{Display, EnumIter, EnumString, IntoStaticStr};
use crate::{
alert::AlertButton,
@@ -26,12 +28,10 @@ use crate::{
editor::location::EditorLocation,
editor_tab::EditorTabChild,
id::EditorTabId,
main_split::{SplitDirection, SplitMoveDirection},
main_split::{SplitDirection, SplitMoveDirection, TabCloseKind},
workspace::LapceWorkspace,
};
pub use floem::views::editor::command::CommandExecuted;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LapceCommand {
pub kind: CommandKind,
@@ -183,6 +183,40 @@ pub enum LapceWorkbenchCommand {
#[strum(message = "Open File")]
OpenFile,
#[strum(serialize = "show_call_hierarchy")]
#[strum(message = "Show Call Hierarchy")]
ShowCallHierarchy,
#[strum(serialize = "find_references")]
#[strum(message = "Find References")]
FindReferences,
#[strum(serialize = "go_to_implementation")]
#[strum(message = "Go to Implementation")]
GoToImplementation,
#[strum(serialize = "reveal_in_panel")]
#[strum(message = "Reveal in Panel")]
RevealInPanel,
#[strum(serialize = "source_control_open_active_file_remote_url")]
#[strum(message = "Source Control: Open Remote File Url")]
SourceControlOpenActiveFileRemoteUrl,
#[cfg(not(target_os = "macos"))]
#[strum(serialize = "reveal_in_file_explorer")]
#[strum(message = "Reveal in System File Explorer")]
RevealInFileExplorer,
#[cfg(target_os = "macos")]
#[strum(serialize = "reveal_in_file_explorer")]
#[strum(message = "Reveal in Finder")]
RevealInFileExplorer,
#[strum(serialize = "run_in_terminal")]
#[strum(message = "Run in Terminal")]
RunInTerminal,
#[strum(serialize = "reveal_active_file_in_file_explorer")]
#[strum(message = "Reveal Active File in File Explorer")]
RevealActiveFileInFileExplorer,
@@ -191,6 +225,10 @@ pub enum LapceWorkbenchCommand {
#[strum(message = "Open Internal UI Inspector")]
OpenUIInspector,
#[strum(serialize = "show_env")]
#[strum(message = "Show Environment")]
ShowEnvironment,
#[strum(serialize = "change_color_theme")]
#[strum(message = "Change Color Theme")]
ChangeColorTheme,
@@ -243,6 +281,14 @@ pub enum LapceWorkbenchCommand {
#[strum(message = "Open Plugins Directory")]
OpenPluginsDirectory,
#[strum(serialize = "open_grammars_directory")]
#[strum(message = "Open Grammars Directory")]
OpenGrammarsDirectory,
#[strum(serialize = "open_queries_directory")]
#[strum(message = "Open Queries Directory")]
OpenQueriesDirectory,
#[strum(serialize = "zoom_in")]
#[strum(message = "Zoom In")]
ZoomIn,
@@ -352,6 +398,10 @@ pub enum LapceWorkbenchCommand {
#[strum(serialize = "palette.palette_help")]
PaletteHelp,
#[strum(message = "List Palette Types and Files")]
#[strum(serialize = "palette.palette_help_and_file")]
PaletteHelpAndFile,
#[strum(message = "Run and Debug Restart Current Running")]
#[strum(serialize = "palette.run_and_debug_restart")]
RunAndDebugRestart,
@@ -542,6 +592,14 @@ pub enum LapceWorkbenchCommand {
#[strum(serialize = "quit")]
#[strum(message = "Quit Editor")]
Quit,
#[strum(serialize = "go_to_location")]
#[strum(message = "Go to Location")]
GoToLocation,
#[strum(serialize = "add_run_debug_config")]
#[strum(message = "Add Run Debug Config")]
AddRunDebugConfig,
}
#[derive(Clone, Debug)]
@@ -550,6 +608,9 @@ pub enum InternalCommand {
OpenFile {
path: PathBuf,
},
OpenAndConfirmedFile {
path: PathBuf,
},
OpenFileInNewTab {
path: PathBuf,
},
@@ -621,10 +682,16 @@ pub enum InternalCommand {
editor_tab_id: EditorTabId,
child: EditorTabChild,
},
EditorTabCloseByKind {
editor_tab_id: EditorTabId,
child: EditorTabChild,
kind: TabCloseKind,
},
ShowCodeActions {
offset: usize,
mouse_click: bool,
code_actions: Arc<(PluginId, Vec<CodeActionOrCommand>)>,
plugin_id: PluginId,
code_actions: im::Vector<CodeActionOrCommand>,
},
RunCodeAction {
plugin_id: PluginId,
@@ -712,6 +779,24 @@ pub enum InternalCommand {
left_path: PathBuf,
right_path: PathBuf,
},
ExecuteProcess {
program: String,
arguments: Vec<String>,
},
ClearTerminalBuffer {
view_id: ViewId,
tab_index: usize,
terminal_index: usize,
},
CallHierarchyIncoming {
item_id: ViewId,
},
StopTerminal {
term_id: TermId,
},
RestartTerminal {
term_id: TermId,
},
}
#[derive(Clone)]
+1 -1
View File
@@ -2,7 +2,7 @@ use std::{borrow::Cow, path::PathBuf, str::FromStr, sync::Arc};
use floem::{
peniko::kurbo::Rect,
reactive::{ReadSignal, RwSignal, Scope},
reactive::{ReadSignal, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith},
views::editor::{id::EditorId, text::Document},
};
use lapce_core::{
+93 -42
View File
@@ -1,11 +1,11 @@
use ::core::slice;
use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::Arc,
};
use floem::peniko::Color;
use ::core::slice;
use floem::{peniko::Color, prelude::palette::css};
use itertools::Itertools;
use lapce_core::directory::Directory;
use lapce_proxy::plugin::wasi::find_all_volts;
@@ -21,7 +21,7 @@ use self::{
color::LapceColor,
color_theme::{ColorThemeConfig, ThemeColor, ThemeColorPreference},
core::CoreConfig,
editor::{EditorConfig, WrapStyle, SCALE_OR_SIZE_LIMIT},
editor::{EditorConfig, SCALE_OR_SIZE_LIMIT, WrapStyle},
icon::LapceIcons,
icon_theme::IconThemeConfig,
svg::SvgStore,
@@ -68,10 +68,18 @@ static DEFAULT_DARK_THEME_COLOR_CONFIG: Lazy<ColorThemeConfig> = Lazy::new(|| {
theme.get::<ColorThemeConfig>("color-theme")
.expect("Failed to load default dark theme. This is likely due to a missing or misnamed field in dark-theme.toml")
});
static DEFAULT_ICON_THEME_CONFIG: Lazy<IconThemeConfig> = Lazy::new(|| {
let (_, theme) =
LapceConfig::load_icon_theme_from_str(DEFAULT_ICON_THEME).unwrap();
theme.get::<IconThemeConfig>("icon-theme")
static DEFAULT_ICON_THEME_CONFIG: Lazy<config::Config> = Lazy::new(|| {
config::Config::builder()
.add_source(config::File::from_str(
DEFAULT_ICON_THEME,
config::FileFormat::Toml,
))
.build()
.unwrap()
});
static DEFAULT_ICON_THEME_ICON_CONFIG: Lazy<IconThemeConfig> = Lazy::new(|| {
DEFAULT_ICON_THEME_CONFIG.get::<IconThemeConfig>("icon-theme")
.expect("Failed to load default icon theme. This is likely due to a missing or misnamed field in icon-theme.toml")
});
@@ -245,7 +253,7 @@ impl LapceConfig {
let mut default_lapce_config: LapceConfig =
DEFAULT_CONFIG.clone().try_deserialize().expect("Failed to deserialize default config, this likely indicates a missing or misnamed field in settings.toml");
default_lapce_config.color_theme = DEFAULT_DARK_THEME_COLOR_CONFIG.clone();
default_lapce_config.icon_theme = DEFAULT_ICON_THEME_CONFIG.clone();
default_lapce_config.icon_theme = DEFAULT_ICON_THEME_ICON_CONFIG.clone();
default_lapce_config.resolve_colors(None);
default_lapce_config
}
@@ -256,40 +264,39 @@ impl LapceConfig {
let color_theme_config = self
.available_color_themes
.get(&self.core.color_theme.to_lowercase())
.map(|(_, config)| config);
.map(|(_, config)| config)
.unwrap_or(&DEFAULT_DARK_THEME_CONFIG);
let icon_theme_config = self
.available_icon_themes
.get(&self.core.icon_theme.to_lowercase())
.map(|(_, config, _)| config);
.map(|(_, config, _)| config)
.unwrap_or(&DEFAULT_ICON_THEME_CONFIG);
let icon_theme_path = self
.available_icon_themes
.get(&self.core.icon_theme.to_lowercase())
.map(|(_, _, path)| path);
if color_theme_config.is_some() || icon_theme_config.is_some() {
if let Ok(new) = Self::merge_config(
workspace,
color_theme_config.cloned(),
icon_theme_config.cloned(),
)
.try_deserialize::<LapceConfig>()
{
self.core = new.core;
self.ui = new.ui;
self.editor = new.editor;
self.terminal = new.terminal;
self.terminal.get_indexed_colors();
if let Ok(new) = Self::merge_config(
workspace,
Some(color_theme_config.clone()),
Some(icon_theme_config.clone()),
)
.try_deserialize::<LapceConfig>()
{
self.core = new.core;
self.ui = new.ui;
self.editor = new.editor;
self.terminal = new.terminal;
self.terminal.get_indexed_colors();
self.color_theme = new.color_theme;
self.icon_theme = new.icon_theme;
if let Some(icon_theme_path) = icon_theme_path {
self.icon_theme.path =
icon_theme_path.clone().unwrap_or_default();
}
self.plugins = new.plugins;
self.color_theme = new.color_theme;
self.icon_theme = new.icon_theme;
if let Some(icon_theme_path) = icon_theme_path {
self.icon_theme.path = icon_theme_path.clone().unwrap_or_default();
}
self.plugins = new.plugins;
}
self.resolve_colors(Some(&default_lapce_config));
self.update_id();
@@ -349,7 +356,7 @@ impl LapceConfig {
Some(c) => *c,
None => {
error!("Failed to find key: {name}");
Color::HOT_PINK
css::HOT_PINK
}
}
}
@@ -398,8 +405,8 @@ impl LapceConfig {
default_config.map(|c| &c.color.syntax),
);
let fg = self.color(LapceColor::EDITOR_FOREGROUND);
let bg = self.color(LapceColor::EDITOR_BACKGROUND);
let fg = self.color(LapceColor::EDITOR_FOREGROUND).to_rgba8();
let bg = self.color(LapceColor::EDITOR_BACKGROUND).to_rgba8();
let is_light = fg.r as u32 + fg.g as u32 + fg.b as u32
> bg.r as u32 + bg.g as u32 + bg.b as u32;
let high_contrast = self.color_theme.high_contrast.unwrap_or(false);
@@ -559,10 +566,13 @@ impl LapceConfig {
let path = Directory::config_directory()?.join("settings.toml");
if !path.exists() {
let _ = std::fs::OpenOptions::new()
if let Err(err) = std::fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(&path);
.open(&path)
{
tracing::error!("{:?}", err);
}
}
Some(path)
@@ -572,10 +582,13 @@ impl LapceConfig {
let path = Directory::config_directory()?.join("keymaps.toml");
if !path.exists() {
let _ = std::fs::OpenOptions::new()
if let Err(err) = std::fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(&path);
.open(&path)
{
tracing::error!("{:?}", err);
}
}
Some(path)
@@ -588,7 +601,7 @@ impl LapceConfig {
});
svg.unwrap_or_else(|| {
let name = DEFAULT_ICON_THEME_CONFIG.ui.get(icon).unwrap();
let name = DEFAULT_ICON_THEME_ICON_CONFIG.ui.get(icon).unwrap();
self.svg_store.write().get_default_svg(name)
})
}
@@ -648,6 +661,36 @@ impl LapceConfig {
Some(self.ui_svg(kind_str))
}
pub fn symbol_color(&self, kind: &SymbolKind) -> Option<Color> {
let theme_str = match *kind {
SymbolKind::METHOD => "method",
SymbolKind::FUNCTION => "method",
SymbolKind::ENUM => "enum",
SymbolKind::ENUM_MEMBER => "enum-member",
SymbolKind::CLASS => "class",
SymbolKind::VARIABLE => "field",
SymbolKind::STRUCT => "structure",
SymbolKind::CONSTANT => "constant",
SymbolKind::PROPERTY => "property",
SymbolKind::FIELD => "field",
SymbolKind::INTERFACE => "interface",
SymbolKind::ARRAY => "",
SymbolKind::BOOLEAN => "",
SymbolKind::EVENT => "",
SymbolKind::FILE => "",
SymbolKind::KEY => "",
SymbolKind::OBJECT => "",
SymbolKind::NAMESPACE => "",
SymbolKind::NUMBER => "number",
SymbolKind::OPERATOR => "",
SymbolKind::TYPE_PARAMETER => "",
SymbolKind::STRING => "string",
_ => return None,
};
self.style_color(theme_str)
}
pub fn logo_svg(&self) -> String {
self.svg_store.read().logo_svg()
}
@@ -705,11 +748,11 @@ impl LapceConfig {
self.terminal_get_named_color(color)
}
alacritty_terminal::vte::ansi::Color::Spec(rgb) => {
Color::rgb8(rgb.r, rgb.g, rgb.b)
Color::from_rgb8(rgb.r, rgb.g, rgb.b)
}
alacritty_terminal::vte::ansi::Color::Indexed(index) => {
if let Some(rgb) = colors[*index as usize] {
return Color::rgb8(rgb.r, rgb.g, rgb.b);
return Color::from_rgb8(rgb.r, rgb.g, rgb.b);
}
const NAMED_COLORS: [alacritty_terminal::vte::ansi::NamedColor; 16] = [
alacritty_terminal::vte::ansi::NamedColor::Black,
@@ -831,7 +874,7 @@ impl LapceConfig {
(LapceColor::TERMINAL_FOREGROUND, 0.66)
}
};
self.color(color).with_alpha_factor(alpha)
self.color(color).multiply_alpha(alpha)
}
/// Get the dropdown information for the specific setting, used for the settings UI.
@@ -875,6 +918,14 @@ impl LapceConfig {
.sorted()
.collect(),
}),
("ui", "tab-separator-height") => Some(DropdownInfo {
active_index: self.ui.tab_separator_height as usize,
items: ui::TabSeparatorHeight::VARIANTS
.iter()
.map(|s| s.to_string())
.sorted()
.collect(),
}),
("terminal", "default-profile") => Some(DropdownInfo {
active_index: self
.terminal
@@ -885,7 +936,7 @@ impl LapceConfig {
== self
.terminal
.default_profile
.get(&std::env::consts::OS.to_string())
.get(std::env::consts::OS)
.unwrap_or(&String::from("default"))
})
.unwrap_or(0),
+13 -12
View File
@@ -1,9 +1,10 @@
use std::{
collections::{BTreeMap, HashMap},
path::PathBuf,
str::FromStr,
};
use floem::peniko::Color;
use floem::{peniko::Color, prelude::palette::css};
use serde::{Deserialize, Serialize};
use super::color::LoadThemeError;
@@ -37,7 +38,7 @@ pub struct ThemeColor {
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct ThemeBaseConfig(BTreeMap<String, String>);
pub struct ThemeBaseConfig(pub BTreeMap<String, String>);
impl ThemeBaseConfig {
/// Resolve the variables in this theme base config into the actual colors.
@@ -61,12 +62,12 @@ impl ThemeBaseConfig {
for (key, value) in self.0.iter() {
match self.resolve_variable(&default, key, value, 0) {
Ok(Some(color)) => {
let color = Color::parse(color)
.unwrap_or_else(|| {
let color = Color::from_str(color)
.unwrap_or_else(|_| {
tracing::warn!(
"Failed to parse color theme variable for ({key}: {value})"
);
Color::HOT_PINK
css::HOT_PINK
});
base.0.insert(key.to_string(), color);
}
@@ -148,14 +149,14 @@ impl ColorThemeConfig {
let color = if let Some(stripped) = hex.strip_prefix('$') {
base.get(stripped)
} else {
Color::parse(hex)
Color::from_str(hex).ok()
};
let color = color
.or_else(|| {
default.and_then(|default| default.get(name).cloned())
})
.unwrap_or(Color::rgb8(0, 0, 0));
.unwrap_or(Color::from_rgb8(0, 0, 0));
(name.to_string(), color)
})
@@ -182,7 +183,7 @@ impl ColorThemeConfig {
#[cfg(test)]
mod tests {
use config::Config;
use floem::peniko::Color;
use floem::{peniko::Color, prelude::palette::css};
use crate::{config::LapceConfig, workspace::LapceWorkspace};
@@ -229,7 +230,7 @@ color-preference = "dark"
lapce_config.resolve_theme(&workspace);
println!("Hot Pink: {:?}", Color::HOT_PINK);
println!("Hot Pink: {:?}", css::HOT_PINK);
// test basic override
assert_eq!(
lapce_config.color("lapce.error"),
@@ -239,13 +240,13 @@ color-preference = "dark"
// test that it falls through to the dark theme for unspecified color
assert_eq!(
lapce_config.color("lapce.warn"),
Color::rgb8(0xE5, 0xC0, 0x7B),
Color::from_rgb8(0xE5, 0xC0, 0x7B),
"Failed to get from fallback dark theme"
); // $yellow
// test that our custom variable worked
// test that our custom variable worked
assert_eq!(
lapce_config.color("editor.background"),
Color::rgb8(0xFF, 0x00, 0xFF),
Color::from_rgb8(0xFF, 0x00, 0xFF),
"Failed to get from custom variable"
);
// test that for text it now uses our redeclared variable
+8
View File
@@ -14,4 +14,12 @@ pub struct CoreConfig {
desc = "Enable customised titlebar and disable OS native one (Linux, BSD, Windows)"
)]
pub custom_titlebar: bool,
#[field_names(
desc = "Only allow double-click to open files in the file explorer"
)]
pub file_explorer_double_click: bool,
#[field_names(
desc = "Enable auto-reload for the plugin when its configuration changes."
)]
pub auto_reload_plugin: bool,
}
+20 -6
View File
@@ -48,9 +48,12 @@ impl WrapStyle {
}
}
}
impl ToString for WrapStyle {
fn to_string(&self) -> String {
self.as_str().to_string()
impl std::fmt::Display for WrapStyle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())?;
Ok(())
}
}
@@ -61,8 +64,8 @@ pub struct EditorConfig {
pub font_family: String,
#[field_names(desc = "Set the editor font size")]
font_size: usize,
#[field_names(desc = "Set the font size in the code lens")]
pub code_lens_font_size: usize,
#[field_names(desc = "Set the font size in the code glance")]
pub code_glance_font_size: usize,
#[field_names(
desc = "Set the editor line height. If less than 5.0, line height will be a multiple of the font size."
)]
@@ -93,10 +96,16 @@ pub struct EditorConfig {
desc = "Show code context like functions and classes at the top of editor when scroll"
)]
pub sticky_header: bool,
#[field_names(desc = "The number of pixels to show completion")]
pub completion_width: usize,
#[field_names(
desc = "If the editor should show the documentation of the current completion item"
)]
pub completion_show_documentation: bool,
#[field_names(
desc = "Should the completion item use the `detail` field to replace the label `field`?"
)]
pub completion_item_show_detail: bool,
#[field_names(
desc = "If the editor should show the signature of the function as the parameters are being typed"
)]
@@ -150,6 +159,11 @@ pub struct EditorConfig {
pub inlay_hint_font_size: usize,
#[field_names(desc = "If diagnostics should be displayed inline")]
pub enable_error_lens: bool,
#[field_names(
desc = "Only render the styling without displaying messages, provided that `Enable ErrorLens` is enabled"
)]
pub only_render_error_styling: bool,
#[field_names(
desc = "Whether error lens should go to the end of view line, or only to the end of the diagnostic"
)]
@@ -234,7 +248,7 @@ pub struct EditorConfig {
impl EditorConfig {
pub fn font_size(&self) -> usize {
self.font_size.max(6).min(32)
self.font_size.clamp(6, 32)
}
pub fn line_height(&self) -> usize {
+7
View File
@@ -38,6 +38,7 @@ impl LapceIcons {
pub const KEYBOARD: &'static str = "keyboard";
pub const BREADCRUMB_SEPARATOR: &'static str = "breadcrumb_separator";
pub const SYMBOL_COLOR: &'static str = "symbol_color";
pub const TYPE_HIERARCHY: &'static str = "type_hierarchy";
pub const FILE: &'static str = "file";
pub const FILE_EXPLORER: &'static str = "file_explorer";
@@ -108,6 +109,12 @@ impl LapceIcons {
pub const FILE_TYPE_SYMLINK_FILE: &'static str = "file-symlink-file";
pub const FILE_TYPE_SYMLINK_DIRECTORY: &'static str = "file-symlink-directory";
pub const DOCUMENT_SYMBOL: &'static str = "document_symbol";
pub const REFERENCES: &'static str = "references";
pub const IMPLEMENTATION: &'static str = "implementation";
pub const SYMBOL_KIND_ARRAY: &'static str = "symbol_kind.array";
pub const SYMBOL_KIND_BOOLEAN: &'static str = "symbol_kind.boolean";
pub const SYMBOL_KIND_CLASS: &'static str = "symbol_kind.class";
+1 -2
View File
@@ -58,9 +58,8 @@ impl IconThemeConfig {
#[cfg(test)]
mod tests {
use crate::config::icon_theme::try_all_equal_value;
use super::IconThemeConfig;
use crate::config::icon_theme::try_all_equal_value;
#[test]
fn try_all_equal_value_empty_none() {
+4 -3
View File
@@ -4,12 +4,13 @@ use std::{
path::{Path, PathBuf},
};
use include_dir::{include_dir, Dir};
use include_dir::{Dir, include_dir};
use crate::config::LOGO;
const CODICONS_ICONS_DIR: Dir = include_dir!("../icons/codicons");
const LAPCE_ICONS_DIR: Dir = include_dir!("../icons/lapce");
const CODICONS_ICONS_DIR: Dir =
include_dir!("$CARGO_MANIFEST_DIR/../icons/codicons");
const LAPCE_ICONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/../icons/lapce");
#[derive(Debug, Clone)]
pub struct SvgStore {
+6 -5
View File
@@ -19,9 +19,10 @@ pub struct TerminalConfig {
desc = "Set the terminal line height, If 0, it uses editor line height"
)]
pub line_height: f64,
#[field_names(desc = "Profiles available in terminal pane")]
#[field_names(skip)]
pub profiles: HashMap<String, TerminalProfile>,
#[field_names(desc = "Default profile for each platform")]
#[field_names(skip)]
pub default_profile: HashMap<String, String>,
#[serde(skip)]
@@ -51,7 +52,7 @@ impl TerminalConfig {
for b in 0..6 {
// Override colors 16..232 with the config (if present).
let index = 16 + r * 36 + g * 6 + b;
let color = Color::rgb8(
let color = Color::from_rgb8(
if r == 0 { 0 } else { r * 40 + 55 },
if g == 0 { 0 } else { g * 40 + 55 },
if b == 0 { 0 } else { b * 40 + 55 },
@@ -67,7 +68,7 @@ impl TerminalConfig {
// Override colors 232..256 with the config (if present).
let value = i * 10 + 8;
indexed_colors.insert(index + i, Color::rgb8(value, value, value));
indexed_colors.insert(index + i, Color::from_rgb8(value, value, value));
}
self.indexed_colors = Arc::new(indexed_colors);
@@ -78,7 +79,7 @@ impl TerminalConfig {
) -> Option<lapce_rpc::terminal::TerminalProfile> {
let profile = self.profiles.get(
self.default_profile
.get(&std::env::consts::OS.to_string())
.get(std::env::consts::OS)
.unwrap_or(&String::from("default")),
)?;
let workdir = if let Some(workdir) = &profile.workdir {
+44 -5
View File
@@ -1,10 +1,13 @@
use floem::cosmic_text::FamilyOwned;
use floem::text::FamilyOwned;
use serde::{Deserialize, Serialize};
use structdesc::FieldNames;
#[derive(FieldNames, Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "kebab-case")]
pub struct UIConfig {
#[field_names(desc = "Set the UI scale. Defaults to 1.0")]
scale: f64,
#[field_names(
desc = "Set the UI font family. If empty, it uses system default."
)]
@@ -27,14 +30,19 @@ pub struct UIConfig {
#[field_names(desc = "Set the minimum width for editor tab")]
tab_min_width: usize,
#[field_names(
desc = "Set whether the editor tab separator should be full height or the height of the content"
)]
pub tab_separator_height: TabSeparatorHeight,
#[field_names(desc = "Set the width for scroll bar")]
scroll_width: usize,
#[field_names(desc = "Controls the width of drop shadow in the UI")]
drop_shadow_width: usize,
#[field_names(desc = "Controls the width of the preview editor")]
pub preview_editor_width: usize,
#[field_names(desc = "Controls the width of the command palette")]
palette_width: usize,
#[field_names(
desc = "Set the hover font family. If empty, it uses the UI font family"
@@ -76,9 +84,32 @@ pub enum TabCloseButton {
Off,
}
#[derive(
Debug,
Clone,
Copy,
Deserialize,
Serialize,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
strum_macros::VariantNames,
)]
pub enum TabSeparatorHeight {
#[default]
Content,
Full,
}
impl UIConfig {
pub fn scale(&self) -> f64 {
self.scale.clamp(0.1, 4.0)
}
pub fn font_size(&self) -> usize {
self.font_size.max(6).min(32)
self.font_size.clamp(6, 32)
}
pub fn font_family(&self) -> Vec<FamilyOwned> {
@@ -94,7 +125,7 @@ impl UIConfig {
if self.icon_size == 0 {
self.font_size()
} else {
self.icon_size.max(6).min(32)
self.icon_size.clamp(6, 32)
}
}
@@ -102,4 +133,12 @@ impl UIConfig {
let font_size = self.font_size();
self.status_height.max(font_size)
}
pub fn palette_width(&self) -> usize {
if self.palette_width == 0 {
500
} else {
self.palette_width.max(100)
}
}
}
+9 -6
View File
@@ -1,6 +1,4 @@
use std::sync::{atomic::AtomicBool, Arc};
use crossbeam_channel::Sender;
use std::sync::{Arc, atomic::AtomicBool, mpsc::Sender};
pub struct ConfigWatcher {
tx: Sender<()>,
@@ -9,8 +7,8 @@ pub struct ConfigWatcher {
impl notify::EventHandler for ConfigWatcher {
fn handle_event(&mut self, event: notify::Result<notify::Event>) {
if let Ok(event) = event {
match event.kind {
match event {
Ok(event) => match event.kind {
notify::EventKind::Create(_)
| notify::EventKind::Modify(_)
| notify::EventKind::Remove(_) => {
@@ -30,13 +28,18 @@ impl notify::EventHandler for ConfigWatcher {
std::thread::sleep(std::time::Duration::from_millis(
500,
));
let _ = tx.send(());
if let Err(err) = tx.send(()) {
tracing::error!("{:?}", err);
}
config_mutex
.store(false, std::sync::atomic::Ordering::Relaxed);
});
}
}
_ => {}
},
Err(err) => {
tracing::error!("{:?}", err);
}
}
}
+99 -50
View File
@@ -4,16 +4,17 @@ use std::{
sync::Arc,
};
use anyhow::{anyhow, Result};
use crossbeam_channel::{unbounded, Sender};
use floem::peniko::kurbo::Vec2;
use anyhow::{Result, anyhow};
use crossbeam_channel::{Sender, unbounded};
use floem::{peniko::kurbo::Vec2, reactive::SignalGet};
use lapce_core::directory::Directory;
use lapce_rpc::plugin::VoltID;
use sha2::{Digest, Sha256};
use crate::{
app::{AppData, AppInfo},
doc::DocInfo,
panel::{data::PanelOrder, kind::PanelKind, position::PanelPosition},
panel::{data::PanelOrder, kind::PanelKind},
window::{WindowData, WindowInfo},
window_tab::WindowTabData,
workspace::{LapceWorkspace, WorkspaceInfo},
@@ -50,7 +51,9 @@ impl LapceDb {
.ok_or_else(|| anyhow!("can't get config directory"))?
.join("db");
let workspace_folder = folder.join("workspaces");
let _ = std::fs::create_dir_all(&workspace_folder);
if let Err(err) = std::fs::create_dir_all(&workspace_folder) {
tracing::error!("{:?}", err);
}
let (save_tx, save_rx) = unbounded();
@@ -60,35 +63,57 @@ impl LapceDb {
folder,
};
let local_db = db.clone();
std::thread::spawn(move || -> Result<()> {
loop {
let event = save_rx.recv()?;
match event {
SaveEvent::App(info) => {
let _ = local_db.insert_app_info(info);
}
SaveEvent::Workspace(workspace, info) => {
let _ = local_db.insert_workspace(&workspace, &info);
}
SaveEvent::RecentWorkspace(workspace) => {
let _ = local_db.insert_recent_workspace(workspace);
}
SaveEvent::Doc(info) => {
let _ = local_db.insert_doc(&info);
}
SaveEvent::DisabledVolts(volts) => {
let _ = local_db.insert_disabled_volts(volts);
}
SaveEvent::WorkspaceDisabledVolts(workspace, volts) => {
let _ = local_db
.insert_workspace_disabled_volts(workspace, volts);
}
SaveEvent::PanelOrder(order) => {
let _ = local_db.insert_panel_orders(&order);
std::thread::Builder::new()
.name("SaveEventHandler".to_owned())
.spawn(move || -> Result<()> {
loop {
let event = save_rx.recv()?;
match event {
SaveEvent::App(info) => {
if let Err(err) = local_db.insert_app_info(info) {
tracing::error!("{:?}", err);
}
}
SaveEvent::Workspace(workspace, info) => {
if let Err(err) =
local_db.insert_workspace(&workspace, &info)
{
tracing::error!("{:?}", err);
}
}
SaveEvent::RecentWorkspace(workspace) => {
if let Err(err) =
local_db.insert_recent_workspace(workspace)
{
tracing::error!("{:?}", err);
}
}
SaveEvent::Doc(info) => {
if let Err(err) = local_db.insert_doc(&info) {
tracing::error!("{:?}", err);
}
}
SaveEvent::DisabledVolts(volts) => {
if let Err(err) = local_db.insert_disabled_volts(volts) {
tracing::error!("{:?}", err);
}
}
SaveEvent::WorkspaceDisabledVolts(workspace, volts) => {
if let Err(err) = local_db
.insert_workspace_disabled_volts(workspace, volts)
{
tracing::error!("{:?}", err);
}
}
SaveEvent::PanelOrder(order) => {
if let Err(err) = local_db.insert_panel_orders(&order) {
tracing::error!("{:?}", err);
}
}
}
}
}
});
})
.unwrap();
Ok(db)
}
@@ -99,7 +124,9 @@ impl LapceDb {
}
pub fn save_disabled_volts(&self, volts: Vec<VoltID>) {
let _ = self.save_tx.send(SaveEvent::DisabledVolts(volts));
if let Err(err) = self.save_tx.send(SaveEvent::DisabledVolts(volts)) {
tracing::error!("{:?}", err);
}
}
pub fn save_workspace_disabled_volts(
@@ -107,9 +134,12 @@ impl LapceDb {
workspace: Arc<LapceWorkspace>,
volts: Vec<VoltID>,
) {
let _ = self
if let Err(err) = self
.save_tx
.send(SaveEvent::WorkspaceDisabledVolts(workspace, volts));
.send(SaveEvent::WorkspaceDisabledVolts(workspace, volts))
{
tracing::error!("{:?}", err);
}
}
pub fn insert_disabled_volts(&self, volts: Vec<VoltID>) -> Result<()> {
@@ -126,7 +156,9 @@ impl LapceDb {
let folder = self
.workspace_folder
.join(workspace_folder_name(&workspace));
let _ = std::fs::create_dir_all(&folder);
if let Err(err) = std::fs::create_dir_all(&folder) {
tracing::error!("{:?}", err);
}
let volts = serde_json::to_string_pretty(&volts)?;
std::fs::write(folder.join(DISABLED_VOLTS), volts)?;
@@ -218,7 +250,9 @@ impl LapceDb {
info: &WorkspaceInfo,
) -> Result<()> {
let folder = self.workspace_folder.join(workspace_folder_name(workspace));
let _ = std::fs::create_dir_all(&folder);
if let Err(err) = std::fs::create_dir_all(&folder) {
tracing::error!("{:?}", err);
}
let workspace_info = serde_json::to_string_pretty(info)?;
std::fs::write(folder.join(WORKSPACE_INFO), workspace_info)?;
Ok(())
@@ -227,7 +261,9 @@ impl LapceDb {
pub fn save_app(&self, data: &AppData) -> Result<()> {
let windows = data.windows.get_untracked();
for (_, window) in &windows {
let _ = self.save_window(window.clone());
if let Err(err) = self.save_window(window.clone()) {
tracing::error!("{:?}", err);
}
}
let info = AppInfo {
@@ -258,7 +294,9 @@ impl LapceDb {
return Ok(());
}
for (_, window) in &windows {
let _ = self.insert_window(window.clone());
if let Err(err) = self.insert_window(window.clone()) {
tracing::error!("{:?}", err);
}
}
let info = AppInfo {
windows: windows
@@ -278,7 +316,7 @@ impl LapceDb {
window.size.width = 800.0;
}
if window.size.height < 10.0 {
window.size.width = 600.0;
window.size.height = 600.0;
}
}
Ok(info)
@@ -291,21 +329,25 @@ impl LapceDb {
info.size.width = 800.0;
}
if info.size.height < 10.0 {
info.size.width = 600.0;
info.size.height = 600.0;
}
Ok(info)
}
pub fn save_window(&self, data: WindowData) -> Result<()> {
for (_, window_tab) in data.window_tabs.get_untracked().into_iter() {
let _ = self.save_window_tab(window_tab);
if let Err(err) = self.save_window_tab(window_tab) {
tracing::error!("{:?}", err);
}
}
Ok(())
}
pub fn insert_window(&self, data: WindowData) -> Result<()> {
for (_, window_tab) in data.window_tabs.get_untracked().into_iter() {
let _ = self.insert_window_tab(window_tab);
if let Err(err) = self.insert_window_tab(window_tab) {
tracing::error!("{:?}", err);
}
}
let info = data.info();
let info = serde_json::to_string_pretty(&info)?;
@@ -330,7 +372,8 @@ impl LapceDb {
use strum::IntoEnumIterator;
for kind in PanelKind::iter() {
if kind.position(&panel_orders).is_none() {
let panels = panel_orders.entry(PanelPosition::LeftTop).or_default();
let panels =
panel_orders.entry(kind.default_position()).or_default();
panels.push_back(kind);
}
}
@@ -339,7 +382,9 @@ impl LapceDb {
}
pub fn save_panel_orders(&self, order: PanelOrder) {
let _ = self.save_tx.send(SaveEvent::PanelOrder(order));
if let Err(err) = self.save_tx.send(SaveEvent::PanelOrder(order)) {
tracing::error!("{:?}", err);
}
}
fn insert_panel_orders(&self, order: &PanelOrder) -> Result<()> {
@@ -361,7 +406,9 @@ impl LapceDb {
scroll_offset: (scroll_offset.x, scroll_offset.y),
cursor_offset,
};
let _ = self.save_tx.send(SaveEvent::Doc(info));
if let Err(err) = self.save_tx.send(SaveEvent::Doc(info)) {
tracing::error!("{:?}", err);
}
}
fn insert_doc(&self, info: &DocInfo) -> Result<()> {
@@ -369,7 +416,9 @@ impl LapceDb {
.workspace_folder
.join(workspace_folder_name(&info.workspace))
.join(WORKSPACE_FILES);
let _ = std::fs::create_dir_all(&folder);
if let Err(err) = std::fs::create_dir_all(&folder) {
tracing::error!("{:?}", err);
}
let contents = serde_json::to_string_pretty(info)?;
std::fs::write(folder.join(doc_path_name(&info.path)), contents)?;
Ok(())
@@ -397,7 +446,7 @@ fn workspace_folder_name(workspace: &LapceWorkspace) -> String {
}
fn doc_path_name(path: &Path) -> String {
url::form_urlencoded::Serializer::new(String::new())
.append_key_only(&path.to_string_lossy())
.finish()
let mut hasher = Sha256::new();
hasher.update(path.to_string_lossy().as_bytes());
format!("{:x}", hasher.finalize())
}
+5 -5
View File
@@ -8,7 +8,7 @@ use std::{
use floem::{
ext_event::create_ext_action,
reactive::{Memo, RwSignal, Scope},
reactive::{Memo, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith},
views::VirtualVector,
};
use lapce_rpc::{
@@ -361,7 +361,7 @@ impl DapData {
let variables_id = self.variables_id;
let send = create_ext_action(self.common.scope, move |result| {
if let Ok(ProxyResponse::DapVariableResponse { varialbes }) = result {
if let Ok(ProxyResponse::DapVariableResponse { variables }) = result {
variables_id.update(|id| {
*id += 1;
});
@@ -370,7 +370,7 @@ impl DapData {
let mut new_parent = parent.clone();
new_parent.push(reference);
var.read = true;
var.children = varialbes
var.children = variables
.into_iter()
.map(|v| DapVariable {
item: ScopeOrVar::Var(v),
@@ -521,7 +521,7 @@ mod tests {
#[test]
fn test_update_count() {
let variables = vec![
let variables = [
(
Scope {
variables_reference: 0,
@@ -610,7 +610,7 @@ mod tests {
let var = root.get_var_mut(&[0], 3).unwrap();
var.expanded = true;
var.read = true;
var.children = vec![
var.children = [
Variable {
variables_reference: 9,
..Default::default()
+364 -109
View File
@@ -5,18 +5,25 @@ use std::{
ops::Range,
path::{Path, PathBuf},
rc::Rc,
sync::{atomic, Arc},
sync::{
Arc,
atomic::{self, AtomicUsize},
},
time::Duration,
};
use floem::{
ViewId,
action::exec_after,
cosmic_text::{Attrs, AttrsList, FamilyOwned, TextLayout},
ext_event::create_ext_action,
keyboard::Modifiers,
peniko::Color,
reactive::{batch, ReadSignal, RwSignal, Scope},
reactive::{
ReadSignal, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith, batch,
},
text::{Attrs, AttrsList, FamilyOwned, TextLayout},
views::editor::{
CursorInfo, Editor, EditorStyle,
actions::CommonAction,
command::{Command, CommandExecuted},
id::EditorId,
@@ -24,19 +31,18 @@ use floem::{
phantom_text::{PhantomText, PhantomTextKind, PhantomTextLine},
text::{Document, DocumentPhantom, PreeditData, Styling, SystemClipboard},
view::{ScreenLines, ScreenLinesBase},
CursorInfo, Editor, EditorStyle,
},
};
use itertools::Itertools;
use lapce_core::{
buffer::{
diff::{rope_diff, DiffLines},
rope_text::RopeText,
Buffer, InvalLines,
diff::{DiffLines, rope_diff},
rope_text::RopeText,
},
char_buffer::CharBuffer,
command::EditCommand,
cursor::Cursor,
cursor::{Cursor, CursorAffinity},
editor::{Action, EditConf, EditType},
indent::IndentStyle,
language::LapceLanguage,
@@ -46,8 +52,8 @@ use lapce_core::{
rope_text_pos::RopeTextPosition,
selection::{InsertDrift, Selection},
style::line_styles,
syntax::{edit::SyntaxEdit, BracketParser, Syntax},
word::WordCursor,
syntax::{BracketParser, Syntax, edit::SyntaxEdit},
word::{CharClassification, WordCursor, get_char_property},
};
use lapce_rpc::{
buffer::BufferId,
@@ -56,24 +62,28 @@ use lapce_rpc::{
style::{LineStyle, LineStyles, Style},
};
use lapce_xi_rope::{
spans::{Spans, SpansBuilder},
Interval, Rope, RopeDelta, Transformer,
spans::{Spans, SpansBuilder},
};
use lsp_types::{
CodeActionResponse, Diagnostic, DiagnosticSeverity, InlayHint, InlayHintLabel,
CodeActionOrCommand, CodeLens, Diagnostic, DiagnosticSeverity,
DocumentSymbolResponse, InlayHint, InlayHintLabel, TextEdit,
};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use crate::{
command::{CommandKind, LapceCommand},
config::{color::LapceColor, LapceConfig},
editor::{compute_screen_lines, EditorData},
config::{LapceConfig, color::LapceColor},
editor::{EditorData, compute_screen_lines, gutter::FoldingRanges},
find::{Find, FindProgress, FindResult},
history::DocumentHistory,
keypress::KeyPressFocus,
main_split::Editors,
panel::kind::PanelKind,
panel::{
document_symbol::{SymbolData, SymbolInformationItemData},
kind::PanelKind,
},
window_tab::{CommonData, Focus},
workspace::LapceWorkspace,
};
@@ -101,7 +111,7 @@ pub struct DocHistory {
pub enum DocContent {
/// A file at some location. This can be a remote path.
File { path: PathBuf, read_only: bool },
/// A local document, which doens't need to be sync to the disk.
/// A local document, which doesn't need to be sync to the disk.
Local,
/// A document of an old version in the source control
History(DocHistory),
@@ -146,7 +156,10 @@ pub struct DocInfo {
}
/// (Offset -> (Plugin the code actions are from, Code Actions))
pub type CodeActions = im::HashMap<usize, Arc<(PluginId, CodeActionResponse)>>;
pub type CodeActions =
im::HashMap<usize, (PluginId, im::Vector<CodeActionOrCommand>)>;
pub type AllCodeLens = im::HashMap<usize, (PluginId, usize, im::Vector<CodeLens>)>;
#[derive(Clone)]
pub struct Doc {
@@ -167,7 +180,7 @@ pub struct Doc {
/// (line, col)
pub completion_pos: RwSignal<(usize, usize)>,
/// Current inline completion text, if any.
/// Current inline completion text, if any.
/// This will be displayed even on views that are not focused.
pub inline_completion: RwSignal<Option<String>>,
/// (line, col)
@@ -176,6 +189,10 @@ pub struct Doc {
/// (Offset -> (Plugin the code actions are from, Code Actions))
pub code_actions: RwSignal<CodeActions>,
pub code_lens: RwSignal<AllCodeLens>,
pub folding_ranges: RwSignal<FoldingRanges>,
/// Stores information about different versions of the document from source control.
histories: RwSignal<im::HashMap<String, DocumentHistory>>,
pub head_changes: RwSignal<im::Vector<DiffLines>>,
@@ -195,6 +212,8 @@ pub struct Doc {
editors: Editors,
pub common: Rc<CommonData>,
pub document_symbol_data: RwSignal<Option<SymbolData>>,
}
impl Doc {
pub fn new(
@@ -238,6 +257,9 @@ impl Doc {
preedit: PreeditData::new(cx),
editors,
common,
code_lens: cx.create_rw_signal(im::HashMap::new()),
document_symbol_data: cx.create_rw_signal(None),
folding_ranges: cx.create_rw_signal(FoldingRanges::default()),
}
}
@@ -286,6 +308,9 @@ impl Doc {
preedit: PreeditData::new(cx),
editors,
common,
code_lens: cx.create_rw_signal(im::HashMap::new()),
document_symbol_data: cx.create_rw_signal(None),
folding_ranges: cx.create_rw_signal(FoldingRanges::default()),
}
}
@@ -334,6 +359,9 @@ impl Doc {
preedit: PreeditData::new(cx),
editors,
common,
code_lens: cx.create_rw_signal(im::HashMap::new()),
document_symbol_data: cx.create_rw_signal(None),
folding_ranges: cx.create_rw_signal(FoldingRanges::default()),
}
}
@@ -436,7 +464,7 @@ impl Doc {
self.parser.borrow_mut().update_code(
code,
&self.buffer.get_untracked(),
Some(syntax.clone()),
Some(syntax),
);
} else {
self.parser.borrow_mut().update_code(
@@ -561,9 +589,9 @@ impl Doc {
buffer.set_cursor_before(old_cursor);
buffer.set_cursor_after(cursor.mode.clone());
});
self.apply_deltas(&deltas);
}
self.apply_deltas(&deltas);
deltas
}
@@ -596,7 +624,7 @@ impl Doc {
SyntaxEdit::from_delta(before_text, delta.clone())
})
.collect();
self.on_update(Some(edits.as_slice()));
self.on_update(Some(edits));
}
pub fn is_pristine(&self) -> bool {
@@ -608,17 +636,16 @@ impl Doc {
self.buffer.with_untracked(|b| b.rev())
}
/// Get the buffer's line-ending.
/// Get the buffer's line-ending.
/// Note: this may not be the same as what the actual line endings in the file are, rather this
/// is what the line-ending is set to (and what it will be saved as).
pub fn line_ending(&self) -> LineEnding {
self.buffer.with_untracked(|b| b.line_ending())
}
fn on_update(&self, edits: Option<&[SyntaxEdit]>) {
fn on_update(&self, edits: Option<SmallVec<[SyntaxEdit; 3]>>) {
batch(|| {
self.trigger_syntax_change(edits);
self.clear_sticky_headers_cache();
self.trigger_head_change();
self.check_auto_save();
self.get_inlay_hints();
@@ -627,35 +654,60 @@ impl Doc {
self.do_bracket_colorization();
self.clear_code_actions();
self.clear_style_cache();
self.get_code_lens();
self.get_document_symbol();
self.get_folding_range();
});
}
fn do_bracket_colorization(&self) {
self.syntax.with_untracked(|syntax| {
if syntax.styles.is_some() {
self.parser.borrow_mut().update_code(
self.buffer.get_untracked().to_string(),
&self.buffer.get_untracked(),
Some(syntax.clone()),
);
} else {
self.parser.borrow_mut().update_code(
self.buffer.get_untracked().to_string(),
&self.buffer.get_untracked(),
None,
);
}
})
if self.parser.borrow().active {
self.syntax.with_untracked(|syntax| {
if syntax.rev == self.rev() && syntax.styles.is_some() {
self.parser.borrow_mut().update_code(
self.buffer.get_untracked().to_string(),
&self.buffer.get_untracked(),
Some(syntax),
);
} else {
self.parser.borrow_mut().update_code(
self.buffer.get_untracked().to_string(),
&self.buffer.get_untracked(),
None,
);
}
})
}
}
pub fn do_text_edit(&self, edits: &[TextEdit]) {
let edits = self.buffer.with_untracked(|buffer| {
edits
.iter()
.map(|edit| {
let selection = lapce_core::selection::Selection::region(
buffer.offset_of_position(&edit.range.start),
buffer.offset_of_position(&edit.range.end),
);
(selection, edit.new_text.as_str())
})
.collect::<Vec<_>>()
});
self.do_raw_edit(&edits, EditType::Completion);
}
fn check_auto_save(&self) {
let config = self.common.config.get_untracked();
if config.editor.autosave_interval > 0 {
if !self.content.with_untracked(|c| c.is_file()) {
let Some(path) = self.content.with_untracked(|c| c.path().cloned())
else {
return;
};
let rev = self.rev();
let doc = self.clone();
let scope = self.scope;
let proxy = self.common.proxy.clone();
let format = config.editor.format_on_save;
exec_after(
Duration::from_millis(config.editor.autosave_interval),
move |_| {
@@ -671,7 +723,26 @@ impl Doc {
return;
}
doc.save(|| {});
if format {
let send = create_ext_action(scope, move |result| {
let current_rev = doc.rev();
if current_rev != rev {
return;
}
if let Ok(ProxyResponse::GetDocumentFormatting {
edits,
}) = result
{
doc.do_text_edit(&edits);
}
doc.save(|| {});
});
proxy.get_document_formatting(path, move |result| {
send(result);
});
} else {
doc.save(|| {});
}
},
);
}
@@ -704,12 +775,28 @@ impl Doc {
});
}
pub fn trigger_syntax_change(&self, edits: Option<&[SyntaxEdit]>) {
pub fn trigger_syntax_change(&self, edits: Option<SmallVec<[SyntaxEdit; 3]>>) {
let (rev, text) =
self.buffer.with_untracked(|b| (b.rev(), b.text().clone()));
let doc = self.clone();
let send = create_ext_action(self.scope, move |syntax| {
if doc.buffer.with_untracked(|b| b.rev()) == rev {
doc.syntax.set(syntax);
doc.do_bracket_colorization();
doc.clear_style_cache();
doc.clear_sticky_headers_cache();
}
});
self.syntax.update(|syntax| {
syntax.parse(rev, text, edits);
syntax.cancel_flag.store(1, atomic::Ordering::Relaxed);
syntax.cancel_flag = Arc::new(AtomicUsize::new(0));
});
let mut syntax = self.syntax.get_untracked();
rayon::spawn(move || {
syntax.parse(rev, text, edits.as_deref());
send(syntax);
});
}
@@ -771,7 +858,7 @@ impl Doc {
}
/// Request semantic styles for the buffer from the LSP through the proxy.
fn get_semantic_styles(&self) {
pub fn get_semantic_styles(&self) {
if !self.loaded() {
return;
}
@@ -783,24 +870,37 @@ impl Doc {
return;
};
let (rev, len) = self.buffer.with_untracked(|b| (b.rev(), b.len()));
let syntactic_styles =
self.syntax.with_untracked(|syntax| syntax.styles.clone());
let (atomic_rev, rev, len) = self
.buffer
.with_untracked(|b| (b.atomic_rev(), b.rev(), b.len()));
let doc = self.clone();
let send = create_ext_action(self.scope, move |styles| {
if doc.buffer.with_untracked(|b| b.rev()) == rev {
doc.semantic_styles.set(Some(styles));
doc.clear_style_cache();
if let Some(styles) = styles {
if doc.buffer.with_untracked(|b| b.rev()) == rev {
doc.semantic_styles.set(Some(styles));
doc.clear_style_cache();
}
}
});
self.common.proxy.get_semantic_tokens(path, move |result| {
if let Ok(ProxyResponse::GetSemanticTokens { styles }) = result {
rayon::spawn(move || {
if styles.styles.is_empty() {
send(None);
return;
}
if atomic_rev.load(atomic::Ordering::Acquire) != rev {
send(None);
return;
}
std::thread::spawn(move || {
let mut styles_span = SpansBuilder::new(len);
for style in styles.styles {
if atomic_rev.load(atomic::Ordering::Acquire) != rev {
send(None);
return;
}
styles_span.add_span(
Interval::new(style.start, style.end),
style.style,
@@ -808,26 +908,103 @@ impl Doc {
}
let styles = styles_span.build();
let styles = if let Some(syntactic_styles) = syntactic_styles {
syntactic_styles.merge(&styles, |a, b| {
if let Some(b) = b {
return b.clone();
}
a.clone()
})
} else {
styles
};
send(styles);
send(Some(styles));
});
} else {
send(None);
}
});
}
pub fn get_code_lens(&self) {
let cx = self.scope;
let doc = self.clone();
self.code_lens.update(|code_lens| {
code_lens.clear();
});
let rev = self.rev();
if let DocContent::File { path, .. } = doc.content.get_untracked() {
let send = create_ext_action(cx, move |result| {
if rev != doc.rev() {
return;
}
if let Ok(ProxyResponse::GetCodeLensResponse { plugin_id, resp }) =
result
{
let Some(codelens) = resp else {
return;
};
doc.code_lens.update(|code_lens| {
for codelens in codelens {
if codelens.command.is_none() {
continue;
}
let entry = code_lens
.entry(codelens.range.start.line as usize)
.or_insert_with(|| {
(
plugin_id,
doc.buffer.with_untracked(|b| {
b.offset_of_line(
codelens.range.start.line as usize,
)
}),
im::Vector::new(),
)
});
entry.2.push_back(codelens);
}
});
}
});
self.common.proxy.get_code_lens(path, move |result| {
send(result);
});
}
}
pub fn get_document_symbol(&self) {
let cx = self.scope;
let doc = self.clone();
let rev = self.rev();
if let DocContent::File { path, .. } = doc.content.get_untracked() {
let send = create_ext_action(cx, {
let path = path.clone();
move |result| {
if rev != doc.rev() {
return;
}
if let Ok(ProxyResponse::GetDocumentSymbols { resp }) = result {
let items: Vec<RwSignal<SymbolInformationItemData>> =
match resp {
DocumentSymbolResponse::Flat(_symbols) => {
Vec::with_capacity(0)
}
DocumentSymbolResponse::Nested(symbols) => symbols
.into_iter()
.map(|x| {
cx.create_rw_signal(
SymbolInformationItemData::from((x, cx)),
)
})
.collect(),
};
let symbol_new = Some(SymbolData::new(items, path, cx));
doc.document_symbol_data.update(|symbol| {
*symbol = symbol_new;
});
}
}
});
self.common.proxy.get_document_symbols(path, move |result| {
send(result);
});
}
}
/// Request inlay hints for the buffer from the LSP through the proxy.
fn get_inlay_hints(&self) {
pub fn get_inlay_hints(&self) {
if !self.loaded() {
return;
}
@@ -911,6 +1088,43 @@ impl Doc {
self.clear_code_actions();
}
pub fn get_folding_range(&self) {
// let cx = self.scope;
// let doc = self.clone();
// let rev = self.rev();
// if let DocContent::File { path, .. } = doc.content.get_untracked() {
// let send = create_ext_action(cx, {
// move |result| {
// if rev != doc.rev() {
// return;
// }
// if let Ok(ProxyResponse::LspFoldingRangeResponse {
// resp, ..
// }) = result
// {
// let folding = resp
// .unwrap_or_default()
// .into_iter()
// .map(|x| {
// crate::editor::gutter::FoldingRange::from_lsp(x)
// })
// .sorted_by(|x, y| x.start.line.cmp(&y.start.line))
// .collect();
// doc.folding_ranges.update(|symbol| {
// symbol.0 = folding;
// });
// }
// }
// });
// self.common
// .proxy
// .get_lsp_folding_range(path, move |result| {
// send(result);
// });
// }
}
/// Get the current completion lens text
pub fn completion_lens(&self) -> Option<String> {
self.completion_lens.get_untracked()
@@ -923,16 +1137,16 @@ impl Doc {
col: usize,
) {
// TODO: more granular invalidation
self.clear_text_cache();
self.completion_lens.set(Some(completion_lens));
self.completion_pos.set((line, col));
self.clear_text_cache();
}
pub fn clear_completion_lens(&self) {
// TODO: more granular invalidation
if self.completion_lens.get_untracked().is_some() {
self.clear_text_cache();
self.completion_lens.set(None);
self.clear_text_cache();
}
}
@@ -979,7 +1193,7 @@ impl Doc {
// In normal typing, if we didn't do this, then the text would jitter forward and then
// backwards as the completion lens is updated.
// TODO: this could also handle simple deletion, but we don't currently keep track of
// the past copmletion lens string content in the field.
// the past completion lens string content in the field.
if delta.as_simple_insert().is_some() {
let (iv, new_len) = delta.summary();
if iv.start() == iv.end()
@@ -1051,7 +1265,7 @@ impl Doc {
.set(FindProgress::InProgress(Selection::new()));
let find_result = self.find_result.clone();
let send = create_ext_action(self.scope, move |occurrences| {
let send = create_ext_action(self.scope, move |occurrences: Selection| {
find_result.occurrences.set(occurrences);
find_result.progress.set(FindProgress::Ready);
});
@@ -1088,11 +1302,7 @@ impl Doc {
.iter()
.filter_map(|offset| {
let l = buffer.line_of_offset(*offset);
if l <= line {
Some(l)
} else {
None
}
if l <= line { Some(l) } else { None }
})
.dedup()
.sorted()
@@ -1121,13 +1331,13 @@ impl Doc {
content, ..
}) = result
{
let hisotry = DocumentHistory::new(
let history = DocumentHistory::new(
path.clone(),
"head".to_string(),
&content,
);
histories.update(|histories| {
histories.insert("head".to_string(), hisotry);
histories.insert("head".to_string(), history);
});
doc.trigger_head_change();
@@ -1219,9 +1429,9 @@ impl Doc {
) {
// TODO: more granular invalidation
batch(|| {
self.clear_text_cache();
self.inline_completion.set(Some(inline_completion));
self.inline_completion_pos.set((line, col));
self.clear_text_cache();
});
}
@@ -1281,9 +1491,11 @@ impl Doc {
/// Uses a language aware algorithm if syntax support is available for the current language,
/// else falls back to a language unaware algorithm.
pub fn find_enclosing_brackets(&self, offset: usize) -> Option<(usize, usize)> {
let rev = self.rev();
self.syntax
.with_untracked(|syntax| {
(!syntax.text.is_empty()).then(|| syntax.find_enclosing_pair(offset))
(!syntax.text.is_empty() && syntax.rev == rev)
.then(|| syntax.find_enclosing_pair(offset))
})
// If syntax.text is empty, either the buffer is empty or we don't have syntax support
// for the current language.
@@ -1405,6 +1617,7 @@ impl Document for Doc {
self.apply_deltas(&[delta]);
}
}
impl DocumentPhantom for Doc {
fn phantom_text(
&self,
@@ -1433,9 +1646,46 @@ impl DocumentPhantom for Doc {
interval.start >= start_offset && interval.start < end_offset
})
.map(|(interval, inlay_hint)| {
let (_, col) = self
.buffer
.with_untracked(|b| b.offset_to_line_col(interval.start));
let (col, affinity) = self.buffer.with_untracked(|b| {
let mut cursor =
lapce_xi_rope::Cursor::new(b.text(), interval.start);
let next_char = cursor.peek_next_codepoint();
let prev_char = cursor.prev_codepoint();
let mut affinity = None;
if let Some(prev_char) = prev_char {
let c = get_char_property(prev_char);
if c == CharClassification::Other {
affinity = Some(CursorAffinity::Backward)
} else if matches!(
c,
CharClassification::Lf
| CharClassification::Cr
| CharClassification::Space
) {
affinity = Some(CursorAffinity::Forward)
}
};
if affinity.is_none() {
if let Some(next_char) = next_char {
let c = get_char_property(next_char);
if c == CharClassification::Other {
affinity = Some(CursorAffinity::Forward)
} else if matches!(
c,
CharClassification::Lf
| CharClassification::Cr
| CharClassification::Space
) {
affinity = Some(CursorAffinity::Backward)
}
}
}
let (_, col) = b.offset_to_line_col(interval.start);
(col, affinity)
});
let text = match &inlay_hint.label {
InlayHintLabel::String(label) => label.to_string(),
InlayHintLabel::LabelParts(parts) => {
@@ -1446,6 +1696,7 @@ impl DocumentPhantom for Doc {
kind: PhantomTextKind::InlayHint,
col,
text,
affinity,
fg: Some(config.color(LapceColor::INLAY_HINT_FOREGROUND)),
// font_family: Some(config.editor.inlay_hint_font_family()),
font_size: Some(config.editor.inlay_hint_font_size()),
@@ -1498,18 +1749,22 @@ impl DocumentPhantom for Doc {
config.color(theme_prop)
};
let text = if config.editor.error_lens_multiline
{
format!(" {}", diag.message)
} else {
format!(
" {}",
diag.message.lines().join(" ")
)
};
let text =
if config.editor.only_render_error_styling {
"".to_string()
} else if config.editor.error_lens_multiline
{
format!(" {}", diag.message)
} else {
format!(
" {}",
diag.message.lines().join(" ")
)
};
Some(PhantomText {
kind: PhantomTextKind::Diagnostic,
col: end_offset - start_offset,
affinity: Some(CursorAffinity::Backward),
text,
fg: Some(fg),
font_size: Some(
@@ -1543,6 +1798,7 @@ impl DocumentPhantom for Doc {
text: completion.clone(),
fg: Some(config.color(LapceColor::COMPLETION_LENS_FOREGROUND)),
font_size: Some(config.editor.completion_lens_font_size()),
affinity: Some(CursorAffinity::Backward),
// font_family: Some(config.editor.completion_lens_font_family()),
bg: None,
under_line: None,
@@ -1567,6 +1823,7 @@ impl DocumentPhantom for Doc {
kind: PhantomTextKind::Completion,
col: inline_completion_col,
text: completion.clone(),
affinity: Some(CursorAffinity::Backward),
fg: Some(config.color(LapceColor::COMPLETION_LENS_FOREGROUND)),
font_size: Some(config.editor.completion_lens_font_size()),
// font_family: Some(config.editor.completion_lens_font_family()),
@@ -1663,7 +1920,8 @@ impl DocStyling {
if let Some(fg_color) = config.style_color(fg_color) {
let start = phantom_text.col_at(bracket_style.start);
let end = phantom_text.col_at(bracket_style.end);
attrs_list.add_span(start..end, attrs.color(fg_color));
attrs_list
.add_span(start..end, attrs.clone().color(fg_color));
}
}
}
@@ -1689,23 +1947,23 @@ impl Styling for DocStyling {
&self,
_: EditorId,
_line: usize,
) -> std::borrow::Cow<[floem::cosmic_text::FamilyOwned]> {
) -> std::borrow::Cow<'_, [floem::text::FamilyOwned]> {
// TODO: cache this
Cow::Owned(self.config.with_untracked(|config| {
FamilyOwned::parse_list(&config.editor.font_family).collect()
}))
}
fn weight(&self, _: EditorId, _line: usize) -> floem::cosmic_text::Weight {
floem::cosmic_text::Weight::NORMAL
fn weight(&self, _: EditorId, _line: usize) -> floem::text::Weight {
floem::text::Weight::NORMAL
}
fn italic_style(&self, _: EditorId, _line: usize) -> floem::cosmic_text::Style {
floem::cosmic_text::Style::Normal
fn italic_style(&self, _: EditorId, _line: usize) -> floem::text::Style {
floem::text::Style::Normal
}
fn stretch(&self, _: EditorId, _line: usize) -> floem::cosmic_text::Stretch {
floem::cosmic_text::Stretch::Normal
fn stretch(&self, _: EditorId, _line: usize) -> floem::text::Stretch {
floem::text::Stretch::Normal
}
fn indent_line(&self, _: EditorId, line: usize, line_content: &str) -> usize {
@@ -1749,7 +2007,7 @@ impl Styling for DocStyling {
if let Some(fg_color) = config.style_color(fg_color) {
let start = phantom_text.col_at(line_style.start);
let end = phantom_text.col_at(line_style.end);
attrs_list.add_span(start..end, default.color(fg_color));
attrs_list.add_span(start..end, default.clone().color(fg_color));
}
}
}
@@ -1804,11 +2062,7 @@ impl Styling for DocStyling {
&& end >= start_offset
&& diag.severity < Some(DiagnosticSeverity::HINT)
{
let start = if start > start_offset {
start - start_offset
} else {
0
};
let start = start.saturating_sub(start_offset);
let end = end - start_offset;
let start = phantom_text.col_after(start, true);
let end = phantom_text.col_after(end, false);
@@ -1910,10 +2164,12 @@ fn syntax_prev_unmatched(
fn should_blink(
focus: RwSignal<Focus>,
keyboard_focus: RwSignal<Option<floem::id::Id>>,
keyboard_focus: RwSignal<Option<ViewId>>,
) -> impl Fn() -> bool {
move || {
let focus = focus.get_untracked();
let Some(focus) = focus.try_get_untracked() else {
return false;
};
if matches!(
focus,
Focus::Workbench
@@ -1967,9 +2223,8 @@ fn extra_styles_for_range(
return None;
}
let y = (run.line_height - run.glyph_ascent - run.glyph_descent) as f64
/ 2.0;
let height = (run.glyph_ascent + run.glyph_descent) as f64;
let height = (run.max_ascent + run.max_descent) as f64;
let y = run.line_y as f64 - run.max_ascent as f64;
Some(LineExtraStyle {
x,
+876 -119
View File
File diff suppressed because it is too large Load Diff
+76 -71
View File
@@ -1,23 +1,24 @@
use std::{rc::Rc, sync::atomic};
use floem::{
event::EventListener,
View,
event::{Event, EventListener},
ext_event::create_ext_action,
reactive::{RwSignal, Scope},
reactive::{RwSignal, Scope, SignalGet, SignalUpdate, SignalWith},
style::CursorStyle,
view::View,
views::{
clip, dyn_stack, editor::id::EditorId, empty, label, stack, svg, Decorators,
Decorators, clip, dyn_stack, editor::id::EditorId, empty, label, stack, svg,
},
};
use lapce_core::buffer::{
diff::{expand_diff_lines, rope_diff, DiffExpand, DiffLines},
diff::{DiffExpand, DiffLines, expand_diff_lines, rope_diff},
rope_text::RopeText,
};
use lapce_rpc::{buffer::BufferId, proxy::ProxyResponse};
use lapce_xi_rope::Rope;
use serde::{Deserialize, Serialize};
use super::{EditorData, EditorViewKind};
use crate::{
config::{color::LapceColor, icon::LapceIcons},
doc::{Doc, DocContent},
@@ -27,8 +28,6 @@ use crate::{
window_tab::CommonData,
};
use super::{EditorData, EditorViewKind};
#[derive(Clone)]
pub struct DiffInfo {
pub is_right: bool,
@@ -56,7 +55,7 @@ impl DiffEditorInfo {
let common = data.common.clone();
move |content: &DocContent| match content {
DocContent::File { path, .. } => {
let (doc, _) = data.get_doc(path.clone());
let (doc, _) = data.get_doc(path.clone(), None);
doc
}
DocContent::Local => {
@@ -306,87 +305,58 @@ impl DiffEditorData {
}
}
#[derive(Clone, PartialEq)]
struct DiffShowMoreSection {
left_actual_line: usize,
right_actual_line: usize,
visual_line: usize,
skip_start: usize,
lines: usize,
}
pub fn diff_show_more_section_view(
left_editor: &EditorData,
right_editor: &EditorData,
) -> impl View {
) -> impl View + use<> {
let left_editor_view = left_editor.kind;
let right_editor_view = right_editor.kind;
let right_screen_lines = right_editor.screen_lines();
let right_scroll_delta = right_editor.editor.scroll_delta;
let viewport = right_editor.viewport();
let config = right_editor.common.config;
let each_fn = move || {
let editor_view = right_editor_view.get();
if let EditorViewKind::Diff(diff_info) = editor_view {
let viewport = viewport.get();
let config = config.get_untracked();
let line_height = config.editor.line_height() as f64;
diff_info
.changes
.iter()
.filter_map(|change| {
let DiffLines::Both(info) = change else {
return None;
};
let min_line = (viewport.y0 / line_height).floor() as usize;
let max_line = (viewport.y1 / line_height).ceil() as usize;
let skip = info.skip.as_ref()?;
let mut visual_line = 0;
let mut last_change: Option<&DiffLines> = None;
let mut changes = diff_info.changes.iter().peekable();
let mut sections = Vec::new();
while let Some(change) = changes.next() {
match change {
DiffLines::Left(range) => {
if let Some(DiffLines::Right(_)) = changes.peek() {
} else {
let len = range.len();
visual_line += len;
}
}
DiffLines::Right(range) => {
let len = range.len();
visual_line += len;
if let Some(DiffLines::Left(r)) = last_change {
let len = r.len() - r.len().min(range.len());
if len > 0 {
visual_line += len;
}
};
}
DiffLines::Both(info) => {
if let Some(skip) = info.skip.as_ref() {
visual_line += skip.start;
if visual_line + 1 >= min_line {
sections.push(DiffShowMoreSection {
left_actual_line: info.left.start,
right_actual_line: info.right.start,
visual_line,
lines: skip.len(),
});
}
visual_line += 1;
visual_line += info.right.len() - skip.end;
} else {
visual_line += info.right.len();
}
}
}
if visual_line > max_line {
break;
}
last_change = Some(change);
}
sections
Some(DiffShowMoreSection {
left_actual_line: info.left.start,
right_actual_line: info.right.start,
skip_start: skip.start,
lines: skip.len(),
})
})
.collect()
} else {
Vec::new()
}
};
let key_fn =
move |section: &DiffShowMoreSection| (section.visual_line, section.lines);
let key_fn = move |section: &DiffShowMoreSection| {
(
section.right_actual_line + section.skip_start,
section.lines,
)
};
let view_fn = move |section: DiffShowMoreSection| {
stack((
@@ -518,17 +488,46 @@ pub fn diff_show_more_section_view(
.hover(|s| s.cursor(CursorStyle::Pointer))
}),
))
.on_event_cont(EventListener::PointerWheel, move |event| {
if let Event::PointerWheel(event) = event {
right_scroll_delta.set(event.delta);
}
})
.style(move |s| {
let right_screen_lines = right_screen_lines.get();
let mut right_line = section.right_actual_line + section.skip_start;
let is_before_skip = if right_line > 0 {
right_line -= 1;
true
} else {
right_line += section.lines;
false
};
let Some(line_info) = right_screen_lines.info_for_line(right_line)
else {
return s.hide();
};
let config = config.get();
let line_height = config.editor.line_height();
let mut y = line_info.y - viewport.get().y0;
if is_before_skip {
y += line_height as f64
} else {
y -= line_height as f64
}
s.absolute()
.width_pct(100.0)
.height(config.editor.line_height() as f32)
.height(line_height as f32)
.justify_center()
.items_center()
.margin_top(
(section.visual_line * config.editor.line_height()) as f32
- viewport.get().y0 as f32,
)
.margin_top(y)
.pointer_events_auto()
.hover(|s| s.cursor(CursorStyle::Default))
})
};
@@ -543,5 +542,11 @@ pub fn diff_show_more_section_view(
)
.style(|s| s.size_pct(100.0, 100.0)),
))
.style(|s| s.absolute().flex_col().size_pct(100.0, 100.0))
.style(|s| {
s.absolute()
.flex_col()
.size_pct(100.0, 100.0)
.pointer_events_none()
})
.debug_name("Diff Show More Section")
}
+235 -44
View File
@@ -1,32 +1,35 @@
use floem::{
Renderer, View, ViewId,
context::PaintCx,
cosmic_text::{Attrs, AttrsList, FamilyOwned, TextLayout},
id::Id,
peniko::kurbo::{Point, Rect, Size},
view::{AnyWidget, View, ViewData, Widget},
Renderer,
reactive::{Memo, SignalGet, SignalWith},
text::{Attrs, AttrsList, FamilyOwned, TextLayout},
};
use im::HashMap;
use lapce_core::{buffer::rope_text::RopeText, mode::Mode};
use serde::{Deserialize, Serialize};
use crate::config::{color::LapceColor, LapceConfig};
use super::{view::changes_colors_screen, EditorData};
use super::{EditorData, view::changes_colors_screen};
use crate::config::{LapceConfig, color::LapceColor};
pub struct EditorGutterView {
id: Id,
data: ViewData,
id: ViewId,
editor: EditorData,
width: f64,
gutter_padding_right: Memo<f32>,
}
pub fn editor_gutter_view(editor: EditorData) -> EditorGutterView {
let id = Id::next();
pub fn editor_gutter_view(
editor: EditorData,
gutter_padding_right: Memo<f32>,
) -> EditorGutterView {
let id = ViewId::new();
EditorGutterView {
id,
data: ViewData::new(id),
editor,
width: 0.0,
gutter_padding_right,
}
}
@@ -45,6 +48,7 @@ impl EditorGutterView {
let changes = e_data.doc().head_changes().get_untracked();
let line_height = config.editor.line_height() as f64;
let gutter_padding_right = self.gutter_padding_right.get_untracked() as f64;
let changes = changes_colors_screen(config, &e_data.editor, changes);
for (y, height, removed, color) in changes {
@@ -58,9 +62,10 @@ impl EditorGutterView {
y -= 5.0;
}
cx.fill(
&Size::new(3.0, height)
.to_rect()
.with_origin(Point::new(self.width + 7.0, y)),
&Size::new(3.0, height).to_rect().with_origin(Point::new(
self.width + 5.0 - gutter_padding_right,
y,
)),
color,
0.0,
)
@@ -80,8 +85,7 @@ impl EditorGutterView {
if !config.editor.sticky_header {
return;
}
let sticky_header_height = self.editor.sticky_header_height;
let sticky_header_height = sticky_header_height.get_untracked();
let sticky_header_height = self.editor.sticky_header_height.get_untracked();
if sticky_header_height == 0.0 {
return;
}
@@ -105,32 +109,15 @@ impl EditorGutterView {
}
impl View for EditorGutterView {
fn view_data(&self) -> &ViewData {
&self.data
}
fn view_data_mut(&mut self) -> &mut ViewData {
&mut self.data
}
fn build(self) -> AnyWidget {
Box::new(self)
}
}
impl Widget for EditorGutterView {
fn view_data(&self) -> &ViewData {
&self.data
}
fn view_data_mut(&mut self) -> &mut ViewData {
&mut self.data
fn id(&self) -> ViewId {
self.id
}
fn compute_layout(
&mut self,
cx: &mut floem::context::ComputeLayoutCx,
_cx: &mut floem::context::ComputeLayoutCx,
) -> Option<floem::peniko::kurbo::Rect> {
if let Some(width) = cx.get_layout(self.id).map(|l| l.size.width as f64) {
if let Some(width) = self.id.get_layout().map(|l| l.size.width as f64) {
self.width = width;
}
None
@@ -160,9 +147,12 @@ impl Widget for EditorGutterView {
.family(&family)
.color(config.color(LapceColor::EDITOR_DIM))
.font_size(config.editor.font_size() as f32);
let attrs_list = AttrsList::new(attrs);
let current_line_attrs_list =
AttrsList::new(attrs.color(config.color(LapceColor::EDITOR_FOREGROUND)));
let attrs_list = AttrsList::new(attrs.clone());
let current_line_attrs_list = AttrsList::new(
attrs
.clone()
.color(config.color(LapceColor::EDITOR_FOREGROUND)),
);
let show_relative = config.core.modal
&& config.editor.modal_mode_relative_line_numbers
&& mode != Mode::Insert
@@ -188,9 +178,13 @@ impl Widget for EditorGutterView {
let mut text_layout = TextLayout::new();
if line == current_line {
text_layout.set_text(&text, current_line_attrs_list.clone());
text_layout.set_text(
&text,
current_line_attrs_list.clone(),
None,
);
} else {
text_layout.set_text(&text, attrs_list.clone());
text_layout.set_text(&text, attrs_list.clone(), None);
}
let size = text_layout.size();
let height = size.height;
@@ -198,7 +192,10 @@ impl Widget for EditorGutterView {
cx.draw_text(
&text_layout,
Point::new(
(self.width - (size.width)).max(0.0),
(self.width
- size.width
- self.gutter_padding_right.get_untracked() as f64)
.max(0.0),
y + (line_height - height) / 2.0 - viewport.y0,
),
);
@@ -208,4 +205,198 @@ impl Widget for EditorGutterView {
self.paint_head_changes(cx, &self.editor, viewport, kind_is_normal, &config);
self.paint_sticky_headers(cx, kind_is_normal, &config);
}
fn debug_name(&self) -> std::borrow::Cow<'static, str> {
"Editor Gutter".into()
}
}
#[derive(Default, Clone)]
pub struct FoldingRanges(pub Vec<FoldingRange>);
#[derive(Default, Clone)]
pub struct FoldedRanges(pub Vec<FoldedRange>);
impl FoldingRanges {
pub fn get_folded_range(&self) -> FoldedRanges {
let mut range = Vec::new();
let mut limit_line = 0;
for item in &self.0 {
if item.start.line < limit_line && limit_line > 0 {
continue;
}
if item.status.is_folded() {
range.push(crate::editor::gutter::FoldedRange {
start: item.start,
end: item.end,
});
limit_line = item.end.line;
}
}
FoldedRanges(range)
}
pub fn to_display_items(&self) -> Vec<FoldingDisplayItem> {
let mut folded = HashMap::new();
let mut unfold_start: HashMap<u32, FoldingDisplayItem> = HashMap::new();
let mut unfold_end = HashMap::new();
let mut limit_line = 0;
for item in &self.0 {
if item.start.line < limit_line && limit_line > 0 {
continue;
}
match item.status {
FoldingRangeStatus::Fold => {
folded.insert(
item.start.line,
FoldingDisplayItem::Folded(item.start),
);
limit_line = item.end.line;
}
FoldingRangeStatus::Unfold => {
unfold_start.insert(
item.start.line,
FoldingDisplayItem::UnfoldStart(item.start),
);
unfold_end.insert(
item.end.line,
FoldingDisplayItem::UnfoldEnd(item.end),
);
limit_line = 0;
}
};
}
for (key, val) in unfold_end {
unfold_start.insert(key, val);
}
for (key, val) in folded {
unfold_start.insert(key, val);
}
unfold_start.into_iter().map(|x| x.1).collect()
}
}
impl FoldedRanges {
pub fn contain_line(&self, start_index: usize, line: u32) -> (bool, usize) {
if start_index >= self.0.len() {
return (false, start_index);
}
let mut last_index = start_index;
for range in self.0[start_index..].iter() {
if range.start.line >= line {
return (false, last_index);
} else if range.start.line < line && range.end.line > line {
return (true, last_index);
} else if range.end.line < line {
last_index += 1;
}
}
(false, last_index)
}
}
#[derive(Debug, Clone)]
pub struct FoldedRange {
pub start: FoldingPosition,
pub end: FoldingPosition,
}
#[derive(Debug, Clone)]
pub struct FoldingRange {
pub start: FoldingPosition,
pub end: FoldingPosition,
pub status: FoldingRangeStatus,
pub collapsed_text: Option<String>,
}
impl FoldingRange {
pub fn from_lsp(value: lsp_types::FoldingRange) -> Self {
let lsp_types::FoldingRange {
start_line,
start_character,
end_line,
end_character,
collapsed_text,
..
} = value;
let status = FoldingRangeStatus::Unfold;
Self {
start: FoldingPosition {
line: start_line,
character: start_character,
// kind: kind.clone().map(|x| FoldingRangeKind::from(x)),
},
end: FoldingPosition {
line: end_line,
character: end_character,
// kind: kind.map(|x| FoldingRangeKind::from(x)),
},
status,
collapsed_text,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Copy)]
pub struct FoldingPosition {
pub line: u32,
pub character: Option<u32>,
// pub kind: Option<FoldingRangeKind>,
}
#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub enum FoldingRangeStatus {
Fold,
#[default]
Unfold,
}
impl FoldingRangeStatus {
pub fn click(&mut self) {
// match self {
// FoldingRangeStatus::Fold => {
// *self = FoldingRangeStatus::Unfold;
// }
// FoldingRangeStatus::Unfold => {
// *self = FoldingRangeStatus::Fold;
// }
// }
}
pub fn is_folded(&self) -> bool {
*self == Self::Fold
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FoldingDisplayItem {
UnfoldStart(FoldingPosition),
Folded(FoldingPosition),
UnfoldEnd(FoldingPosition),
}
impl FoldingDisplayItem {
pub fn position(&self) -> FoldingPosition {
match self {
FoldingDisplayItem::UnfoldStart(x) => *x,
FoldingDisplayItem::Folded(x) => *x,
FoldingDisplayItem::UnfoldEnd(x) => *x,
}
}
}
#[derive(Debug, Eq, PartialEq, Deserialize, Serialize, Clone, Hash, Copy)]
pub enum FoldingRangeKind {
Comment,
Imports,
Region,
}
impl From<lsp_types::FoldingRangeKind> for FoldingRangeKind {
fn from(value: lsp_types::FoldingRangeKind) -> Self {
match value {
lsp_types::FoldingRangeKind::Comment => FoldingRangeKind::Comment,
lsp_types::FoldingRangeKind::Imports => FoldingRangeKind::Imports,
lsp_types::FoldingRangeKind::Region => FoldingRangeKind::Region,
}
}
}
File diff suppressed because it is too large Load Diff
+30 -19
View File
@@ -6,22 +6,25 @@ use std::{
use floem::{
peniko::{
kurbo::{Point, Rect},
Color,
kurbo::{Point, Rect},
},
reactive::{
Memo, ReadSignal, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith,
create_memo, create_rw_signal,
},
reactive::{create_memo, create_rw_signal, Memo, ReadSignal, RwSignal, Scope},
views::editor::id::EditorId,
};
use lapce_rpc::plugin::VoltID;
use serde::{Deserialize, Serialize};
use crate::{
config::{color::LapceColor, icon::LapceIcons, LapceConfig},
config::{LapceConfig, color::LapceColor, icon::LapceIcons},
doc::{Doc, DocContent},
editor::{
EditorData, EditorInfo,
diff::{DiffEditorData, DiffEditorInfo},
location::EditorLocation,
EditorData, EditorInfo,
},
id::{
DiffEditorId, EditorTabId, KeymapId, SettingsId, SplitId,
@@ -144,7 +147,8 @@ pub enum EditorTabChild {
pub struct EditorTabChildViewInfo {
pub icon: String,
pub color: Option<Color>,
pub path: String,
pub name: String,
pub path: Option<PathBuf>,
pub confirmed: Option<RwSignal<bool>>,
pub is_pristine: bool,
}
@@ -225,9 +229,9 @@ impl EditorTabChild {
} else {
None
};
let (icon, color, path, confirmed, is_pristine) = match path {
Some((path, confirmed, is_pritine)) => {
let (svg, color) = config.file_svg(&path);
let (icon, color, name, confirmed, is_pristine) = match path {
Some((ref path, confirmed, is_pritine)) => {
let (svg, color) = config.file_svg(path);
(
svg,
color,
@@ -250,7 +254,8 @@ impl EditorTabChild {
EditorTabChildViewInfo {
icon,
color,
path,
name,
path: path.map(|opt| opt.0),
confirmed: Some(confirmed),
is_pristine,
}
@@ -301,8 +306,10 @@ impl EditorTabChild {
is_pristine,
)
}
[Some((left_path, left_is_pristine)), Some((right_path, right_is_pristine))] =>
{
[
Some((left_path, left_is_pristine)),
Some((right_path, right_is_pristine)),
] => {
let (svg, color) =
config.files_svg(&[&left_path, &right_path]);
let [left_file_name, right_file_name] =
@@ -328,7 +335,8 @@ impl EditorTabChild {
EditorTabChildViewInfo {
icon,
color,
path,
name: path,
path: None,
confirmed,
is_pristine,
}
@@ -338,7 +346,8 @@ impl EditorTabChild {
EditorTabChildViewInfo {
icon: config.ui_svg(LapceIcons::SETTINGS),
color: Some(config.color(LapceColor::LAPCE_ICON_ACTIVE)),
path: "Settings".to_string(),
name: "Settings".to_string(),
path: None,
confirmed: None,
is_pristine: true,
}
@@ -348,7 +357,8 @@ impl EditorTabChild {
EditorTabChildViewInfo {
icon: config.ui_svg(LapceIcons::SYMBOL_COLOR),
color: Some(config.color(LapceColor::LAPCE_ICON_ACTIVE)),
path: "Theme Colors".to_string(),
name: "Theme Colors".to_string(),
path: None,
confirmed: None,
is_pristine: true,
}
@@ -358,7 +368,8 @@ impl EditorTabChild {
EditorTabChildViewInfo {
icon: config.ui_svg(LapceIcons::KEYBOARD),
color: Some(config.color(LapceColor::LAPCE_ICON_ACTIVE)),
path: "Keyboard Shortcuts".to_string(),
name: "Keyboard Shortcuts".to_string(),
path: None,
confirmed: None,
is_pristine: true,
}
@@ -381,7 +392,8 @@ impl EditorTabChild {
EditorTabChildViewInfo {
icon: config.ui_svg(LapceIcons::EXTENSIONS),
color: Some(config.color(LapceColor::LAPCE_ICON_ACTIVE)),
path: display_name,
name: display_name,
path: None,
confirmed: None,
is_pristine: true,
}
@@ -458,7 +470,7 @@ impl EditorTabData {
}
pub fn tab_info(&self, data: &WindowTabData) -> EditorTabInfo {
let info = EditorTabInfo {
EditorTabInfo {
active: self.active,
is_focus: data.main_split.active_editor_tab.get_untracked()
== Some(self.editor_tab_id),
@@ -467,7 +479,6 @@ impl EditorTabData {
.iter()
.map(|(_, _, child)| child.child_info(data))
.collect(),
};
info
}
}
}
+112 -41
View File
@@ -4,16 +4,17 @@ use std::{
ffi::OsStr,
path::{Path, PathBuf},
rc::Rc,
sync::Arc,
};
use floem::{
action::show_context_menu,
event::EventPropagation,
ext_event::create_ext_action,
keyboard::Modifiers,
menu::{Menu, MenuItem},
reactive::{RwSignal, Scope},
reactive::{ReadSignal, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith},
views::editor::text::SystemClipboard,
EventPropagation,
};
use globset::Glob;
use lapce_core::{
@@ -22,14 +23,18 @@ use lapce_core::{
register::Clipboard,
};
use lapce_rpc::{
file::{Duplicating, FileNodeItem, Naming, NamingState, NewNode, Renaming},
file::{
Duplicating, FileNodeItem, FileNodeViewKind, Naming, NamingState, NewNode,
Renaming,
},
proxy::ProxyResponse,
};
use crate::{
command::{CommandExecuted, CommandKind, InternalCommand, LapceCommand},
config::LapceConfig,
editor::EditorData,
keypress::{condition::Condition, KeyPressFocus},
keypress::{KeyPressFocus, condition::Condition},
main_split::Editors,
window_tab::CommonData,
};
@@ -43,13 +48,15 @@ enum RenamedPath {
},
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct FileExplorerData {
pub root: RwSignal<FileNodeItem>,
pub naming: RwSignal<Naming>,
pub naming_editor_data: EditorData,
pub common: Rc<CommonData>,
pub scroll_to_line: RwSignal<Option<f64>>,
left_diff_path: RwSignal<Option<PathBuf>>,
pub select: RwSignal<Option<FileNodeViewKind>>,
}
impl KeyPressFocus for FileExplorerData {
@@ -128,7 +135,9 @@ impl FileExplorerData {
naming,
naming_editor_data,
common,
scroll_to_line: cx.create_rw_signal(None),
left_diff_path: cx.create_rw_signal(None),
select: cx.create_rw_signal(None),
};
if data.common.workspace.path.is_some() {
// only fill in the child files if there is open folder
@@ -147,26 +156,22 @@ impl FileExplorerData {
/// Toggle whether the directory is expanded or not.
/// Does nothing if the path does not exist or is not a directory.
pub fn toggle_expand(&self, path: &Path) {
let Some(read) = self
.root
.try_update(|root| {
let read = if let Some(node) = root.get_file_node_mut(path) {
if !node.is_dir {
return None;
}
node.open = !node.open;
Some(node.read)
} else {
None
};
if Some(true) == read {
root.update_node_count_recursive(path);
let Some(Some(read)) = self.root.try_update(|root| {
let read = if let Some(node) = root.get_file_node_mut(path) {
if !node.is_dir {
return None;
}
read
})
.unwrap()
else {
node.open = !node.open;
Some(node.read)
} else {
None
};
if Some(true) == read {
root.update_node_count_recursive(path);
}
read
}) else {
return;
};
@@ -387,10 +392,10 @@ impl FileExplorerData {
self.naming.set(Naming::None);
}
pub fn click(&self, path: &Path) {
pub fn click(&self, path: &Path, config: ReadSignal<Arc<LapceConfig>>) {
if self.is_dir(path) {
self.toggle_expand(path);
} else {
} else if !config.get_untracked().core.file_explorer_double_click {
self.common
.internal_command
.send(InternalCommand::OpenFile {
@@ -399,9 +404,73 @@ impl FileExplorerData {
}
}
pub fn double_click(&self, path: &Path) -> EventPropagation {
pub fn reveal_in_file_tree(&self, path: PathBuf) {
let done = self
.root
.try_update(|root| {
// the directories in which the file are located are all read and opened
if root.get_file_node(&path).is_some() {
for current_path in path.ancestors() {
if let Some(file) = root.get_file_node_mut(current_path) {
if file.is_dir {
file.open = true;
}
}
}
root.update_node_count_recursive(&path);
true
} else {
// read and open the directories in which the file are located
let mut read_dir = None;
// Whether the file is in the workspace
let mut exist = false;
for current_path in path.ancestors() {
if let Some(file) = root.get_file_node_mut(current_path) {
exist = true;
if file.is_dir {
file.open = true;
if !file.read {
read_dir = Some(current_path.to_path_buf())
}
}
break;
}
}
if let (true, Some(dir)) = (exist, read_dir) {
let explorer = self.clone();
let select_path = path.clone();
self.read_dir_cb(&dir, move |_| {
explorer.reveal_in_file_tree(select_path);
})
}
false
}
})
.unwrap_or(false);
if done {
let (found, line) =
self.root.with_untracked(|x| x.find_file_at_line(&path));
if found {
self.scroll_to_line.set(Some(line));
self.select.set(Some(FileNodeViewKind::Path(path)));
}
}
}
pub fn double_click(
&self,
path: &Path,
config: ReadSignal<Arc<LapceConfig>>,
) -> EventPropagation {
if self.is_dir(path) {
EventPropagation::Continue
} else if config.get_untracked().core.file_explorer_double_click {
self.common.internal_command.send(
InternalCommand::OpenAndConfirmedFile {
path: path.to_path_buf(),
},
);
EventPropagation::Stop
} else {
self.common
.internal_command
@@ -486,21 +555,23 @@ impl FileExplorerData {
// TODO: there are situations where we can open the file explorer to remote files
if !common.workspace.kind.is_remote() {
let path = path_a.clone();
menu = menu.entry(MenuItem::new("Reveal in file explorer").action(
move || {
let path = path.parent().unwrap_or(&path);
if !path.exists() {
return;
}
#[cfg(not(target_os = "macos"))]
let title = "Reveal in system file explorer";
#[cfg(target_os = "macos")]
let title = "Reveal in Finder";
menu = menu.entry(MenuItem::new(title).action(move || {
let path = path.parent().unwrap_or(&path);
if !path.exists() {
return;
}
if let Err(err) = open::that(path) {
tracing::error!(
"Failed to reveal file in system file explorer: {}",
err
);
}
},
));
if let Err(err) = open::that(path) {
tracing::error!(
"Failed to reveal file in system file explorer: {}",
err
);
}
}));
}
if !is_workspace {
+2 -2
View File
@@ -17,7 +17,7 @@ impl FileNodeVirtualList {
impl VirtualVector<FileNodeViewData> for FileNodeVirtualList {
fn total_len(&self) -> usize {
self.file_node_item.children_open_count
self.file_node_item.children_open_count + 1
}
fn slice(
@@ -31,7 +31,7 @@ impl VirtualVector<FileNodeViewData> for FileNodeVirtualList {
let max = range.end;
let mut view_items = Vec::new();
root.append_children_view_slice(&mut view_items, naming, min, max, 0, 0);
root.append_view_slice(&mut view_items, naming, min, max, 0, 1);
view_items.into_iter()
}
+147 -74
View File
@@ -1,15 +1,18 @@
use std::{path::Path, rc::Rc, sync::Arc};
use floem::{
cosmic_text::Style as FontStyle,
View,
event::{Event, EventListener},
kurbo::Rect,
peniko::Color,
reactive::{create_rw_signal, ReadSignal, RwSignal},
reactive::{
ReadSignal, RwSignal, SignalGet, SignalUpdate, SignalWith, create_rw_signal,
},
style::{AlignItems, CursorStyle, Position, Style},
view::View,
text::Style as FontStyle,
views::{
container, dyn_stack, label, scroll, stack, svg, virtual_stack, Container,
Decorators, VirtualDirection, VirtualItemSize,
Container, Decorators, container, dyn_stack, label, scroll, stack, svg,
virtual_stack,
},
};
use lapce_core::selection::Selection;
@@ -23,9 +26,12 @@ use super::{data::FileExplorerData, node::FileNodeVirtualList};
use crate::{
app::clickable_icon,
command::InternalCommand,
config::{color::LapceColor, icon::LapceIcons, LapceConfig},
config::{LapceConfig, color::LapceColor, icon::LapceIcons},
editor_tab::{EditorTabChild, EditorTabData},
panel::{kind::PanelKind, position::PanelPosition, view::PanelBuilder},
panel::{
data::PanelSection, kind::PanelKind, position::PanelPosition,
view::PanelBuilder,
},
plugin::PluginData,
source_control::SourceControlData,
text_input::TextInputBuilder,
@@ -39,30 +45,19 @@ use crate::{
///
/// The result is always opaque regardless of the transparency of the inputs.
fn blend_colors(background: Color, foreground: Color) -> Color {
let Color {
r: background_r,
g: background_g,
b: background_b,
..
} = background;
let Color {
r: foreground_r,
g: foreground_g,
b: foreground_b,
a,
} = foreground;
let a: u16 = a.into();
let background = background.to_rgba8();
let foreground = foreground.to_rgba8();
let a: u16 = foreground.a.into();
let [r, g, b] = [
[background_r, foreground_r],
[background_g, foreground_g],
[background_b, foreground_b],
[background.r, foreground.r],
[background.g, foreground.g],
[background.b, foreground.b],
]
.map(|x| x.map(u16::from))
.map(|[b, f]| (a * f + (255 - a) * b) / 255)
.map(|x| x as u8);
Color { r, g, b, a: 255 }
Color::from_rgba8(r, g, b, 255)
}
pub fn file_explorer_panel(
@@ -78,16 +73,19 @@ pub fn file_explorer_panel(
150.0,
container(open_editors_view(window_tab_data.clone()))
.style(|s| s.size_full()),
move |s| s.apply_if(!config.get().ui.open_editors_visible, Style::hide),
window_tab_data.panel.section_open(PanelSection::OpenEditor),
move |s| s.apply_if(!config.get().ui.open_editors_visible, |s| s.hide()),
)
.add(
"File Explorer",
container(
new_file_node_view(data, source_control).style(|s| s.absolute()),
)
.style(|s| s.size_full().line_height(1.6)),
container(file_explorer_view(data, source_control))
.style(|s| s.size_full()),
window_tab_data
.panel
.section_open(PanelSection::FileExplorer),
)
.build()
.debug_name("File Explorer Panel")
}
/// Initialize the file explorer's naming (renaming, creating, etc.) editor with the given path.
@@ -165,23 +163,57 @@ fn file_node_text_view(
let config = data.common.config;
let ui_line_height = data.common.ui_line_height;
let view = match node.kind.clone() {
FileNodeViewKind::Path(path) => container(
label(move || {
path.file_name()
.map(|f| f.to_string_lossy().to_string())
.unwrap_or_default()
})
.style(move |s| {
s.flex_grow(1.0).height(ui_line_height.get()).color(
file_node_text_color(
config,
node.clone(),
source_control.clone(),
match node.kind.clone() {
FileNodeViewKind::Path(path) => {
if node.is_root {
let file = path.clone();
container((
label(move || {
file.file_name()
.map(|f| f.to_string_lossy().to_string())
.unwrap_or_default()
})
.style(move |s| {
s.height(ui_line_height.get())
.color(file_node_text_color(
config,
node.clone(),
source_control.clone(),
))
.padding_right(5.0)
.selectable(false)
}),
label(move || path.to_string_lossy().to_string()).style(
move |s| {
s.height(ui_line_height.get())
.color(
config
.get()
.color(LapceColor::PANEL_FOREGROUND_DIM),
)
.selectable(false)
},
),
))
} else {
container(
label(move || {
path.file_name()
.map(|f| f.to_string_lossy().to_string())
.unwrap_or_default()
})
.style(move |s| {
s.height(ui_line_height.get())
.color(file_node_text_color(
config,
node.clone(),
source_control.clone(),
))
.selectable(false)
}),
)
}),
),
}
}
FileNodeViewKind::Renaming { path, err } => {
if data.naming.with_untracked(Naming::editor_needs_reset) {
initialize_naming_editor_with_path(&data, &path);
@@ -203,9 +235,7 @@ fn file_node_text_view(
file_node_input_view(data, err.clone())
}
};
view.style(|s| s.flex_grow(1.0).padding(0.0).margin(0.0))
}
}
/// Input used for naming a file/directory
@@ -271,9 +301,10 @@ fn file_node_input_view(data: FileExplorerData, err: Option<String>) -> Containe
} else {
container(text_input_view)
}
.style(move |s| s.width_full())
}
fn new_file_node_view(
fn file_explorer_view(
data: FileExplorerData,
source_control: SourceControlData,
) -> impl View {
@@ -281,13 +312,13 @@ fn new_file_node_view(
let ui_line_height = data.common.ui_line_height;
let config = data.common.config;
let naming = data.naming;
let scroll_to_line = data.scroll_to_line;
let select = data.select;
let secondary_click_data = data.clone();
let scroll_rect = create_rw_signal(Rect::ZERO);
scroll(
virtual_stack(
VirtualDirection::Vertical,
VirtualItemSize::Fixed(Box::new(move || ui_line_height.get())),
move || FileNodeVirtualList::new(root.get(), data.naming.get()),
move |node| (node.kind.clone(), node.is_dir, node.open, node.level),
move |node| {
@@ -366,31 +397,53 @@ fn new_file_node_view(
},
file_node_text_view(data, node, source_control.clone()),
))
.style(move |s| {
s.padding_right(5.0)
.padding_left((level * 10) as f32)
.align_items(AlignItems::Center)
.hover(|s| {
s.background(
config
.get()
.color(LapceColor::PANEL_HOVERED_BACKGROUND),
.style({
let kind = kind.clone();
move |s| {
s.padding_right(15.0)
.min_width_full()
.padding_left((level * 10) as f32)
.align_items(AlignItems::Center)
.hover(|s| {
s.background(
config
.get()
.color(LapceColor::PANEL_HOVERED_BACKGROUND),
)
.cursor(CursorStyle::Pointer)
})
.apply_if(
select.get().map(|x| x == kind).unwrap_or_default(),
|x| {
x.background(
config.get().color(
LapceColor::PANEL_CURRENT_BACKGROUND,
),
)
},
)
.cursor(CursorStyle::Pointer)
})
});
}
})
.debug_name("file item");
// Only handle click events if we are not naming the file node
if let FileNodeViewKind::Path(path) = kind {
if let FileNodeViewKind::Path(path) = &kind {
let click_path = path.clone();
let double_click_path = path.clone();
let secondary_click_path = path.clone();
let aux_click_path = path;
view.on_click_stop(move |_| {
click_data.click(&click_path);
let aux_click_path = path.clone();
view.on_click_stop({
let kind = kind.clone();
move |_| {
click_data.click(&click_path, config);
select.update(|x| *x = Some(kind.clone()));
}
})
.on_double_click(move |_| {
double_click_data.double_click(&double_click_path)
.on_double_click({
move |_| {
double_click_data
.double_click(&double_click_path, config)
}
})
.on_secondary_click_stop(move |_| {
secondary_click_data.secondary_click(&secondary_click_path);
@@ -410,9 +463,10 @@ fn new_file_node_view(
}
},
)
.style(|s| s.flex_col().align_items(AlignItems::Stretch).width_full()),
.item_size_fixed(move || ui_line_height.get())
.style(|s| s.absolute().flex_col().min_width_full()),
)
.style(|s| s.size_full())
.style(|s| s.absolute().size_full().line_height(1.8))
.on_secondary_click_stop(move |_| {
if let Naming::None = naming.get_untracked() {
if let Some(path) = &secondary_click_data.common.workspace.path {
@@ -420,6 +474,23 @@ fn new_file_node_view(
}
}
})
.on_resize(move |rect| {
scroll_rect.set(rect);
})
.scroll_to(move || {
if let Some(line) = scroll_to_line.get() {
let line_height = ui_line_height.get_untracked();
Some(
(
0.0,
line * line_height - scroll_rect.get_untracked().height() / 2.0,
)
.into(),
)
} else {
None
}
})
}
fn open_editors_view(window_tab_data: Rc<WindowTabData>) -> impl View {
@@ -479,7 +550,7 @@ fn open_editors_view(window_tab_data: Rc<WindowTabData>) -> impl View {
},
))
.style(|s| s.padding_horiz(6.0)),
label(move || info.with(|info| info.path.clone())).style(move |s| {
label(move || info.with(|info| info.name.clone())).style(move |s| {
s.apply_if(
!info
.with(|info| info.confirmed)
@@ -493,6 +564,7 @@ fn open_editors_view(window_tab_data: Rc<WindowTabData>) -> impl View {
let config = config.get();
s.items_center()
.width_pct(100.0)
.cursor(CursorStyle::Pointer)
.apply_if(
active_editor_tab.get() == Some(editor_tab_id)
&& editor_tab.with(|editor_tab| editor_tab.active)
@@ -543,5 +615,6 @@ fn open_editors_view(window_tab_data: Rc<WindowTabData>) -> impl View {
)
.style(|s| s.flex_col().width_pct(100.0)),
)
.style(|s| s.absolute().size_pct(100.0, 100.0).line_height(1.6))
.style(|s| s.absolute().size_full().line_height(1.8))
.debug_name("Open Editors")
}
+11 -5
View File
@@ -1,13 +1,16 @@
use std::cmp::{max, min};
use floem::reactive::{RwSignal, Scope};
use floem::{
prelude::SignalTrack,
reactive::{RwSignal, Scope, SignalGet, SignalUpdate, SignalWith},
};
use lapce_core::{
selection::{SelRegion, Selection},
word::WordCursor,
};
use lapce_xi_rope::{
find::{find, is_multiline_regex, CaseMatching},
Cursor, Interval, Rope,
find::{CaseMatching, find, is_multiline_regex},
};
use regex::{Regex, RegexBuilder};
use serde::{Deserialize, Serialize};
@@ -74,6 +77,8 @@ pub struct Find {
pub replace_active: RwSignal<bool>,
/// replace editor is focused
pub replace_focus: RwSignal<bool>,
/// Triggered by changes in the search string
pub triggered_by_changes: RwSignal<bool>,
}
impl Find {
@@ -87,6 +92,7 @@ impl Find {
is_regex: cx.create_rw_signal(false),
replace_active: cx.create_rw_signal(false),
replace_focus: cx.create_rw_signal(false),
triggered_by_changes: cx.create_rw_signal(false),
};
{
@@ -160,7 +166,7 @@ impl Find {
let is_regex = self.is_regex.get_untracked();
let search_string_unchanged = self.search_string.with_untracked(|search| {
if let Some(ref s) = search {
if let Some(s) = search {
s.content == search_string && s.regex.is_some() == is_regex
} else {
false
@@ -180,11 +186,11 @@ impl Find {
.build()
.ok(),
};
self.triggered_by_changes.set(true);
self.search_string.set(Some(FindSearchString {
content: search_string.to_string(),
regex,
}))
}));
}
pub fn next(
+29 -49
View File
@@ -1,16 +1,14 @@
use floem::{
cosmic_text::{Attrs, AttrsList, FamilyOwned, TextLayout, Weight},
id::Id,
Renderer, View, ViewId,
peniko::{
kurbo::{Point, Rect},
Color,
kurbo::{Point, Rect},
},
prop_extractor,
reactive::create_effect,
style::{FontFamily, FontSize, LineHeight, Style, TextColor},
taffy::prelude::NodeId,
view::{AnyWidget, View, ViewData, Widget},
Renderer,
text::{Attrs, AttrsList, FamilyOwned, TextLayout, Weight},
};
prop_extractor! {
@@ -33,7 +31,7 @@ pub fn focus_text(
focus_indices: impl Fn() -> Vec<usize> + 'static,
focus_color: impl Fn() -> Color + 'static,
) -> FocusText {
let id = Id::next();
let id = ViewId::new();
create_effect(move |_| {
let new_text = text();
@@ -52,10 +50,9 @@ pub fn focus_text(
FocusText {
id,
data: ViewData::new(id),
text: "".to_string(),
text_layout: None,
focus_color: Color::default(),
focus_color: Color::BLACK,
focus_indices: Vec::new(),
text_node: None,
available_text: None,
@@ -66,8 +63,7 @@ pub fn focus_text(
}
pub struct FocusText {
id: Id,
data: ViewData,
id: ViewId,
text: String,
text_layout: Option<TextLayout>,
focus_color: Color,
@@ -81,7 +77,8 @@ pub struct FocusText {
impl FocusText {
fn set_text_layout(&mut self) {
let mut attrs = Attrs::new().color(self.style.color().unwrap_or_default());
let mut attrs =
Attrs::new().color(self.style.color().unwrap_or(Color::BLACK));
if let Some(font_size) = self.style.font_size() {
attrs = attrs.font_size(font_size);
}
@@ -97,7 +94,7 @@ impl FocusText {
attrs = attrs.line_height(line_height);
}
let mut attrs_list = AttrsList::new(attrs);
let mut attrs_list = AttrsList::new(attrs.clone());
for &i_start in &self.focus_indices {
let i_end = self
@@ -112,18 +109,18 @@ impl FocusText {
};
attrs_list.add_span(
i_start..i_end,
attrs.color(self.focus_color).weight(Weight::BOLD),
attrs.clone().color(self.focus_color).weight(Weight::BOLD),
);
}
let mut text_layout = TextLayout::new();
text_layout.set_text(&self.text, attrs_list);
text_layout.set_text(&self.text, attrs_list, None);
self.text_layout = Some(text_layout);
if let Some(new_text) = self.available_text.as_ref() {
let new_text_len = new_text.len();
let mut attrs =
Attrs::new().color(self.style.color().unwrap_or_default());
Attrs::new().color(self.style.color().unwrap_or(Color::BLACK));
if let Some(font_size) = self.style.font_size() {
attrs = attrs.font_size(font_size);
}
@@ -136,7 +133,7 @@ impl FocusText {
attrs = attrs.family(font_family);
}
let mut attrs_list = AttrsList::new(attrs);
let mut attrs_list = AttrsList::new(attrs.clone());
for &i_start in &self.focus_indices {
if i_start + 3 > new_text_len {
@@ -154,41 +151,24 @@ impl FocusText {
};
attrs_list.add_span(
i_start..i_end,
attrs.color(self.focus_color).weight(Weight::BOLD),
attrs.clone().color(self.focus_color).weight(Weight::BOLD),
);
}
let mut text_layout = TextLayout::new();
text_layout.set_text(new_text, attrs_list);
text_layout.set_text(new_text, attrs_list, None);
self.available_text_layout = Some(text_layout);
}
}
}
impl View for FocusText {
fn view_data(&self) -> &ViewData {
&self.data
}
fn view_data_mut(&mut self) -> &mut ViewData {
&mut self.data
}
fn build(self) -> AnyWidget {
Box::new(self)
}
}
impl Widget for FocusText {
fn view_data(&self) -> &ViewData {
&self.data
}
fn view_data_mut(&mut self) -> &mut ViewData {
&mut self.data
fn id(&self) -> ViewId {
self.id
}
fn update(
&mut self,
cx: &mut floem::context::UpdateCx,
_cx: &mut floem::context::UpdateCx,
state: Box<dyn std::any::Any>,
) {
if let Ok(state) = state.downcast() {
@@ -204,14 +184,14 @@ impl Widget for FocusText {
}
}
self.set_text_layout();
cx.request_layout(self.id());
self.id.request_layout();
}
}
fn style(&mut self, cx: &mut floem::context::StyleCx<'_>) {
fn style_pass(&mut self, cx: &mut floem::context::StyleCx<'_>) {
if self.style.read(cx) {
self.set_text_layout();
cx.app_state_mut().request_layout(self.id());
self.id.request_layout();
}
}
@@ -219,7 +199,7 @@ impl Widget for FocusText {
&mut self,
cx: &mut floem::context::LayoutCx,
) -> floem::taffy::prelude::NodeId {
cx.layout_node(self.id, true, |cx| {
cx.layout_node(self.id, true, |_cx| {
if self.text_layout.is_none() {
self.set_text_layout();
}
@@ -230,22 +210,22 @@ impl Widget for FocusText {
let height = size.height as f32;
if self.text_node.is_none() {
self.text_node = Some(cx.new_node());
self.text_node = Some(self.id.new_taffy_node());
}
let text_node = self.text_node.unwrap();
let style = Style::new().width(width).height(height).to_taffy_style();
cx.set_style(text_node, style);
self.id.set_taffy_style(text_node, style);
vec![text_node]
})
}
fn compute_layout(
&mut self,
cx: &mut floem::context::ComputeLayoutCx,
_cx: &mut floem::context::ComputeLayoutCx,
) -> Option<Rect> {
let text_node = self.text_node.unwrap();
let layout = cx.layout(text_node).unwrap();
let layout = self.id.taffy_layout(text_node).unwrap_or_default();
let text_layout = self.text_layout.as_ref().unwrap();
let width = text_layout.size().width as f32;
if width > layout.size.width {
@@ -254,7 +234,7 @@ impl Widget for FocusText {
let mut attrs = Attrs::new().color(
self.style
.color()
.unwrap_or_else(|| Color::rgb8(0xf0, 0xf0, 0xea)),
.unwrap_or_else(|| Color::from_rgb8(0xf0, 0xf0, 0xea)),
);
if let Some(font_size) = self.style.font_size() {
attrs = attrs.font_size(font_size);
@@ -268,7 +248,7 @@ impl Widget for FocusText {
if let Some(font_family) = font_family.as_ref() {
attrs = attrs.family(font_family);
}
dots_text.set_text("...", AttrsList::new(attrs));
dots_text.set_text("...", AttrsList::new(attrs), None);
let dots_width = dots_text.size().width as f32;
let width_left = layout.size.width - dots_width;
@@ -296,7 +276,7 @@ impl Widget for FocusText {
fn paint(&mut self, cx: &mut floem::context::PaintCx) {
let text_node = self.text_node.unwrap();
let location = cx.layout(text_node).unwrap().location;
let location = self.id.taffy_layout(text_node).unwrap_or_default().location;
let point = Point::new(location.x as f64, location.y as f64);
if let Some(text_layout) = self.available_text_layout.as_ref() {
cx.draw_text(text_layout, point);
+3 -3
View File
@@ -3,7 +3,7 @@ use std::{ops::Range, path::PathBuf, rc::Rc};
use floem::{
ext_event::create_ext_action,
keyboard::Modifiers,
reactive::{Memo, RwSignal, Scope},
reactive::{Memo, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith},
views::VirtualVector,
};
use indexmap::IndexMap;
@@ -14,7 +14,7 @@ use lapce_xi_rope::Rope;
use crate::{
command::{CommandExecuted, CommandKind},
editor::EditorData,
keypress::{condition::Condition, KeyPressFocus},
keypress::{KeyPressFocus, condition::Condition},
main_split::MainSplitData,
window_tab::CommonData,
};
@@ -38,7 +38,7 @@ impl SearchMatchData {
}
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct GlobalSearchData {
pub editor: EditorData,
pub search_result: RwSignal<IndexMap<PathBuf, SearchMatchData>>,
+2 -3
View File
@@ -1,15 +1,14 @@
use std::{borrow::Cow, ops::Range, path::PathBuf, str::FromStr};
use floem::reactive::{batch, RwSignal, Scope};
use floem::reactive::{RwSignal, Scope, SignalGet, SignalUpdate, SignalWith, batch};
use lapce_core::{
buffer::{
rope_text::{RopeText, RopeTextRef},
Buffer,
rope_text::{RopeText, RopeTextRef},
},
rope_text_pos::RopeTextPosition,
selection::Selection,
};
use lsp_types::InsertTextFormat;
use crate::{config::LapceConfig, doc::Doc, editor::EditorData, snippet::Snippet};
+27 -24
View File
@@ -1,26 +1,25 @@
use std::{rc::Rc, sync::Arc};
use floem::{
View,
event::{Event, EventListener},
reactive::{
create_effect, create_memo, create_rw_signal, Memo, ReadSignal, RwSignal,
Scope,
Memo, ReadSignal, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith,
create_effect, create_memo, create_rw_signal,
},
style::CursorStyle,
view::View,
views::{
container, dyn_stack, label, scroll, stack, text, virtual_stack, Decorators,
VirtualDirection, VirtualItemSize,
Decorators, container, dyn_stack, label, scroll, stack, text, virtual_stack,
},
};
use lapce_core::mode::Modes;
use crate::{
command::LapceCommand,
config::{color::LapceColor, LapceConfig},
config::{LapceConfig, color::LapceColor},
keypress::{
keymap::{KeyMap, KeyMapPress},
KeyPressData,
keymap::{KeyMap, KeyMapPress},
},
main_split::Editors,
text_input::TextInputBuilder,
@@ -283,7 +282,8 @@ pub fn keymap_view(editors: Editors, common: Rc<CommonData>) -> impl View {
container(
text_input_view
.placeholder(|| "Search Key Bindings".to_string())
.keyboard_navigatable()
.keyboard_navigable()
.request_focus(|| {})
.style(move |s| {
s.width_pct(100.0)
.border_radius(6.0)
@@ -346,8 +346,6 @@ pub fn keymap_view(editors: Editors, common: Rc<CommonData>) -> impl View {
container(
scroll(
virtual_stack(
VirtualDirection::Vertical,
VirtualItemSize::Fixed(Box::new(ui_line_height)),
items,
|(i, (cmd, keymap)): &(
usize,
@@ -355,6 +353,7 @@ pub fn keymap_view(editors: Editors, common: Rc<CommonData>) -> impl View {
)| { (*i, cmd.kind.str(), keymap.clone()) },
view_fn,
)
.item_size_fixed(ui_line_height)
.style(|s| s.flex_col().width_pct(100.0)),
)
.style(|s| s.absolute().size_pct(100.0, 100.0)),
@@ -409,6 +408,7 @@ fn keyboard_picker_view(
s.padding_horiz(5.0)
.padding_vert(1.0)
.margin_right(5.0)
.height(ui_line_height.get() as f32)
.border(1.0)
.border_radius(6.0)
.border_color(
@@ -423,7 +423,7 @@ fn keyboard_picker_view(
.justify_center()
.width_pct(100.0)
.margin_top(20.0)
.height(ui_line_height.get() as f32 + 16.0)
.height((ui_line_height.get() as f32) * 1.2)
.border(1.0)
.border_radius(6.0)
.border_color(config.color(LapceColor::LAPCE_BORDER))
@@ -500,6 +500,7 @@ fn keyboard_picker_view(
.border_color(config.color(LapceColor::LAPCE_BORDER))
}),
))
.on_event_stop(EventListener::PointerDown, |_| {})
.style(move |s| {
let config = config.get();
s.items_center()
@@ -512,22 +513,23 @@ fn keyboard_picker_view(
.background(config.color(LapceColor::PANEL_BACKGROUND))
}),
)
.keyboard_navigatable()
.keyboard_navigable()
.on_event_stop(EventListener::KeyDown, move |event| {
if let Event::KeyDown(key_event) = event {
if let Some(keypress) = KeyPressData::keypress(key_event) {
let keypress = keypress.keymap_press();
picker.keys.update(|keys| {
if let Some((last_key, last_key_confirmed)) = keys.last() {
if !*last_key_confirmed && last_key.is_modifiers() {
keys.pop();
if let Some(keypress) = keypress.keymap_press() {
picker.keys.update(|keys| {
if let Some((last_key, last_key_confirmed)) = keys.last() {
if !*last_key_confirmed && last_key.is_modifiers() {
keys.pop();
}
}
}
if keys.len() == 2 {
keys.clear();
}
keys.push((keypress, false));
})
if keys.len() == 2 {
keys.clear();
}
keys.push((keypress, false));
})
}
}
}
})
@@ -546,7 +548,8 @@ fn keyboard_picker_view(
.items_center()
.justify_center()
.apply_if(picker.keymap.with(|keymap| keymap.is_none()), |s| s.hide())
});
})
.debug_name("keyboard picker");
let id = view.id();
create_effect(move |_| {
+136 -45
View File
@@ -4,13 +4,13 @@ pub mod keymap;
mod loader;
mod press;
use std::{path::PathBuf, rc::Rc, str::FromStr};
use std::{path::PathBuf, rc::Rc, str::FromStr, time::SystemTime};
use anyhow::Result;
use floem::{
keyboard::{Key, KeyEvent, KeyEventExtModifierSupplement, Modifiers, NamedKey},
pointer::PointerInputEvent,
reactive::{RwSignal, Scope},
pointer::{MouseButton, PointerButton, PointerInputEvent},
reactive::{RwSignal, Scope, SignalUpdate, SignalWith},
};
use indexmap::IndexMap;
use itertools::Itertools;
@@ -23,12 +23,13 @@ use self::{
loader::KeyMapLoader,
};
use crate::{
command::{lapce_internal_commands, CommandExecuted, CommandKind, LapceCommand},
command::{CommandExecuted, CommandKind, LapceCommand, lapce_internal_commands},
config::LapceConfig,
keypress::{
condition::{CheckCondition, Condition},
keymap::KeymapMatch,
},
tracing::*,
};
const DEFAULT_KEYMAPS_COMMON: &str =
@@ -38,7 +39,7 @@ const DEFAULT_KEYMAPS_MACOS: &str =
const DEFAULT_KEYMAPS_NONMACOS: &str =
include_str!("../../defaults/keymaps-nonmacos.toml");
pub trait KeyPressFocus {
pub trait KeyPressFocus: std::fmt::Debug {
fn get_mode(&self) -> Mode;
fn check_condition(&self, condition: Condition) -> bool;
@@ -137,10 +138,16 @@ impl<'a> From<&'a PointerInputEvent> for EventRef<'a> {
}
}
#[derive(Clone)]
pub struct KeyPressHandle {
pub handled: bool,
pub keypress: KeyPress,
pub keymatch: KeymapMatch,
}
#[derive(Clone, Debug)]
pub struct KeyPressData {
count: RwSignal<Option<usize>>,
pending_keypress: RwSignal<Vec<KeyPress>>,
pending_keypress: RwSignal<(Vec<KeyPress>, Option<SystemTime>)>,
pub commands: Rc<IndexMap<String, LapceCommand>>,
pub keymaps: Rc<IndexMap<Vec<KeyMapPress>, Vec<KeyMap>>>,
pub command_keymaps: Rc<IndexMap<String, Vec<KeyMap>>>,
@@ -154,7 +161,7 @@ impl KeyPressData {
Self::get_keymaps(config).unwrap_or((IndexMap::new(), IndexMap::new()));
let mut keypress = Self {
count: cx.create_rw_signal(None),
pending_keypress: cx.create_rw_signal(Vec::new()),
pending_keypress: cx.create_rw_signal((Vec::new(), None)),
keymaps: Rc::new(keymaps),
command_keymaps: Rc::new(command_keymaps),
commands: Rc::new(lapce_internal_commands()),
@@ -185,7 +192,12 @@ impl KeyPressData {
}
for (_, cmd) in self.commands.iter() {
if !self.command_keymaps.contains_key(cmd.kind.str()) {
if self
.command_keymaps
.get(cmd.kind.str())
.map(|x| x.is_empty())
.unwrap_or(true)
{
commands_without_keymap.push(cmd.clone());
}
}
@@ -244,7 +256,6 @@ impl KeyPressData {
pub fn keypress<'a>(event: impl Into<EventRef<'a>>) -> Option<KeyPress> {
let event = event.into();
tracing::trace!("{event:?}");
let keypress = match event {
EventRef::Keyboard(ev) => KeyPress {
@@ -252,6 +263,8 @@ impl KeyPressData {
logical: ev.key.logical_key.to_owned(),
physical: ev.key.physical_key,
key_without_modifiers: ev.key.key_without_modifiers(),
location: ev.key.location,
repeat: ev.key.repeat,
},
mods: Self::get_key_modifiers(ev),
},
@@ -267,58 +280,119 @@ impl KeyPressData {
&self,
event: impl Into<EventRef<'a>>,
focus: &T,
) -> bool {
) -> KeyPressHandle {
let keypress = match Self::keypress(event) {
Some(keypress) => keypress,
None => return false,
None => {
return KeyPressHandle {
handled: false,
keymatch: KeymapMatch::None,
keypress: KeyPress {
key: KeyInput::Pointer(PointerButton::Mouse(
MouseButton::Primary,
)),
mods: Modifiers::empty(),
},
};
}
};
let mods = keypress.mods;
if self.handle_count(focus, &keypress) {
return true;
return KeyPressHandle {
handled: true,
keymatch: KeymapMatch::None,
keypress,
};
}
self.pending_keypress.update(|pending_keypress| {
pending_keypress.push(keypress.clone());
});
self.pending_keypress
.update(|(pending_keypress, last_time)| {
let last_time = last_time.replace(SystemTime::now());
if let Some(last_time_val) = last_time {
if last_time_val
.elapsed()
.map(|x| x.as_millis() > 1000)
.unwrap_or_default()
{
pending_keypress.clear();
}
}
pending_keypress.push(keypress.clone());
});
let keymatch = self.pending_keypress.with_untracked(|pending_keypress| {
self.match_keymap(pending_keypress, focus)
});
match keymatch {
KeymapMatch::Full(command) => {
self.pending_keypress.update(|pending_keypress| {
pending_keypress.clear();
let keymatch =
self.pending_keypress
.with_untracked(|(pending_keypress, _)| {
self.match_keymap(pending_keypress, focus)
});
self.handle_keymatch(focus, keymatch, keypress)
}
pub fn handle_keymatch<T: KeyPressFocus + ?Sized>(
&self,
focus: &T,
keymatch: KeymapMatch,
keypress: KeyPress,
) -> KeyPressHandle {
let mods = keypress.mods;
match &keymatch {
KeymapMatch::Full(command) => {
self.pending_keypress
.update(|(pending_keypress, last_time)| {
last_time.take();
pending_keypress.clear();
});
let count = self.count.try_update(|count| count.take()).unwrap();
return self.run_command(&command, count, mods, focus)
let handled = self.run_command(command, count, mods, focus)
== CommandExecuted::Yes;
return KeyPressHandle {
handled,
keymatch,
keypress,
};
}
KeymapMatch::Multiple(commands) => {
self.pending_keypress.update(|pending_keypress| {
pending_keypress.clear();
});
self.pending_keypress
.update(|(pending_keypress, last_time)| {
last_time.take();
pending_keypress.clear();
});
let count = self.count.try_update(|count| count.take()).unwrap();
for command in commands {
if self.run_command(&command, count, mods, focus)
== CommandExecuted::Yes
{
return true;
let handled = self.run_command(command, count, mods, focus)
== CommandExecuted::Yes;
if handled {
return KeyPressHandle {
handled,
keymatch,
keypress,
};
}
}
return false;
return KeyPressHandle {
handled: false,
keymatch,
keypress,
};
}
KeymapMatch::Prefix => {
// Here pending_keypress contains only a prefix of some keymap, so let's keep
// collecting key presses.
return true;
return KeyPressHandle {
handled: true,
keymatch,
keypress,
};
}
KeymapMatch::None => {
self.pending_keypress.update(|pending_keypress| {
pending_keypress.clear();
});
self.pending_keypress
.update(|(pending_keypress, last_time)| {
pending_keypress.clear();
last_time.take();
});
if focus.get_mode() == Mode::Insert {
let old_keypress = keypress.clone();
let mut keypress = keypress.clone();
keypress.mods.set(Modifiers::SHIFT, false);
if let KeymapMatch::Full(command) =
@@ -326,8 +400,13 @@ impl KeyPressData {
{
if let Some(cmd) = self.commands.get(&command) {
if let CommandKind::Move(_) = cmd.kind {
return focus.run_command(cmd, None, mods)
let handled = focus.run_command(cmd, None, mods)
== CommandExecuted::Yes;
return KeyPressHandle {
handled,
keymatch,
keypress: old_keypress,
};
}
}
}
@@ -352,16 +431,28 @@ impl KeyPressData {
if let Key::Character(c) = logical {
focus.receive_char(c);
self.count.set(None);
return true;
return KeyPressHandle {
handled: true,
keymatch,
keypress,
};
} else if let Key::Named(NamedKey::Space) = logical {
focus.receive_char(" ");
self.count.set(None);
return true;
return KeyPressHandle {
handled: true,
keymatch,
keypress,
};
}
}
}
false
KeyPressHandle {
handled: false,
keymatch,
keypress,
}
}
fn get_key_modifiers(key_event: &KeyEvent) -> Modifiers {
@@ -385,7 +476,7 @@ impl KeyPressData {
check: &T,
) -> KeymapMatch {
let keypresses: Vec<KeyMapPress> =
keypresses.iter().map(|k| k.keymap_press()).collect();
keypresses.iter().filter_map(|k| k.keymap_press()).collect();
let matches: Vec<_> = self
.keymaps
.get(&keypresses)
@@ -483,7 +574,7 @@ impl KeyPressData {
let mut loader = KeyMapLoader::new();
if let Err(err) = loader.load_from_str(DEFAULT_KEYMAPS_COMMON, is_modal) {
tracing::error!("Failed to load common defaults: {err}");
trace!(TraceLevel::ERROR, "Failed to load common defaults: {err}");
}
let os_keymaps = if std::env::consts::OS == "macos" {
@@ -493,13 +584,13 @@ impl KeyPressData {
};
if let Err(err) = loader.load_from_str(os_keymaps, is_modal) {
tracing::error!("Failed to load OS defaults: {err}");
trace!(TraceLevel::ERROR, "Failed to load OS defaults: {err}");
}
if let Some(path) = Self::file() {
if let Ok(content) = std::fs::read_to_string(&path) {
if let Err(err) = loader.load_from_str(&content, is_modal) {
tracing::warn!("Failed to load from {path:?}: {err}");
trace!(TraceLevel::WARN, "Failed to load from {path:?}: {err}");
}
}
}
+5 -2
View File
@@ -37,7 +37,7 @@ impl<'a> CheckCondition<'a> {
}
}
#[derive(EnumString, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, EnumString, PartialEq, Eq)]
pub enum Condition {
#[strum(serialize = "editor_focus")]
EditorFocus,
@@ -65,6 +65,8 @@ pub enum Condition {
RenameFocus,
#[strum(serialize = "search_active")]
SearchActive,
#[strum(serialize = "on_screen_find_active")]
OnScreenFindActive,
#[strum(serialize = "search_focus")]
SearchFocus,
#[strum(serialize = "replace_focus")]
@@ -77,8 +79,9 @@ mod test {
use lapce_core::mode::Mode;
use super::Condition;
use crate::keypress::{condition::CheckCondition, KeyPressData, KeyPressFocus};
use crate::keypress::{KeyPressData, KeyPressFocus, condition::CheckCondition};
#[derive(Clone, Copy, Debug)]
struct MockFocus {
accepted_conditions: &'static [Condition],
}
+51 -18
View File
@@ -1,4 +1,4 @@
use floem::keyboard::{Key, NamedKey, PhysicalKey};
use floem::keyboard::{Key, KeyLocation, NamedKey, PhysicalKey};
use super::keymap::KeyMapKey;
@@ -7,35 +7,68 @@ pub(crate) enum KeyInput {
Keyboard {
physical: PhysicalKey,
logical: Key,
location: KeyLocation,
key_without_modifiers: Key,
repeat: bool,
},
Pointer(floem::pointer::PointerButton),
}
impl KeyInput {
pub fn keymap_key(&self) -> KeyMapKey {
match self {
pub fn keymap_key(&self) -> Option<KeyMapKey> {
if let KeyInput::Keyboard {
repeat, logical, ..
} = self
{
if *repeat
&& (matches!(
logical,
Key::Named(NamedKey::Meta)
| Key::Named(NamedKey::Shift)
| Key::Named(NamedKey::Alt)
| Key::Named(NamedKey::Control),
))
{
return None;
}
}
Some(match self {
KeyInput::Pointer(b) => KeyMapKey::Pointer(*b),
KeyInput::Keyboard {
physical,
key_without_modifiers,
logical,
location,
..
} => match key_without_modifiers {
Key::Named(_) => {
KeyMapKey::Logical(key_without_modifiers.to_owned())
}
Key::Character(c) => {
if c == " " {
KeyMapKey::Logical(Key::Named(NamedKey::Space))
} else if c.len() == 1 && c.is_ascii() {
KeyMapKey::Logical(Key::Character(c.to_lowercase().into()))
} else {
KeyMapKey::Physical(*physical)
} => {
#[allow(clippy::single_match)]
match location {
KeyLocation::Numpad => {
return Some(KeyMapKey::Logical(logical.to_owned()));
}
_ => {}
}
Key::Unidentified(_) => KeyMapKey::Physical(*physical),
Key::Dead(_) => KeyMapKey::Physical(*physical),
},
}
match key_without_modifiers {
Key::Named(_) => {
KeyMapKey::Logical(key_without_modifiers.to_owned())
}
Key::Character(c) => {
if c == " " {
KeyMapKey::Logical(Key::Named(NamedKey::Space))
} else if c.len() == 1 && c.is_ascii() {
KeyMapKey::Logical(Key::Character(
c.to_lowercase().into(),
))
} else {
KeyMapKey::Physical(*physical)
}
}
Key::Unidentified(_) => KeyMapKey::Physical(*physical),
Key::Dead(_) => KeyMapKey::Physical(*physical),
}
}
})
}
}
+25 -13
View File
@@ -2,12 +2,12 @@ use std::{fmt::Display, str::FromStr};
use floem::{
keyboard::{Key, KeyCode, Modifiers, NamedKey, PhysicalKey},
pointer::PointerButton,
pointer::{MouseButton, PointerButton},
};
use lapce_core::mode::Modes;
#[derive(PartialEq, Debug)]
pub(super) enum KeymapMatch {
#[derive(PartialEq, Debug, Clone)]
pub enum KeymapMatch {
Full(String),
Multiple(Vec<String>),
Prefix,
@@ -32,7 +32,7 @@ pub enum KeyMapKey {
impl std::hash::Hash for KeyMapKey {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Pointer(btn) => (*btn as u8).hash(state),
Self::Pointer(btn) => (btn.mouse_button() as u8).hash(state),
Self::Logical(key) => key.hash(state),
Self::Physical(physical) => physical.hash(state),
}
@@ -232,7 +232,7 @@ impl FromStr for KeyMapKey {
return Err(anyhow::anyhow!(
"unrecognized physical key code {}",
&s[1..s.len() - 2]
))
));
}
};
KeyMapKey::Physical(PhysicalKey::Code(code))
@@ -317,19 +317,29 @@ impl FromStr for KeyMapKey {
impl Display for KeyMapPress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.mods.contains(Modifiers::CONTROL) {
let _ = f.write_str("Ctrl+");
if let Err(err) = f.write_str("Ctrl+") {
tracing::error!("{:?}", err);
}
}
if self.mods.contains(Modifiers::ALT) {
let _ = f.write_str("Alt+");
if let Err(err) = f.write_str("Alt+") {
tracing::error!("{:?}", err);
}
}
if self.mods.contains(Modifiers::ALTGR) {
let _ = f.write_str("AltGr+");
if let Err(err) = f.write_str("AltGr+") {
tracing::error!("{:?}", err);
}
}
if self.mods.contains(Modifiers::META) {
let _ = f.write_str("Meta+");
if let Err(err) = f.write_str("Meta+") {
tracing::error!("{:?}", err);
}
}
if self.mods.contains(Modifiers::SHIFT) {
let _ = f.write_str("Shift+");
if let Err(err) = f.write_str("Shift+") {
tracing::error!("{:?}", err);
}
}
f.write_str(&self.key.to_string())
}
@@ -686,9 +696,11 @@ impl Display for KeyMapKey {
Key::Unidentified(_) => f.write_str("Unidentified"),
Key::Dead(_) => f.write_str("dead"),
},
Self::Pointer(B::Auxiliary) => f.write_str("MouseMiddle"),
Self::Pointer(B::X2) => f.write_str("MouseForward"),
Self::Pointer(B::X1) => f.write_str("MouseBackward"),
Self::Pointer(B::Mouse(MouseButton::Auxiliary)) => {
f.write_str("MouseMiddle")
}
Self::Pointer(B::Mouse(MouseButton::X2)) => f.write_str("MouseForward"),
Self::Pointer(B::Mouse(MouseButton::X1)) => f.write_str("MouseBackward"),
Self::Pointer(_) => f.write_str("MouseUnimplemented"),
}
}
+2 -3
View File
@@ -1,4 +1,4 @@
use anyhow::{anyhow, Result};
use anyhow::{Result, anyhow};
use indexmap::IndexMap;
use lapce_core::mode::Modes;
use tracing::{debug, error};
@@ -139,9 +139,8 @@ fn get_modes(toml_keymap: &toml_edit::Table) -> Modes {
mod tests {
use floem::keyboard::Key;
use crate::keypress::keymap::KeyMapKey;
use super::*;
use crate::keypress::keymap::KeyMapKey;
#[test]
fn test_keymap() {
+4 -4
View File
@@ -9,10 +9,10 @@ pub struct KeyPress {
}
impl KeyPress {
pub fn keymap_press(&self) -> KeyMapPress {
KeyMapPress {
key: self.key.keymap_key(),
pub fn keymap_press(&self) -> Option<KeyMapPress> {
self.key.keymap_key().map(|key| KeyMapPress {
key,
mods: self.mods,
}
})
}
}
+3
View File
@@ -2,6 +2,7 @@ pub mod about;
pub mod alert;
pub mod app;
pub mod code_action;
pub mod code_lens;
pub mod command;
pub mod completion;
pub mod config;
@@ -21,6 +22,7 @@ pub mod inline_completion;
pub mod keymap;
pub mod keypress;
pub mod listener;
pub mod lsp;
pub mod main_split;
pub mod markdown;
pub mod palette;
@@ -36,6 +38,7 @@ pub mod terminal;
pub mod text_area;
pub mod text_input;
pub mod title;
pub mod tracing;
pub mod update;
pub mod wave;
pub mod web_link;
+1 -1
View File
@@ -1,4 +1,4 @@
use floem::reactive::{RwSignal, Scope};
use floem::reactive::{RwSignal, Scope, SignalGet, SignalUpdate};
/// A signal listener that receives 'events' from the outside and runs the callback.
/// This is implemented using effects and normal rw signals. This should be used when it doesn't
+92
View File
@@ -0,0 +1,92 @@
use std::path::PathBuf;
use tracing::{Level, event};
use url::Url;
// Rust-analyzer returns paths in the form of "file:///<drive>:/...", which gets parsed into URL
// as "/<drive>://" which is then interpreted by PathBuf::new() as a UNIX-like path from root.
// This function strips the additional / from the beginning, if the first segment is a drive letter.
#[cfg(windows)]
pub fn path_from_url(url: &Url) -> PathBuf {
use percent_encoding::percent_decode_str;
event!(Level::DEBUG, "Converting `{:?}` to path", url);
if let Ok(path) = url.to_file_path() {
return path;
}
let path = url.path();
let path = if path.contains('%') {
percent_decode_str(path)
.decode_utf8()
.unwrap_or(std::borrow::Cow::from(path))
} else {
std::borrow::Cow::from(path)
};
if let Some(path) = path.strip_prefix('/') {
event!(Level::DEBUG, "Found `/` prefix");
if let Some((maybe_drive_letter, path_second_part)) =
path.split_once(['/', '\\'])
{
event!(Level::DEBUG, maybe_drive_letter);
event!(Level::DEBUG, path_second_part);
let b = maybe_drive_letter.as_bytes();
if !b.is_empty() && !b[0].is_ascii_alphabetic() {
event!(Level::ERROR, "First byte is not ascii alphabetic: {b:?}");
}
match maybe_drive_letter.len() {
2 => match maybe_drive_letter.chars().nth(1) {
Some(':') => {
event!(Level::DEBUG, "Returning path `{:?}`", path);
return PathBuf::from(path);
}
v => {
event!(
Level::ERROR,
"Unhandled 'maybe_drive_letter' chars: {v:?}"
);
}
},
4 => {
if maybe_drive_letter.contains("%3A") {
let path = path.replace("%3A", ":");
event!(Level::DEBUG, "Returning path `{:?}`", path);
return PathBuf::from(path);
} else {
event!(
Level::ERROR,
"Unhandled 'maybe_drive_letter' pattern: {maybe_drive_letter:?}"
);
}
}
v => {
event!(
Level::ERROR,
"Unhandled 'maybe_drive_letter' length: {v}"
);
}
}
}
}
event!(Level::DEBUG, "Returning unmodified path `{:?}`", path);
PathBuf::from(path.into_owned())
}
#[cfg(not(windows))]
pub fn path_from_url(url: &Url) -> PathBuf {
event!(Level::DEBUG, "Converting `{:?}` to path", url);
url.to_file_path().unwrap_or_else(|_| {
let path = url.path();
if let Ok(path) = percent_encoding::percent_decode_str(path).decode_utf8() {
return PathBuf::from(path.into_owned());
}
PathBuf::from(path)
})
}
+256 -64
View File
@@ -10,7 +10,7 @@ use floem::{
file::{FileDialogOptions, FileInfo},
keyboard::Modifiers,
peniko::kurbo::{Point, Rect, Vec2},
reactive::{Memo, RwSignal, Scope},
reactive::{Memo, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith},
views::editor::id::EditorId,
};
use itertools::Itertools;
@@ -20,25 +20,28 @@ use lapce_core::{
};
use lapce_rpc::{
buffer::BufferId,
core::FileChanged,
plugin::{PluginId, VoltID},
proxy::ProxyResponse,
};
use lapce_xi_rope::{spans::SpansBuilder, Rope};
use lapce_xi_rope::{Rope, spans::SpansBuilder};
use lsp_types::{
CodeAction, CodeActionOrCommand, DiagnosticSeverity, DocumentChangeOperation,
DocumentChanges, OneOf, Position, TextEdit, Url, WorkspaceEdit,
};
use serde::{Deserialize, Serialize};
use tracing::warn;
use serde_json::Value;
use tracing::{Level, event};
use crate::{
alert::AlertButton,
code_lens::CodeLensData,
command::InternalCommand,
doc::{DiagnosticData, Doc, DocContent, DocHistory, EditorDiagnostic},
editor::{
EditorData,
diff::DiffEditorData,
location::{EditorLocation, EditorPosition},
EditorData,
},
editor_tab::{
EditorTabChild, EditorTabChildSource, EditorTabData, EditorTabInfo,
@@ -47,7 +50,8 @@ use crate::{
DiffEditorId, EditorTabId, KeymapId, SettingsId, SplitId,
ThemeColorSettingsId, VoltViewId,
},
keypress::{EventRef, KeyPressData},
keypress::{EventRef, KeyPressData, KeyPressHandle},
panel::implementation_view::ReferencesRoot,
window_tab::{CommonData, Focus, WindowTabData},
};
@@ -203,15 +207,14 @@ impl SplitContentInfo {
impl SplitData {
pub fn split_info(&self, data: &WindowTabData) -> SplitInfo {
let info = SplitInfo {
SplitInfo {
direction: self.direction,
children: self
.children
.iter()
.map(|(_, child)| child.content_info(data))
.collect(),
};
info
}
}
pub fn editor_tab_index(&self, editor_tab_id: EditorTabId) -> Option<usize> {
@@ -233,13 +236,13 @@ impl Editors {
Self(cx.create_rw_signal(im::HashMap::new()))
}
/// Add an editor to the editors.
/// Add an editor to the editors.
/// Returns the id of the editor.
pub fn insert(&self, editor: EditorData) -> EditorId {
let id = editor.id();
self.0.update(|editors| {
if editors.insert(id, editor).is_some() {
warn!("Inserted EditorId that already exists");
event!(Level::WARN, "Inserted EditorId that already exists");
}
});
@@ -338,6 +341,23 @@ impl Editors {
self.0.try_update(|editors| editors.remove(&id)).unwrap()
}
pub fn get_editor_id_by_path(&self, path: &Path) -> Option<EditorId> {
self.0.with_untracked(|x| {
for (id, data) in x {
if data.doc().content.with_untracked(|x| {
if let Some(doc_path) = x.path() {
doc_path == path
} else {
false
}
}) {
return Some(*id);
}
}
None
})
}
pub fn contains_untracked(&self, id: EditorId) -> bool {
self.0.with_untracked(|editors| editors.contains_key(&id))
}
@@ -379,15 +399,26 @@ pub struct MainSplitData {
pub docs: RwSignal<im::HashMap<PathBuf, Rc<Doc>>>,
pub scratch_docs: RwSignal<im::HashMap<String, Rc<Doc>>>,
pub diagnostics: RwSignal<im::HashMap<PathBuf, DiagnosticData>>,
pub references: RwSignal<ReferencesRoot>,
pub implementations: RwSignal<crate::panel::implementation_view::ReferencesRoot>,
pub active_editor: Memo<Option<EditorData>>,
pub find_editor: EditorData,
pub replace_editor: EditorData,
pub locations: RwSignal<im::Vector<EditorLocation>>,
pub current_location: RwSignal<usize>,
pub width: RwSignal<f64>,
pub code_lens: RwSignal<CodeLensData>,
pub common: Rc<CommonData>,
}
impl std::fmt::Debug for MainSplitData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MainSplitData")
.field("root_split", &self.root_split)
.finish()
}
}
impl MainSplitData {
pub fn new(cx: Scope, common: Rc<CommonData>) -> Self {
let splits = cx.create_rw_signal(im::HashMap::new());
@@ -402,6 +433,10 @@ impl MainSplitData {
cx.create_rw_signal(im::HashMap::new());
let scratch_docs = cx.create_rw_signal(im::HashMap::new());
let locations = cx.create_rw_signal(im::Vector::new());
let references = cx.create_rw_signal(ReferencesRoot::default());
let implementations = cx.create_rw_signal(
crate::panel::implementation_view::ReferencesRoot::default(),
);
let current_location = cx.create_rw_signal(0);
let diagnostics = cx.create_rw_signal(im::HashMap::new());
let find_editor = editors.make_local(cx, common.clone());
@@ -459,7 +494,10 @@ impl MainSplitData {
locations,
current_location,
width: cx.create_rw_signal(0.0),
code_lens: cx.create_rw_signal(CodeLensData::new(common.clone())),
common,
references,
implementations,
}
}
@@ -467,7 +505,7 @@ impl MainSplitData {
&self,
event: impl Into<EventRef<'a>>,
keypress: &KeyPressData,
) -> Option<bool> {
) -> Option<KeyPressHandle> {
let active_editor_tab = self.active_editor_tab.get_untracked()?;
let editor_tab = self.editor_tabs.with_untracked(|editor_tabs| {
editor_tabs.get(&active_editor_tab).copied()
@@ -478,9 +516,9 @@ impl MainSplitData {
match child {
EditorTabChild::Editor(editor_id) => {
let editor = self.editors.editor_untracked(editor_id)?;
let proccesed = keypress.key_down(event, &editor);
let handle = keypress.key_down(event, &editor);
editor.get_code_actions();
Some(proccesed)
Some(handle)
}
EditorTabChild::DiffEditor(diff_editor_id) => {
let diff_editor =
@@ -492,9 +530,9 @@ impl MainSplitData {
} else {
&diff_editor.left
};
let processed = keypress.key_down(event, editor);
let handle = keypress.key_down(event, editor);
editor.get_code_actions();
Some(processed)
Some(handle)
}
EditorTabChild::Settings(_) => None,
EditorTabChild::ThemeColorSettings(_) => None,
@@ -575,7 +613,11 @@ impl MainSplitData {
self.go_to_location(location, edits);
}
pub fn get_doc(&self, path: PathBuf) -> (Rc<Doc>, bool) {
pub fn get_doc(
&self,
path: PathBuf,
unsaved: Option<String>,
) -> (Rc<Doc>, bool) {
let cx = self.scope;
let doc = self.docs.with_untracked(|docs| docs.get(&path).cloned());
if let Some(doc) = doc {
@@ -611,6 +653,8 @@ impl MainSplitData {
*read_only = true;
}
});
} else if let Some(unsaved) = unsaved {
local_doc.reload(Rope::from(unsaved), false);
}
}
});
@@ -621,7 +665,9 @@ impl MainSplitData {
send(result);
});
}
doc.get_code_lens();
doc.get_folding_range();
doc.get_document_symbol();
(doc, true)
}
}
@@ -635,7 +681,7 @@ impl MainSplitData {
self.common.focus.set(Focus::Workbench);
}
let path = location.path.clone();
let (doc, new_doc) = self.get_doc(path.clone());
let (doc, new_doc) = self.get_doc(path.clone(), None);
let child = self.get_editor_tab_child(
EditorTabChildSource::Editor { path, doc },
@@ -650,7 +696,7 @@ impl MainSplitData {
}
pub fn open_file_changes(&self, path: PathBuf) {
let (right, _) = self.get_doc(path.clone());
let (right, _) = self.get_doc(path.clone(), None);
let left = Doc::new_history(
self.scope,
DocContent::History(DocHistory {
@@ -683,7 +729,8 @@ impl MainSplitData {
}
pub fn open_diff_files(&self, left_path: PathBuf, right_path: PathBuf) {
let [left, right] = [left_path, right_path].map(|path| self.get_doc(path).0);
let [left, right] =
[left_path, right_path].map(|path| self.get_doc(path, None).0);
self.get_editor_tab_child(
EditorTabChildSource::DiffEditor { left, right },
@@ -1676,8 +1723,8 @@ impl MainSplitData {
let splits = self.splits.get_untracked();
let split = splits.get(&split_id).copied()?;
let split_chilren = split.with_untracked(|split| split.children.clone());
let content = split_chilren.first()?;
let split_children = split.with_untracked(|split| split.children.clone());
let content = split_children.first()?;
self.split_content_focus(&content.1);
Some(())
@@ -1902,6 +1949,60 @@ impl MainSplitData {
Some(())
}
pub fn editor_tab_child_close_by_kind(
&self,
editor_tab_id: EditorTabId,
child: EditorTabChild,
kind: TabCloseKind,
) -> Option<()> {
let tabs_to_close: Vec<EditorTabChild> = {
let editor_tabs = self.editor_tabs.get_untracked();
let editor_tab = editor_tabs.get(&editor_tab_id).copied()?;
let editor_tab = editor_tab.get_untracked();
match kind {
TabCloseKind::CloseOther => editor_tab
.children
.iter()
.filter_map(|x| {
if x.2 != child {
Some(x.2.clone())
} else {
None
}
})
.collect(),
TabCloseKind::CloseToLeft => {
let mut tabs_to_close = Vec::new();
for child_tab in &editor_tab.children {
if child_tab.2 != child {
tabs_to_close.push(child_tab.2.clone());
} else {
break;
}
}
tabs_to_close
}
TabCloseKind::CloseToRight => {
let mut tabs_to_close = Vec::new();
let mut add_to_tabs = false;
for child_tab in &editor_tab.children {
if child_tab.2 != child && add_to_tabs {
tabs_to_close.push(child_tab.2.clone());
} else {
add_to_tabs = true;
}
}
tabs_to_close
}
}
};
for child_tab in tabs_to_close {
self.editor_tab_child_close(editor_tab_id, child_tab, false);
}
Some(())
}
pub fn editor_tab_child_close(
&self,
editor_tab_id: EditorTabId,
@@ -1926,7 +2027,7 @@ impl MainSplitData {
let doc = doc.clone();
internal_command.send(InternalCommand::HideAlert);
save_as(
FileDialogOptions::new(),
FileDialogOptions::new().title("Save File"),
move |file: Option<FileInfo>| {
let main_split = main_split.clone();
let child = child.clone();
@@ -1992,8 +2093,8 @@ impl MainSplitData {
.internal_command
.send(InternalCommand::ShowAlert {
title: format!(
"Do you want to save the changes you made to {name}?"
),
"Do you want to save the changes you made to {name}?"
),
msg: "Your changes will be lost if you don't save them."
.to_string(),
buttons: vec![
@@ -2045,6 +2146,8 @@ impl MainSplitData {
.unwrap();
if let Some(diff_editor) = removed_diff_editor {
diff_editor.right.save_doc_position();
self.editors.remove(diff_editor.right.editor.id());
self.editors.remove(diff_editor.left.editor.id());
}
}
EditorTabChild::Settings(_) => {}
@@ -2081,7 +2184,12 @@ impl MainSplitData {
pub fn run_code_action(&self, plugin_id: PluginId, action: CodeActionOrCommand) {
match action {
CodeActionOrCommand::Command(_) => {}
CodeActionOrCommand::Command(command) => {
self.run_code_lens(
&command.command,
command.arguments.unwrap_or_default(),
);
}
CodeActionOrCommand::CodeAction(action) => {
if let Some(edit) = action.edit.as_ref() {
self.apply_workspace_edit(edit);
@@ -2092,6 +2200,10 @@ impl MainSplitData {
}
}
pub fn run_code_lens(&self, command: &str, args: Vec<Value>) {
self.code_lens.get_untracked().run(command, args);
}
/// Resolve a code action and apply its held workspace edit
fn resolve_code_action(&self, plugin_id: PluginId, action: CodeAction) {
let main_split = self.clone();
@@ -2243,14 +2355,44 @@ impl MainSplitData {
}
}
pub fn open_file_changed(&self, path: &Path, content: &str) {
let doc = self.docs.with_untracked(|docs| docs.get(path).cloned());
let doc = match doc {
Some(doc) => doc,
None => return,
};
doc.handle_file_changed(Rope::from(content));
pub fn open_file_changed(&self, path: &Path, content: &FileChanged) {
tracing::debug!("open_file_changed {:?}", path);
match content {
FileChanged::Change(content) => {
let doc = self.docs.with_untracked(|docs| docs.get(path).cloned());
let doc = match doc {
Some(doc) => doc,
None => return,
};
doc.handle_file_changed(Rope::from(content));
}
FileChanged::Delete => {
if self.docs.with_untracked(|x| x.get(path).is_none()) {
return;
}
let Some(editor_id) = self.editors.get_editor_id_by_path(path)
else {
return;
};
let id = editor_id.to_raw();
if let Some(tab_id) = self.editor_tabs.with_untracked(|x| {
for (tab_id, tab_data) in x {
if tab_data.with_untracked(|x| {
x.children.iter().any(|(_, _, child)| child.id() == id)
}) {
return Some(*tab_id);
}
}
None
}) {
self.editor_tab_close(tab_id);
}
self.editors.remove(editor_id);
self.docs.update(|x| {
x.remove(path);
});
}
}
}
pub fn set_find_pattern(&self, pattern: Option<String>) {
@@ -2304,7 +2446,11 @@ impl MainSplitData {
let path = path.clone();
create_ext_action(self.scope, move |result| {
if let Err(err) = result {
warn!("Failed to save as a file: {:?}", err);
event!(
Level::WARN,
"Failed to save as a file: {:?}",
err
);
} else {
let syntax = Syntax::init(&path);
doc.content.set(DocContent::File {
@@ -2355,7 +2501,11 @@ impl MainSplitData {
let path = path.clone();
create_ext_action(self.scope, move |result| {
if let Err(err) = result {
warn!("Failed to save as a file: {:?}", err);
event!(
Level::WARN,
"Failed to save as a file: {:?}",
err
);
} else {
let syntax = Syntax::init(&path);
doc.content.set(DocContent::File {
@@ -2439,38 +2589,44 @@ impl MainSplitData {
pub fn save_scratch_doc(&self, doc: Rc<Doc>) {
let main_split = self.clone();
save_as(FileDialogOptions::new(), move |file: Option<FileInfo>| {
if let Some(mut file) = file {
main_split.save_as(
doc.clone(),
if let Some(path) = file.path.pop() {
path
} else {
tracing::error!("No path");
return;
},
move || {},
);
}
});
save_as(
FileDialogOptions::new().title("Save File"),
move |file: Option<FileInfo>| {
if let Some(mut file) = file {
main_split.save_as(
doc.clone(),
if let Some(path) = file.path.pop() {
path
} else {
tracing::error!("No path");
return;
},
move || {},
);
}
},
);
}
pub fn save_scratch_doc2(&self, doc: Rc<Doc>) {
let main_split = self.clone();
save_as(FileDialogOptions::new(), move |file: Option<FileInfo>| {
if let Some(mut file) = file {
main_split.save_as2(
doc.clone(),
if let Some(path) = file.path.pop() {
path
} else {
tracing::error!("No path");
return;
},
move || {},
);
}
});
save_as(
FileDialogOptions::new().title("Save File"),
move |file: Option<FileInfo>| {
if let Some(mut file) = file {
main_split.save_as2(
doc.clone(),
if let Some(path) = file.path.pop() {
path
} else {
tracing::error!("No path");
return;
},
move || {},
);
}
},
);
}
pub fn move_editor_tab_child(
@@ -2809,6 +2965,35 @@ impl MainSplitData {
}
}
}
pub fn show_env(&self) {
let child = self.new_file();
if let EditorTabChild::Editor(id) = child {
if let Some(editor) = self.editors.editor_untracked(id) {
let doc = editor.doc();
doc.reload(
Rope::from(
std::env::vars().map(|(k, v)| format!("{k}={v}")).join("\n"),
),
true,
);
}
}
}
pub fn get_active_editor(&self) -> Option<EditorData> {
let active_editor_tab = self.active_editor_tab.get()?;
let editor_tabs = self.editor_tabs;
let editor_tab = editor_tabs
.with(|editor_tabs| editor_tabs.get(&active_editor_tab).copied())?;
let (_, _, child) = editor_tab.with(|editor_tab| {
editor_tab.children.get(editor_tab.active).cloned()
})?;
match child {
EditorTabChild::Editor(editor_id) => self.editors.editor(editor_id),
_ => None,
}
}
}
fn workspace_edits(edit: &WorkspaceEdit) -> Option<HashMap<Url, Vec<TextEdit>>> {
@@ -2911,3 +3096,10 @@ fn next_in_file_errors_offset(
},
)
}
#[derive(Clone, Copy, Debug)]
pub enum TabCloseKind {
CloseOther,
CloseToLeft,
CloseToRight,
}
+17 -13
View File
@@ -1,4 +1,4 @@
use floem::cosmic_text::{
use floem::text::{
Attrs, AttrsList, FamilyOwned, LineHeightValue, Style, TextLayout, Weight,
};
use lapce_core::{language::LapceLanguage, syntax::Syntax};
@@ -7,7 +7,7 @@ use lsp_types::MarkedString;
use pulldown_cmark::{CodeBlockKind, CowStr, Event, Options, Parser, Tag};
use smallvec::SmallVec;
use crate::config::{color::LapceColor, LapceConfig};
use crate::config::{LapceConfig, color::LapceColor};
#[derive(Clone)]
pub enum MarkdownContent {
@@ -31,7 +31,7 @@ pub fn parse_markdown(
.color(config.color(LapceColor::EDITOR_FOREGROUND))
.font_size(config.ui.font_size() as f32)
.line_height(LineHeightValue::Normal(line_height as f32));
let mut attr_list = AttrsList::new(default_attrs);
let mut attr_list = AttrsList::new(default_attrs.clone());
let mut builder_dirty = false;
@@ -72,7 +72,7 @@ pub fn parse_markdown(
}
if let Some(attrs) = attribute_for_tag(
default_attrs,
default_attrs.clone(),
&tag,
&code_font_family,
config,
@@ -96,7 +96,7 @@ pub fn parse_markdown(
highlight_as_code(
&mut attr_list,
default_attrs.family(&code_font_family),
default_attrs.clone().family(&code_font_family),
language,
&last_text,
start_offset,
@@ -115,9 +115,9 @@ pub fn parse_markdown(
if builder_dirty {
let mut text_layout = TextLayout::new();
text_layout.set_text(&current_text, attr_list);
text_layout.set_text(&current_text, attr_list, None);
res.push(MarkdownContent::Text(text_layout));
attr_list = AttrsList::new(default_attrs);
attr_list = AttrsList::new(default_attrs.clone());
current_text.clear();
pos = 0;
builder_dirty = false;
@@ -151,7 +151,7 @@ pub fn parse_markdown(
Event::Code(text) => {
attr_list.add_span(
pos..pos + text.len(),
default_attrs.family(&code_font_family),
default_attrs.clone().family(&code_font_family),
);
current_text.push_str(&text);
pos += text.len();
@@ -163,6 +163,7 @@ pub fn parse_markdown(
attr_list.add_span(
pos..pos + text.len(),
default_attrs
.clone()
.family(&code_font_family)
.color(config.color(LapceColor::MARKDOWN_BLOCKQUOTE)),
);
@@ -184,12 +185,14 @@ pub fn parse_markdown(
Event::FootnoteReference(_text) => {}
Event::TaskListMarker(_text) => {}
Event::InlineHtml(_) => {} // TODO(panekj): Implement
Event::InlineMath(_) => {} // TODO(panekj): Implement
Event::DisplayMath(_) => {} // TODO(panekj): Implement
}
}
if builder_dirty {
let mut text_layout = TextLayout::new();
text_layout.set_text(&current_text, attr_list);
text_layout.set_text(&current_text, attr_list, None);
res.push(MarkdownContent::Text(text_layout));
}
@@ -227,7 +230,7 @@ fn attribute_for_tag<'a>(
.weight(Weight::BOLD),
)
}
Tag::BlockQuote => Some(
Tag::BlockQuote(_block_quote) => Some(
default_attrs
.style(Style::Italic)
.color(config.color(LapceColor::MARKDOWN_BLOCKQUOTE)),
@@ -296,7 +299,7 @@ pub fn highlight_as_code(
{
attr_list.add_span(
start_offset + range.start..start_offset + range.end,
default_attrs.color(color),
default_attrs.clone().color(color),
);
}
}
@@ -308,14 +311,14 @@ pub fn from_marked_string(
config: &LapceConfig,
) -> Vec<MarkdownContent> {
match text {
MarkedString::String(text) => parse_markdown(&text, 1.5, config),
MarkedString::String(text) => parse_markdown(&text, 1.8, config),
// This is a short version of a code block
MarkedString::LanguageString(code) => {
// TODO: We could simply construct the MarkdownText directly
// Simply construct the string as if it was written directly
parse_markdown(
&format!("```{}\n{}\n```", code.language, code.value),
1.5,
1.8,
config,
)
}
@@ -335,6 +338,7 @@ pub fn from_plaintext(
.font_size(config.ui.font_size() as f32)
.line_height(LineHeightValue::Normal(line_height as f32)),
),
None,
);
vec![MarkdownContent::Text(text_layout)]
}
+145 -75
View File
@@ -4,19 +4,23 @@ use std::{
path::PathBuf,
rc::Rc,
sync::{
atomic::{AtomicU64, Ordering},
Arc,
atomic::{AtomicU64, Ordering},
mpsc::{Receiver, Sender, TryRecvError, channel},
},
time::Instant,
};
use anyhow::Result;
use crossbeam_channel::{Receiver, Sender, TryRecvError};
use floem::{
ext_event::{create_ext_action, create_signal_from_channel},
keyboard::Modifiers,
reactive::{use_context, ReadSignal, RwSignal, Scope},
reactive::{
ReadSignal, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith,
use_context,
},
};
use im::Vector;
use itertools::Itertools;
use lapce_core::{
buffer::rope_text::RopeText, command::FocusCommand, language::LapceLanguage,
@@ -25,7 +29,7 @@ use lapce_core::{
};
use lapce_rpc::proxy::ProxyResponse;
use lapce_xi_rope::Rope;
use lsp_types::DocumentSymbolResponse;
use lsp_types::{DocumentSymbol, DocumentSymbolResponse};
use nucleo::Utf32Str;
use strum::{EnumMessage, IntoEnumIterator};
use tracing::error;
@@ -41,12 +45,12 @@ use crate::{
db::LapceDb,
debug::{RunDebugConfigs, RunDebugMode},
editor::{
location::{EditorLocation, EditorPosition},
EditorData,
location::{EditorLocation, EditorPosition},
},
keypress::{condition::Condition, KeyPressData, KeyPressFocus},
keypress::{KeyPressData, KeyPressFocus, condition::Condition},
lsp::path_from_url,
main_split::MainSplitData,
proxy::path_from_url,
source_control::SourceControlData,
window_tab::{CommonData, Focus},
workspace::{LapceWorkspace, LapceWorkspaceType, SshHost},
@@ -55,7 +59,7 @@ use crate::{
pub mod item;
pub mod kind;
const DEFAULT_RUN_TOML: &str = include_str!("../../defaults/run.toml");
pub const DEFAULT_RUN_TOML: &str = include_str!("../../defaults/run.toml");
#[derive(Clone, PartialEq, Eq)]
pub enum PaletteStatus {
@@ -105,6 +109,12 @@ pub struct PaletteData {
left_diff_path: RwSignal<Option<PathBuf>>,
}
impl std::fmt::Debug for PaletteData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PaletteData").finish()
}
}
impl PaletteData {
pub fn new(
cx: Scope,
@@ -130,7 +140,7 @@ impl PaletteData {
let run_id = cx.create_rw_signal(0);
let run_id_counter = Arc::new(AtomicU64::new(0));
let (run_tx, run_rx) = crossbeam_channel::unbounded();
let (run_tx, run_rx) = channel();
{
let run_id = run_id.read_only();
let input = input.read_only();
@@ -145,7 +155,11 @@ impl PaletteData {
let run_id = run_id.get_untracked();
let preselect_index =
preselect_index.try_update(|i| i.take()).unwrap();
let _ = tx.send((run_id, input.input, items, preselect_index));
if let Err(err) =
tx.send((run_id, input.input, items, preselect_index))
{
tracing::error!("{:?}", err);
}
});
}
// this effect only monitors input change
@@ -157,16 +171,21 @@ impl PaletteData {
}
let items = items.get_untracked();
let run_id = run_id.get_untracked();
let _ = tx.send((run_id, input.input, items, None));
if let Err(err) = tx.send((run_id, input.input, items, None)) {
tracing::error!("{:?}", err);
}
kind
});
}
let (resp_tx, resp_rx) = crossbeam_channel::unbounded();
let (resp_tx, resp_rx) = channel();
{
let run_id = run_id_counter.clone();
std::thread::spawn(move || {
Self::update_process(run_id, run_rx, resp_tx);
});
std::thread::Builder::new()
.name("PaletteUpdateProcess".to_owned())
.spawn(move || {
Self::update_process(run_id, run_rx, resp_tx);
})
.unwrap();
}
let (filtered_items, set_filtered_items) =
cx.create_signal(im::Vector::new());
@@ -324,14 +343,18 @@ impl PaletteData {
/// Get the placeholder text to use in the palette input field.
pub fn placeholder_text(&self) -> &'static str {
if self.kind.get() == PaletteKind::DiffFiles {
if self.left_diff_path.with(Option::is_some) {
"Select right file"
} else {
"Seleft left file"
match self.kind.get() {
PaletteKind::SshHost => {
"Type [user@]host or select a previously connected workspace below"
}
} else {
""
PaletteKind::DiffFiles => {
if self.left_diff_path.with(Option::is_some) {
"Select right file"
} else {
"Seleft left file"
}
}
_ => "",
}
}
@@ -348,6 +371,7 @@ impl PaletteData {
PaletteKind::File | PaletteKind::DiffFiles => {
self.get_files();
}
PaletteKind::HelpAndFile => self.get_palette_help_and_file(),
PaletteKind::Line => {
self.get_lines();
}
@@ -397,7 +421,12 @@ impl PaletteData {
/// Initialize the palette with a list of the available palette kinds.
fn get_palette_help(&self) {
let items = PaletteKind::iter()
let items = self.get_palette_help_items();
self.items.set(items);
}
fn get_palette_help_items(&self) -> Vector<PaletteItem> {
PaletteKind::iter()
.filter_map(|kind| {
// Don't include PaletteHelp as the user is already here.
(kind != PaletteKind::PaletteHelp)
@@ -424,13 +453,18 @@ impl PaletteData {
indices: vec![],
}
})
.collect();
self.items.set(items);
.collect()
}
/// Initialize the palette with the files in the current workspace.
fn get_files(&self) {
fn get_palette_help_and_file(&self) {
let help_items: Vector<PaletteItem> = self.get_palette_help_items();
self.get_files_and_prepend(Some(help_items));
}
// get the files in the current workspace
// and prepend items if prepend is some
// e.g. help_and_file
fn get_files_and_prepend(&self, prepend: Option<im::Vector<PaletteItem>>) {
let workspace = self.workspace.clone();
let set_items = self.items.write_only();
let send =
@@ -457,7 +491,12 @@ impl PaletteData {
}
})
.collect::<im::Vector<_>>();
set_items.set(items);
let mut new_items = im::Vector::new();
if let Some(prepend) = prepend {
new_items.append(prepend);
}
new_items.append(items);
set_items.set(new_items);
});
self.common.proxy.get_files(move |result| {
if let Ok(ProxyResponse::GetFilesResponse { items }) = result {
@@ -466,6 +505,11 @@ impl PaletteData {
});
}
/// Initialize the palette with the files in the current workspace.
fn get_files(&self) {
self.get_files_and_prepend(None);
}
/// Initialize the palette with the lines in the current document.
fn get_lines(&self) {
let editor = self.main_split.active_editor.get_untracked();
@@ -636,42 +680,7 @@ impl PaletteData {
let set_items = self.items.write_only();
let send = create_ext_action(self.common.scope, move |result| {
if let Ok(ProxyResponse::GetDocumentSymbols { resp }) = result {
let items: im::Vector<PaletteItem> = match resp {
DocumentSymbolResponse::Flat(symbols) => symbols
.iter()
.map(|s| {
let mut filter_text = s.name.clone();
if let Some(container_name) = s.container_name.as_ref() {
filter_text += container_name;
}
PaletteItem {
content: PaletteItemContent::DocumentSymbol {
kind: s.kind,
name: s.name.clone(),
range: s.location.range,
container_name: s.container_name.clone(),
},
filter_text,
score: 0,
indices: Vec::new(),
}
})
.collect(),
DocumentSymbolResponse::Nested(symbols) => symbols
.iter()
.map(|s| PaletteItem {
content: PaletteItemContent::DocumentSymbol {
kind: s.kind,
name: s.name.clone(),
range: s.range,
container_name: None,
},
filter_text: s.name.clone(),
score: 0,
indices: Vec::new(),
})
.collect(),
};
let items = Self::format_document_symbol_resp(resp);
set_items.set(items);
} else {
set_items.update(|items| items.clear());
@@ -683,6 +692,64 @@ impl PaletteData {
});
}
fn format_document_symbol_resp(
resp: DocumentSymbolResponse,
) -> im::Vector<PaletteItem> {
match resp {
DocumentSymbolResponse::Flat(symbols) => symbols
.iter()
.map(|s| {
let mut filter_text = s.name.clone();
if let Some(container_name) = s.container_name.as_ref() {
filter_text += container_name;
}
PaletteItem {
content: PaletteItemContent::DocumentSymbol {
kind: s.kind,
name: s.name.replace('\n', ""),
range: s.location.range,
container_name: s.container_name.clone(),
},
filter_text,
score: 0,
indices: Vec::new(),
}
})
.collect(),
DocumentSymbolResponse::Nested(symbols) => {
let mut items = im::Vector::new();
for s in symbols {
Self::format_document_symbol(&mut items, None, s)
}
items
}
}
}
fn format_document_symbol(
items: &mut im::Vector<PaletteItem>,
parent: Option<String>,
s: DocumentSymbol,
) {
items.push_back(PaletteItem {
content: PaletteItemContent::DocumentSymbol {
kind: s.kind,
name: s.name.replace('\n', ""),
range: s.range,
container_name: parent,
},
filter_text: s.name.clone(),
score: 0,
indices: Vec::new(),
});
if let Some(children) = s.children {
let parent = Some(s.name.replace('\n', ""));
for child in children {
Self::format_document_symbol(items, parent.clone(), child);
}
}
}
fn get_workspace_symbols(&self) {
let input = self.input.get_untracked().input;
@@ -755,8 +822,7 @@ impl PaletteData {
#[cfg(windows)]
fn get_wsl_hosts(&self) {
use std::os::windows::process::CommandExt;
use std::process;
use std::{os::windows::process::CommandExt, process};
let cmd = process::Command::new("wsl")
.creation_flags(0x08000000) // CREATE_NO_WINDOW
.arg("-l")
@@ -877,7 +943,7 @@ impl PaletteData {
fn get_run_configs(&self) {
if let Some(workspace) = self.common.workspace.path.as_deref() {
let run_toml = workspace.join(".lapce").join("run.toml");
let (doc, new_doc) = self.main_split.get_doc(run_toml.clone());
let (doc, new_doc) = self.main_split.get_doc(run_toml.clone(), None);
if !new_doc {
let content = doc.buffer.with_untracked(|b| b.to_string());
self.set_run_configs(content);
@@ -1331,7 +1397,7 @@ impl PaletteData {
PaletteItemContent::Reference { location, .. } => {
self.has_preview.set(true);
let (doc, new_doc) =
self.main_split.get_doc(location.path.clone());
self.main_split.get_doc(location.path.clone(), None);
self.preview_editor.update_doc(doc);
self.preview_editor.go_to_location(
location.clone(),
@@ -1371,7 +1437,7 @@ impl PaletteData {
PaletteItemContent::WorkspaceSymbol { location, .. } => {
self.has_preview.set(true);
let (doc, new_doc) =
self.main_split.get_doc(location.path.clone());
self.main_split.get_doc(location.path.clone(), None);
self.preview_editor.update_doc(doc);
self.preview_editor.go_to_location(
location.clone(),
@@ -1497,6 +1563,8 @@ impl PaletteData {
// NOTE: We collect into a Vec to sort as we are hitting a worst-case behavior in
// `im::Vector` that can lead to a stack overflow!
let mut filtered_items = Vec::new();
let mut indices = Vec::new();
let mut filter_text_buf = Vec::new();
for i in &items {
// If the run id has ever changed, then we'll just bail out of this filtering to avoid
// wasting effort. This would happen, for example, on the user continuing to type.
@@ -1504,14 +1572,14 @@ impl PaletteData {
return None;
}
let mut indices = Vec::new();
let mut filter_text_buf = Vec::new();
indices.clear();
filter_text_buf.clear();
let filter_text = Utf32Str::new(&i.filter_text, &mut filter_text_buf);
if let Some(score) = pattern.indices(filter_text, matcher, &mut indices)
{
let mut item = i.clone();
item.score = score;
item.indices = indices.into_iter().map(|i| i as usize).collect();
item.indices = indices.iter().map(|i| *i as usize).collect();
filtered_items.push(item);
}
}
@@ -1573,12 +1641,14 @@ impl PaletteData {
items,
&mut matcher,
) {
let _ = resp_tx.send((
if let Err(err) = resp_tx.send((
current_run_id,
input,
filtered_items,
preselect_index,
));
)) {
tracing::error!("{:?}", err);
}
}
} else {
return;
+15 -2
View File
@@ -23,6 +23,7 @@ pub enum PaletteKind {
SCMReferences,
TerminalProfile,
DiffFiles,
HelpAndFile,
}
impl PaletteKind {
@@ -46,6 +47,7 @@ impl PaletteKind {
| PaletteKind::Language
| PaletteKind::LineEnding
| PaletteKind::SCMReferences
| PaletteKind::HelpAndFile
| PaletteKind::DiffFiles => "",
#[cfg(windows)]
PaletteKind::WslHost => "",
@@ -80,6 +82,9 @@ impl PaletteKind {
PaletteKind::Workspace => Some(LapceWorkbenchCommand::PaletteWorkspace),
PaletteKind::Command => Some(LapceWorkbenchCommand::PaletteCommand),
PaletteKind::File => Some(LapceWorkbenchCommand::Palette),
PaletteKind::HelpAndFile => {
Some(LapceWorkbenchCommand::PaletteHelpAndFile)
}
PaletteKind::Reference => None, // InternalCommand::PaletteReferences
PaletteKind::SshHost => Some(LapceWorkbenchCommand::ConnectSshHost),
#[cfg(windows)]
@@ -124,7 +129,7 @@ impl PaletteKind {
| PaletteKind::IconTheme
| PaletteKind::Language
| PaletteKind::LineEnding
| PaletteKind::SCMReferences
| PaletteKind::SCMReferences | PaletteKind::HelpAndFile
| PaletteKind::DiffFiles => input,
PaletteKind::PaletteHelp
| PaletteKind::Command
@@ -141,9 +146,17 @@ impl PaletteKind {
/// Get the palette kind that it should be considered as based on the current
/// [`PaletteKind`] and the current input.
pub fn get_palette_kind(&self, input: &str) -> PaletteKind {
if self != &PaletteKind::File && self.symbol() == "" {
if self == &PaletteKind::HelpAndFile && input.is_empty() {
return *self;
}
if self != &PaletteKind::File
&& self != &PaletteKind::HelpAndFile
&& self.symbol() == ""
{
return *self;
}
PaletteKind::from_input(input)
}
}
+251
View File
@@ -0,0 +1,251 @@
use std::{ops::AddAssign, rc::Rc};
use floem::{
IntoView, View, ViewId,
reactive::{RwSignal, SignalGet, SignalUpdate, SignalWith},
style::CursorStyle,
views::{
Decorators, VirtualVector, container, empty, label, scroll, stack, svg,
virtual_stack,
},
};
use lsp_types::{CallHierarchyItem, Range};
use super::position::PanelPosition;
use crate::{
command::InternalCommand,
config::{color::LapceColor, icon::LapceIcons},
editor::location::EditorLocation,
window_tab::{CommonData, WindowTabData},
};
#[derive(Clone, Debug)]
pub struct CallHierarchyData {
pub root: RwSignal<Option<RwSignal<CallHierarchyItemData>>>,
pub common: Rc<CommonData>,
pub scroll_to_line: RwSignal<Option<f64>>,
}
#[derive(Debug, Clone)]
pub struct CallHierarchyItemData {
pub view_id: ViewId,
pub item: Rc<CallHierarchyItem>,
pub from_range: Range,
pub init: bool,
pub open: RwSignal<bool>,
pub children: RwSignal<Vec<RwSignal<CallHierarchyItemData>>>,
}
impl CallHierarchyItemData {
pub fn child_count(&self) -> usize {
let mut count = 1;
if self.open.get() {
for child in self.children.get_untracked() {
count += child.with(|x| x.child_count())
}
}
count
}
pub fn find_by_id(
root: RwSignal<CallHierarchyItemData>,
view_id: ViewId,
) -> Option<RwSignal<CallHierarchyItemData>> {
if root.get_untracked().view_id == view_id {
Some(root)
} else {
root.get_untracked()
.children
.get_untracked()
.into_iter()
.find_map(|x| Self::find_by_id(x, view_id))
}
}
}
fn get_children(
data: RwSignal<CallHierarchyItemData>,
next: &mut usize,
min: usize,
max: usize,
level: usize,
) -> Vec<(usize, usize, RwSignal<CallHierarchyItemData>)> {
let mut children = Vec::new();
if *next >= min && *next < max {
children.push((*next, level, data));
} else if *next >= max {
return children;
}
next.add_assign(1);
if data.get_untracked().open.get() {
for child in data.get().children.get_untracked() {
let child_children = get_children(child, next, min, max, level + 1);
children.extend(child_children);
if *next > max {
break;
}
}
}
children
}
pub struct VirtualList {
root: Option<RwSignal<CallHierarchyItemData>>,
}
impl VirtualList {
pub fn new(root: Option<RwSignal<CallHierarchyItemData>>) -> Self {
Self { root }
}
}
impl VirtualVector<(usize, usize, RwSignal<CallHierarchyItemData>)> for VirtualList {
fn total_len(&self) -> usize {
if let Some(root) = &self.root {
root.with(|x| x.child_count())
} else {
0
}
}
fn slice(
&mut self,
range: std::ops::Range<usize>,
) -> impl Iterator<Item = (usize, usize, RwSignal<CallHierarchyItemData>)> {
if let Some(root) = &self.root {
let min = range.start;
let max = range.end;
let children = get_children(*root, &mut 0, min, max, 0);
children.into_iter()
} else {
Vec::new().into_iter()
}
}
}
pub fn show_hierarchy_panel(
window_tab_data: Rc<WindowTabData>,
_position: PanelPosition,
) -> impl View {
let call_hierarchy_data = window_tab_data.call_hierarchy_data.clone();
let config = call_hierarchy_data.common.config;
let ui_line_height = call_hierarchy_data.common.ui_line_height;
let scroll_to_line = call_hierarchy_data.scroll_to_line;
scroll(
virtual_stack(
move || VirtualList::new(call_hierarchy_data.root.get()),
move |(_, _, item)| item.get_untracked().view_id,
move |(_, level, rw_data)| {
let data = rw_data.get_untracked();
let open = data.open;
let kind = data.item.kind;
stack((
container(
svg(move || {
let config = config.get();
let svg_str = match open.get() {
true => LapceIcons::ITEM_OPENED,
false => LapceIcons::ITEM_CLOSED,
};
config.ui_svg(svg_str)
})
.style(move |s| {
let config = config.get();
let size = config.ui.icon_size() as f32;
s.size(size, size)
.color(config.color(LapceColor::LAPCE_ICON_ACTIVE))
})
)
.style(|s| s.padding(4.0).margin_left(6.0).margin_right(2.0))
.on_click_stop({
let window_tab_data = window_tab_data.clone();
move |_x| {
open.update(|x| {
*x = !*x;
});
if !rw_data.get_untracked().init {
window_tab_data.common.internal_command.send(
InternalCommand::CallHierarchyIncoming {
item_id: rw_data.get_untracked().view_id,
},
);
}
}
}),
svg(move || {
let config = config.get();
config
.symbol_svg(&kind)
.unwrap_or_else(|| config.ui_svg(LapceIcons::FILE))
}).style(move |s| {
let config = config.get();
let size = config.ui.icon_size() as f32;
s.min_width(size)
.size(size, size)
.margin_right(5.0)
.color(config.symbol_color(&kind).unwrap_or_else(|| {
config.color(LapceColor::LAPCE_ICON_ACTIVE)
}))
}),
data.item.name.clone().into_view(),
if data.item.detail.is_some() {
label(move || {
data.item.detail.clone().unwrap_or_default().replace('\n', "")
}).style(move |s| s.margin_left(6.0)
.color(config.get().color(LapceColor::EDITOR_DIM))
).into_any()
} else {
empty().into_any()
},
))
.style(move |s| {
s.padding_right(5.0)
.height(ui_line_height.get())
.padding_left((level * 10) as f32)
.items_center()
.hover(|s| {
s.background(
config
.get()
.color(LapceColor::PANEL_HOVERED_BACKGROUND),
)
.cursor(CursorStyle::Pointer)
})
})
.on_click_stop({
let window_tab_data = window_tab_data.clone();
let data = rw_data;
move |_| {
if !rw_data.get_untracked().init {
window_tab_data.common.internal_command.send(
InternalCommand::CallHierarchyIncoming { item_id: rw_data.get_untracked().view_id },
);
}
let data = data.get_untracked();
if let Ok(path) = data.item.uri.to_file_path() {
window_tab_data
.common
.internal_command
.send(InternalCommand::JumpToLocation { location: EditorLocation {
path,
position: Some(crate::editor::location::EditorPosition::Position(data.from_range.start)),
scroll_offset: None,
ignore_unconfirmed: false,
same_editor_tab: false,
} });
}
}
})
},
).item_size_fixed(move || ui_line_height.get())
.style(|s| s.flex_col().absolute().min_width_full()),
)
.style(|s| s.absolute().size_full())
.scroll_to(move || {
if let Some(line) = scroll_to_line.get() {
let line_height = ui_line_height.get();
Some((0.0, line * line_height).into())
} else {
None
}
})
}
+61 -2
View File
@@ -2,7 +2,9 @@ use std::{rc::Rc, sync::Arc};
use floem::{
kurbo::Size,
reactive::{use_context, Memo, RwSignal, Scope},
reactive::{
Memo, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith, use_context,
},
};
use serde::{Deserialize, Serialize};
@@ -31,12 +33,38 @@ pub fn default_panel_order() -> PanelOrder {
);
order.insert(
PanelPosition::BottomLeft,
im::vector![PanelKind::Terminal, PanelKind::Search, PanelKind::Problem,],
im::vector![
PanelKind::Terminal,
PanelKind::Search,
PanelKind::Problem,
PanelKind::CallHierarchy,
PanelKind::References,
PanelKind::Implementation
],
);
order.insert(
PanelPosition::RightTop,
im::vector![PanelKind::DocumentSymbol,],
);
order
}
#[derive(Clone, Copy, Serialize, Deserialize, Hash, PartialEq, Eq)]
pub enum PanelSection {
OpenEditor,
FileExplorer,
Error,
Warn,
Changes,
Installed,
Available,
Process,
Variable,
StackFrame,
Breakpoint,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct PanelSize {
pub left: f64,
@@ -52,6 +80,7 @@ pub struct PanelInfo {
pub panels: PanelOrder,
pub styles: im::HashMap<PanelPosition, PanelStyle>,
pub size: PanelSize,
pub sections: im::HashMap<PanelSection, bool>,
}
#[derive(Clone)]
@@ -60,6 +89,7 @@ pub struct PanelData {
pub styles: RwSignal<im::HashMap<PanelPosition, PanelStyle>>,
pub size: RwSignal<PanelSize>,
pub available_size: Memo<Size>,
pub sections: RwSignal<im::HashMap<PanelSection, RwSignal<bool>>>,
pub common: Rc<CommonData>,
}
@@ -68,6 +98,7 @@ impl PanelData {
cx: Scope,
panels: im::HashMap<PanelPosition, im::Vector<PanelKind>>,
available_size: Memo<Size>,
sections: im::HashMap<PanelSection, bool>,
common: Rc<CommonData>,
) -> Self {
let panels = cx.create_rw_signal(panels);
@@ -130,12 +161,19 @@ impl PanelData {
right: 250.0,
right_split: 0.5,
});
let sections = cx.create_rw_signal(
sections
.into_iter()
.map(|(key, value)| (key, cx.create_rw_signal(value)))
.collect(),
);
Self {
panels,
styles,
size,
available_size,
sections,
common,
}
}
@@ -145,6 +183,12 @@ impl PanelData {
panels: self.panels.get_untracked(),
styles: self.styles.get_untracked(),
size: self.size.get_untracked(),
sections: self
.sections
.get_untracked()
.into_iter()
.map(|(key, value)| (key, value.get_untracked()))
.collect(),
}
}
@@ -389,6 +433,21 @@ impl PanelData {
let db: Arc<LapceDb> = use_context().unwrap();
db.save_panel_orders(self.panels.get_untracked());
}
pub fn section_open(&self, section: PanelSection) -> RwSignal<bool> {
let open = self
.sections
.with_untracked(|sections| sections.get(&section).cloned());
if let Some(open) = open {
return open;
}
let open = self.common.scope.create_rw_signal(true);
self.sections.update(|sections| {
sections.insert(section, open);
});
open
}
}
pub fn panel_position(
+20 -11
View File
@@ -1,15 +1,17 @@
use std::{rc::Rc, sync::Arc};
use floem::{
cosmic_text::Style as FontStyle,
View,
event::EventListener,
peniko::Color,
reactive::{create_rw_signal, ReadSignal, RwSignal},
reactive::{
ReadSignal, RwSignal, SignalGet, SignalUpdate, SignalWith, create_rw_signal,
},
style::CursorStyle,
view::View,
text::Style as FontStyle,
views::{
container, dyn_stack, label, scroll, stack, svg, text, virtual_stack,
Decorators, VirtualDirection, VirtualItemSize,
Decorators, container, dyn_stack, label, scroll, stack, svg, text,
virtual_stack,
},
};
use lapce_rpc::{
@@ -17,11 +19,11 @@ use lapce_rpc::{
terminal::TermId,
};
use super::{position::PanelPosition, view::PanelBuilder};
use super::{data::PanelSection, position::PanelPosition, view::PanelBuilder};
use crate::{
app::clickable_icon,
command::InternalCommand,
config::{color::LapceColor, icon::LapceIcons, LapceConfig},
config::{LapceConfig, color::LapceColor, icon::LapceIcons},
debug::{DapVariable, RunDebugMode, StackTraceData},
editor::location::{EditorLocation, EditorPosition},
listener::Listener,
@@ -43,18 +45,26 @@ pub fn debug_panel(
"Processes",
150.0,
debug_processes(terminal.clone(), config),
window_tab_data.panel.section_open(PanelSection::Process),
)
.add(
"Variables",
variables_view(window_tab_data.clone()),
window_tab_data.panel.section_open(PanelSection::Variable),
)
.add("Variables", variables_view(window_tab_data.clone()))
.add(
"Stack Frames",
debug_stack_traces(terminal.clone(), internal_command, config),
window_tab_data.panel.section_open(PanelSection::StackFrame),
)
.add_height(
"Breakpoints",
150.0,
breakpoints_view(window_tab_data.clone()),
window_tab_data.panel.section_open(PanelSection::Breakpoint),
)
.build()
.debug_name("Debug Panel")
}
fn debug_process_icons(
@@ -310,7 +320,7 @@ fn debug_processes(
.hover(|s| {
s.cursor(CursorStyle::Pointer).background(
(config.color(LapceColor::PANEL_HOVERED_BACKGROUND))
.with_alpha_factor(0.3),
.multiply_alpha(0.3),
)
})
})
@@ -328,8 +338,6 @@ fn variables_view(window_tab_data: Rc<WindowTabData>) -> impl View {
container(
scroll(
virtual_stack(
VirtualDirection::Vertical,
VirtualItemSize::Fixed(Box::new(move || ui_line_height.get())),
move || {
let dap = terminal.get_active_dap(true);
dap.map(|dap| {
@@ -435,6 +443,7 @@ fn variables_view(window_tab_data: Rc<WindowTabData>) -> impl View {
})
},
)
.item_size_fixed(move || ui_line_height.get())
.style(|s| s.flex_col().min_width_full()),
)
.style(|s| s.absolute().size_full()),
+321
View File
@@ -0,0 +1,321 @@
use std::{ops::AddAssign, path::PathBuf, rc::Rc};
use floem::{
View,
peniko::Color,
reactive::{RwSignal, Scope, SignalGet, SignalUpdate, SignalWith},
style::CursorStyle,
views::{
Decorators, VirtualVector, container, editor::id::Id, label, scroll, stack,
svg, virtual_stack,
},
};
use lsp_types::{DocumentSymbol, SymbolKind};
use super::position::PanelPosition;
use crate::{
command::InternalCommand,
config::{color::LapceColor, icon::LapceIcons},
editor::location::EditorLocation,
window_tab::WindowTabData,
};
#[derive(Clone, Debug)]
pub struct SymbolData {
pub path: PathBuf,
pub file: RwSignal<SymbolInformationItemData>,
}
impl SymbolData {
pub fn new(
items: Vec<RwSignal<SymbolInformationItemData>>,
path: PathBuf,
cx: Scope,
) -> Self {
let name = path
.file_name()
.and_then(|x| x.to_str())
.map(|x| x.to_string())
.unwrap_or_default();
#[allow(deprecated)]
let file_ds = DocumentSymbol {
name: name.clone(),
detail: None,
kind: SymbolKind::FILE,
tags: None,
deprecated: None,
range: Default::default(),
selection_range: Default::default(),
children: None,
};
let file = cx.create_rw_signal(SymbolInformationItemData {
id: Id::next(),
name,
detail: None,
item: file_ds,
open: cx.create_rw_signal(true),
children: items,
});
Self { path, file }
}
fn get_children(
&self,
min: usize,
max: usize,
) -> Vec<(
usize,
usize,
Rc<PathBuf>,
RwSignal<SymbolInformationItemData>,
)> {
let path = Rc::new(self.path.clone());
let level: usize = 0;
let mut next = 0;
get_children(self.file, &mut next, min, max, level, path.clone())
}
}
#[derive(Debug, Clone)]
pub struct SymbolInformationItemData {
pub id: Id,
pub name: String,
pub detail: Option<String>,
pub item: DocumentSymbol,
pub open: RwSignal<bool>,
pub children: Vec<RwSignal<SymbolInformationItemData>>,
}
impl From<(DocumentSymbol, Scope)> for SymbolInformationItemData {
fn from((mut item, cx): (DocumentSymbol, Scope)) -> Self {
let children = if let Some(children) = item.children.take() {
children
.into_iter()
.map(|x| cx.create_rw_signal(Self::from((x, cx))))
.collect()
} else {
Vec::with_capacity(0)
};
Self {
id: Id::next(),
name: item.name.clone(),
detail: item.detail.clone(),
item,
open: cx.create_rw_signal(true),
children,
}
}
}
impl SymbolInformationItemData {
pub fn child_count(&self) -> usize {
let mut count = 1;
if self.open.get() {
for child in &self.children {
count += child.with(|x| x.child_count())
}
}
count
}
}
fn get_children(
data: RwSignal<SymbolInformationItemData>,
next: &mut usize,
min: usize,
max: usize,
level: usize,
path: Rc<PathBuf>,
) -> Vec<(
usize,
usize,
Rc<PathBuf>,
RwSignal<SymbolInformationItemData>,
)> {
let mut children = Vec::new();
if *next >= min && *next < max {
children.push((*next, level, path.clone(), data));
} else if *next >= max {
return children;
}
next.add_assign(1);
if data.get_untracked().open.get() {
for child in data.get().children {
let child_children =
get_children(child, next, min, max, level + 1, path.clone());
children.extend(child_children);
if *next > max {
break;
}
}
}
children
}
pub struct VirtualList {
root: Option<RwSignal<Option<SymbolData>>>,
}
impl VirtualList {
pub fn new(root: Option<RwSignal<Option<SymbolData>>>) -> Self {
Self { root }
}
}
impl
VirtualVector<(
usize,
usize,
Rc<PathBuf>,
RwSignal<SymbolInformationItemData>,
)> for VirtualList
{
fn total_len(&self) -> usize {
if let Some(root) = self.root.as_ref().and_then(|x| x.get()) {
root.file.get_untracked().child_count()
} else {
0
}
}
fn slice(
&mut self,
range: std::ops::Range<usize>,
) -> impl Iterator<
Item = (
usize,
usize,
Rc<PathBuf>,
RwSignal<SymbolInformationItemData>,
),
> {
if let Some(root) = self.root.as_ref().and_then(|x| x.get()) {
let min = range.start;
let max = range.end;
let children = root.get_children(min, max);
children.into_iter()
} else {
Vec::new().into_iter()
}
}
}
pub fn symbol_panel(
window_tab_data: Rc<WindowTabData>,
_position: PanelPosition,
) -> impl View {
let config = window_tab_data.common.config;
let ui_line_height = window_tab_data.common.ui_line_height;
scroll(
virtual_stack(
{
let window_tab_data = window_tab_data.clone();
move || {
let editor = window_tab_data.main_split.get_active_editor();
VirtualList::new(editor.map(|x| x.doc().document_symbol_data))
}
},
move |(_, _, _, item)| item.get_untracked().id,
move |(_, level, path, rw_data)| {
let data = rw_data.get_untracked();
let open = data.open;
let has_child = !data.children.is_empty();
let kind = data.item.kind;
stack((
container(
svg(move || {
let config = config.get();
let svg_str = match open.get() {
true => LapceIcons::ITEM_OPENED,
false => LapceIcons::ITEM_CLOSED,
};
config.ui_svg(svg_str)
})
.style(move |s| {
let config = config.get();
let color = if has_child {
config.color(LapceColor::LAPCE_ICON_ACTIVE)
} else {
Color::TRANSPARENT
};
let size = config.ui.icon_size() as f32;
s.size(size, size)
.color(color)
})
).style(|s| s.padding(4.0).margin_left(6.0).margin_right(2.0))
.on_click_stop({
move |_x| {
if has_child {
open.update(|x| {
*x = !*x;
});
}
}
}),
svg(move || {
let config = config.get();
config
.symbol_svg(&kind)
.unwrap_or_else(|| config.ui_svg(LapceIcons::FILE))
}).style(move |s| {
let config = config.get();
let size = config.ui.icon_size() as f32;
s.min_width(size)
.size(size, size)
.margin_right(5.0)
.color(config.symbol_color(&kind).unwrap_or_else(|| {
config.color(LapceColor::LAPCE_ICON_ACTIVE)
}))
}),
label(move || {
data.name.replace('\n', "")
})
.style(move |s| {
s.selectable(false)
}),
label(move || {
data.detail.clone().unwrap_or_default()
}).style(move |s| s.margin_left(6.0)
.color(config.get().color(LapceColor::EDITOR_DIM))
.selectable(false)
.apply_if(
data.item.detail.clone().is_none(),
|s| s.hide())
),
))
.style(move |s| {
s.padding_right(5.0)
.padding_left((level * 10) as f32)
.items_center()
.height(ui_line_height.get())
.hover(|s| {
s.background(
config
.get()
.color(LapceColor::PANEL_HOVERED_BACKGROUND),
)
.cursor(CursorStyle::Pointer)
})
})
.on_click_stop({
let window_tab_data = window_tab_data.clone();
let data = rw_data;
move |_| {
let data = data.get_untracked();
window_tab_data
.common
.internal_command
.send(InternalCommand::JumpToLocation { location: EditorLocation {
path: path.to_path_buf(),
position: Some(crate::editor::location::EditorPosition::Position(data.item.selection_range.start)),
scroll_offset: None,
ignore_unconfirmed: false,
same_editor_tab: false,
} });
}
})
},
).item_size_fixed(move || ui_line_height.get())
.style(|s| s.flex_col().absolute().min_width_full()),
)
.style(|s| s.absolute().size_full())
}
+10 -18
View File
@@ -1,14 +1,11 @@
use std::{path::PathBuf, rc::Rc, sync::Arc};
use floem::{
View,
event::EventListener,
reactive::ReadSignal,
reactive::{ReadSignal, SignalGet, SignalUpdate},
style::{CursorStyle, Style},
view::View,
views::{
container, label, scroll, stack, svg, virtual_stack, Decorators,
VirtualDirection, VirtualItemSize,
},
views::{Decorators, container, label, scroll, stack, svg, virtual_stack},
};
use lapce_xi_rope::find::CaseMatching;
@@ -16,7 +13,7 @@ use super::{kind::PanelKind, position::PanelPosition};
use crate::{
app::clickable_icon,
command::InternalCommand,
config::{color::LapceColor, icon::LapceIcons, LapceConfig},
config::{LapceConfig, color::LapceColor, icon::LapceIcons},
editor::location::{EditorLocation, EditorPosition},
focus_text::focus_text,
global_search::{GlobalSearchData, SearchMatchData},
@@ -107,6 +104,7 @@ pub fn global_search_panel(
search_result(workspace, global_search, internal_command, config),
))
.style(|s| s.absolute().size_pct(100.0, 100.0).flex_col())
.debug_name("Global Search Panel")
}
fn search_result(
@@ -119,12 +117,6 @@ fn search_result(
container({
scroll({
virtual_stack(
VirtualDirection::Vertical,
VirtualItemSize::Fn(Box::new(
|(_, match_data): &(PathBuf, SearchMatchData)| {
match_data.height()
},
)),
move || global_search_data.clone(),
move |(path, _)| path.to_owned(),
move |(path, match_data)| {
@@ -216,10 +208,6 @@ fn search_result(
})
}),
virtual_stack(
VirtualDirection::Vertical,
VirtualItemSize::Fixed(Box::new(move || {
ui_line_height.get()
})),
move || {
if expanded.get() {
match_data.matches.get()
@@ -304,12 +292,16 @@ fn search_result(
)
},
)
.item_size_fixed(move || ui_line_height.get())
.style(|s| s.flex_col()),
))
.style(|s| s.flex_col())
},
)
.style(|s| s.flex_col().min_width_pct(100.0).line_height(1.6))
.item_size_fn(|(_, match_data): &(PathBuf, SearchMatchData)| {
match_data.height()
})
.style(|s| s.flex_col().min_width_pct(100.0).line_height(1.8))
})
.style(|s| s.absolute().size_pct(100.0, 100.0))
})
+384
View File
@@ -0,0 +1,384 @@
use std::{ops::AddAssign, path::PathBuf, rc::Rc};
use floem::{
IntoView, View, ViewId,
reactive::{RwSignal, Scope, SignalGet, SignalUpdate},
style::CursorStyle,
views::{
Decorators, VirtualVector, container, label, scroll, stack, svg,
virtual_stack,
},
};
use im::HashMap;
use itertools::Itertools;
use lapce_rpc::file_line::FileLine;
use lsp_types::{Location, SymbolKind, request::GotoImplementationResponse};
use super::position::PanelPosition;
use crate::{
command::InternalCommand,
config::{color::LapceColor, icon::LapceIcons},
editor::location::EditorLocation,
window_tab::WindowTabData,
};
pub fn implementation_panel(
window_tab_data: Rc<WindowTabData>,
_position: PanelPosition,
) -> impl View {
common_reference_panel(window_tab_data.clone(), _position, move || {
window_tab_data.main_split.implementations.get()
})
.debug_name("implementation panel")
}
pub fn common_reference_panel(
window_tab_data: Rc<WindowTabData>,
_position: PanelPosition,
each_fn: impl Fn() -> ReferencesRoot + 'static,
) -> impl View {
let config = window_tab_data.common.config;
let ui_line_height = window_tab_data.common.ui_line_height;
scroll(
virtual_stack(
each_fn,
move |(_, _, data)| data.view_id(),
move |(_, level, rw_data)| {
match rw_data {
ReferenceLocation::File { path, open, .. } => stack((
container(
svg(move || {
let config = config.get();
let svg_str = match open.get() {
true => LapceIcons::ITEM_OPENED,
false => LapceIcons::ITEM_CLOSED,
};
config.ui_svg(svg_str)
})
.style(move |s| {
let config = config.get();
let size = config.ui.icon_size() as f32;
s.size(size, size).color(
config.color(LapceColor::LAPCE_ICON_ACTIVE),
)
}),
)
.style(|s| s.padding(4.0).margin_left(6.0).margin_right(2.0))
.on_click_stop({
move |_x| {
open.update(|x| {
*x = !*x;
});
}
}),
svg(move || {
let config = config.get();
config
.symbol_svg(&SymbolKind::FILE)
.unwrap_or_else(|| config.ui_svg(LapceIcons::FILE))
})
.style(move |s| {
let config = config.get();
let size = config.ui.icon_size() as f32;
s.min_width(size)
.size(size, size)
.margin_right(5.0)
.color(
config
.symbol_color(&SymbolKind::FILE)
.unwrap_or_else(|| {
config
.color(LapceColor::LAPCE_ICON_ACTIVE)
}),
)
}),
label(move || format!("{:?}", path))
.style(move |s| {
s.margin_left(6.0).color(
config.get().color(LapceColor::EDITOR_DIM),
)
})
.into_any(),
))
.style(move |s| {
s.padding_right(5.0)
.height(ui_line_height.get())
.padding_left((level * 10) as f32)
.items_center()
.hover(|s| {
s.background(
config
.get()
.color(LapceColor::PANEL_HOVERED_BACKGROUND),
)
.cursor(CursorStyle::Pointer)
})
}),
ReferenceLocation::Line { file_line, .. } => stack((container(
label(move || format!("{} {}", file_line.position.line + 1, file_line.content))
.style(move |s| {
s.margin_left(6.0).color(
config.get().color(LapceColor::EDITOR_DIM),
)
})
.into_any(),
)
.style(move |s| {
s.padding_right(5.0)
.height(ui_line_height.get())
.padding_left((level * 20) as f32)
.items_center()
.hover(|s| {
s.background(
config
.get()
.color(LapceColor::PANEL_HOVERED_BACKGROUND),
)
.cursor(CursorStyle::Pointer)
})
}),))
.on_click_stop({
let window_tab_data = window_tab_data.clone();
let position = file_line.position;
move |_| {
window_tab_data.common.internal_command.send(
InternalCommand::JumpToLocation {
location: EditorLocation {
path: file_line.path.clone(),
position: Some(
crate::editor::location::EditorPosition::Position(
position,
),
),
scroll_offset: None,
ignore_unconfirmed: false,
same_editor_tab: false,
},
},
);
}
}),
}
.style(move |s| {
s.padding_right(5.0)
.height(ui_line_height.get())
.padding_left((level * 10) as f32)
.items_center()
.hover(|s| {
s.background(
config
.get()
.color(LapceColor::PANEL_HOVERED_BACKGROUND),
)
.cursor(CursorStyle::Pointer)
})
})
},
).item_size_fixed(move || ui_line_height.get())
.style(|s| s.flex_col().absolute().min_width_full()),
)
.style(|s| s.absolute().size_full())
}
pub fn map_to_location(resp: Option<GotoImplementationResponse>) -> Vec<Location> {
let Some(resp) = resp else {
return Vec::new();
};
match resp {
GotoImplementationResponse::Scalar(local) => {
vec![local]
}
GotoImplementationResponse::Array(items) => items,
GotoImplementationResponse::Link(items) => items
.into_iter()
.map(|x| Location {
uri: x.target_uri,
range: x.target_range,
})
.collect(),
}
}
pub fn init_implementation_root(
items: Vec<FileLine>,
scope: Scope,
) -> ReferencesRoot {
let mut refs_map: HashMap<PathBuf, HashMap<u32, Reference>> = HashMap::new();
for item in items {
let entry = refs_map.entry(item.path.clone()).or_default();
(*entry).insert(
item.position.line,
Reference::Line {
location: ReferenceLocation::Line {
file_line: item,
view_id: ViewId::new(),
},
},
);
}
let mut refs = Vec::new();
for (path, items) in refs_map {
let open = scope.create_rw_signal(true);
let children = items
.into_iter()
.sorted_by(|x, y| x.0.cmp(&y.0))
.map(|x| x.1)
.collect();
let ref_item = Reference::File {
location: ReferenceLocation::File {
open,
path,
view_id: ViewId::new(),
},
children,
open,
};
refs.push(ref_item);
}
ReferencesRoot { children: refs }
}
#[derive(Clone, Default)]
pub struct ReferencesRoot {
pub(crate) children: Vec<Reference>,
}
impl ReferencesRoot {
pub fn total(&self) -> usize {
let mut total = 0;
for child in &self.children {
total += child.total_len()
}
total
}
fn get_children(
&self,
next: &mut usize,
min: usize,
max: usize,
level: usize,
) -> Vec<(usize, usize, ReferenceLocation)> {
let mut children = Vec::new();
for child in &self.children {
let child_children = child.get_children(next, min, max, level + 1);
if !child_children.is_empty() {
children.extend(child_children);
}
if *next > max {
break;
}
}
children
}
}
impl VirtualVector<(usize, usize, ReferenceLocation)> for ReferencesRoot {
fn total_len(&self) -> usize {
self.total()
}
fn slice(
&mut self,
range: std::ops::Range<usize>,
) -> impl Iterator<Item = (usize, usize, ReferenceLocation)> {
let min = range.start;
let max = range.end;
let children = self.get_children(&mut 0, min, max, 0);
children.into_iter()
}
}
#[derive(Clone)]
pub enum Reference {
File {
location: ReferenceLocation,
open: RwSignal<bool>,
children: Vec<Reference>,
},
Line {
location: ReferenceLocation,
},
}
#[derive(Clone)]
pub enum ReferenceLocation {
File {
path: PathBuf,
open: RwSignal<bool>,
view_id: ViewId,
},
Line {
file_line: FileLine,
view_id: ViewId,
},
}
impl ReferenceLocation {
pub fn view_id(&self) -> ViewId {
match self {
ReferenceLocation::File { view_id, .. } => *view_id,
ReferenceLocation::Line { view_id, .. } => *view_id,
}
}
}
impl Reference {
pub fn location(&self) -> ReferenceLocation {
match self {
Reference::File { location, .. } => location.clone(),
Reference::Line { location } => location.clone(),
}
}
pub fn total_len(&self) -> usize {
match self {
Reference::File { children, .. } => {
let mut total = 1;
for child in children {
total += child.total_len()
}
total
}
Reference::Line { .. } => 1,
}
}
pub fn children(&self) -> Option<&Vec<Reference>> {
match self {
Reference::File { children, open, .. } => {
if open.get() {
return Some(children);
}
None
}
Reference::Line { .. } => None,
}
}
fn get_children(
&self,
next: &mut usize,
min: usize,
max: usize,
level: usize,
) -> Vec<(usize, usize, ReferenceLocation)> {
let mut children = Vec::new();
if *next >= min && *next < max {
children.push((*next, level, self.location()));
} else if *next >= max {
return children;
}
next.add_assign(1);
if let Some(children_tmp) = self.children() {
for child in children_tmp {
let child_children = child.get_children(next, min, max, level + 1);
if !child_children.is_empty() {
children.extend(child_children);
}
if *next > max {
break;
}
}
}
children
}
}
+24
View File
@@ -15,6 +15,10 @@ pub enum PanelKind {
Search,
Problem,
Debug,
CallHierarchy,
DocumentSymbol,
References,
Implementation,
}
impl PanelKind {
@@ -27,6 +31,10 @@ impl PanelKind {
PanelKind::Search => LapceIcons::SEARCH,
PanelKind::Problem => LapceIcons::PROBLEM,
PanelKind::Debug => LapceIcons::DEBUG,
PanelKind::CallHierarchy => LapceIcons::TYPE_HIERARCHY,
PanelKind::DocumentSymbol => LapceIcons::DOCUMENT_SYMBOL,
PanelKind::References => LapceIcons::REFERENCES,
PanelKind::Implementation => LapceIcons::IMPLEMENTATION,
}
}
@@ -39,4 +47,20 @@ impl PanelKind {
}
None
}
pub fn default_position(&self) -> PanelPosition {
match self {
PanelKind::Terminal => PanelPosition::BottomLeft,
PanelKind::FileExplorer => PanelPosition::LeftTop,
PanelKind::SourceControl => PanelPosition::LeftTop,
PanelKind::Plugin => PanelPosition::LeftTop,
PanelKind::Search => PanelPosition::BottomLeft,
PanelKind::Problem => PanelPosition::BottomLeft,
PanelKind::Debug => PanelPosition::LeftTop,
PanelKind::CallHierarchy => PanelPosition::BottomLeft,
PanelKind::DocumentSymbol => PanelPosition::RightTop,
PanelKind::References => PanelPosition::BottomLeft,
PanelKind::Implementation => PanelPosition::BottomLeft,
}
}
}
+4
View File
@@ -1,10 +1,14 @@
pub mod call_hierarchy_view;
pub mod data;
pub mod debug_view;
pub mod document_symbol;
pub mod global_search_view;
pub mod implementation_view;
pub mod kind;
pub mod plugin_view;
pub mod position;
pub mod problem_view;
pub mod references_view;
pub mod source_control_view;
pub mod style;
pub mod terminal_view;

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