use ely_gpui_component::{ buttons::Button, editor::{ ActionKind, Call, CallHierarchy, CodeAction, CodeActionMenu, CompletionItem, CompletionMenu, FileHits, GoToSymbol, Hit, HoverInfo, ReferencesPanel, RenameInput, Signature, SignatureHelp, Symbol, SymbolKind, SymbolOutline, completions, }, forms::TextInput, primitives::Severity, typography::Caption, }; use gpui::{App, IntoElement, ParentElement, SharedString, Styled, Window, div, px}; use crate::ui::{keep, row, section, set}; fn items() -> Vec { vec![ CompletionItem::new("lookup", SymbolKind::Method) .detail("fn(&self, &str, f32) -> Color") .docs("The color for a name, blended toward white by lift."), CompletionItem::new("insert", SymbolKind::Method).detail("fn(&mut self, &str, Color)"), CompletionItem::new("colors", SymbolKind::Field).detail("HashMap"), CompletionItem::new("fallback", SymbolKind::Field).detail("Color"), CompletionItem::new("len", SymbolKind::Method).detail("fn(&self) -> usize"), CompletionItem::new("Palette", SymbolKind::Struct).detail("struct Palette"), CompletionItem::new("loop", SymbolKind::Keyword), ] } pub fn hints(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> { let overload = keep("intel-overload", || 0usize, window, cx); let told = keep("intel-told", || None::, window, cx); let (over, said) = (*overload.read(cx), told.read(cx).clone()); let chosen = completions(&items(), "lo") .iter() .position(|(item, _)| item.label.as_ref() == "lookup") .expect("lookup fits lo"); let param = |label: &str, text: &str| { let start = label.find(text).expect("the parameter is in the label"); start..start + text.len() }; let (long, short) = ( "fn lookup(&self, name: &str, lift: f32) -> Color", "fn lookup(&self, name: &str) -> Color", ); let (pick, step, tell) = (told.clone(), overload.clone(), told.clone()); let signatures = [ Signature { label: long.into(), params: vec![param(long, "name: &str"), param(long, "lift: f32")], docs: Some("The color for a name, blended toward white by lift.".into()), }, Signature { label: short.into(), params: vec![param(short, "name: &str")], docs: Some("The color for a name as it is.".into()), }, ]; section( "CompletionMenu / SignatureHelp / HoverInfo", "Completions as you type, the typed letters marked and the chosen one's documentation beside; the call's signature with the parameter at the cursor; and what a hover over a symbol reads.", cx, ) .child( row() .items_start() .gap_8() .child( CompletionMenu::new("intel-menu", items()) .typed("lo") .selected(chosen) .on_pick(move |item, _, cx| set(&pick, Some(format!("Picked {}.", item.label).into()), cx)), ) .child( div() .flex() .flex_col() .gap_4() .child( SignatureHelp::new("intel-signature", signatures) .at(over, 1) .on_step(move |next, _, cx| set(&step, next, cx)), ) .child( HoverInfo::new("intel-hover", "pub fn lookup(&self, name: &str, lift: f32) -> Color") .problem(Severity::Warning, "`lift` outside 0..=1 is clamped") .docs("The color for `name`, blended toward white by `lift`.") .docs("Unknown names read as the fallback color.") .link("Go to definition", move |_, cx| set(&tell, Some("Went to palette.rs:23.".into()), cx)) .link("Find references", |_, _| {}), ), ), ) .children(said.map(Caption::new)) } fn symbols() -> Vec { vec![ Symbol::new("Palette", SymbolKind::Struct, 3).children([ Symbol::new("colors", SymbolKind::Field, 4).detail("HashMap"), Symbol::new("fallback", SymbolKind::Field, 5).detail("Color"), ]), Symbol::new("Color", SymbolKind::Struct, 9), Symbol::new("impl Palette", SymbolKind::Module, 15).children([ Symbol::new("new", SymbolKind::Method, 16).detail("fn(Color) -> Self"), Symbol::new("insert", SymbolKind::Method, 23), Symbol::new("lookup", SymbolKind::Method, 28).detail("fn(&self, &str, f32) -> Color"), ]), Symbol::new("main", SymbolKind::Function, 42), ] } pub fn navigate(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> { let cursor = keep("intel-cursor", || 30usize, window, cx); let palette = keep("intel-goto", || false, window, cx); let incoming = keep("intel-incoming", || true, window, cx); let rename_open = keep("intel-rename", || false, window, cx); let field = window.use_keyed_state("intel-rename-field", cx, |window, cx| { let mut field = TextInput::new(window, cx); field.set_text("lookup", cx); field }); let (line, open, inbound, renaming) = ( *cursor.read(cx), *palette.read(cx), *incoming.read(cx), *rename_open.read(cx), ); let (pick, go, close, flip) = ( cursor.clone(), cursor.clone(), palette.clone(), incoming.clone(), ); let opener = palette.clone(); let (renamer, done, cancel) = ( rename_open.clone(), rename_open.clone(), rename_open.clone(), ); let hit = |line: usize, text: &str| { let start = text.find("lookup").expect("the line names lookup"); let name = start..start + "lookup".len(); Hit { line, text: text.to_string().into(), ranges: vec![name], } }; let references = [ FileHits { path: "src/palette.rs".into(), hits: vec![hit( 27, " pub fn lookup(&self, name: &str, lift: f32) -> Color {", )], }, FileHits { path: "src/main.rs".into(), hits: vec![ hit( 44, " let tints: Vec = (0..5u8).map(|step| palette.lookup(\"accent\", step as f32 / 4.0)).collect();", ), hit(51, " let text = palette.lookup(\"fg\", 0.0);"), ], }, ]; let callers = Call { name: "lookup".into(), kind: SymbolKind::Method, place: "palette.rs:28".into(), calls: vec![ Call { name: "main".into(), kind: SymbolKind::Function, place: "main.rs:44".into(), calls: Vec::new(), }, Call { name: "paint_theme".into(), kind: SymbolKind::Function, place: "theme.rs:12".into(), calls: vec![Call { name: "render".into(), kind: SymbolKind::Method, place: "app.rs:88".into(), calls: Vec::new(), }], }, ], }; let fixes = [ CodeAction::new("Change type of `count` to `usize`", ActionKind::QuickFix).preferred(), CodeAction::new("Convert with `u32::try_from`", ActionKind::QuickFix), CodeAction::new("Extract into function", ActionKind::Refactor), CodeAction::new("Inline variable", ActionKind::Refactor), CodeAction::new("Organize imports", ActionKind::Source), ]; section( "CodeActionMenu / RenameInput / PeekView / ReferencesPanel / SymbolOutline / GoToSymbol / CallHierarchy / TypeHierarchy", "Fixes and refactors behind a lightbulb; renaming in place; where a symbol is used; a file's symbols as a tree that follows the cursor, or as a palette to jump by name; and who calls a function.", cx, ) .child( row() .gap_4() .child(CodeActionMenu::new("intel-actions", fixes)) .child(Button::new("intel-goto-open", "Go to symbol…").on_click(move |_, _, cx| set(&opener, true, cx))) .child(Button::new("intel-rename-open", "Rename lookup").on_click(move |_, _, cx| set(&renamer, true, cx))) .child(Caption::new(format!("Cursor on line {}.", line + 1))), ) .children(renaming.then(|| { RenameInput::new("intel-rename-box", &field) .on_rename(move |_, _, cx| set(&done, false, cx)) .on_cancel(move |_, cx| set(&cancel, false, cx)) })) .child( row() .items_start() .gap_8() .child( div().w(px(300.)).h(px(240.)).child( SymbolOutline::new("intel-outline", symbols()) .cursor(line) .on_pick(move |line, _, cx| set(&pick, line, cx)), ), ) .child( div().w(px(300.)).h(px(240.)).child( CallHierarchy::new("intel-calls", callers, inbound) .on_flip(move |incoming, _, cx| set(&flip, incoming, cx)), ), ), ) .child(div().w(px(840.)).child(ReferencesPanel::new("intel-references", "lookup", references))) .children(open.then(|| { GoToSymbol::new("intel-goto-palette", symbols(), move |_, cx| set(&close, false, cx)) .on_pick(move |line, _, cx| set(&go, line, cx)) })) }