diff --git a/CHANGELOG.md b/CHANGELOG.md index e31fa27cad..0a4a3c8306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ ## Unreleased +### 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. + +### 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 libary support by downloading from https://github.com/lapce/tree-sitter-grammars diff --git a/Cargo.lock b/Cargo.lock index f08869645e..3c98b00bd9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1684,7 +1684,7 @@ checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" [[package]] name = "floem" version = "0.1.1" -source = "git+https://github.com/lapce/floem?rev=54f0d1bcf0e1a91d82492ee7300a526adb60eb5c#54f0d1bcf0e1a91d82492ee7300a526adb60eb5c" +source = "git+https://github.com/lapce/floem?rev=3a974c9bbc0c1dfefc17affb976ea8e9ae31369b#3a974c9bbc0c1dfefc17affb976ea8e9ae31369b" dependencies = [ "bitflags 2.6.0", "copypasta", @@ -1702,7 +1702,6 @@ dependencies = [ "image", "indexmap", "lapce-xi-rope", - "once_cell", "parking_lot", "peniko", "raw-window-handle 0.6.0", @@ -1722,13 +1721,12 @@ dependencies = [ [[package]] name = "floem-editor-core" version = "0.1.1" -source = "git+https://github.com/lapce/floem?rev=54f0d1bcf0e1a91d82492ee7300a526adb60eb5c#54f0d1bcf0e1a91d82492ee7300a526adb60eb5c" +source = "git+https://github.com/lapce/floem?rev=3a974c9bbc0c1dfefc17affb976ea8e9ae31369b#3a974c9bbc0c1dfefc17affb976ea8e9ae31369b" dependencies = [ "bitflags 2.6.0", "itertools 0.12.1", "lapce-xi-rope", "memchr", - "once_cell", "serde", "strum", "strum_macros", @@ -1800,7 +1798,7 @@ dependencies = [ [[package]] name = "floem_reactive" version = "0.1.1" -source = "git+https://github.com/lapce/floem?rev=54f0d1bcf0e1a91d82492ee7300a526adb60eb5c#54f0d1bcf0e1a91d82492ee7300a526adb60eb5c" +source = "git+https://github.com/lapce/floem?rev=3a974c9bbc0c1dfefc17affb976ea8e9ae31369b#3a974c9bbc0c1dfefc17affb976ea8e9ae31369b" dependencies = [ "smallvec", ] @@ -1808,7 +1806,7 @@ dependencies = [ [[package]] name = "floem_renderer" version = "0.1.1" -source = "git+https://github.com/lapce/floem?rev=54f0d1bcf0e1a91d82492ee7300a526adb60eb5c#54f0d1bcf0e1a91d82492ee7300a526adb60eb5c" +source = "git+https://github.com/lapce/floem?rev=3a974c9bbc0c1dfefc17affb976ea8e9ae31369b#3a974c9bbc0c1dfefc17affb976ea8e9ae31369b" dependencies = [ "cosmic-text", "image", @@ -1821,7 +1819,7 @@ dependencies = [ [[package]] name = "floem_tiny_skia_renderer" version = "0.1.1" -source = "git+https://github.com/lapce/floem?rev=54f0d1bcf0e1a91d82492ee7300a526adb60eb5c#54f0d1bcf0e1a91d82492ee7300a526adb60eb5c" +source = "git+https://github.com/lapce/floem?rev=3a974c9bbc0c1dfefc17affb976ea8e9ae31369b#3a974c9bbc0c1dfefc17affb976ea8e9ae31369b" dependencies = [ "anyhow", "bytemuck", @@ -1838,7 +1836,7 @@ dependencies = [ [[package]] name = "floem_vger_renderer" version = "0.1.1" -source = "git+https://github.com/lapce/floem?rev=54f0d1bcf0e1a91d82492ee7300a526adb60eb5c#54f0d1bcf0e1a91d82492ee7300a526adb60eb5c" +source = "git+https://github.com/lapce/floem?rev=3a974c9bbc0c1dfefc17affb976ea8e9ae31369b#3a974c9bbc0c1dfefc17affb976ea8e9ae31369b" dependencies = [ "anyhow", "floem-vger", @@ -2906,7 +2904,7 @@ dependencies = [ [[package]] name = "lapce" -version = "0.4.0" +version = "0.4.1" dependencies = [ "lapce-app", "lapce-proxy", @@ -2914,7 +2912,7 @@ dependencies = [ [[package]] name = "lapce-app" -version = "0.4.0" +version = "0.4.1" dependencies = [ "Inflector", "alacritty_terminal", @@ -2971,14 +2969,14 @@ dependencies = [ "tracing-subscriber", "unicode-width", "url", - "windows-sys 0.52.0", + "windows-sys 0.36.1", "zip", "zstd", ] [[package]] name = "lapce-core" -version = "0.4.0" +version = "0.4.1" dependencies = [ "ahash", "anyhow", @@ -3006,7 +3004,7 @@ dependencies = [ [[package]] name = "lapce-proxy" -version = "0.4.0" +version = "0.4.1" dependencies = [ "alacritty_terminal", "anyhow", @@ -3055,7 +3053,7 @@ dependencies = [ [[package]] name = "lapce-rpc" -version = "0.4.0" +version = "0.4.1" dependencies = [ "anyhow", "crossbeam-channel", @@ -4510,9 +4508,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.122" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "itoa", "memchr", diff --git a/Cargo.toml b/Cargo.toml index 658c97f93d..28ddba1ae6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ path = "lapce-proxy/src/bin/lapce-proxy.rs" members = ["lapce-app", "lapce-proxy", "lapce-rpc", "lapce-core"] [workspace.package] -version = "0.4.0" +version = "0.4.1" edition = "2021" rust-version = "1.77.0" license = "Apache-2.0" @@ -76,9 +76,9 @@ lapce-core = { path = "./lapce-core" } lapce-rpc = { path = "./lapce-rpc" } lapce-proxy = { path = "./lapce-proxy" } -floem = { git = "https://github.com/lapce/floem", rev = "54f0d1bcf0e1a91d82492ee7300a526adb60eb5c", features = ["editor", "serde", "default-image-formats", "rfd-async-std"] } +floem = { git = "https://github.com/lapce/floem", rev = "3a974c9bbc0c1dfefc17affb976ea8e9ae31369b", features = ["editor", "serde", "default-image-formats", "rfd-async-std"] } # floem = { path = "../floem", features = ["editor", "serde", "default-image-formats", "rfd-async-std"] } -floem-editor-core = { git = "https://github.com/lapce/floem", rev = "54f0d1bcf0e1a91d82492ee7300a526adb60eb5c", features = ["serde"] } +floem-editor-core = { git = "https://github.com/lapce/floem", rev = "3a974c9bbc0c1dfefc17affb976ea8e9ae31369b", features = ["serde"] } # floem-editor-core = { path = "../floem/editor-core/", features = ["serde"] } [patch.crates-io] diff --git a/defaults/dark-theme.toml b/defaults/dark-theme.toml index 478d501a64..8d3c6ce0c4 100644 --- a/defaults/dark-theme.toml +++ b/defaults/dark-theme.toml @@ -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" diff --git a/defaults/icon-theme.toml b/defaults/icon-theme.toml index 8bda5b1de8..4a818c9bee 100644 --- a/defaults/icon-theme.toml +++ b/defaults/icon-theme.toml @@ -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" @@ -103,6 +104,7 @@ name = "Lapce Codicons" "search.replace" = "replace.svg" "search.replace_all" = "replace-all.svg" +"document_symbol" = "symbol-class.svg" "symbol_kind.array" = "symbol-array.svg" "symbol_kind.boolean" = "symbol-boolean.svg" "symbol_kind.class" = "symbol-class.svg" diff --git a/defaults/light-theme.toml b/defaults/light-theme.toml index 5cef4861b2..570e6f75b2 100644 --- a/defaults/light-theme.toml +++ b/defaults/light-theme.toml @@ -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" diff --git a/extra/linux/dev.lapce.lapce.metainfo.xml b/extra/linux/dev.lapce.lapce.metainfo.xml index 99d322aff3..3c6ad2db59 100644 --- a/extra/linux/dev.lapce.lapce.metainfo.xml +++ b/extra/linux/dev.lapce.lapce.metainfo.xml @@ -30,6 +30,6 @@ - + diff --git a/extra/macos/Lapce.app/Contents/Info.plist b/extra/macos/Lapce.app/Contents/Info.plist index 2f6f78e208..5896e12833 100644 --- a/extra/macos/Lapce.app/Contents/Info.plist +++ b/extra/macos/Lapce.app/Contents/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.4.0 + 0.4.1 CFBundleSupportedPlatforms MacOSX diff --git a/extra/windows/wix/lapce.wxs b/extra/windows/wix/lapce.wxs index b2d0e4c600..305f835e22 100644 --- a/extra/windows/wix/lapce.wxs +++ b/extra/windows/wix/lapce.wxs @@ -1,6 +1,6 @@ - + diff --git a/icons/codicons/type-hierarchy.svg b/icons/codicons/type-hierarchy.svg new file mode 100644 index 0000000000..bcbc902e40 --- /dev/null +++ b/icons/codicons/type-hierarchy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lapce-app/src/app.rs b/lapce-app/src/app.rs index 36a04eb11d..b9e6a2a7d6 100644 --- a/lapce-app/src/app.rs +++ b/lapce-app/src/app.rs @@ -2261,10 +2261,11 @@ fn palette_item( .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.color(LapceColor::LAPCE_ICON_ACTIVE)) + 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) + }), + ) }), focus_text( move || text.clone(), @@ -2795,10 +2796,10 @@ fn window_message_view( }), stack(( text(title.clone()).style(|s| { - s.min_width(0.0).line_height(1.6).font_weight(Weight::BOLD) + s.min_width(0.0).line_height(1.8).font_weight(Weight::BOLD) }), text(message.message.clone()).style(|s| { - s.min_width(0.0).line_height(1.6).margin_top(5.0) + s.min_width(0.0).line_height(1.8).margin_top(5.0) }), )) .style(move |s| { @@ -2818,6 +2819,7 @@ fn window_message_view( ) .style(|s| s.margin_left(6.0)), )) + .on_event_stop(EventListener::PointerDown, |_| {}) .style(move |s| { let config = config.get(); s.width_full() @@ -2846,7 +2848,11 @@ fn window_message_view( .style(|s| s.flex_col().width_full()), ) .style(|s| { - s.absolute().width_full().min_height(0.0).max_height_full() + s.absolute() + .width_full() + .min_height(0.0) + .max_height_full() + .set(PropagatePointerWheel, false) }), ) .style(|s| s.size_full()), @@ -2930,6 +2936,7 @@ fn hover(window_tab_data: Rc) -> impl View { layout_rect.set(rect); }) .on_event_stop(EventListener::PointerMove, |_| {}) + .on_event_stop(EventListener::PointerDown, |_| {}) .style(move |s| { let active = window_tab_data.common.hover.active.get(); if !active { diff --git a/lapce-app/src/command.rs b/lapce-app/src/command.rs index 1ea715861c..b4bafd768c 100644 --- a/lapce-app/src/command.rs +++ b/lapce-app/src/command.rs @@ -187,9 +187,23 @@ pub enum LapceWorkbenchCommand { #[strum(message = "Show Call Hierarchy")] ShowCallHierarchy, - #[strum(serialize = "reveal_in_file_tree")] - #[strum(message = "Reveal in File Tree")] - RevealInFileTree, + #[strum(serialize = "reveal_in_panel")] + #[strum(message = "Reveal in Panel")] + RevealInPanel, + + #[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")] @@ -562,6 +576,10 @@ pub enum LapceWorkbenchCommand { #[strum(serialize = "quit")] #[strum(message = "Quit Editor")] Quit, + + #[strum(serialize = "go_to_location")] + #[strum(message = "Go to Location")] + GoToLocation, } #[derive(Clone, Debug)] diff --git a/lapce-app/src/config.rs b/lapce-app/src/config.rs index 4258f636a0..7c70e30340 100644 --- a/lapce-app/src/config.rs +++ b/lapce-app/src/config.rs @@ -655,6 +655,36 @@ impl LapceConfig { Some(self.ui_svg(kind_str)) } + pub fn symbol_color(&self, kind: &SymbolKind) -> Option { + 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() } diff --git a/lapce-app/src/config/icon.rs b/lapce-app/src/config/icon.rs index a53812e88b..61a17bd421 100644 --- a/lapce-app/src/config/icon.rs +++ b/lapce-app/src/config/icon.rs @@ -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,7 @@ 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 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"; diff --git a/lapce-app/src/doc.rs b/lapce-app/src/doc.rs index cd64958b1c..b733036af9 100644 --- a/lapce-app/src/doc.rs +++ b/lapce-app/src/doc.rs @@ -64,8 +64,8 @@ use lapce_xi_rope::{ Interval, Rope, RopeDelta, Transformer, }; use lsp_types::{ - CodeActionOrCommand, CodeLens, Diagnostic, DiagnosticSeverity, InlayHint, - InlayHintLabel, + CodeActionOrCommand, CodeLens, Diagnostic, DiagnosticSeverity, + DocumentSymbolResponse, InlayHint, InlayHintLabel, }; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; @@ -78,7 +78,10 @@ use crate::{ history::DocumentHistory, keypress::KeyPressFocus, main_split::Editors, - panel::kind::PanelKind, + panel::{ + document_symbol::{SymbolData, SymbolInformationItemData}, + kind::PanelKind, + }, window_tab::{CommonData, Focus}, workspace::LapceWorkspace, }; @@ -205,6 +208,8 @@ pub struct Doc { editors: Editors, pub common: Rc, + + pub document_symbol_data: RwSignal>, } impl Doc { pub fn new( @@ -249,6 +254,7 @@ impl Doc { editors, common, code_lens: cx.create_rw_signal(im::HashMap::new()), + document_symbol_data: cx.create_rw_signal(None), } } @@ -298,6 +304,7 @@ impl Doc { editors, common, code_lens: cx.create_rw_signal(im::HashMap::new()), + document_symbol_data: cx.create_rw_signal(None), } } @@ -347,6 +354,7 @@ impl Doc { editors, common, code_lens: cx.create_rw_signal(im::HashMap::new()), + document_symbol_data: cx.create_rw_signal(None), } } @@ -640,6 +648,7 @@ impl Doc { self.clear_code_actions(); self.clear_style_cache(); self.get_code_lens(); + self.get_document_symbol(); }); } @@ -880,6 +889,9 @@ impl Doc { }; 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(|| { @@ -904,6 +916,46 @@ impl Doc { } } + 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> = + 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 { items, path }); + 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) { if !self.loaded() { diff --git a/lapce-app/src/editor.rs b/lapce-app/src/editor.rs index bf7655523e..76d68fab04 100644 --- a/lapce-app/src/editor.rs +++ b/lapce-app/src/editor.rs @@ -51,6 +51,7 @@ use lsp_types::{ HoverContents, InlayHint, InlayHintLabel, InlineCompletionTriggerKind, Location, MarkedString, MarkupKind, Range, TextEdit, }; +use nucleo::Utf32Str; use serde::{Deserialize, Serialize}; use self::{ @@ -187,6 +188,13 @@ impl EditorViewKind { } } +#[derive(Clone)] +pub struct OnScreenFind { + pub active: bool, + pub pattern: String, + pub regions: Vec, +} + pub type SnippetIndex = Vec<(usize, (usize, usize))>; /// Shares data between cloned instances as long as the signals aren't swapped out. @@ -198,6 +206,7 @@ pub struct EditorData { pub confirmed: RwSignal, pub snippet: RwSignal>, pub inline_find: RwSignal>, + pub on_screen_find: RwSignal, pub last_inline_find: RwSignal>, pub find_focus: RwSignal, pub editor: Rc, @@ -231,6 +240,11 @@ impl EditorData { confirmed, snippet: cx.create_rw_signal(None), inline_find: cx.create_rw_signal(None), + on_screen_find: cx.create_rw_signal(OnScreenFind { + active: false, + pattern: "".to_string(), + regions: Vec::new(), + }), last_inline_find: cx.create_rw_signal(None), find_focus: cx.create_rw_signal(false), editor: Rc::new(editor), @@ -435,6 +449,7 @@ impl EditorData { // Cancel so that there's no flickering self.cancel_inline_completion(); self.update_inline_completion(InlineCompletionTriggerKind::Automatic); + self.quit_on_screen_find(); } else if show_inline_completion(cmd) { self.update_inline_completion(InlineCompletionTriggerKind::Automatic); } else { @@ -444,6 +459,7 @@ impl EditorData { self.apply_deltas(&deltas); if let EditCommand::NormalMode = cmd { self.snippet.set(None); + self.quit_on_screen_find(); } CommandExecuted::Yes @@ -1028,6 +1044,13 @@ impl EditorData { FocusCommand::InlineFindRight => { self.inline_find.set(Some(InlineFindDirection::Right)); } + FocusCommand::OnScreenFind => { + self.on_screen_find.update(|find| { + find.active = true; + find.pattern.clear(); + find.regions.clear(); + }); + } FocusCommand::RepeatLastInlineFind => { if let Some((direction, c)) = self.last_inline_find.get_untracked() { self.inline_find(direction, &c); @@ -1130,6 +1153,72 @@ impl EditorData { } } + fn quit_on_screen_find(&self) { + if self.on_screen_find.with_untracked(|s| s.active) { + self.on_screen_find.update(|f| { + f.active = false; + f.pattern.clear(); + f.regions.clear(); + }) + } + } + + fn on_screen_find(&self, pattern: &str) -> Vec { + let screen_lines = self.screen_lines().get_untracked(); + let lines: HashSet = + screen_lines.lines.iter().map(|l| l.line).collect(); + + let mut matcher = nucleo::Matcher::new(nucleo::Config::DEFAULT); + let pattern = nucleo::pattern::Pattern::parse( + pattern, + nucleo::pattern::CaseMatching::Ignore, + nucleo::pattern::Normalization::Smart, + ); + let mut indices = Vec::new(); + let mut filter_text_buf = Vec::new(); + let mut items = Vec::new(); + + let buffer = self.doc().buffer; + + for line in lines { + filter_text_buf.clear(); + indices.clear(); + + buffer.with_untracked(|buffer| { + let start = buffer.offset_of_line(line); + let end = buffer.offset_of_line(line + 1); + let text = buffer.text().slice_to_cow(start..end); + let filter_text = Utf32Str::new(&text, &mut filter_text_buf); + + if let Some(score) = + pattern.indices(filter_text, &mut matcher, &mut indices) + { + indices.sort(); + let left = + start + indices.first().copied().unwrap_or(0) as usize; + let right = + start + indices.last().copied().unwrap_or(0) as usize + 1; + let right = if right == left { left + 1 } else { right }; + items.push((score, left, right)); + } + }); + } + + items.sort_by_key(|(score, _, _)| -(*score as i64)); + if let Some((_, offset, _)) = items.first().copied() { + self.run_move_command( + &lapce_core::movement::Movement::Offset(offset), + None, + Modifiers::empty(), + ); + } + + items + .into_iter() + .map(|(_, start, end)| SelRegion::new(start, end, None)) + .collect() + } + fn go_to_definition(&self) { let doc = self.doc(); let path = match if doc.loaded() { @@ -2640,23 +2729,25 @@ impl EditorData { let is_file = doc.content.with_untracked(|content| content.is_file()); let mut menu = Menu::new(""); - let cmds = if is_file { + let mut cmds = if is_file { vec![ Some(CommandKind::Focus(FocusCommand::GotoDefinition)), Some(CommandKind::Focus(FocusCommand::GotoTypeDefinition)), Some(CommandKind::Workbench( LapceWorkbenchCommand::ShowCallHierarchy, )), - None, Some(CommandKind::Focus(FocusCommand::Rename)), + Some(CommandKind::Workbench(LapceWorkbenchCommand::RunInTerminal)), + None, + Some(CommandKind::Workbench(LapceWorkbenchCommand::RevealInPanel)), + Some(CommandKind::Workbench( + LapceWorkbenchCommand::RevealInFileExplorer, + )), None, Some(CommandKind::Edit(EditCommand::ClipboardCut)), Some(CommandKind::Edit(EditCommand::ClipboardCopy)), Some(CommandKind::Edit(EditCommand::ClipboardPaste)), None, - Some(CommandKind::Workbench( - LapceWorkbenchCommand::RevealInFileTree, - )), Some(CommandKind::Workbench( LapceWorkbenchCommand::PaletteCommand, )), @@ -2672,6 +2763,11 @@ impl EditorData { )), ] }; + if self.diff_editor_id.get_untracked().is_some() && is_file { + cmds.push(Some(CommandKind::Workbench( + LapceWorkbenchCommand::GoToLocation, + ))); + } let lapce_command = self.common.lapce_command; for cmd in cmds { if let Some(cmd) = cmd { @@ -2946,6 +3042,9 @@ impl KeyPressFocus for EditorData { Condition::ListFocus => self.has_completions(), Condition::CompletionFocus => self.has_completions(), Condition::InlineCompletionVisible => self.has_inline_completions(), + Condition::OnScreenFindActive => { + self.on_screen_find.with_untracked(|f| f.active) + } Condition::InSnippet => self.snippet.with_untracked(|s| s.is_some()), Condition::EditorFocus => self .doc() @@ -3052,6 +3151,7 @@ impl KeyPressFocus for EditorData { false } else { self.inline_find.with_untracked(|f| f.is_some()) + || self.on_screen_find.with_untracked(|f| f.active) } } @@ -3097,6 +3197,12 @@ impl KeyPressFocus for EditorData { self.inline_find(direction.clone(), c); self.last_inline_find.set(Some((direction, c.to_string()))); self.inline_find.set(None); + } else if self.on_screen_find.with_untracked(|f| f.active) { + self.on_screen_find.update(|find| { + let pattern = format!("{}{c}", find.pattern); + find.regions = self.on_screen_find(&pattern); + find.pattern = pattern; + }); } } } @@ -3292,7 +3398,7 @@ pub(crate) fn compute_screen_lines( let is_right = diff_info.is_right; let line_y = |info: VLineInfo<()>, vline_y: usize| -> usize { - vline_y - info.rvline.line_index * line_height + vline_y.saturating_sub(info.rvline.line_index * line_height) }; while let Some(change) = changes.next() { @@ -3501,10 +3607,10 @@ fn parse_hover_resp( ) -> Vec { match hover.contents { HoverContents::Scalar(text) => match text { - MarkedString::String(text) => parse_markdown(&text, 1.5, config), + MarkedString::String(text) => parse_markdown(&text, 1.8, config), MarkedString::LanguageString(code) => parse_markdown( &format!("```{}\n{}\n```", code.language, code.value), - 1.5, + 1.8, config, ), }, @@ -3519,8 +3625,8 @@ fn parse_hover_resp( }) .unwrap_or_default(), HoverContents::Markup(content) => match content.kind { - MarkupKind::PlainText => from_plaintext(&content.value, 1.5, config), - MarkupKind::Markdown => parse_markdown(&content.value, 1.5, config), + MarkupKind::PlainText => from_plaintext(&content.value, 1.8, config), + MarkupKind::Markdown => parse_markdown(&content.value, 1.8, config), }, } } diff --git a/lapce-app/src/editor/view.rs b/lapce-app/src/editor/view.rs index df80fc1478..0f249822a2 100644 --- a/lapce-app/src/editor/view.rs +++ b/lapce-app/src/editor/view.rs @@ -39,6 +39,7 @@ use itertools::Itertools; use lapce_core::{ buffer::{diff::DiffLines, rope_text::RopeText, Buffer}, cursor::{CursorAffinity, CursorMode}, + selection::SelRegion, }; use lapce_rpc::{ dap_types::{DapId, SourceBreakpoint}, @@ -477,8 +478,8 @@ impl EditorView { } fn paint_find(&self, cx: &mut PaintCx, screen_lines: &ScreenLines) { - let visual = self.editor.common.find.visual; - if !visual.get_untracked() { + let find_visual = self.editor.common.find.visual.get_untracked(); + if !find_visual && self.editor.on_screen_find.with_untracked(|f| !f.active) { return; } if screen_lines.lines.is_empty() { @@ -499,78 +500,108 @@ impl EditorView { let config = config.get_untracked(); let line_height = config.editor.line_height() as f64; + let color = config.color(LapceColor::EDITOR_FOREGROUND); - doc.update_find(); let start = ed.offset_of_line(min_line); let end = ed.offset_of_line(max_line + 1); // TODO: The selection rect creation logic for find is quite similar to the version // within insert cursor. It would be good to deduplicate it. - let mut rects = Vec::new(); - for region in occurrences.with_untracked(|selection| { - selection.regions_in_range(start, end).to_vec() - }) { - let start = region.min(); - let end = region.max(); - - // TODO(minor): the proper affinity here should probably be tracked by selregion - let (start_rvline, start_col) = - ed.rvline_col_of_offset(start, CursorAffinity::Forward); - let (end_rvline, end_col) = - ed.rvline_col_of_offset(end, CursorAffinity::Backward); - - for line_info in screen_lines.iter_line_info() { - let rvline_info = line_info.vline_info; - let rvline = rvline_info.rvline; - let line = rvline.line; - - if rvline < start_rvline { - continue; - } + if find_visual { + doc.update_find(); + for region in occurrences.with_untracked(|selection| { + selection.regions_in_range(start, end).to_vec() + }) { + self.paint_find_region( + cx, + ed, + ®ion, + color, + screen_lines, + line_height, + ); + } + } - if rvline > end_rvline { - break; + self.editor.on_screen_find.with_untracked(|find| { + if find.active { + for region in &find.regions { + self.paint_find_region( + cx, + ed, + region, + color, + screen_lines, + line_height, + ); } + } + }); + } - let left_col = if rvline == start_rvline { start_col } else { 0 }; - let (right_col, _vline_end) = if rvline == end_rvline { - let max_col = ed.last_col(rvline_info, true); - (end_col.min(max_col), false) - } else { - (ed.last_col(rvline_info, true), true) - }; + fn paint_find_region( + &self, + cx: &mut PaintCx, + ed: &Editor, + region: &SelRegion, + color: Color, + screen_lines: &ScreenLines, + line_height: f64, + ) { + let start = region.min(); + let end = region.max(); - // TODO(minor): sel region should have the affinity of the start/end - let x0 = ed - .line_point_of_line_col( - line, - left_col, - CursorAffinity::Forward, - true, - ) - .x; - let x1 = ed - .line_point_of_line_col( - line, - right_col, - CursorAffinity::Backward, - true, - ) - .x; + // TODO(minor): the proper affinity here should probably be tracked by selregion + let (start_rvline, start_col) = + ed.rvline_col_of_offset(start, CursorAffinity::Forward); + let (end_rvline, end_col) = + ed.rvline_col_of_offset(end, CursorAffinity::Backward); - if !rvline_info.is_empty() && start != end && left_col != right_col { - rects.push( - Size::new(x1 - x0, line_height) - .to_rect() - .with_origin(Point::new(x0, line_info.vline_y)), - ); - } + for line_info in screen_lines.iter_line_info() { + let rvline_info = line_info.vline_info; + let rvline = rvline_info.rvline; + let line = rvline.line; + + if rvline < start_rvline { + continue; } - } - let color = config.color(LapceColor::EDITOR_FOREGROUND); - for rect in rects { - cx.stroke(&rect, color, 1.0); + if rvline > end_rvline { + break; + } + + let left_col = if rvline == start_rvline { start_col } else { 0 }; + let (right_col, _vline_end) = if rvline == end_rvline { + let max_col = ed.last_col(rvline_info, true); + (end_col.min(max_col), false) + } else { + (ed.last_col(rvline_info, true), true) + }; + + // TODO(minor): sel region should have the affinity of the start/end + let x0 = ed + .line_point_of_line_col( + line, + left_col, + CursorAffinity::Forward, + true, + ) + .x; + let x1 = ed + .line_point_of_line_col( + line, + right_col, + CursorAffinity::Backward, + true, + ) + .x; + + if !rvline_info.is_empty() && start != end && left_col != right_col { + let rect = Size::new(x1 - x0, line_height) + .to_rect() + .with_origin(Point::new(x0, line_info.vline_y)); + cx.stroke(&rect, color, 1.0); + } } } diff --git a/lapce-app/src/file_explorer/data.rs b/lapce-app/src/file_explorer/data.rs index 0ea95b8930..44a98a688d 100644 --- a/lapce-app/src/file_explorer/data.rs +++ b/lapce-app/src/file_explorer/data.rs @@ -22,7 +22,10 @@ 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, }; @@ -51,6 +54,7 @@ pub struct FileExplorerData { pub common: Rc, pub scroll_to_line: RwSignal>, left_diff_path: RwSignal>, + pub select: RwSignal>, } impl KeyPressFocus for FileExplorerData { @@ -131,6 +135,7 @@ impl FileExplorerData { 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 @@ -448,7 +453,8 @@ impl FileExplorerData { let (found, line) = self.root.with_untracked(|x| x.find_file_at_line(&path)); if found { - self.scroll_to_line.set(Some(line)) + self.scroll_to_line.set(Some(line)); + self.select.set(Some(FileNodeViewKind::Path(path))); } } } @@ -540,21 +546,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 { diff --git a/lapce-app/src/file_explorer/view.rs b/lapce-app/src/file_explorer/view.rs index 752497699d..7304705d44 100644 --- a/lapce-app/src/file_explorer/view.rs +++ b/lapce-app/src/file_explorer/view.rs @@ -2,6 +2,7 @@ use std::{path::Path, rc::Rc, sync::Arc}; use floem::{ event::{Event, EventListener}, + kurbo::Rect, peniko::Color, reactive::{create_rw_signal, ReadSignal, RwSignal}, style::{AlignItems, CursorStyle, Position, Style}, @@ -86,10 +87,8 @@ pub fn file_explorer_panel( ) .add( "File Explorer", - container( - new_file_node_view(data, source_control).style(|s| s.absolute()), - ) - .style(|s| s.size_full().line_height(1.8)), + container(file_explorer_view(data, source_control)) + .style(|s| s.size_full()), window_tab_data .panel .section_open(PanelSection::FileExplorer), @@ -173,7 +172,7 @@ 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() { + match node.kind.clone() { FileNodeViewKind::Path(path) => container( label(move || { path.file_name() @@ -181,8 +180,7 @@ fn file_node_text_view( .unwrap_or_default() }) .style(move |s| { - s.flex_grow(1.0) - .height(ui_line_height.get()) + s.height(ui_line_height.get()) .color(file_node_text_color( config, node.clone(), @@ -212,9 +210,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 @@ -282,7 +278,7 @@ fn file_node_input_view(data: FileExplorerData, err: Option) -> Containe } } -fn new_file_node_view( +fn file_explorer_view( data: FileExplorerData, source_control: SourceControlData, ) -> impl View { @@ -291,7 +287,9 @@ fn new_file_node_view( 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( @@ -375,28 +373,46 @@ 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) - }) + } }); // 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); + select.update(|x| *x = Some(kind.clone())); + } }) .on_double_click(move |_| { double_click_data.double_click(&double_click_path) @@ -419,9 +435,9 @@ fn new_file_node_view( } }, ) - .style(|s| s.flex_col().align_items(AlignItems::Stretch).width_full()), + .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 { @@ -429,10 +445,19 @@ 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(); - Some((0.0, line * line_height).into()) + let line_height = ui_line_height.get_untracked(); + Some( + ( + 0.0, + line * line_height - scroll_rect.get_untracked().height() / 2.0, + ) + .into(), + ) } else { None } diff --git a/lapce-app/src/keypress/condition.rs b/lapce-app/src/keypress/condition.rs index 3d6e61c56c..cb2d23cf74 100644 --- a/lapce-app/src/keypress/condition.rs +++ b/lapce-app/src/keypress/condition.rs @@ -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")] diff --git a/lapce-app/src/main_split.rs b/lapce-app/src/main_split.rs index 01f696bc11..11c8e79a7b 100644 --- a/lapce-app/src/main_split.rs +++ b/lapce-app/src/main_split.rs @@ -640,6 +640,7 @@ impl MainSplitData { }); } doc.get_code_lens(); + doc.get_document_symbol(); (doc, true) } } @@ -2914,6 +2915,20 @@ impl MainSplitData { } } } + + pub fn get_active_editor(&self) -> Option { + 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>> { diff --git a/lapce-app/src/markdown.rs b/lapce-app/src/markdown.rs index f9e384365a..623c310464 100644 --- a/lapce-app/src/markdown.rs +++ b/lapce-app/src/markdown.rs @@ -310,14 +310,14 @@ pub fn from_marked_string( config: &LapceConfig, ) -> Vec { 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, ) } diff --git a/lapce-app/src/palette.rs b/lapce-app/src/palette.rs index 8c97c677be..1a5e549aed 100644 --- a/lapce-app/src/palette.rs +++ b/lapce-app/src/palette.rs @@ -25,7 +25,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; @@ -646,42 +646,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 = 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()); @@ -693,6 +658,64 @@ impl PaletteData { }); } + fn format_document_symbol_resp( + resp: DocumentSymbolResponse, + ) -> im::Vector { + 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, + parent: Option, + 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; @@ -1506,6 +1529,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. @@ -1513,14 +1538,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); } } diff --git a/lapce-app/src/panel/call_hierarchy_view.rs b/lapce-app/src/panel/call_hierarchy_view.rs index 6e76bbf4c4..47934c38b9 100644 --- a/lapce-app/src/panel/call_hierarchy_view.rs +++ b/lapce-app/src/panel/call_hierarchy_view.rs @@ -1,14 +1,13 @@ use std::{ops::AddAssign, rc::Rc}; use floem::{ - event::EventPropagation, reactive::RwSignal, - style::{AlignItems, CursorStyle}, + style::CursorStyle, views::{ - container, label, scroll, stack, svg, virtual_stack, Decorators, + container, empty, label, scroll, stack, svg, virtual_stack, Decorators, VirtualDirection, VirtualItemSize, VirtualVector, }, - View, ViewId, + IntoView, View, ViewId, }; use lsp_types::{CallHierarchyItem, Range}; @@ -17,8 +16,7 @@ use crate::{ command::InternalCommand, config::{color::LapceColor, icon::LapceIcons}, editor::location::EditorLocation, - window_tab::CommonData, - window_tab::WindowTabData, + window_tab::{CommonData, WindowTabData}, }; #[derive(Clone, Debug)] @@ -141,24 +139,26 @@ pub fn show_hierarchy_panel( move |(_, level, rw_data)| { let data = rw_data.get_untracked(); let open = data.open; + let kind = data.item.kind; stack(( - 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) - .flex_shrink(0.0) - .margin_left(10.0) - .margin_right(6.0) - .color(config.color(LapceColor::LAPCE_ICON_ACTIVE)) - }).on_click_stop({ + 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| { @@ -173,27 +173,37 @@ pub fn show_hierarchy_panel( } } }), - container( - label(move || { - format!( - "{} {} {}", - data.item.name, - data.item.detail.as_deref().unwrap_or(""), data.from_range.start.line - ) - }) - .style(move |s| { - s.flex_grow(1.0) - .height(ui_line_height.get()) - .selectable(false) - .align_items(AlignItems::Center) + 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) + })) }), - ) - .style(|s| s.flex_grow(1.0).padding(0.0).margin(0.0)), + 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) - .align_items(AlignItems::Center) + .items_center() .hover(|s| { s.background( config @@ -203,13 +213,15 @@ pub fn show_hierarchy_panel( .cursor(CursorStyle::Pointer) }) }) - .on_double_click({ + .on_click_stop({ let window_tab_data = window_tab_data.clone(); let data = rw_data; move |_| { - window_tab_data.common.internal_command.send( - InternalCommand::CallHierarchyIncoming { item_id: rw_data.get_untracked().view_id }, - ); + 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 @@ -223,14 +235,13 @@ pub fn show_hierarchy_panel( same_editor_tab: false, } }); } - EventPropagation::Stop } }) }, ) - .style(|s| s.flex_col().align_items(AlignItems::Stretch).width_full()), + .style(|s| s.flex_col().absolute().min_width_full()), ) - .style(|s| s.size_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(); diff --git a/lapce-app/src/panel/data.rs b/lapce-app/src/panel/data.rs index edc270678e..4aba1d9440 100644 --- a/lapce-app/src/panel/data.rs +++ b/lapce-app/src/panel/data.rs @@ -38,6 +38,10 @@ pub fn default_panel_order() -> PanelOrder { PanelKind::CallHierarchy ], ); + order.insert( + PanelPosition::RightTop, + im::vector![PanelKind::DocumentSymbol,], + ); order } diff --git a/lapce-app/src/panel/document_symbol.rs b/lapce-app/src/panel/document_symbol.rs new file mode 100644 index 0000000000..8c4e8b2f2f --- /dev/null +++ b/lapce-app/src/panel/document_symbol.rs @@ -0,0 +1,305 @@ +use std::{ops::AddAssign, path::PathBuf, rc::Rc}; + +use floem::{ + peniko::Color, + reactive::{RwSignal, Scope}, + style::CursorStyle, + views::{ + container, editor::id::Id, label, scroll, stack, svg, virtual_stack, + Decorators, VirtualDirection, VirtualItemSize, VirtualVector, + }, + View, +}; +use lsp_types::DocumentSymbol; + +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 items: Vec>, + pub path: PathBuf, +} + +impl SymbolData { + fn get_children( + &self, + min: usize, + max: usize, + ) -> Vec<( + usize, + usize, + Rc, + RwSignal, + )> { + let mut children = Vec::new(); + let path = Rc::new(self.path.clone()); + let level: usize = 0; + let mut next = 0; + for item in &self.items { + if next >= max { + return children; + } + let child_children = + get_children(*item, &mut next, min, max, level, path.clone()); + children.extend(child_children); + } + children + } +} + +#[derive(Debug, Clone)] +pub struct SymbolInformationItemData { + pub id: Id, + pub name: String, + pub detail: Option, + pub item: DocumentSymbol, + pub open: RwSignal, + pub children: Vec>, +} + +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, + next: &mut usize, + min: usize, + max: usize, + level: usize, + path: Rc, +) -> Vec<( + usize, + usize, + Rc, + RwSignal, +)> { + 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>>, +} + +impl VirtualList { + pub fn new(root: Option>>) -> Self { + Self { root } + } +} + +impl + VirtualVector<( + usize, + usize, + Rc, + RwSignal, + )> for VirtualList +{ + fn total_len(&self) -> usize { + if let Some(root) = self.root.as_ref().and_then(|x| x.get()) { + let len = root.items.iter().fold(0, |mut x, item| { + x += item.get_untracked().child_count(); + x + }); + len + } else { + 0 + } + } + + fn slice( + &mut self, + range: std::ops::Range, + ) -> impl Iterator< + Item = ( + usize, + usize, + Rc, + RwSignal, + ), + > { + 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, + _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( + VirtualDirection::Vertical, + VirtualItemSize::Fixed(Box::new(move || ui_line_height.get())), + { + 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::GoToLocation { 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, + } }); + } + }) + }, + ) + .style(|s| s.flex_col().absolute().min_width_full()), + ) + .style(|s| s.absolute().size_full()) +} diff --git a/lapce-app/src/panel/kind.rs b/lapce-app/src/panel/kind.rs index 671571ebbf..0f29f5c241 100644 --- a/lapce-app/src/panel/kind.rs +++ b/lapce-app/src/panel/kind.rs @@ -16,6 +16,7 @@ pub enum PanelKind { Problem, Debug, CallHierarchy, + DocumentSymbol, } impl PanelKind { @@ -28,7 +29,8 @@ impl PanelKind { PanelKind::Search => LapceIcons::SEARCH, PanelKind::Problem => LapceIcons::PROBLEM, PanelKind::Debug => LapceIcons::DEBUG, - PanelKind::CallHierarchy => LapceIcons::LINK, + PanelKind::CallHierarchy => LapceIcons::TYPE_HIERARCHY, + PanelKind::DocumentSymbol => LapceIcons::DOCUMENT_SYMBOL, } } diff --git a/lapce-app/src/panel/mod.rs b/lapce-app/src/panel/mod.rs index a539e9f9f3..2398e4f5bb 100644 --- a/lapce-app/src/panel/mod.rs +++ b/lapce-app/src/panel/mod.rs @@ -1,6 +1,7 @@ pub mod call_hierarchy_view; pub mod data; pub mod debug_view; +pub mod document_symbol; pub mod global_search_view; pub mod kind; pub mod plugin_view; diff --git a/lapce-app/src/panel/view.rs b/lapce-app/src/panel/view.rs index 1fdc4c40b5..4e7d370b31 100644 --- a/lapce-app/src/panel/view.rs +++ b/lapce-app/src/panel/view.rs @@ -28,7 +28,9 @@ use crate::{ app::{clickable_icon, clickable_icon_base}, config::{color::LapceColor, icon::LapceIcons, LapceConfig}, file_explorer::view::file_explorer_panel, - panel::call_hierarchy_view::show_hierarchy_panel, + panel::{ + call_hierarchy_view::show_hierarchy_panel, document_symbol::symbol_panel, + }, window_tab::{DragContent, WindowTabData}, }; @@ -489,6 +491,9 @@ fn panel_view( show_hierarchy_panel(window_tab_data.clone(), position) .into_any() } + PanelKind::DocumentSymbol => { + symbol_panel(window_tab_data.clone(), position).into_any() + } }; view.style(|s| s.size_pct(100.0, 100.0)) }, @@ -533,18 +538,18 @@ fn panel_picker( |p| *p, move |p| { let window_tab_data = window_tab_data.clone(); - let (icon, tooltip) = match p { - PanelKind::Terminal => (LapceIcons::TERMINAL, "Terminal"), - PanelKind::FileExplorer => { - (LapceIcons::FILE_EXPLORER, "File Explorer") - } - PanelKind::SourceControl => (LapceIcons::SCM, "Source Control"), - PanelKind::Plugin => (LapceIcons::EXTENSIONS, "Plugins"), - PanelKind::Search => (LapceIcons::SEARCH, "Search"), - PanelKind::Problem => (LapceIcons::PROBLEM, "Problems"), - PanelKind::Debug => (LapceIcons::DEBUG_ALT, "Debug"), - PanelKind::CallHierarchy => (LapceIcons::LINK, "Call Hierarchy"), + let tooltip = match p { + PanelKind::Terminal => "Terminal", + PanelKind::FileExplorer => "File Explorer", + PanelKind::SourceControl => "Source Control", + PanelKind::Plugin => "Plugins", + PanelKind::Search => "Search", + PanelKind::Problem => "Problems", + PanelKind::Debug => "Debug", + PanelKind::CallHierarchy => "Call Hierarchy", + PanelKind::DocumentSymbol => "Document Symbol", }; + let icon = p.svg_name(); let is_active = { let window_tab_data = window_tab_data.clone(); move || { diff --git a/lapce-app/src/proxy/remote.rs b/lapce-app/src/proxy/remote.rs index 73a487451a..919583e3ce 100644 --- a/lapce-app/src/proxy/remote.rs +++ b/lapce-app/src/proxy/remote.rs @@ -6,7 +6,10 @@ use std::{ use anyhow::{anyhow, Result}; use flate2::read::GzDecoder; -use lapce_core::{directory::Directory, meta}; +use lapce_core::{ + directory::Directory, + meta::{self, ReleaseType}, +}; use lapce_rpc::{ core::CoreRpcHandler, proxy::{ProxyRpc, ProxyRpcHandler}, @@ -118,7 +121,7 @@ pub fn start_remote( .args([&remote_proxy_file, "--version"]) .output() .map(|output| { - if meta::VERSION == "debug" { + if meta::RELEASE == ReleaseType::Debug { String::from_utf8_lossy(&output.stdout).starts_with("Lapce-proxy") } else { String::from_utf8_lossy(&output.stdout).trim() @@ -267,10 +270,10 @@ fn download_remote( _ => { let proxy_script = general_purpose::STANDARD.encode(UNIX_PROXY_SCRIPT); - let version = if meta::VERSION == "debug" { - "nightly" - } else { - meta::VERSION + let version = match meta::RELEASE { + ReleaseType::Debug => "nightly".to_string(), + ReleaseType::Nightly => "nightly".to_string(), + ReleaseType::Stable => format!("v{}", meta::VERSION), }; let cmd = remote .command_builder() @@ -283,7 +286,7 @@ fn download_remote( "|", "sh", "/dev/stdin", - version, + &version, remote_proxy_path, ]) .output()?; diff --git a/lapce-app/src/window_tab.rs b/lapce-app/src/window_tab.rs index ed487a9542..49895a31bc 100644 --- a/lapce-app/src/window_tab.rs +++ b/lapce-app/src/window_tab.rs @@ -1,4 +1,3 @@ -use alacritty_terminal::vte::ansi::Handler; use std::{ collections::{BTreeMap, HashSet}, env, @@ -8,6 +7,7 @@ use std::{ time::Instant, }; +use alacritty_terminal::vte::ansi::Handler; use crossbeam_channel::Sender; use floem::{ action::{open_file, remove_overlay, TimerToken}, @@ -684,7 +684,12 @@ impl WindowTabData { OpenFolder => { if !self.workspace.kind.is_remote() { let window_command = self.common.window_common.window_command; - let options = FileDialogOptions::new().select_directories(); + let mut options = FileDialogOptions::new().select_directories(); + options = if let Some(parent) = self.workspace.path.as_ref().and_then(|x| x.parent()) { + options.force_starting_directory(parent) + } else { + options + }; open_file(options, move |file| { if let Some(mut file) = file { let workspace = LapceWorkspace { @@ -1373,7 +1378,7 @@ impl WindowTabData { Quit => { floem::quit_app(); } - RevealInFileTree => { + RevealInPanel => { if let Some(editor_data) = self.main_split.active_editor.get_untracked() { @@ -1387,6 +1392,24 @@ impl WindowTabData { } } } + RevealInFileExplorer => { + if let Some(editor_data) = + self.main_split.active_editor.get_untracked() + { + if let DocContent::File {path, ..} = editor_data.doc().content.get_untracked() { + let path = path.parent().unwrap_or(&path); + if !path.exists() { + return; + } + if let Err(err) = open::that(path) { + error!( + "Failed to reveal file in system file explorer: {}", + err + ); + } + } + } + } ShowCallHierarchy => { if let Some(editor_data) = self.main_split.active_editor.get_untracked() @@ -1394,6 +1417,65 @@ impl WindowTabData { editor_data.call_hierarchy(self.clone()); } } + RunInTerminal => { + if let Some(editor_data) = + self.main_split.active_editor.get_untracked() + { + let name = editor_data.word_at_cursor(); + if !name.is_empty() { + let mut args_str = name.split(" "); + let program = args_str.next().map(|x| x.to_string()).unwrap(); + let args: Vec = args_str.map(|x| x.to_string()).collect(); + let args = if args.is_empty() { + None + } else { + Some(args) + }; + + let config = RunDebugConfig { + ty: None, + name, + program, + args, + cwd: None, + env: None, + prelaunch: None, + debug_command: None, + dap_id: Default::default(), + }; + self.common + .internal_command + .send(InternalCommand::RunAndDebug { mode: RunDebugMode::Run, config }); + } + } + } + GoToLocation => { + if let Some(editor_data) = + self.main_split.active_editor.get_untracked() + { + let doc = editor_data.doc(); + let path = match if doc.loaded() { + doc.content.with_untracked(|c| c.path().cloned()) + } else { + None + } { + Some(path) => path, + None => return, + }; + let offset = editor_data.cursor().with_untracked(|c| c.offset()); + let internal_command = self.common.internal_command; + + internal_command.send(InternalCommand::MakeConfirmed); + internal_command.send(InternalCommand::GoToLocation { location: EditorLocation { + path, + position: Some(EditorPosition::Offset(offset)), + scroll_offset: None, + ignore_unconfirmed: false, + same_editor_tab: false, + } }); + } + } + } } @@ -1948,6 +2030,7 @@ impl WindowTabData { self.main_split.docs.with_untracked(|x| { for doc in x.values() { doc.get_code_lens(); + doc.get_document_symbol(); doc.get_semantic_styles(); } }); @@ -2412,7 +2495,8 @@ impl WindowTabData { | PanelKind::Plugin | PanelKind::Problem | PanelKind::Debug - | PanelKind::CallHierarchy => { + | PanelKind::CallHierarchy + | PanelKind::DocumentSymbol => { // Some panels don't accept focus (yet). Fall back to visibility check // in those cases. self.panel.is_panel_visible(&kind) diff --git a/lapce-core/src/language.rs b/lapce-core/src/language.rs index 53c9ae4991..292f66d9ee 100644 --- a/lapce-core/src/language.rs +++ b/lapce-core/src/language.rs @@ -713,7 +713,7 @@ const LANGUAGES: &[SyntaxProperties] = &[ SyntaxProperties { id: LapceLanguage::Dockerfile, indent: Indent::space(2), - files: &["dockerfile", "containerfile"], + files: &["Dockerfile", "Containerfile"], extensions: &["containerfile", "dockerfile"], comment: comment_properties!("#"), tree_sitter: TreeSitterProperties::DEFAULT, @@ -1069,8 +1069,8 @@ const LANGUAGES: &[SyntaxProperties] = &[ SyntaxProperties { id: LapceLanguage::Just, indent: Indent::tab(), - files: &[], - extensions: &[], + files: &["justfile", "Justfile", ".justfile", ".Justfile"], + extensions: &["just"], comment: comment_properties!(), tree_sitter: TreeSitterProperties::DEFAULT, }, @@ -1169,8 +1169,8 @@ const LANGUAGES: &[SyntaxProperties] = &[ extensions: &[], comment: comment_properties!(), tree_sitter: TreeSitterProperties { - grammar: Some("markdown"), - grammar_fn: None, + grammar: Some("markdown_inline"), + grammar_fn: Some("markdown_inline"), query: Some("markdown.inline"), code_glance: (DEFAULT_CODE_GLANCE_LIST, DEFAULT_CODE_GLANCE_IGNORE_LIST), sticky_headers: &[], @@ -1567,7 +1567,7 @@ const LANGUAGES: &[SyntaxProperties] = &[ SyntaxProperties { id: LapceLanguage::Toml, indent: Indent::space(2), - files: &[], + files: &["Cargo.lock"], extensions: &["toml"], comment: comment_properties!("#"), tree_sitter: TreeSitterProperties::DEFAULT, @@ -1683,20 +1683,14 @@ impl LapceLanguage { } pub fn from_path_raw(path: &Path) -> Option { - let filename = path - .file_stem() - .and_then(|s| s.to_str().map(|s| s.to_lowercase())); + let filename = path.file_name().and_then(|s| s.to_str()); let extension = path .extension() .and_then(|s| s.to_str().map(|s| s.to_lowercase())); // NOTE: This is a linear search. It is assumed that this function // isn't called in any tight loop. for properties in LANGUAGES { - if properties - .files - .iter() - .any(|f| Some(*f) == filename.as_deref()) - { + if properties.files.iter().any(|f| Some(*f) == filename) { return Some(properties.id); } if properties diff --git a/lapce-core/src/meta.rs b/lapce-core/src/meta.rs index d79ab2f8d2..1add227339 100644 --- a/lapce-core/src/meta.rs +++ b/lapce-core/src/meta.rs @@ -1,4 +1,4 @@ -#[derive(strum_macros::AsRefStr)] +#[derive(strum_macros::AsRefStr, PartialEq, Eq)] pub enum ReleaseType { Debug, Stable, diff --git a/lapce-core/src/style.rs b/lapce-core/src/style.rs index fed6642423..9f6a5cd211 100644 --- a/lapce-core/src/style.rs +++ b/lapce-core/src/style.rs @@ -33,6 +33,14 @@ pub const SCOPES: &[&str] = &[ "conceal", "none", "tag", + "markup.bold", + "markup.italic", + "markup.list", + "markup.quote", + "markup.heading", + "markup.link.url", + "markup.link.label", + "markup.link.text", ]; pub fn line_styles( diff --git a/lapce-proxy/src/plugin/mod.rs b/lapce-proxy/src/plugin/mod.rs index 009a899047..8b09527ccc 100644 --- a/lapce-proxy/src/plugin/mod.rs +++ b/lapce-proxy/src/plugin/mod.rs @@ -49,10 +49,10 @@ use lsp_types::{ CodeActionResponse, CodeLens, CodeLensParams, CompletionClientCapabilities, CompletionItem, CompletionItemCapability, CompletionItemCapabilityResolveSupport, CompletionParams, CompletionResponse, - Diagnostic, DocumentFormattingParams, DocumentSymbolParams, - DocumentSymbolResponse, FormattingOptions, GotoCapability, GotoDefinitionParams, - GotoDefinitionResponse, Hover, HoverClientCapabilities, HoverParams, InlayHint, - InlayHintClientCapabilities, InlayHintParams, + Diagnostic, DocumentFormattingParams, DocumentSymbolClientCapabilities, + DocumentSymbolParams, DocumentSymbolResponse, FormattingOptions, GotoCapability, + GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverClientCapabilities, + HoverParams, InlayHint, InlayHintClientCapabilities, InlayHintParams, InlineCompletionClientCapabilities, InlineCompletionParams, InlineCompletionResponse, InlineCompletionTriggerKind, Location, MarkupKind, MessageActionItemCapabilities, ParameterInformationSettings, @@ -1623,6 +1623,10 @@ fn client_capabilities() -> ClientCapabilities { call_hierarchy: Some(CallHierarchyClientCapabilities { dynamic_registration: Some(true), }), + document_symbol: Some(DocumentSymbolClientCapabilities { + hierarchical_document_symbol_support: Some(true), + ..Default::default() + }), ..Default::default() }), window: Some(WindowClientCapabilities { diff --git a/lapce-proxy/src/terminal.rs b/lapce-proxy/src/terminal.rs index e7c996964c..76a89e6cdf 100644 --- a/lapce-proxy/src/terminal.rs +++ b/lapce-proxy/src/terminal.rs @@ -1,4 +1,3 @@ -use std::time::Duration; use std::{ borrow::Cow, collections::VecDeque, @@ -6,6 +5,7 @@ use std::{ num::NonZeroUsize, path::PathBuf, sync::Arc, + time::Duration, }; use alacritty_terminal::{ diff --git a/lapce.spec b/lapce.spec index 0a6761d063..9996f3b8ca 100644 --- a/lapce.spec +++ b/lapce.spec @@ -1,5 +1,5 @@ Name: lapce-git -Version: 0.4.0.{{{ git_dir_version }}} +Version: 0.4.1.{{{ git_dir_version }}} Release: 1 Summary: Lightning-fast and Powerful Code Editor written in Rust License: Apache-2.0