Indent guides (#11503)

Builds on top of existing work from #2249, but here's a showcase:


https://github.com/zed-industries/zed/assets/53836821/4b346965-6654-496c-b379-75425d9b493f

TODO:
- [x] handle line wrapping
- [x] implement handling in multibuffer (crashes currently)
- [x] add configuration option
- [x] new theme properties? What colors to use?
- [x] Possibly support indents with different colors or background
colors
- [x] investigate edge cases (e.g. indent guides and folds continue on
empty lines even if the next indent is different)
- [x] add more tests (also test `find_active_indent_index`)
- [x] docs (will do in a follow up PR)
- [x] benchmark performance impact

Release Notes:

- Added indent guides
([#5373](https://github.com/zed-industries/zed/issues/5373))

---------

Co-authored-by: Nate Butler <1714999+iamnbutler@users.noreply.github.com>
Co-authored-by: Remco <djsmits12@gmail.com>
This commit is contained in:
Bennet Bo Fenner 2024-05-23 15:50:59 +02:00 committed by GitHub
parent 3eb0418bda
commit feea607bac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 1705 additions and 65 deletions

View file

@ -12,9 +12,9 @@ use language::{
char_kind,
language_settings::{language_settings, LanguageSettings},
AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, CharKind, Chunk,
CursorShape, DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt,
OffsetUtf16, Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
CursorShape, DiagnosticEntry, File, IndentGuide, IndentSize, Language, LanguageScope,
OffsetRangeExt, OffsetUtf16, Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension,
ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
};
use smallvec::SmallVec;
use std::{
@ -281,6 +281,20 @@ struct ExcerptBytes<'a> {
reversed: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct MultiBufferIndentGuide {
pub multibuffer_row_range: Range<MultiBufferRow>,
pub buffer: IndentGuide,
}
impl std::ops::Deref for MultiBufferIndentGuide {
type Target = IndentGuide;
fn deref(&self) -> &Self::Target {
&self.buffer
}
}
impl MultiBuffer {
pub fn new(replica_id: ReplicaId, capability: Capability) -> Self {
Self {
@ -1255,6 +1269,15 @@ impl MultiBuffer {
excerpts
}
pub fn excerpt_buffer_ids(&self) -> Vec<BufferId> {
self.snapshot
.borrow()
.excerpts
.iter()
.map(|entry| entry.buffer_id)
.collect()
}
pub fn excerpt_ids(&self) -> Vec<ExcerptId> {
self.snapshot
.borrow()
@ -3182,6 +3205,52 @@ impl MultiBufferSnapshot {
})
}
pub fn indent_guides_in_range(
&self,
range: Range<Anchor>,
cx: &AppContext,
) -> Vec<MultiBufferIndentGuide> {
// Fast path for singleton buffers, we can skip the conversion between offsets.
if let Some((_, _, snapshot)) = self.as_singleton() {
return snapshot
.indent_guides_in_range(range.start.text_anchor..range.end.text_anchor, cx)
.into_iter()
.map(|guide| MultiBufferIndentGuide {
multibuffer_row_range: MultiBufferRow(guide.start_row)
..MultiBufferRow(guide.end_row),
buffer: guide,
})
.collect();
}
let range = range.start.to_offset(self)..range.end.to_offset(self);
self.excerpts_for_range(range.clone())
.flat_map(move |(excerpt, excerpt_offset)| {
let excerpt_buffer_start_row =
excerpt.range.context.start.to_point(&excerpt.buffer).row;
let excerpt_offset_row = crate::ToPoint::to_point(&excerpt_offset, self).row;
excerpt
.buffer
.indent_guides_in_range(excerpt.range.context.clone(), cx)
.into_iter()
.map(move |indent_guide| {
let start_row = excerpt_offset_row
+ (indent_guide.start_row - excerpt_buffer_start_row);
let end_row =
excerpt_offset_row + (indent_guide.end_row - excerpt_buffer_start_row);
MultiBufferIndentGuide {
multibuffer_row_range: MultiBufferRow(start_row)
..MultiBufferRow(end_row),
buffer: indent_guide,
}
})
})
.collect()
}
pub fn diagnostics_update_count(&self) -> usize {
self.diagnostics_update_count
}