Support bracket colorization (rainbow brackets) (#43172)
Deals with https://github.com/zed-industries/zed/issues/5259 Highlights brackets with different colors based on their depth. Uses existing tree-sitter queries from brackets.scm to find brackets, uses theme's accents to color them. https://github.com/user-attachments/assets/cc5f3aba-22fa-446d-9af7-ba6e772029da 1. Adds `colorize_brackets` language setting that allows, per language or globally for all languages, to configure whether Zed should color the brackets for a particular language. Disabled for all languages by default. 2. Any given language can opt-out a certain bracket pair by amending the brackets.scm like `("\"" @open "\"" @close) ` -> `(("\"" @open "\"" @close) (#set! rainbow.exclude))` 3. Brackets are using colors from theme accents, which can be overridden as ```jsonc "theme_overrides": { "One Dark": { "accents": ["#ff69b4", "#7fff00", "#ff1493", "#00ffff", "#ff8c00", "#9400d3"] } }, ``` Release Notes: - Added bracket colorization (rainbow brackets) support. Use `colorize_brackets` language setting to enable. --------- Co-authored-by: MrSubidubi <dev@bahn.sh> Co-authored-by: Lukas Wirth <lukas@zed.dev> Co-authored-by: MrSubidubi <finn@zed.dev> Co-authored-by: Lukas Wirth <me@lukaswirth.dev> Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
This commit is contained in:
co-authored by
MrSubidubi
Lukas Wirth
MrSubidubi
Lukas Wirth
Smit Barmase
parent
e6e5ccbf10
commit
7e341bcf94
+92
-16
@@ -13,6 +13,7 @@
|
||||
//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
|
||||
pub mod actions;
|
||||
mod blink_manager;
|
||||
mod bracket_colorization;
|
||||
mod clangd_ext;
|
||||
pub mod code_context_menus;
|
||||
pub mod display_map;
|
||||
@@ -118,11 +119,11 @@ use language::{
|
||||
AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
|
||||
BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
|
||||
DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
|
||||
IndentSize, Language, LanguageRegistry, OffsetRangeExt, OutlineItem, Point, Runnable,
|
||||
Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery,
|
||||
IndentSize, Language, LanguageName, LanguageRegistry, OffsetRangeExt, OutlineItem, Point,
|
||||
Runnable, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery,
|
||||
language_settings::{
|
||||
self, LspInsertMode, RewrapBehavior, WordsCompletionMode, all_language_settings,
|
||||
language_settings,
|
||||
self, LanguageSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
|
||||
all_language_settings, language_settings,
|
||||
},
|
||||
point_from_lsp, point_to_lsp, text_diff_with_options,
|
||||
};
|
||||
@@ -175,6 +176,7 @@ use std::{
|
||||
borrow::Cow,
|
||||
cell::{OnceCell, RefCell},
|
||||
cmp::{self, Ordering, Reverse},
|
||||
collections::hash_map,
|
||||
iter::{self, Peekable},
|
||||
mem,
|
||||
num::NonZeroU32,
|
||||
@@ -1193,6 +1195,9 @@ pub struct Editor {
|
||||
folding_newlines: Task<()>,
|
||||
select_next_is_case_sensitive: Option<bool>,
|
||||
pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
|
||||
applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
|
||||
accent_overrides: Vec<SharedString>,
|
||||
fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
|
||||
}
|
||||
|
||||
fn debounce_value(debounce_ms: u64) -> Option<Duration> {
|
||||
@@ -2333,12 +2338,18 @@ impl Editor {
|
||||
folding_newlines: Task::ready(()),
|
||||
lookup_key: None,
|
||||
select_next_is_case_sensitive: None,
|
||||
applicable_language_settings: HashMap::default(),
|
||||
accent_overrides: Vec::new(),
|
||||
fetched_tree_sitter_chunks: HashMap::default(),
|
||||
};
|
||||
|
||||
if is_minimap {
|
||||
return editor;
|
||||
}
|
||||
|
||||
editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
|
||||
editor.accent_overrides = editor.fetch_accent_overrides(cx);
|
||||
|
||||
if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
|
||||
editor
|
||||
._subscriptions
|
||||
@@ -2378,6 +2389,7 @@ impl Editor {
|
||||
InlayHintRefreshReason::NewLinesShown,
|
||||
cx,
|
||||
);
|
||||
editor.colorize_brackets(false, cx);
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
@@ -21141,13 +21153,16 @@ impl Editor {
|
||||
key: usize,
|
||||
ranges: Vec<Range<Anchor>>,
|
||||
style: HighlightStyle,
|
||||
merge: bool,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.display_map.update(cx, |map, _| {
|
||||
self.display_map.update(cx, |map, cx| {
|
||||
map.highlight_text(
|
||||
HighlightKey::TypePlus(TypeId::of::<T>(), key),
|
||||
ranges,
|
||||
style,
|
||||
merge,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
cx.notify();
|
||||
@@ -21159,8 +21174,14 @@ impl Editor {
|
||||
style: HighlightStyle,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.display_map.update(cx, |map, _| {
|
||||
map.highlight_text(HighlightKey::Type(TypeId::of::<T>()), ranges, style)
|
||||
self.display_map.update(cx, |map, cx| {
|
||||
map.highlight_text(
|
||||
HighlightKey::Type(TypeId::of::<T>()),
|
||||
ranges,
|
||||
style,
|
||||
false,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
@@ -21308,7 +21329,6 @@ impl Editor {
|
||||
self.active_indent_guides_state.dirty = true;
|
||||
self.refresh_active_diagnostics(cx);
|
||||
self.refresh_code_actions(window, cx);
|
||||
self.refresh_selected_text_highlights(true, window, cx);
|
||||
self.refresh_single_line_folds(window, cx);
|
||||
self.refresh_matching_bracket_highlights(window, cx);
|
||||
if self.has_active_edit_prediction() {
|
||||
@@ -21364,6 +21384,7 @@ impl Editor {
|
||||
}
|
||||
self.update_lsp_data(Some(buffer_id), window, cx);
|
||||
self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
|
||||
self.colorize_brackets(false, cx);
|
||||
cx.emit(EditorEvent::ExcerptsAdded {
|
||||
buffer: buffer.clone(),
|
||||
predecessor: *predecessor,
|
||||
@@ -21401,10 +21422,16 @@ impl Editor {
|
||||
multi_buffer::Event::ExcerptsExpanded { ids } => {
|
||||
self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
|
||||
self.refresh_document_highlights(cx);
|
||||
for id in ids {
|
||||
self.fetched_tree_sitter_chunks.remove(id);
|
||||
}
|
||||
self.colorize_brackets(false, cx);
|
||||
cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
|
||||
}
|
||||
multi_buffer::Event::Reparsed(buffer_id) => {
|
||||
self.tasks_update_task = Some(self.refresh_runnables(window, cx));
|
||||
self.refresh_selected_text_highlights(true, window, cx);
|
||||
self.colorize_brackets(true, cx);
|
||||
jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
|
||||
|
||||
cx.emit(EditorEvent::Reparsed(*buffer_id));
|
||||
@@ -21475,7 +21502,52 @@ impl Editor {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn fetch_accent_overrides(&self, cx: &App) -> Vec<SharedString> {
|
||||
if !self.mode.is_full() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
theme::ThemeSettings::get_global(cx)
|
||||
.theme_overrides
|
||||
.get(cx.theme().name.as_ref())
|
||||
.map(|theme_style| &theme_style.accents)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.flat_map(|accent| accent.0.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn fetch_applicable_language_settings(
|
||||
&self,
|
||||
cx: &App,
|
||||
) -> HashMap<Option<LanguageName>, LanguageSettings> {
|
||||
if !self.mode.is_full() {
|
||||
return HashMap::default();
|
||||
}
|
||||
|
||||
self.buffer().read(cx).all_buffers().into_iter().fold(
|
||||
HashMap::default(),
|
||||
|mut acc, buffer| {
|
||||
let buffer = buffer.read(cx);
|
||||
let language = buffer.language().map(|language| language.name());
|
||||
if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
|
||||
let file = buffer.file();
|
||||
v.insert(language_settings(language, file, cx).into_owned());
|
||||
}
|
||||
acc
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let new_language_settings = self.fetch_applicable_language_settings(cx);
|
||||
let language_settings_changed = new_language_settings != self.applicable_language_settings;
|
||||
self.applicable_language_settings = new_language_settings;
|
||||
|
||||
let new_accent_overrides = self.fetch_accent_overrides(cx);
|
||||
let accent_overrides_changed = new_accent_overrides != self.accent_overrides;
|
||||
self.accent_overrides = new_accent_overrides;
|
||||
|
||||
if self.diagnostics_enabled() {
|
||||
let new_severity = EditorSettings::get_global(cx)
|
||||
.diagnostics_max_severity
|
||||
@@ -21547,15 +21619,19 @@ impl Editor {
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
|
||||
colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
|
||||
}) {
|
||||
if !inlay_splice.is_empty() {
|
||||
self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
|
||||
if language_settings_changed || accent_overrides_changed {
|
||||
self.colorize_brackets(true, cx);
|
||||
}
|
||||
|
||||
if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
|
||||
colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
|
||||
}) {
|
||||
if !inlay_splice.is_empty() {
|
||||
self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
|
||||
}
|
||||
self.refresh_colors_for_visible_range(None, window, cx);
|
||||
}
|
||||
self.refresh_colors_for_visible_range(None, window, cx);
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
@@ -22668,7 +22744,7 @@ fn insert_extra_newline_tree_sitter(
|
||||
_ => return false,
|
||||
};
|
||||
let pair = {
|
||||
let mut result: Option<BracketMatch> = None;
|
||||
let mut result: Option<BracketMatch<usize>> = None;
|
||||
|
||||
for pair in buffer
|
||||
.all_bracket_ranges(range.start.0..range.end.0)
|
||||
|
||||
Reference in New Issue
Block a user