diff --git a/Cargo.lock b/Cargo.lock index d2d7a477..9d101f02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1587,7 +1587,7 @@ dependencies = [ "leptos_reactive", "once_cell", "parking_lot 0.12.1", - "parley 0.1.0 (git+https://github.com/lapce/parley?rev=421e5c1eb07d108a9c05da4f021a34f5bd6532cd)", + "parley 0.1.0 (git+https://github.com/lapce/parley?rev=dd30cc9e4ae733d80650ad3ab0db0462ccd67e57)", "rustc-hash", "smallvec", "taffy", @@ -1733,8 +1733,9 @@ dependencies = [ [[package]] name = "fount" version = "0.1.0" -source = "git+https://github.com/lapce/fount?rev=0ac37ef400d4048e2dbebe34b1a74f55dab4654d#0ac37ef400d4048e2dbebe34b1a74f55dab4654d" +source = "git+https://github.com/lapce/fount?rev=7952647649c119be454691764c20768330a6e8f2#7952647649c119be454691764c20768330a6e8f2" dependencies = [ + "peniko", "swash 0.1.6 (git+https://github.com/dfrg/swash)", ] @@ -4201,9 +4202,9 @@ dependencies = [ [[package]] name = "parley" version = "0.1.0" -source = "git+https://github.com/lapce/parley?rev=421e5c1eb07d108a9c05da4f021a34f5bd6532cd#421e5c1eb07d108a9c05da4f021a34f5bd6532cd" +source = "git+https://github.com/lapce/parley?rev=dd30cc9e4ae733d80650ad3ab0db0462ccd67e57#dd30cc9e4ae733d80650ad3ab0db0462ccd67e57" dependencies = [ - "fount 0.1.0 (git+https://github.com/lapce/fount?rev=0ac37ef400d4048e2dbebe34b1a74f55dab4654d)", + "fount 0.1.0 (git+https://github.com/lapce/fount?rev=7952647649c119be454691764c20768330a6e8f2)", "once_cell", "swash 0.1.6 (git+https://github.com/dfrg/swash)", ] @@ -4257,7 +4258,7 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "peniko" version = "0.1.0" -source = "git+https://github.com/linebender/peniko?rev=8cb710f#8cb710ffcb0f04fa24648440f9b9fb038174a341" +source = "git+https://github.com/linebender/peniko?rev=cafdac9a211a0fb2fec5656bd663d1ac770bcc81#cafdac9a211a0fb2fec5656bd663d1ac770bcc81" dependencies = [ "kurbo 0.9.0", "smallvec", @@ -6575,8 +6576,8 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vello" -version = "0.1.0" -source = "git+https://github.com/linebender/vello#020a7f5c016ab5c572cefbe169f19082a0e34ffe" +version = "0.0.1" +source = "git+https://github.com/linebender/vello?rev=2f268d4e0f8596c2c05f4982ddc558e1cae0ffa2#2f268d4e0f8596c2c05f4982ddc558e1cae0ffa2" dependencies = [ "bytemuck", "futures-intrusive", @@ -6590,8 +6591,8 @@ dependencies = [ [[package]] name = "vello_svg" -version = "0.1.0" -source = "git+https://github.com/linebender/vello#020a7f5c016ab5c572cefbe169f19082a0e34ffe" +version = "0.0.1" +source = "git+https://github.com/linebender/vello?rev=2f268d4e0f8596c2c05f4982ddc558e1cae0ffa2#2f268d4e0f8596c2c05f4982ddc558e1cae0ffa2" dependencies = [ "usvg 0.28.0", "vello", diff --git a/lapce-app/src/app.rs b/lapce-app/src/app.rs index 0f62acd2..e140668d 100644 --- a/lapce-app/src/app.rs +++ b/lapce-app/src/app.rs @@ -1503,6 +1503,7 @@ fn palette_content(cx: AppContext, window_tab_data: WindowTabData) -> impl View let index = window_tab_data.palette.index.read_only(); let config = window_tab_data.palette.config; let run_id = window_tab_data.palette.run_id; + let input = window_tab_data.palette.input.read_only(); let palette_item_height = 24.0; container(cx, |cx| { scroll(cx, |cx| { @@ -1510,7 +1511,9 @@ fn palette_content(cx: AppContext, window_tab_data: WindowTabData) -> impl View cx, VirtualListDirection::Vertical, move || PaletteItems(items.get()), - move |(i, _item)| (run_id.get_untracked(), *i), + move |(i, _item)| { + (run_id.get_untracked(), *i, input.get_untracked().input) + }, move |cx, (i, item)| { palette_item(cx, i, item, index, palette_item_height, config) }, @@ -1636,15 +1639,14 @@ fn completion(cx: AppContext, window_tab_data: WindowTabData) -> impl View { let completion_data = window_tab_data.completion; let config = window_tab_data.config; let active = completion_data.with_untracked(|c| c.active); - let request_id = create_memo(cx.scope, move |_| { - completion_data.with(|c| (c.request_id, c.input_id)) - }); + let request_id = + move || completion_data.with_untracked(|c| (c.request_id, c.input_id)); scroll(cx, move |cx| { virtual_list( cx, VirtualListDirection::Vertical, move || completion_data.with(|c| VectorItems(c.filtered_items.clone())), - move |(i, item)| (request_id.get_untracked(), *i), + move |(i, item)| (request_id(), *i), move |cx, (i, item)| { stack(cx, |cx| { ( diff --git a/lapce-app/src/completion.rs b/lapce-app/src/completion.rs index 72fcdc9a..b8d6f1c0 100644 --- a/lapce-app/src/completion.rs +++ b/lapce-app/src/completion.rs @@ -75,7 +75,6 @@ pub fn receive( return; } - println!("receive completion"); let items = match resp { CompletionResponse::Array(items) => items, CompletionResponse::List(list) => &list.items, diff --git a/lapce-app/src/editor.rs b/lapce-app/src/editor.rs index 58b8cca6..2d8619f8 100644 --- a/lapce-app/src/editor.rs +++ b/lapce-app/src/editor.rs @@ -282,7 +282,6 @@ fn run_focus_command( } } FocusCommand::SplitExchange => { - println!("split exchage"); if let Some(editor_tab_id) = self.editor_tab_id { self.internal_command .set(Some(InternalCommand::SplitExchange { editor_tab_id })); @@ -575,7 +574,6 @@ fn update_completion(&self, display_if_empty_input: bool) { }); return; } - println!("offset {offset} start offset {start_offset} input {input}"); if self.completion.with_untracked(|completion| { completion.status != CompletionStatus::Inactive @@ -608,7 +606,6 @@ fn update_completion(&self, display_if_empty_input: bool) { } self.completion.update(|completion| { - println!("new completion"); completion.path = path.clone(); completion.offset = start_offset; completion.input = input.clone(); @@ -689,7 +686,6 @@ fn apply_completion_item(&self, item: &CompletionItem) -> Result<()> { &selection, additional_edit, start_offset, - edit_start, )?; return Ok(()); } @@ -725,7 +721,6 @@ fn completion_apply_snippet( selection: &Selection, additional_edit: Vec<(Selection, &str)>, start_offset: usize, - edit_start: usize, ) -> Result<()> { let snippet = Snippet::from_str(snippet)?; let text = snippet.text(); @@ -747,13 +742,6 @@ fn completion_apply_snippet( let selection = selection.apply_delta(&delta, true, InsertDrift::Default); - let start_offset = additional_edit - .iter() - .map(|(selection, _)| selection.min_offset()) - .min() - .map(|offset| offset.min(start_offset).min(edit_start)) - .unwrap_or(start_offset); - let mut transformer = Transformer::new(&delta); let offset = transformer.transform(start_offset, false); let snippet_tabs = snippet.tabs(offset); diff --git a/lapce-app/src/palette.rs b/lapce-app/src/palette.rs index 533c7aaf..179f1ef5 100644 --- a/lapce-app/src/palette.rs +++ b/lapce-app/src/palette.rs @@ -62,19 +62,32 @@ pub enum PaletteStatus { Done, } +#[derive(Clone, Debug)] +pub struct PaletteInput { + pub input: String, + pub kind: PaletteKind, +} + +impl PaletteInput { + pub fn update_input(&mut self, input: String) { + self.kind = self.kind.get_palette_kind(&input); + self.input = self.kind.get_input(&input).to_string(); + } +} + #[derive(Clone)] pub struct PaletteData { run_id_counter: Arc, run_tx: Sender<(u64, String, im::Vector)>, internal_command: WriteSignal>, - pub run_id: ReadSignal, + pub run_id: RwSignal, pub workspace: Arc, pub status: RwSignal, pub index: RwSignal, - pub kind: RwSignal, pub items: RwSignal>, pub filtered_items: ReadSignal>, pub proxy_rpc: ProxyRpcHandler, + pub input: RwSignal, pub editor: EditorData, pub focus: RwSignal, pub keypress: ReadSignal, @@ -95,9 +108,15 @@ pub fn new( config: ReadSignal>, ) -> Self { let status = create_rw_signal(cx.scope, PaletteStatus::Inactive); - let kind = create_rw_signal(cx.scope, PaletteKind::File); let items = create_rw_signal(cx.scope, im::Vector::new()); let index = create_rw_signal(cx.scope, 0); + let input = create_rw_signal( + cx.scope, + PaletteInput { + input: "".to_string(), + kind: PaletteKind::File, + }, + ); let editor = EditorData::new_local( cx, EditorId::next(), @@ -107,19 +126,43 @@ pub fn new( proxy_rpc.clone(), config, ); + let run_id = create_rw_signal(cx.scope, 0); let run_id_counter = Arc::new(AtomicU64::new(0)); let (run_tx, run_rx) = crossbeam_channel::unbounded(); { - let run_id = run_id_counter.clone(); - let doc = editor.doc.read_only(); + let run_id = run_id.read_only(); + let input = input.read_only(); let items = items.read_only(); let tx = run_tx.clone(); - create_effect(cx.scope, move |_| { - let run_id = run_id.fetch_add(1, Ordering::Relaxed) + 1; - let input = doc.with(|doc| doc.buffer().text().to_string()); - let items = items.get(); - let _ = tx.send((run_id, input, items)); + + { + let tx = tx.clone(); + // this effect only monitors items change + create_effect(cx.scope, move |_| { + let items = items.get(); + println!("filter items when items change"); + let input = input.get_untracked(); + let run_id = run_id.get_untracked(); + let _ = tx.send((run_id, input.input, items)); + }); + } + + // this effect only monitors input change + create_effect(cx.scope, move |last_kind| { + let input = input.get(); + println!("new input {input:?}"); + let kind = input.kind; + if last_kind != Some(kind) { + println!("got new kind {kind:?}"); + return kind; + } + + println!("filter items when input change"); + let items = items.get_untracked(); + let run_id = run_id.get_untracked(); + let _ = tx.send((run_id, input.input, items)); + kind }); } @@ -131,20 +174,18 @@ pub fn new( }); } - let (run_id, set_run_id) = create_signal(cx.scope, 0); - let (filtered_items, set_filtered_items) = create_signal(cx.scope, im::Vector::new()); { let resp = create_signal_from_channel(cx, resp_rx); - let run_id = run_id_counter.clone(); + let run_id = run_id.read_only(); + let input = input.read_only(); let index = index.write_only(); create_effect(cx.scope, move |_| { - if let Some((current_run_id, items)) = resp.get() { - if run_id.load(std::sync::atomic::Ordering::Acquire) - == current_run_id + if let Some((filter_run_id, filter_input, items)) = resp.get() { + if run_id.get_untracked() == filter_run_id + && input.get_untracked().input == filter_input { - set_run_id.set(current_run_id); set_filtered_items.set(items); index.set(0); } @@ -152,7 +193,7 @@ pub fn new( }); } - Self { + let palette = Self { run_id_counter, run_tx, internal_command, @@ -160,26 +201,80 @@ pub fn new( focus, workspace, status, - kind, index, items, filtered_items, editor, + input, proxy_rpc, keypress, config, executed_commands: Rc::new(RefCell::new(HashMap::new())), + }; + + { + let palette = palette.clone(); + let doc = palette.editor.doc.read_only(); + let input = palette.input.write_only(); + let status = palette.status.read_only(); + // this effect monitors the document change in the palette input editor + create_effect(cx.scope, move |last_input| { + let new_input = doc.with(|doc| doc.buffer().text().to_string()); + let status = status.get_untracked(); + if status == PaletteStatus::Inactive { + // If the status is inactive, we set the input to None, + // so that when we actually run the palette, the input + // can be compared with this None. + return None; + } + + let last_input_is_none = !matches!(last_input, Some(Some(_))); + + let changed = match last_input { + None => true, + Some(last_input) => { + Some(new_input.as_str()) != last_input.as_deref() + } + }; + + if changed { + let new_kind = input + .update_returning(|input| { + let kind = input.kind; + input.update_input(new_input.clone()); + if last_input_is_none || kind != input.kind { + Some(input.kind) + } else { + None + } + }) + .unwrap(); + if let Some(new_kind) = new_kind { + palette.run_inner(cx, new_kind); + } + } + Some(new_input) + }); } + + palette } pub fn run(&self, cx: AppContext, kind: PaletteKind) { self.status.set(PaletteStatus::Started); + let symbol = kind.symbol(); self.editor .doc - .update(|doc| doc.reload(Rope::from(""), true)); + .update(|doc| doc.reload(Rope::from(symbol), true)); self.editor .cursor - .update(|cursor| cursor.set_insert(Selection::caret(0))); + .update(|cursor| cursor.set_insert(Selection::caret(symbol.len()))); + } + + fn run_inner(&self, cx: AppContext, kind: PaletteKind) { + println!("palette run inner"); + let run_id = self.run_id_counter.fetch_add(1, Ordering::Relaxed) + 1; + self.run_id.set(run_id); match kind { PaletteKind::File => { self.get_files(cx); @@ -188,7 +283,6 @@ pub fn run(&self, cx: AppContext, kind: PaletteKind) { self.get_commands(cx); } } - self.kind.set(kind); } fn get_files(&self, cx: AppContext) { @@ -299,13 +393,13 @@ fn select(&self, cx: AppContext) { fn cancel(&self, cx: AppContext) { self.status.set(PaletteStatus::Inactive); self.focus.set(Focus::Workbench); + self.items.update(|items| items.clear()); self.editor .doc .update(|doc| doc.reload(Rope::from(""), true)); self.editor .cursor .update(|cursor| cursor.set_insert(Selection::caret(0))); - self.items.update(|items| items.clear()); } fn next(&self) { @@ -384,9 +478,11 @@ fn filter_items( } filtered_items.sort_by(|a, b| { - b.score - .partial_cmp(&a.score) - .unwrap_or(std::cmp::Ordering::Less) + let order = b.score.cmp(&a.score); + match order { + std::cmp::Ordering::Equal => a.filter_text.cmp(&b.filter_text), + _ => order, + } }); if run_id.load(std::sync::atomic::Ordering::Acquire) != current_run_id { @@ -398,7 +494,7 @@ fn filter_items( fn update_process( run_id: Arc, receiver: Receiver<(u64, String, im::Vector)>, - resp_tx: Sender<(u64, im::Vector)>, + resp_tx: Sender<(u64, String, im::Vector)>, ) { fn receive_batch( receiver: &Receiver<(u64, String, im::Vector)>, @@ -428,7 +524,7 @@ fn receive_batch( items, &matcher, ) { - let _ = resp_tx.send((current_run_id, filtered_items)); + let _ = resp_tx.send((current_run_id, input, filtered_items)); } } else { return; diff --git a/lapce-app/src/palette/kind.rs b/lapce-app/src/palette/kind.rs index af2d91f1..d12aca7d 100644 --- a/lapce-app/src/palette/kind.rs +++ b/lapce-app/src/palette/kind.rs @@ -1,5 +1,77 @@ -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum PaletteKind { File, Command, } + +impl PaletteKind { + pub fn symbol(&self) -> &'static str { + match &self { + // PaletteKind::Line => "/", + // PaletteKind::DocumentSymbol => "@", + // PaletteKind::WorkspaceSymbol => "#", + // PaletteKind::GlobalSearch => "?", + // PaletteKind::Workspace => ">", + PaletteKind::Command => ":", + PaletteKind::File + // | PaletteKind::Reference + // | PaletteKind::ColorTheme + // | PaletteKind::IconTheme + // | PaletteKind::SshHost + // | PaletteKind::RunAndDebug + // | PaletteKind::Language + => "", + } + } + + pub fn from_input(input: &str) -> PaletteKind { + match input { + // _ if input.starts_with('/') => PaletteKind::Line, + // _ if input.starts_with('@') => PaletteKind::DocumentSymbol, + // _ if input.starts_with('#') => PaletteKind::WorkspaceSymbol, + // _ if input.starts_with('>') => PaletteKind::Workspace, + _ if input.starts_with(':') => PaletteKind::Command, + _ => PaletteKind::File, + } + } + + // pub fn has_preview(&self) -> bool { + // matches!( + // self, + // PaletteType::Line + // | PaletteType::DocumentSymbol + // | PaletteType::WorkspaceSymbol + // | PaletteType::GlobalSearch + // | PaletteType::Reference + // ) + // } + + pub fn get_input<'a>(&self, input: &'a str) -> &'a str { + match self { + PaletteKind::File + // | PaletteKind::Reference + // | PaletteKind::ColorTheme + // | PaletteKind::IconTheme + // | PaletteKind::Language + // | PaletteKind::RunAndDebug + // | PaletteKind::SshHost + => input, + PaletteKind::Command + // | PaletteType::DocumentSymbol + // | PaletteType::WorkspaceSymbol + // | PaletteType::Workspace + // | PaletteType::Line + // | PaletteType::GlobalSearch + => if !input.is_empty() {&input[1..]} else {input}, + } + } + + /// 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() == "" { + return self.clone(); + } + PaletteKind::from_input(input) + } +}