Add an Amp-like jump command
Co-authored-by: Michael Davis <mcarsondavis@gmail.com>
This commit is contained in:
parent
3001f22b31
commit
b46064b8c4
11 changed files with 244 additions and 6 deletions
|
@ -68,6 +68,7 @@ Its settings will be merged with the configuration directory `config.toml` and t
|
||||||
| `insert-final-newline` | Whether to automatically insert a trailing line-ending on write if missing | `true` |
|
| `insert-final-newline` | Whether to automatically insert a trailing line-ending on write if missing | `true` |
|
||||||
| `popup-border` | Draw border around `popup`, `menu`, `all`, or `none` | `none` |
|
| `popup-border` | Draw border around `popup`, `menu`, `all`, or `none` | `none` |
|
||||||
| `indent-heuristic` | How the indentation for a newly inserted line is computed: `simple` just copies the indentation level from the previous line, `tree-sitter` computes the indentation based on the syntax tree and `hybrid` combines both approaches. If the chosen heuristic is not available, a different one will be used as a fallback (the fallback order being `hybrid` -> `tree-sitter` -> `simple`). | `hybrid`
|
| `indent-heuristic` | How the indentation for a newly inserted line is computed: `simple` just copies the indentation level from the previous line, `tree-sitter` computes the indentation based on the syntax tree and `hybrid` combines both approaches. If the chosen heuristic is not available, a different one will be used as a fallback (the fallback order being `hybrid` -> `tree-sitter` -> `simple`). | `hybrid`
|
||||||
|
| `jump-label-alphabet` | The characters that are used to generate two character jump labels. Characters at the start of the alphabet are used first. | "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
### `[editor.statusline]` Section
|
### `[editor.statusline]` Section
|
||||||
|
|
||||||
|
|
|
@ -224,6 +224,7 @@ Jumps to various locations.
|
||||||
| `.` | Go to last modification in current file | `goto_last_modification` |
|
| `.` | Go to last modification in current file | `goto_last_modification` |
|
||||||
| `j` | Move down textual (instead of visual) line | `move_line_down` |
|
| `j` | Move down textual (instead of visual) line | `move_line_down` |
|
||||||
| `k` | Move up textual (instead of visual) line | `move_line_up` |
|
| `k` | Move up textual (instead of visual) line | `move_line_up` |
|
||||||
|
| `w` | Show labels at each word and select the word that belongs to the entered labels | `goto_word` |
|
||||||
|
|
||||||
#### Match mode
|
#### Match mode
|
||||||
|
|
||||||
|
|
|
@ -314,6 +314,7 @@ These scopes are used for theming the editor interface:
|
||||||
| `ui.virtual.inlay-hint.parameter` | Style for inlay hints of kind `parameter` (LSPs are not required to set a kind) |
|
| `ui.virtual.inlay-hint.parameter` | Style for inlay hints of kind `parameter` (LSPs are not required to set a kind) |
|
||||||
| `ui.virtual.inlay-hint.type` | Style for inlay hints of kind `type` (LSPs are not required to set a kind) |
|
| `ui.virtual.inlay-hint.type` | Style for inlay hints of kind `type` (LSPs are not required to set a kind) |
|
||||||
| `ui.virtual.wrap` | Soft-wrap indicator (see the [`editor.soft-wrap` config][editor-section]) |
|
| `ui.virtual.wrap` | Soft-wrap indicator (see the [`editor.soft-wrap` config][editor-section]) |
|
||||||
|
| `ui.virtual.jump-label` | Style for virtual jump labels |
|
||||||
| `ui.menu` | Code and command completion menus |
|
| `ui.menu` | Code and command completion menus |
|
||||||
| `ui.menu.selected` | Selected autocomplete item |
|
| `ui.menu.selected` | Selected autocomplete item |
|
||||||
| `ui.menu.scroll` | `fg` sets thumb color, `bg` sets track color of scrollbar |
|
| `ui.menu.scroll` | `fg` sets thumb color, `bg` sets track color of scrollbar |
|
||||||
|
|
|
@ -705,6 +705,15 @@ impl IntoIterator for Selection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Range> for Selection {
|
||||||
|
fn from(range: Range) -> Self {
|
||||||
|
Self {
|
||||||
|
ranges: smallvec![range],
|
||||||
|
primary_index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: checkSelection -> check if valid for doc length && sorted
|
// TODO: checkSelection -> check if valid for doc length && sorted
|
||||||
|
|
||||||
pub fn keep_or_remove_matches(
|
pub fn keep_or_remove_matches(
|
||||||
|
|
|
@ -10,9 +10,12 @@ use tui::widgets::Row;
|
||||||
pub use typed::*;
|
pub use typed::*;
|
||||||
|
|
||||||
use helix_core::{
|
use helix_core::{
|
||||||
char_idx_at_visual_offset, comment,
|
char_idx_at_visual_offset,
|
||||||
|
chars::char_is_word,
|
||||||
|
comment,
|
||||||
doc_formatter::TextFormat,
|
doc_formatter::TextFormat,
|
||||||
encoding, find_workspace, graphemes,
|
encoding, find_workspace,
|
||||||
|
graphemes::{self, next_grapheme_boundary, RevRopeGraphemes},
|
||||||
history::UndoKind,
|
history::UndoKind,
|
||||||
increment, indent,
|
increment, indent,
|
||||||
indent::IndentStyle,
|
indent::IndentStyle,
|
||||||
|
@ -24,7 +27,7 @@ use helix_core::{
|
||||||
search::{self, CharMatcher},
|
search::{self, CharMatcher},
|
||||||
selection, shellwords, surround,
|
selection, shellwords, surround,
|
||||||
syntax::{BlockCommentToken, LanguageServerFeature},
|
syntax::{BlockCommentToken, LanguageServerFeature},
|
||||||
text_annotations::TextAnnotations,
|
text_annotations::{Overlay, TextAnnotations},
|
||||||
textobject,
|
textobject,
|
||||||
unicode::width::UnicodeWidthChar,
|
unicode::width::UnicodeWidthChar,
|
||||||
visual_offset_from_block, Deletion, LineEnding, Position, Range, Rope, RopeGraphemes,
|
visual_offset_from_block, Deletion, LineEnding, Position, Range, Rope, RopeGraphemes,
|
||||||
|
@ -502,6 +505,8 @@ impl MappableCommand {
|
||||||
record_macro, "Record macro",
|
record_macro, "Record macro",
|
||||||
replay_macro, "Replay macro",
|
replay_macro, "Replay macro",
|
||||||
command_palette, "Open command palette",
|
command_palette, "Open command palette",
|
||||||
|
goto_word, "Jump to a two-character label",
|
||||||
|
extend_to_word, "Extend to a two-character label",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5814,3 +5819,182 @@ fn replay_macro(cx: &mut Context) {
|
||||||
cx.editor.macro_replaying.pop();
|
cx.editor.macro_replaying.pop();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn goto_word(cx: &mut Context) {
|
||||||
|
jump_to_word(cx, Movement::Move)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extend_to_word(cx: &mut Context) {
|
||||||
|
jump_to_word(cx, Movement::Extend)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jump_to_label(cx: &mut Context, labels: Vec<Range>, behaviour: Movement) {
|
||||||
|
let doc = doc!(cx.editor);
|
||||||
|
let alphabet = &cx.editor.config().jump_label_alphabet;
|
||||||
|
if labels.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let alphabet_char = |i| {
|
||||||
|
let mut res = Tendril::new();
|
||||||
|
res.push(alphabet[i]);
|
||||||
|
res
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add label for each jump candidate to the View as virtual text.
|
||||||
|
let text = doc.text().slice(..);
|
||||||
|
let mut overlays: Vec<_> = labels
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(i, range)| {
|
||||||
|
[
|
||||||
|
Overlay::new(range.from(), alphabet_char(i / alphabet.len())),
|
||||||
|
Overlay::new(
|
||||||
|
graphemes::next_grapheme_boundary(text, range.from()),
|
||||||
|
alphabet_char(i % alphabet.len()),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
overlays.sort_unstable_by_key(|overlay| overlay.char_idx);
|
||||||
|
let (view, doc) = current!(cx.editor);
|
||||||
|
doc.set_jump_labels(view.id, overlays);
|
||||||
|
|
||||||
|
// Accept two characters matching a visible label. Jump to the candidate
|
||||||
|
// for that label if it exists.
|
||||||
|
let primary_selection = doc.selection(view.id).primary();
|
||||||
|
let view = view.id;
|
||||||
|
let doc = doc.id();
|
||||||
|
cx.on_next_key(move |cx, event| {
|
||||||
|
let alphabet = &cx.editor.config().jump_label_alphabet;
|
||||||
|
let Some(i ) = event.char().and_then(|ch| alphabet.iter().position(|&it| it == ch)) else {
|
||||||
|
doc_mut!(cx.editor, &doc).remove_jump_labels(view);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let outer = i * alphabet.len();
|
||||||
|
// Bail if the given character cannot be a jump label.
|
||||||
|
if outer > labels.len() {
|
||||||
|
doc_mut!(cx.editor, &doc).remove_jump_labels(view);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cx.on_next_key(move |cx, event| {
|
||||||
|
doc_mut!(cx.editor, &doc).remove_jump_labels(view);
|
||||||
|
let alphabet = &cx.editor.config().jump_label_alphabet;
|
||||||
|
let Some(inner ) = event.char().and_then(|ch| alphabet.iter().position(|&it| it == ch)) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(mut range) = labels.get(outer + inner).copied() {
|
||||||
|
range = if behaviour == Movement::Extend {
|
||||||
|
let anchor = if range.anchor < range.head {
|
||||||
|
let from = primary_selection.from();
|
||||||
|
if range.anchor < from {
|
||||||
|
range.anchor
|
||||||
|
} else {
|
||||||
|
from
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let to = primary_selection.to();
|
||||||
|
if range.anchor > to {
|
||||||
|
range.anchor
|
||||||
|
} else {
|
||||||
|
to
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Range::new(anchor, range.head)
|
||||||
|
}else{
|
||||||
|
range.with_direction(Direction::Forward)
|
||||||
|
};
|
||||||
|
doc_mut!(cx.editor, &doc).set_selection(view, range.into());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jump_to_word(cx: &mut Context, behaviour: Movement) {
|
||||||
|
// Calculate the jump candidates: ranges for any visible words with two or
|
||||||
|
// more characters.
|
||||||
|
let alphabet = &cx.editor.config().jump_label_alphabet;
|
||||||
|
let jump_label_limit = alphabet.len() * alphabet.len();
|
||||||
|
let mut words = Vec::with_capacity(jump_label_limit);
|
||||||
|
let (view, doc) = current_ref!(cx.editor);
|
||||||
|
let text = doc.text().slice(..);
|
||||||
|
|
||||||
|
// This is not necessarily exact if there is virtual text like soft wrap.
|
||||||
|
// It's ok though because the extra jump labels will not be rendered.
|
||||||
|
let start = text.line_to_char(text.char_to_line(view.offset.anchor));
|
||||||
|
let end = text.line_to_char(view.estimate_last_doc_line(doc) + 1);
|
||||||
|
|
||||||
|
let primary_selection = doc.selection(view.id).primary();
|
||||||
|
let cursor = primary_selection.cursor(text);
|
||||||
|
let mut cursor_fwd = Range::point(cursor);
|
||||||
|
let mut cursor_rev = Range::point(cursor);
|
||||||
|
if text.get_char(cursor).is_some_and(|c| !c.is_whitespace()) {
|
||||||
|
let cursor_word_end = movement::move_next_word_end(text, cursor_fwd, 1);
|
||||||
|
// single grapheme words need a specical case
|
||||||
|
if cursor_word_end.anchor == cursor {
|
||||||
|
cursor_fwd = cursor_word_end;
|
||||||
|
}
|
||||||
|
let cursor_word_start = movement::move_prev_word_start(text, cursor_rev, 1);
|
||||||
|
if cursor_word_start.anchor == next_grapheme_boundary(text, cursor) {
|
||||||
|
cursor_rev = cursor_word_start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'outer: loop {
|
||||||
|
let mut changed = false;
|
||||||
|
while cursor_fwd.head < end {
|
||||||
|
cursor_fwd = movement::move_next_word_end(text, cursor_fwd, 1);
|
||||||
|
// The cursor is on a word that is atleast two graphemes long and
|
||||||
|
// madeup of word characters. The latter condition is needed because
|
||||||
|
// move_next_word_end simply treats a sequence of characters from
|
||||||
|
// the same char class as a word so `=<` would also count as a word.
|
||||||
|
let add_label = RevRopeGraphemes::new(text.slice(..cursor_fwd.head))
|
||||||
|
.take(2)
|
||||||
|
.take_while(|g| g.chars().all(char_is_word))
|
||||||
|
.count()
|
||||||
|
== 2;
|
||||||
|
if !add_label {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
// skip any leading whitespace
|
||||||
|
cursor_fwd.anchor += text
|
||||||
|
.chars_at(cursor_fwd.anchor)
|
||||||
|
.take_while(|&c| !char_is_word(c))
|
||||||
|
.count();
|
||||||
|
words.push(cursor_fwd);
|
||||||
|
if words.len() == jump_label_limit {
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while cursor_rev.head > start {
|
||||||
|
cursor_rev = movement::move_prev_word_start(text, cursor_rev, 1);
|
||||||
|
// The cursor is on a word that is atleast two graphemes long and
|
||||||
|
// madeup of word characters. The latter condition is needed because
|
||||||
|
// move_prev_word_start simply treats a sequence of characters from
|
||||||
|
// the same char class as a word so `=<` would also count as a word.
|
||||||
|
let add_label = RopeGraphemes::new(text.slice(cursor_rev.head..))
|
||||||
|
.take(2)
|
||||||
|
.take_while(|g| g.chars().all(char_is_word))
|
||||||
|
.count()
|
||||||
|
== 2;
|
||||||
|
if !add_label {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
cursor_rev.anchor -= text
|
||||||
|
.chars_at(cursor_rev.anchor)
|
||||||
|
.reversed()
|
||||||
|
.take_while(|&c| !char_is_word(c))
|
||||||
|
.count();
|
||||||
|
words.push(cursor_rev);
|
||||||
|
if words.len() == jump_label_limit {
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if !changed {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jump_to_label(cx, words, behaviour)
|
||||||
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
|
||||||
"k" => move_line_up,
|
"k" => move_line_up,
|
||||||
"j" => move_line_down,
|
"j" => move_line_down,
|
||||||
"." => goto_last_modification,
|
"." => goto_last_modification,
|
||||||
|
"w" => goto_word,
|
||||||
},
|
},
|
||||||
":" => command_mode,
|
":" => command_mode,
|
||||||
|
|
||||||
|
@ -360,6 +361,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
|
||||||
"g" => { "Goto"
|
"g" => { "Goto"
|
||||||
"k" => extend_line_up,
|
"k" => extend_line_up,
|
||||||
"j" => extend_line_down,
|
"j" => extend_line_down,
|
||||||
|
"w" => extend_to_word,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
let insert = keymap!({ "Insert mode"
|
let insert = keymap!({ "Insert mode"
|
||||||
|
|
|
@ -8,7 +8,7 @@ use helix_core::chars::char_is_word;
|
||||||
use helix_core::doc_formatter::TextFormat;
|
use helix_core::doc_formatter::TextFormat;
|
||||||
use helix_core::encoding::Encoding;
|
use helix_core::encoding::Encoding;
|
||||||
use helix_core::syntax::{Highlight, LanguageServerFeature};
|
use helix_core::syntax::{Highlight, LanguageServerFeature};
|
||||||
use helix_core::text_annotations::InlineAnnotation;
|
use helix_core::text_annotations::{InlineAnnotation, Overlay};
|
||||||
use helix_lsp::util::lsp_pos_to_pos;
|
use helix_lsp::util::lsp_pos_to_pos;
|
||||||
use helix_vcs::{DiffHandle, DiffProviderRegistry};
|
use helix_vcs::{DiffHandle, DiffProviderRegistry};
|
||||||
|
|
||||||
|
@ -125,6 +125,7 @@ pub struct Document {
|
||||||
///
|
///
|
||||||
/// To know if they're up-to-date, check the `id` field in `DocumentInlayHints`.
|
/// To know if they're up-to-date, check the `id` field in `DocumentInlayHints`.
|
||||||
pub(crate) inlay_hints: HashMap<ViewId, DocumentInlayHints>,
|
pub(crate) inlay_hints: HashMap<ViewId, DocumentInlayHints>,
|
||||||
|
pub(crate) jump_labels: HashMap<ViewId, Vec<Overlay>>,
|
||||||
/// Set to `true` when the document is updated, reset to `false` on the next inlay hints
|
/// Set to `true` when the document is updated, reset to `false` on the next inlay hints
|
||||||
/// update from the LSP
|
/// update from the LSP
|
||||||
pub inlay_hints_oudated: bool,
|
pub inlay_hints_oudated: bool,
|
||||||
|
@ -665,6 +666,7 @@ impl Document {
|
||||||
version_control_head: None,
|
version_control_head: None,
|
||||||
focused_at: std::time::Instant::now(),
|
focused_at: std::time::Instant::now(),
|
||||||
readonly: false,
|
readonly: false,
|
||||||
|
jump_labels: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1138,6 +1140,7 @@ impl Document {
|
||||||
pub fn remove_view(&mut self, view_id: ViewId) {
|
pub fn remove_view(&mut self, view_id: ViewId) {
|
||||||
self.selections.remove(&view_id);
|
self.selections.remove(&view_id);
|
||||||
self.inlay_hints.remove(&view_id);
|
self.inlay_hints.remove(&view_id);
|
||||||
|
self.jump_labels.remove(&view_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a [`Transaction`] to the [`Document`] to change its text.
|
/// Apply a [`Transaction`] to the [`Document`] to change its text.
|
||||||
|
@ -1943,6 +1946,14 @@ impl Document {
|
||||||
self.inlay_hints.insert(view_id, inlay_hints);
|
self.inlay_hints.insert(view_id, inlay_hints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_jump_labels(&mut self, view_id: ViewId, labels: Vec<Overlay>) {
|
||||||
|
self.jump_labels.insert(view_id, labels);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_jump_labels(&mut self, view_id: ViewId) {
|
||||||
|
self.jump_labels.remove(&view_id);
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the inlay hints for this document and `view_id`.
|
/// Get the inlay hints for this document and `view_id`.
|
||||||
pub fn inlay_hints(&self, view_id: ViewId) -> Option<&DocumentInlayHints> {
|
pub fn inlay_hints(&self, view_id: ViewId) -> Option<&DocumentInlayHints> {
|
||||||
self.inlay_hints.get(&view_id)
|
self.inlay_hints.get(&view_id)
|
||||||
|
|
|
@ -22,7 +22,7 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap, HashSet},
|
||||||
fs,
|
fs,
|
||||||
io::{self, stdin},
|
io::{self, stdin},
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
|
@ -212,6 +212,23 @@ impl Default for FilePickerConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deserialize_alphabet<'de, D>(deserializer: D) -> Result<Vec<char>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
use serde::de::Error;
|
||||||
|
|
||||||
|
let str = String::deserialize(deserializer)?;
|
||||||
|
let chars: Vec<_> = str.chars().collect();
|
||||||
|
let unique_chars: HashSet<_> = chars.iter().copied().collect();
|
||||||
|
if unique_chars.len() != chars.len() {
|
||||||
|
return Err(<D::Error as Error>::custom(
|
||||||
|
"jump-label-alphabet must contain unique characters",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(chars)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
@ -305,6 +322,9 @@ pub struct Config {
|
||||||
/// Which indent heuristic to use when a new line is inserted
|
/// Which indent heuristic to use when a new line is inserted
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub indent_heuristic: IndentationHeuristic,
|
pub indent_heuristic: IndentationHeuristic,
|
||||||
|
/// labels characters used in jumpmode
|
||||||
|
#[serde(skip_serializing, deserialize_with = "deserialize_alphabet")]
|
||||||
|
pub jump_label_alphabet: Vec<char>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq, PartialOrd, Ord)]
|
||||||
|
@ -870,6 +890,7 @@ impl Default for Config {
|
||||||
smart_tab: Some(SmartTabConfig::default()),
|
smart_tab: Some(SmartTabConfig::default()),
|
||||||
popup_border: PopupBorderConfig::None,
|
popup_border: PopupBorderConfig::None,
|
||||||
indent_heuristic: IndentationHeuristic::default(),
|
indent_heuristic: IndentationHeuristic::default(),
|
||||||
|
jump_label_alphabet: ('a'..='z').collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -415,6 +415,13 @@ impl View {
|
||||||
) -> TextAnnotations<'a> {
|
) -> TextAnnotations<'a> {
|
||||||
let mut text_annotations = TextAnnotations::default();
|
let mut text_annotations = TextAnnotations::default();
|
||||||
|
|
||||||
|
if let Some(labels) = doc.jump_labels.get(&self.id) {
|
||||||
|
let style = theme
|
||||||
|
.and_then(|t| t.find_scope_index("ui.virtual.jump-label"))
|
||||||
|
.map(Highlight);
|
||||||
|
text_annotations.add_overlay(labels, style);
|
||||||
|
}
|
||||||
|
|
||||||
let DocumentInlayHints {
|
let DocumentInlayHints {
|
||||||
id: _,
|
id: _,
|
||||||
type_inlay_hints,
|
type_inlay_hints,
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
"constructor" = "green"
|
"constructor" = "green"
|
||||||
"module" = "yellow"
|
"module" = "yellow"
|
||||||
"special" = "blue"
|
"special" = "blue"
|
||||||
|
"ui.virtual.jump-label" = { fg = "#00dfff", modifiers = ["bold"] }
|
||||||
|
|
||||||
"markup.heading.marker" = "grey1"
|
"markup.heading.marker" = "grey1"
|
||||||
"markup.heading.1" = { fg = "red", modifiers = ["bold"] }
|
"markup.heading.1" = { fg = "red", modifiers = ["bold"] }
|
||||||
|
|
|
@ -52,11 +52,11 @@ label = "honey"
|
||||||
"ui.popup" = { bg = "revolver" }
|
"ui.popup" = { bg = "revolver" }
|
||||||
"ui.window" = { fg = "bossanova" }
|
"ui.window" = { fg = "bossanova" }
|
||||||
"ui.help" = { bg = "#7958DC", fg = "#171452" }
|
"ui.help" = { bg = "#7958DC", fg = "#171452" }
|
||||||
|
|
||||||
"ui.text" = { fg = "lavender" }
|
"ui.text" = { fg = "lavender" }
|
||||||
"ui.text.focus" = { fg = "white" }
|
"ui.text.focus" = { fg = "white" }
|
||||||
"ui.text.inactive" = "sirocco"
|
"ui.text.inactive" = "sirocco"
|
||||||
"ui.virtual" = { fg = "comet" }
|
"ui.virtual" = { fg = "comet" }
|
||||||
|
"ui.virtual.jump-label" = { fg = "apricot", modifiers = ["bold"] }
|
||||||
|
|
||||||
"ui.virtual.indent-guide" = { fg = "comet" }
|
"ui.virtual.indent-guide" = { fg = "comet" }
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue