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
@@ -29,7 +29,6 @@ use crate::{
|
||||
lsp_command::{self, *},
|
||||
lsp_store::{
|
||||
self,
|
||||
inlay_hint_cache::BufferChunk,
|
||||
log_store::{GlobalLogStore, LanguageServerKind},
|
||||
},
|
||||
manifest_tree::{
|
||||
@@ -73,6 +72,7 @@ use language::{
|
||||
serialize_lsp_edit, serialize_version,
|
||||
},
|
||||
range_from_lsp, range_to_lsp,
|
||||
row_chunk::RowChunk,
|
||||
};
|
||||
use lsp::{
|
||||
AdapterServerCapabilities, CodeActionKind, CompletionContext, CompletionOptions,
|
||||
@@ -117,7 +117,7 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use sum_tree::Dimensions;
|
||||
use text::{Anchor, BufferId, LineEnding, OffsetRangeExt, Point, ToPoint as _};
|
||||
use text::{Anchor, BufferId, LineEnding, OffsetRangeExt, ToPoint as _};
|
||||
|
||||
use util::{
|
||||
ConnectionResult, ResultExt as _, debug_panic, defer, maybe, merge_json_value_into,
|
||||
@@ -3590,7 +3590,7 @@ pub struct BufferLspData {
|
||||
code_lens: Option<CodeLensData>,
|
||||
inlay_hints: BufferInlayHints,
|
||||
lsp_requests: HashMap<LspKey, HashMap<LspRequestId, Task<()>>>,
|
||||
chunk_lsp_requests: HashMap<LspKey, HashMap<BufferChunk, LspRequestId>>,
|
||||
chunk_lsp_requests: HashMap<LspKey, HashMap<RowChunk, LspRequestId>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@@ -6706,7 +6706,7 @@ impl LspStore {
|
||||
self.latest_lsp_data(buffer, cx)
|
||||
.inlay_hints
|
||||
.applicable_chunks(ranges)
|
||||
.map(|chunk| chunk.start..chunk.end)
|
||||
.map(|chunk| chunk.row_range())
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -6729,7 +6729,6 @@ impl LspStore {
|
||||
known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>> {
|
||||
let buffer_snapshot = buffer.read(cx).snapshot();
|
||||
let next_hint_id = self.next_hint_id.clone();
|
||||
let lsp_data = self.latest_lsp_data(&buffer, cx);
|
||||
let query_version = lsp_data.buffer_version.clone();
|
||||
@@ -6758,14 +6757,12 @@ impl LspStore {
|
||||
let mut ranges_to_query = None;
|
||||
let applicable_chunks = existing_inlay_hints
|
||||
.applicable_chunks(ranges.as_slice())
|
||||
.filter(|chunk| !known_chunks.contains(&(chunk.start..chunk.end)))
|
||||
.filter(|chunk| !known_chunks.contains(&chunk.row_range()))
|
||||
.collect::<Vec<_>>();
|
||||
if applicable_chunks.is_empty() {
|
||||
return HashMap::default();
|
||||
}
|
||||
|
||||
let last_chunk_number = existing_inlay_hints.buffer_chunks_len() - 1;
|
||||
|
||||
for row_chunk in applicable_chunks {
|
||||
match (
|
||||
existing_inlay_hints
|
||||
@@ -6779,16 +6776,12 @@ impl LspStore {
|
||||
.cloned(),
|
||||
) {
|
||||
(None, None) => {
|
||||
let end = if last_chunk_number == row_chunk.id {
|
||||
Point::new(row_chunk.end, buffer_snapshot.line_len(row_chunk.end))
|
||||
} else {
|
||||
Point::new(row_chunk.end, 0)
|
||||
let Some(chunk_range) = existing_inlay_hints.chunk_range(row_chunk) else {
|
||||
continue;
|
||||
};
|
||||
ranges_to_query.get_or_insert_with(Vec::new).push((
|
||||
row_chunk,
|
||||
buffer_snapshot.anchor_before(Point::new(row_chunk.start, 0))
|
||||
..buffer_snapshot.anchor_after(end),
|
||||
));
|
||||
ranges_to_query
|
||||
.get_or_insert_with(Vec::new)
|
||||
.push((row_chunk, chunk_range));
|
||||
}
|
||||
(None, Some(fetched_hints)) => hint_fetch_tasks.push((row_chunk, fetched_hints)),
|
||||
(Some(cached_hints), None) => {
|
||||
@@ -6796,7 +6789,7 @@ impl LspStore {
|
||||
if for_server.is_none_or(|for_server| for_server == server_id) {
|
||||
cached_inlay_hints
|
||||
.get_or_insert_with(HashMap::default)
|
||||
.entry(row_chunk.start..row_chunk.end)
|
||||
.entry(row_chunk.row_range())
|
||||
.or_insert_with(HashMap::default)
|
||||
.entry(server_id)
|
||||
.or_insert_with(Vec::new)
|
||||
@@ -6810,7 +6803,7 @@ impl LspStore {
|
||||
if for_server.is_none_or(|for_server| for_server == server_id) {
|
||||
cached_inlay_hints
|
||||
.get_or_insert_with(HashMap::default)
|
||||
.entry(row_chunk.start..row_chunk.end)
|
||||
.entry(row_chunk.row_range())
|
||||
.or_insert_with(HashMap::default)
|
||||
.entry(server_id)
|
||||
.or_insert_with(Vec::new)
|
||||
@@ -6896,7 +6889,7 @@ impl LspStore {
|
||||
.map(|(row_chunk, hints)| (row_chunk, Task::ready(Ok(hints))))
|
||||
.chain(hint_fetch_tasks.into_iter().map(|(chunk, hints_fetch)| {
|
||||
(
|
||||
chunk.start..chunk.end,
|
||||
chunk.row_range(),
|
||||
cx.spawn(async move |_, _| {
|
||||
hints_fetch.await.map_err(|e| {
|
||||
if e.error_code() != ErrorCode::Internal {
|
||||
|
||||
@@ -3,10 +3,12 @@ use std::{collections::hash_map, ops::Range, sync::Arc};
|
||||
use collections::HashMap;
|
||||
use futures::future::Shared;
|
||||
use gpui::{App, Entity, Task};
|
||||
use language::{Buffer, BufferRow, BufferSnapshot};
|
||||
use language::{
|
||||
Buffer,
|
||||
row_chunk::{RowChunk, RowChunks},
|
||||
};
|
||||
use lsp::LanguageServerId;
|
||||
use text::OffsetRangeExt;
|
||||
use util::RangeExt as _;
|
||||
use text::Anchor;
|
||||
|
||||
use crate::{InlayHint, InlayId};
|
||||
|
||||
@@ -46,8 +48,7 @@ impl InvalidationStrategy {
|
||||
}
|
||||
|
||||
pub struct BufferInlayHints {
|
||||
snapshot: BufferSnapshot,
|
||||
buffer_chunks: Vec<BufferChunk>,
|
||||
chunks: RowChunks,
|
||||
hints_by_chunks: Vec<Option<CacheInlayHints>>,
|
||||
fetches_by_chunks: Vec<Option<CacheInlayHintsTask>>,
|
||||
hints_by_id: HashMap<InlayId, HintForId>,
|
||||
@@ -62,25 +63,10 @@ struct HintForId {
|
||||
position: usize,
|
||||
}
|
||||
|
||||
/// An range of rows, exclusive as [`lsp::Range`] and
|
||||
/// <https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#range>
|
||||
/// denote.
|
||||
///
|
||||
/// Represents an area in a text editor, adjacent to other ones.
|
||||
/// Together, chunks form entire document at a particular version [`clock::Global`].
|
||||
/// Each chunk is queried for inlays as `(start_row, 0)..(end_exclusive, 0)` via
|
||||
/// <https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#inlayHintParams>
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct BufferChunk {
|
||||
pub id: usize,
|
||||
pub start: BufferRow,
|
||||
pub end: BufferRow,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for BufferInlayHints {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("BufferInlayHints")
|
||||
.field("buffer_chunks", &self.buffer_chunks)
|
||||
.field("buffer_chunks", &self.chunks)
|
||||
.field("hints_by_chunks", &self.hints_by_chunks)
|
||||
.field("fetches_by_chunks", &self.fetches_by_chunks)
|
||||
.field("hints_by_id", &self.hints_by_id)
|
||||
@@ -92,58 +78,30 @@ const MAX_ROWS_IN_A_CHUNK: u32 = 50;
|
||||
|
||||
impl BufferInlayHints {
|
||||
pub fn new(buffer: &Entity<Buffer>, cx: &mut App) -> Self {
|
||||
let buffer = buffer.read(cx);
|
||||
let snapshot = buffer.snapshot();
|
||||
let buffer_point_range = (0..buffer.len()).to_point(&snapshot);
|
||||
let last_row = buffer_point_range.end.row;
|
||||
let buffer_chunks = (buffer_point_range.start.row..=last_row)
|
||||
.step_by(MAX_ROWS_IN_A_CHUNK as usize)
|
||||
.enumerate()
|
||||
.map(|(id, chunk_start)| BufferChunk {
|
||||
id,
|
||||
start: chunk_start,
|
||||
end: (chunk_start + MAX_ROWS_IN_A_CHUNK).min(last_row),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let chunks = RowChunks::new(buffer.read(cx).text_snapshot(), MAX_ROWS_IN_A_CHUNK);
|
||||
|
||||
Self {
|
||||
hints_by_chunks: vec![None; buffer_chunks.len()],
|
||||
fetches_by_chunks: vec![None; buffer_chunks.len()],
|
||||
hints_by_chunks: vec![None; chunks.len()],
|
||||
fetches_by_chunks: vec![None; chunks.len()],
|
||||
latest_invalidation_requests: HashMap::default(),
|
||||
hints_by_id: HashMap::default(),
|
||||
hint_resolves: HashMap::default(),
|
||||
snapshot,
|
||||
buffer_chunks,
|
||||
chunks,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn applicable_chunks(
|
||||
&self,
|
||||
ranges: &[Range<text::Anchor>],
|
||||
) -> impl Iterator<Item = BufferChunk> {
|
||||
let row_ranges = ranges
|
||||
.iter()
|
||||
.map(|range| range.to_point(&self.snapshot))
|
||||
// Be lenient and yield multiple chunks if they "touch" the exclusive part of the range.
|
||||
// This will result in LSP hints [re-]queried for more ranges, but also more hints already visible when scrolling around.
|
||||
.map(|point_range| point_range.start.row..point_range.end.row + 1)
|
||||
.collect::<Vec<_>>();
|
||||
self.buffer_chunks
|
||||
.iter()
|
||||
.filter(move |chunk| {
|
||||
let chunk_range = chunk.start..=chunk.end;
|
||||
row_ranges
|
||||
.iter()
|
||||
.any(|row_range| chunk_range.overlaps(&row_range))
|
||||
})
|
||||
.copied()
|
||||
) -> impl Iterator<Item = RowChunk> {
|
||||
self.chunks.applicable_chunks(ranges)
|
||||
}
|
||||
|
||||
pub fn cached_hints(&mut self, chunk: &BufferChunk) -> Option<&CacheInlayHints> {
|
||||
pub fn cached_hints(&mut self, chunk: &RowChunk) -> Option<&CacheInlayHints> {
|
||||
self.hints_by_chunks[chunk.id].as_ref()
|
||||
}
|
||||
|
||||
pub fn fetched_hints(&mut self, chunk: &BufferChunk) -> &mut Option<CacheInlayHintsTask> {
|
||||
pub fn fetched_hints(&mut self, chunk: &RowChunk) -> &mut Option<CacheInlayHintsTask> {
|
||||
&mut self.fetches_by_chunks[chunk.id]
|
||||
}
|
||||
|
||||
@@ -177,8 +135,8 @@ impl BufferInlayHints {
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.hints_by_chunks = vec![None; self.buffer_chunks.len()];
|
||||
self.fetches_by_chunks = vec![None; self.buffer_chunks.len()];
|
||||
self.hints_by_chunks = vec![None; self.chunks.len()];
|
||||
self.fetches_by_chunks = vec![None; self.chunks.len()];
|
||||
self.hints_by_id.clear();
|
||||
self.hint_resolves.clear();
|
||||
self.latest_invalidation_requests.clear();
|
||||
@@ -186,7 +144,7 @@ impl BufferInlayHints {
|
||||
|
||||
pub fn insert_new_hints(
|
||||
&mut self,
|
||||
chunk: BufferChunk,
|
||||
chunk: RowChunk,
|
||||
server_id: LanguageServerId,
|
||||
new_hints: Vec<(InlayId, InlayHint)>,
|
||||
) {
|
||||
@@ -225,10 +183,6 @@ impl BufferInlayHints {
|
||||
Some(hint)
|
||||
}
|
||||
|
||||
pub fn buffer_chunks_len(&self) -> usize {
|
||||
self.buffer_chunks.len()
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_for_server_refresh(
|
||||
&mut self,
|
||||
for_server: LanguageServerId,
|
||||
@@ -263,7 +217,7 @@ impl BufferInlayHints {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn invalidate_for_chunk(&mut self, chunk: BufferChunk) {
|
||||
pub(crate) fn invalidate_for_chunk(&mut self, chunk: RowChunk) {
|
||||
self.fetches_by_chunks[chunk.id] = None;
|
||||
if let Some(hints_by_server) = self.hints_by_chunks[chunk.id].take() {
|
||||
for (hint_id, _) in hints_by_server.into_values().flatten() {
|
||||
@@ -272,4 +226,8 @@ impl BufferInlayHints {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chunk_range(&self, chunk: RowChunk) -> Option<Range<Anchor>> {
|
||||
self.chunks.chunk_range(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user