diff --git a/Cargo.lock b/Cargo.lock index 86ae08159c232..1d1dac9565309 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1096,9 +1096,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.18.2" +version = "1.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395" +checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" dependencies = [ "bytes", "libc", @@ -1127,9 +1127,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" dependencies = [ "futures-core", "pin-project-lite", diff --git a/book/src/configuration.md b/book/src/configuration.md index 4d7e440a09fe0..f46c508008a4e 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -42,15 +42,17 @@ hidden = false | `auto-format` | Enable automatic formatting on save. | `true` | | `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. | `400` | | `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` | +| `completion-doc-preview` | Show the documentation of the first item in the completion menu | `false` | | `auto-info` | Whether to display infoboxes | `true` | | `true-color` | Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative. | `false` | | `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file. | `[]` | ### `[editor.lsp]` Section -| Key | Description | Default | -| --- | ----------- | ------- | -| `display-messages` | Display LSP progress messages below statusline[^1] | `false` | +| Key | Description | Default | +| --- | ----------- | ------- | +| `display-messages` | Display LSP progress messages below statusline[^1] | `false` | +| `preselect` | Show the LSP server's preselected suggestions first | `true` | [^1]: A progress spinner is always shown in the statusline beside the file path. diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 1d9b89a407776..980b3bdc9a96a 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -15,10 +15,10 @@ | edoc | ✓ | | | | | eex | ✓ | | | | | ejs | ✓ | | | | -| elixir | ✓ | | | `elixir-ls` | +| elixir | ✓ | ✓ | | `elixir-ls` | | elm | ✓ | | | `elm-language-server` | | erb | ✓ | | | | -| erlang | ✓ | | | `erlang_ls` | +| erlang | ✓ | ✓ | | `erlang_ls` | | fish | ✓ | ✓ | ✓ | | | gdscript | ✓ | | ✓ | | | git-attributes | ✓ | | | | @@ -27,7 +27,7 @@ | git-diff | ✓ | | | | | git-ignore | ✓ | | | | | git-rebase | ✓ | | | | -| gleam | ✓ | | | | +| gleam | ✓ | ✓ | | | | glsl | ✓ | | ✓ | | | go | ✓ | ✓ | ✓ | `gopls` | | gomod | ✓ | | | `gopls` | diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml index 6eec89f03b272..fb36758f03490 100644 --- a/helix-lsp/Cargo.toml +++ b/helix-lsp/Cargo.toml @@ -23,6 +23,6 @@ lsp-types = { version = "0.93", features = ["proposed"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" -tokio = { version = "1.18", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } -tokio-stream = "0.1.8" +tokio = { version = "1.19", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } +tokio-stream = "0.1.9" which = "4.2" diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 2dfccf0435ecd..24b72a47ea43a 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -606,7 +606,14 @@ impl Application { self.lsp_progress.update(server_id, token, work); } - if self.config.load().editor.lsp.display_messages { + if self + .config + .load() + .editor + .lsp + .display_messages + .unwrap_or(false) + { self.editor.set_status(status); } } diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 6f0780107b687..9275172fd947b 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -210,6 +210,10 @@ impl ui::menu::Item for lsp::CodeActionOrCommand { lsp::CodeActionOrCommand::Command(command) => command.title.as_str(), } } + + fn preselected(&self) -> bool { + false + } } pub fn code_action(cx: &mut Context) { @@ -252,34 +256,39 @@ pub fn code_action(cx: &mut Context) { return; } - let mut picker = ui::Menu::new(actions, move |editor, code_action, event| { - if event != PromptEvent::Validate { - return; - } + let mut picker = ui::Menu::new( + actions, + ui::menu::SortStrategy::Text, + false, + move |editor, code_action, event| { + if event != PromptEvent::Validate { + return; + } - // always present here - let code_action = code_action.unwrap(); + // always present here + let code_action = code_action.unwrap(); - match code_action { - lsp::CodeActionOrCommand::Command(command) => { - log::debug!("code action command: {:?}", command); - execute_lsp_command(editor, command.clone()); - } - lsp::CodeActionOrCommand::CodeAction(code_action) => { - log::debug!("code action: {:?}", code_action); - if let Some(ref workspace_edit) = code_action.edit { - log::debug!("edit: {:?}", workspace_edit); - apply_workspace_edit(editor, offset_encoding, workspace_edit); + match code_action { + lsp::CodeActionOrCommand::Command(command) => { + log::debug!("code action command: {:?}", command); + execute_lsp_command(editor, command.clone()); } + lsp::CodeActionOrCommand::CodeAction(code_action) => { + log::debug!("code action: {:?}", code_action); + if let Some(ref workspace_edit) = code_action.edit { + log::debug!("edit: {:?}", workspace_edit); + apply_workspace_edit(editor, offset_encoding, workspace_edit); + } - // if code action provides both edit and command first the edit - // should be applied and then the command - if let Some(command) = &code_action.command { - execute_lsp_command(editor, command.clone()); + // if code action provides both edit and command first the edit + // should be applied and then the command + if let Some(command) = &code_action.command { + execute_lsp_command(editor, command.clone()); + } } } - } - }); + }, + ); picker.move_down(); // pre-select the first item let popup = Popup::new("code-action", picker).margin(helix_view::graphics::Margin { diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 38005aad033b9..e5ee993e98aa0 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -66,6 +66,10 @@ impl menu::Item for CompletionItem { // .as_str(), ]) } + + fn preselected(&self) -> bool { + self.preselect.unwrap_or(false) + } } /// Wraps a Menu. @@ -85,128 +89,136 @@ impl Completion { start_offset: usize, trigger_offset: usize, ) -> Self { - let menu = Menu::new(items, move |editor: &mut Editor, item, event| { - fn item_to_transaction( - doc: &Document, - item: &CompletionItem, - offset_encoding: helix_lsp::OffsetEncoding, - start_offset: usize, - trigger_offset: usize, - ) -> Transaction { - let transaction = if let Some(edit) = &item.text_edit { - let edit = match edit { - lsp::CompletionTextEdit::Edit(edit) => edit.clone(), - lsp::CompletionTextEdit::InsertAndReplace(item) => { - unimplemented!("completion: insert_and_replace {:?}", item) - } + let menu = Menu::new( + items, + menu::SortStrategy::Score, + editor.config().lsp.preselect.unwrap_or(false), + move |editor: &mut Editor, item, event| { + fn item_to_transaction( + doc: &Document, + item: &CompletionItem, + offset_encoding: helix_lsp::OffsetEncoding, + start_offset: usize, + trigger_offset: usize, + ) -> Transaction { + let transaction = if let Some(edit) = &item.text_edit { + let edit = match edit { + lsp::CompletionTextEdit::Edit(edit) => edit.clone(), + lsp::CompletionTextEdit::InsertAndReplace(item) => { + unimplemented!("completion: insert_and_replace {:?}", item) + } + }; + + util::generate_transaction_from_edits( + doc.text(), + vec![edit], + offset_encoding, // TODO: should probably transcode in Client + ) + } else { + let text = item.insert_text.as_ref().unwrap_or(&item.label); + // Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯ + // in these cases we need to check for a common prefix and remove it + let prefix = Cow::from(doc.text().slice(start_offset..trigger_offset)); + let text = text.trim_start_matches::<&str>(&prefix); + Transaction::change( + doc.text(), + vec![(trigger_offset, trigger_offset, Some(text.into()))].into_iter(), + ) }; - util::generate_transaction_from_edits( - doc.text(), - vec![edit], - offset_encoding, // TODO: should probably transcode in Client - ) - } else { - let text = item.insert_text.as_ref().unwrap_or(&item.label); - // Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯ - // in these cases we need to check for a common prefix and remove it - let prefix = Cow::from(doc.text().slice(start_offset..trigger_offset)); - let text = text.trim_start_matches::<&str>(&prefix); - Transaction::change( - doc.text(), - vec![(trigger_offset, trigger_offset, Some(text.into()))].into_iter(), - ) - }; - - transaction - } - - fn completion_changes(transaction: &Transaction, trigger_offset: usize) -> Vec { - transaction - .changes_iter() - .filter(|(start, end, _)| (*start..=*end).contains(&trigger_offset)) - .collect() - } + transaction + } - let (view, doc) = current!(editor); + fn completion_changes( + transaction: &Transaction, + trigger_offset: usize, + ) -> Vec { + transaction + .changes_iter() + .filter(|(start, end, _)| (*start..=*end).contains(&trigger_offset)) + .collect() + } - // if more text was entered, remove it - doc.restore(view.id); + let (view, doc) = current!(editor); - match event { - PromptEvent::Abort => { - doc.restore(view.id); - editor.last_completion = None; - } - PromptEvent::Update => { - // always present here - let item = item.unwrap(); - - let transaction = item_to_transaction( - doc, - item, - offset_encoding, - start_offset, - trigger_offset, - ); - - // initialize a savepoint - doc.savepoint(); - doc.apply(&transaction, view.id); - - editor.last_completion = Some(CompleteAction { - trigger_offset, - changes: completion_changes(&transaction, trigger_offset), - }); - } - PromptEvent::Validate => { - // always present here - let item = item.unwrap(); - - let transaction = item_to_transaction( - doc, - item, - offset_encoding, - start_offset, - trigger_offset, - ); - - doc.apply(&transaction, view.id); - - editor.last_completion = Some(CompleteAction { - trigger_offset, - changes: completion_changes(&transaction, trigger_offset), - }); - - // apply additional edits, mostly used to auto import unqualified types - let resolved_item = if item - .additional_text_edits - .as_ref() - .map(|edits| !edits.is_empty()) - .unwrap_or(false) - { - None - } else { - Self::resolve_completion_item(doc, item.clone()) - }; + // if more text was entered, remove it + doc.restore(view.id); - if let Some(additional_edits) = resolved_item - .as_ref() - .and_then(|item| item.additional_text_edits.as_ref()) - .or(item.additional_text_edits.as_ref()) - { - if !additional_edits.is_empty() { - let transaction = util::generate_transaction_from_edits( - doc.text(), - additional_edits.clone(), - offset_encoding, // TODO: should probably transcode in Client - ); - doc.apply(&transaction, view.id); + match event { + PromptEvent::Abort => { + doc.restore(view.id); + editor.last_completion = None; + } + PromptEvent::Update => { + // always present here + let item = item.unwrap(); + + let transaction = item_to_transaction( + doc, + item, + offset_encoding, + start_offset, + trigger_offset, + ); + + // initialize a savepoint + doc.savepoint(); + doc.apply(&transaction, view.id); + + editor.last_completion = Some(CompleteAction { + trigger_offset, + changes: completion_changes(&transaction, trigger_offset), + }); + } + PromptEvent::Validate => { + // always present here + let item = item.unwrap(); + + let transaction = item_to_transaction( + doc, + item, + offset_encoding, + start_offset, + trigger_offset, + ); + + doc.apply(&transaction, view.id); + + editor.last_completion = Some(CompleteAction { + trigger_offset, + changes: completion_changes(&transaction, trigger_offset), + }); + + // apply additional edits, mostly used to auto import unqualified types + let resolved_item = if item + .additional_text_edits + .as_ref() + .map(|edits| !edits.is_empty()) + .unwrap_or(false) + { + None + } else { + Self::resolve_completion_item(doc, item.clone()) + }; + + if let Some(additional_edits) = resolved_item + .as_ref() + .and_then(|item| item.additional_text_edits.as_ref()) + .or(item.additional_text_edits.as_ref()) + { + if !additional_edits.is_empty() { + let transaction = util::generate_transaction_from_edits( + doc.text(), + additional_edits.clone(), + offset_encoding, // TODO: should probably transcode in Client + ); + doc.apply(&transaction, view.id); + } } } - } - }; - }); + }; + }, + ); let popup = Popup::new("completion", menu); let mut completion = Self { popup, @@ -304,8 +316,17 @@ impl Component for Completion { fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) { self.popup.render(area, surface, cx); - // if we have a selection, render a markdown popup on top/below with info - if let Some(option) = self.popup.contents().selection() { + // If we have a selection or a doc preview, render a markdown popup on top/below with info + let mut option = self.popup.contents().selection(); + if option.is_none() + && cx.editor.config().completion_doc_preview + && !self.popup.contents().is_empty() + { + // The doc preview will show the doc for the first completion item even when not selected + option = self.popup.contents().get_match(0); + } + + if let Some(option) = option { // need to render: // option.detail // --- diff --git a/helix-term/src/ui/markdown.rs b/helix-term/src/ui/markdown.rs index 5f78c3cc4d6b7..452730025d3ee 100644 --- a/helix-term/src/ui/markdown.rs +++ b/helix-term/src/ui/markdown.rs @@ -171,6 +171,8 @@ impl Markdown { Event::Start(Tag::List(list)) => list_stack.push(list), Event::End(Tag::List(_)) => { list_stack.pop(); + // whenever list closes, new line + lines.push(Spans::default()); } Event::Start(Tag::Item) => { tags.push(Tag::Item); @@ -186,11 +188,19 @@ impl Markdown { | Tag::Paragraph | Tag::CodeBlock(CodeBlockKind::Fenced(_)) | Tag::Item => { - // whenever code block or paragraph closes, new line let spans = std::mem::take(&mut spans); if !spans.is_empty() { lines.push(Spans::from(spans)); } + } + _ => (), + } + + // whenever heading, code block or paragraph closes, new line + match tag { + Tag::Heading(_, _, _) + | Tag::Paragraph + | Tag::CodeBlock(CodeBlockKind::Fenced(_)) => { lines.push(Spans::default()); } _ => (), diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs index d67a429e37f3e..b91c1747b9664 100644 --- a/helix-term/src/ui/menu.rs +++ b/helix-term/src/ui/menu.rs @@ -13,6 +13,11 @@ use fuzzy_matcher::FuzzyMatcher; use helix_view::{graphics::Rect, Editor}; use tui::layout::Constraint; +pub enum SortStrategy { + Text, + Score, +} + pub trait Item { fn label(&self) -> &str; @@ -26,6 +31,8 @@ pub trait Item { fn row(&self) -> Row { Row::new(vec![Cell::from(self.label())]) } + + fn preselected(&self) -> bool; } pub struct Menu { @@ -45,6 +52,8 @@ pub struct Menu { size: (u16, u16), viewport: (u16, u16), recalculate: bool, + sort_strategy: SortStrategy, + use_preselect: bool, } impl Menu { @@ -52,6 +61,8 @@ impl Menu { // rendering) pub fn new( options: Vec, + sort_strategy: SortStrategy, + use_preselect: bool, callback_fn: impl Fn(&mut Editor, Option<&T>, MenuEvent) + 'static, ) -> Self { let mut menu = Self { @@ -65,6 +76,8 @@ impl Menu { size: (0, 0), viewport: (0, 0), recalculate: true, + sort_strategy, + use_preselect, }; // TODO: scoring on empty input should just use a fastpath @@ -74,6 +87,10 @@ impl Menu { } pub fn score(&mut self, pattern: &str) { + // dereference pointers here, not inside of the filter_map() loop + let is_pattern_empty = pattern.is_empty(); + let use_preselect = self.use_preselect; + // reuse the matches allocation self.matches.clear(); self.matches.extend( @@ -83,14 +100,33 @@ impl Menu { .filter_map(|(index, option)| { let text = option.filter_text(); // TODO: using fuzzy_indices could give us the char idx for match highlighting - self.matcher - .fuzzy_match(text, pattern) - .map(|score| (index, score)) + + // If prompt pattern is empty, show the preselected item as first option + if use_preselect && is_pattern_empty { + if option.preselected() { + // The LSP server can preselect multiple items, however it doesn't give any preference + // for one over the other, so they all have the same score + Some((index, 1)) + } else { + Some((index, 0)) + } + } else { + self.matcher + .fuzzy_match(text, pattern) + .map(|score| (index, score)) + } }), ); - // matches.sort_unstable_by_key(|(_, score)| -score); - self.matches - .sort_unstable_by_key(|(index, _score)| self.options[*index].sort_text()); + + match self.sort_strategy { + SortStrategy::Text => { + self.matches + .sort_unstable_by_key(|(index, _score)| self.options[*index].sort_text()); + } + SortStrategy::Score => { + self.matches.sort_unstable_by_key(|(_, score)| -score); + } + }; // reset cursor position self.cursor = None; @@ -179,12 +215,14 @@ impl Menu { } } + pub fn get_match(&self, index: usize) -> Option<&T> { + self.matches + .get(index) + .map(|(index, _score)| &self.options[*index]) + } + pub fn selection(&self) -> Option<&T> { - self.cursor.and_then(|cursor| { - self.matches - .get(cursor) - .map(|(index, _score)| &self.options[*index]) - }) + self.cursor.and_then(|cursor| self.get_match(cursor)) } pub fn is_empty(&self) -> bool { @@ -194,6 +232,14 @@ impl Menu { pub fn len(&self) -> usize { self.matches.len() } + + pub fn options(&self) -> &[T] { + &self.options + } + + pub fn matches(&self) -> &[(usize, i64)] { + self.matches.as_ref() + } } use super::PromptEvent as MenuEvent; @@ -271,6 +317,7 @@ impl Component for Menu { .try_get("ui.menu") .unwrap_or_else(|| theme.get("ui.text")); let selected = theme.get("ui.menu.selected"); + let highlighted = theme.get("ui.menu.highlighted"); let scroll = self.scroll; @@ -299,7 +346,8 @@ impl Component for Menu { let rows = options.iter().map(|option| option.row()); let table = Table::new(rows) .style(style) - .highlight_style(selected) + .selected_style(selected) + .highlighted_style(highlighted) .column_spacing(1) .widths(&self.widths); @@ -311,6 +359,10 @@ impl Component for Menu { &mut TableState { offset: scroll, selected: self.cursor, + highlighted: match cx.editor.config().completion_doc_preview { + true => Some(0), + false => None, + }, }, ); diff --git a/helix-tui/src/widgets/table.rs b/helix-tui/src/widgets/table.rs index eb03704ea05ff..b578d6c4b47d2 100644 --- a/helix-tui/src/widgets/table.rs +++ b/helix-tui/src/widgets/table.rs @@ -175,7 +175,9 @@ impl<'a> Row<'a> { /// // ...and they can be separated by a fixed spacing. /// .column_spacing(1) /// // If you wish to highlight a row in any specific way when it is selected... -/// .highlight_style(Style::default().add_modifier(Modifier::BOLD)) +/// .selected_style(Style::default().add_modifier(Modifier::BOLD)) +/// // If you wish to highlight a row in any specific way when it is preselected... +/// .highlighted_style(Style::default().add_modifier(Modifier::BOLD)) /// // ...and potentially show a symbol in front of the selection. /// .highlight_symbol(">>"); /// ``` @@ -190,7 +192,9 @@ pub struct Table<'a> { /// Space between each column column_spacing: u16, /// Style used to render the selected row - highlight_style: Style, + selected_style: Style, + /// Style used so render a highlighted row + highlighted_style: Style, /// Symbol in front of the selected rom highlight_symbol: Option<&'a str>, /// Optional header @@ -209,7 +213,8 @@ impl<'a> Table<'a> { style: Style::default(), widths: &[], column_spacing: 1, - highlight_style: Style::default(), + selected_style: Style::default(), + highlighted_style: Style::default(), highlight_symbol: None, header: None, rows: rows.into_iter().collect(), @@ -249,8 +254,13 @@ impl<'a> Table<'a> { self } - pub fn highlight_style(mut self, highlight_style: Style) -> Self { - self.highlight_style = highlight_style; + pub fn selected_style(mut self, selected_style: Style) -> Self { + self.selected_style = selected_style; + self + } + + pub fn highlighted_style(mut self, highlighted_style: Style) -> Self { + self.highlighted_style = highlighted_style; self } @@ -367,6 +377,7 @@ impl<'a> Table<'a> { pub struct TableState { pub offset: usize, pub selected: Option, + pub highlighted: Option, } impl TableState { @@ -463,8 +474,15 @@ impl<'a> Table<'a> { }; buf.set_style(table_row_area, table_row.style); let is_selected = state.selected.map(|s| s == i).unwrap_or(false); + let is_highlighted = match has_selection { + true => false, + false => match state.highlighted { + Some(h) => h == i, + None => false, + }, + }; let table_row_start_col = if has_selection { - let symbol = if is_selected { + let symbol = if is_selected || is_highlighted { highlight_symbol } else { &blank_symbol @@ -490,7 +508,9 @@ impl<'a> Table<'a> { col += *width + self.column_spacing; } if is_selected { - buf.set_style(table_row_area, self.highlight_style); + buf.set_style(table_row_area, self.selected_style); + } else if is_highlighted { + buf.set_style(table_row_area, self.highlighted_style); } } } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 8e53936a4197c..401f2853f9731 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -152,12 +152,17 @@ pub struct Config { pub rulers: Vec, #[serde(default)] pub whitespace: WhitespaceConfig, + /// Whether to show the documentation of the first item in the completion menu. Defaults to false. + pub completion_doc_preview: bool, } #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] pub struct LspConfig { - pub display_messages: bool, + /// Whether to display LSP status messages below the statusline + pub display_messages: Option, + /// Whether to use show the LSP server preselected suggestion at the top of the menu. Defaults to true. + pub preselect: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -387,6 +392,7 @@ impl Default for Config { lsp: LspConfig::default(), rulers: Vec::new(), whitespace: WhitespaceConfig::default(), + completion_doc_preview: false, } } } diff --git a/languages.toml b/languages.toml index 7ab8ff84507ac..6ab394b76f452 100644 --- a/languages.toml +++ b/languages.toml @@ -1049,7 +1049,7 @@ language-server = { command = "erlang_ls" } [[grammar]] name = "erlang" -source = { git = "https://github.com/the-mikedavis/tree-sitter-erlang", rev = "481e7f8ddf27f07a47d1531b6e2b154b89ece31d" } +source = { git = "https://github.com/the-mikedavis/tree-sitter-erlang", rev = "c0ebc82600caaf4339f2b00691f958e9df97c065" } [[language]] name = "kotlin" diff --git a/runtime/queries/elixir/textobjects.scm b/runtime/queries/elixir/textobjects.scm new file mode 100644 index 0000000000000..52a6f66de4fb7 --- /dev/null +++ b/runtime/queries/elixir/textobjects.scm @@ -0,0 +1,27 @@ +; Function heads and guards have no body at all, so `keywords` and `do_block` nodes are both optional +((call + target: (identifier) @_keyword + (arguments + [ + (call + (arguments (_)? @parameter.inside)) + ; function has a guard + (binary_operator + left: + (call + (arguments (_)? @parameter.inside))) + ] + ; body is "do: body" instead of a do-block + (keywords + (pair + value: (_) @function.inside))?)? + (do_block (_)* @function.inside)?) + (#match? @_keyword "^(def|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp|defp|test|describe|setup)$")) @function.around + +(anonymous_function + (stab_clause right: (body) @function.inside)) @function.around + +((call + target: (identifier) @_keyword + (do_block (_)* @class.inside)) + (#match? @_keyword "^(defmodule|defprotocol|defimpl)$")) @class.around diff --git a/runtime/queries/erlang/highlights.scm b/runtime/queries/erlang/highlights.scm index bea3871a683b6..0cb60ca999778 100644 --- a/runtime/queries/erlang/highlights.scm +++ b/runtime/queries/erlang/highlights.scm @@ -58,7 +58,7 @@ (#eq? @keyword "(spec|callback)")) ; Functions -(function name: (atom) @function) +(function_clause name: (atom) @function) (call module: (atom) @module) (call function: (atom) @function) (stab_clause name: (atom) @function) diff --git a/runtime/queries/erlang/textobjects.scm b/runtime/queries/erlang/textobjects.scm new file mode 100644 index 0000000000000..c46b5c6f64c35 --- /dev/null +++ b/runtime/queries/erlang/textobjects.scm @@ -0,0 +1,8 @@ +(function_clause + pattern: (arguments (_)? @parameter.inside) + body: (_) @function.inside) @function.around + +(anonymous_function + (stab_clause body: (_) @function.inside)) @function.around + +(comment (comment_content) @comment.inside) @comment.around diff --git a/runtime/queries/gleam/textobjects.scm b/runtime/queries/gleam/textobjects.scm new file mode 100644 index 0000000000000..b382f4bd0078f --- /dev/null +++ b/runtime/queries/gleam/textobjects.scm @@ -0,0 +1,6 @@ +(function + parameters: (function_parameters (function_parameter)? @parameter.inside) + body: (function_body) @function.inside) @function.around + +(anonymous_function + body: (function_body) @function.inside) @function.around diff --git a/runtime/queries/rust/textobjects.scm b/runtime/queries/rust/textobjects.scm index b2769c1393ef0..ba86050b51773 100644 --- a/runtime/queries/rust/textobjects.scm +++ b/runtime/queries/rust/textobjects.scm @@ -7,6 +7,8 @@ (function_item body: (_) @function.inside)) @function.around +(closure_expression body: (_) @function.inside) @function.around + ( [ (attribute_item)+ diff --git a/runtime/themes/autumn.toml b/runtime/themes/autumn.toml index 68e41f5af0797..9ea420ba7831f 100644 --- a/runtime/themes/autumn.toml +++ b/runtime/themes/autumn.toml @@ -10,6 +10,7 @@ "ui.background" = { bg = "my_gray0" } "ui.menu" = { fg = "my_white", bg = "my_gray2" } "ui.menu.selected" = { fg = "my_gray2", bg = "my_gray5" } +"ui.menu.highlighted" = { fg = "my_red", bg = "my_gray2" } "ui.linenr" = { fg = "my_gray4", bg = "my_gray2" } "ui.popup" = { bg = "my_gray2" } "ui.window" = { fg = "my_gray4", bg = "my_gray2" } diff --git a/runtime/themes/bogster.toml b/runtime/themes/bogster.toml index df3a7f3153062..1d2615560adda 100644 --- a/runtime/themes/bogster.toml +++ b/runtime/themes/bogster.toml @@ -62,6 +62,7 @@ "ui.menu" = { fg = "#e5ded6bg", bg = "#232d38" } "ui.menu.selected" = { bg = "#313f4e" } +"ui.menu.highlighted" = { fg = "#dcb659" } "warning" = "#dc7759" "error" = "#dc597f" diff --git a/runtime/themes/catpuccin.toml b/runtime/themes/catppuccin.toml similarity index 100% rename from runtime/themes/catpuccin.toml rename to runtime/themes/catppuccin.toml diff --git a/runtime/themes/dracula.toml b/runtime/themes/dracula.toml index e32c3117b1db9..4696d1ae5cd84 100644 --- a/runtime/themes/dracula.toml +++ b/runtime/themes/dracula.toml @@ -28,6 +28,7 @@ "ui.linenr.selected" = { fg = "foreground" } "ui.menu" = { fg = "foreground", bg = "background_dark" } "ui.menu.selected" = { fg = "cyan", bg = "background_dark" } +"ui.menu.highlighted" = { fg = "cyan" } "ui.popup" = { fg = "foreground", bg = "background_dark" } "ui.selection" = { bg = "secondary_highlight" } "ui.selection.primary" = { bg = "primary_highlight" } diff --git a/runtime/themes/gruvbox.toml b/runtime/themes/gruvbox.toml index 6de35244a5def..9332569dad20e 100644 --- a/runtime/themes/gruvbox.toml +++ b/runtime/themes/gruvbox.toml @@ -53,6 +53,7 @@ "ui.cursor.match" = { bg = "bg2" } "ui.menu" = { fg = "fg1", bg = "bg2" } "ui.menu.selected" = { fg = "bg2", bg = "blue1", modifiers = ["bold"] } +"ui.menu.highlighted" = { fg = "green1" } "ui.virtual" = "bg2" "diagnostic" = { modifiers = ["underlined"] } diff --git a/runtime/themes/ingrid.toml b/runtime/themes/ingrid.toml index 79b749b14d93c..5f3105f518008 100644 --- a/runtime/themes/ingrid.toml +++ b/runtime/themes/ingrid.toml @@ -59,6 +59,7 @@ # "ui.cursor.match" # TODO might want to override this because dimmed is not widely supported "ui.menu" = { fg = "#7B91B3", bg = "#F3EAE9" } "ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" } +"ui.menu.highlighted" = { fg = "#D74E50" } "warning" = "#D4A520" "error" = "#D74E50" diff --git a/runtime/themes/monokai.toml b/runtime/themes/monokai.toml index 3fb1fadc30f47..6f230b28d932a 100644 --- a/runtime/themes/monokai.toml +++ b/runtime/themes/monokai.toml @@ -61,6 +61,7 @@ "ui.help" = { bg = "widget" } "ui.menu" = { bg = "widget" } "ui.menu.selected" = { bg = "#414339" } +"ui.menu.highlighted" = { fg = "#F92672" } "ui.cursor" = { fg = "cursor", modifiers = ["reversed"] } "ui.cursor.primary" = { fg = "cursor", modifiers = ["reversed"] } diff --git a/runtime/themes/nord.toml b/runtime/themes/nord.toml index a61c17157f333..975e7b4ce5492 100644 --- a/runtime/themes/nord.toml +++ b/runtime/themes/nord.toml @@ -4,6 +4,7 @@ "ui.text.focus" = { fg = "nord8", modifiers= ["bold"] } "ui.menu" = { fg = "nord6", bg = "#232d38" } "ui.menu.selected" = { fg = "nord8", bg = "nord2" } +"ui.menu.highlighted" = { fg = "nord8" } "ui.virtual" = "gray" "info" = "nord8" diff --git a/runtime/themes/onedark.toml b/runtime/themes/onedark.toml index 280f6914da48b..dc344d7411ba5 100644 --- a/runtime/themes/onedark.toml +++ b/runtime/themes/onedark.toml @@ -71,6 +71,7 @@ diagnostic = { modifiers = ["underlined"] } "ui.window" = { bg = "gray" } "ui.menu" = { fg = "white", bg = "gray" } "ui.menu.selected" = { fg = "black", bg = "blue" } +"ui.menu.highlighted" = { fg = "red" } [palette] diff --git a/runtime/themes/rose_pine.toml b/runtime/themes/rose_pine.toml index f05758801ae5a..42581116c3f3a 100644 --- a/runtime/themes/rose_pine.toml +++ b/runtime/themes/rose_pine.toml @@ -4,6 +4,7 @@ "ui.background" = { bg = "base" } "ui.menu" = { fg = "text", bg = "overlay" } "ui.menu.selected" = { fg = "iris", bg = "surface" } +"ui.menu.highlighted" = { fg = "iris"} "ui.linenr" = {fg = "subtle" } "ui.liner.selected" = "highlightOverlay" "ui.selection" = { bg = "highlight" } diff --git a/runtime/themes/snazzy.toml b/runtime/themes/snazzy.toml index c0547f33b6143..2415d22fef6fa 100644 --- a/runtime/themes/snazzy.toml +++ b/runtime/themes/snazzy.toml @@ -30,6 +30,7 @@ "ui.linenr.selected" = { fg = "foreground" } "ui.menu" = { fg = "foreground", bg = "background_dark" } "ui.menu.selected" = { fg = "cyan", bg = "background_dark" } +"ui.menu.highlighted" = { fg = "cyan" } "ui.popup" = { fg = "foreground", bg = "background_dark" } "ui.selection" = { bg = "secondary_highlight" } "ui.selection.primary" = { bg = "primary_highlight" } diff --git a/theme.toml b/theme.toml index 7a518b2fe4063..a04b90c355d26 100644 --- a/theme.toml +++ b/theme.toml @@ -67,6 +67,7 @@ label = "honey" "ui.menu" = { fg = "lavender", bg = "revolver" } "ui.menu.selected" = { fg = "revolver", bg = "white" } +"ui.menu.highlighted" = { fg = "almond"} diagnostic = { modifiers = ["underlined"] } # "diagnostic.hint" = { fg = "revolver", bg = "lilac" }