Extract logic around custom text highlights out of InlayChunks iterator (#22104)
This is a pure refactor, extracted from https://github.com/zed-industries/zed/tree/new-diff-map Release Notes: - N/A Co-authored-by: Conrad <conrad@zed.dev> Co-authored-by: Agus <agus@zed.dev>
This commit is contained in:
parent
4bf005ef52
commit
8127decd2d
3 changed files with 191 additions and 161 deletions
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
mod block_map;
|
mod block_map;
|
||||||
mod crease_map;
|
mod crease_map;
|
||||||
|
mod custom_highlights;
|
||||||
mod fold_map;
|
mod fold_map;
|
||||||
mod inlay_map;
|
mod inlay_map;
|
||||||
pub(crate) mod invisibles;
|
pub(crate) mod invisibles;
|
||||||
|
|
174
crates/editor/src/display_map/custom_highlights.rs
Normal file
174
crates/editor/src/display_map/custom_highlights.rs
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
use collections::BTreeMap;
|
||||||
|
use gpui::HighlightStyle;
|
||||||
|
use language::Chunk;
|
||||||
|
use multi_buffer::{Anchor, MultiBufferChunks, MultiBufferSnapshot, ToOffset as _};
|
||||||
|
use std::{
|
||||||
|
any::TypeId,
|
||||||
|
cmp,
|
||||||
|
iter::{self, Peekable},
|
||||||
|
ops::Range,
|
||||||
|
sync::Arc,
|
||||||
|
vec,
|
||||||
|
};
|
||||||
|
use sum_tree::TreeMap;
|
||||||
|
|
||||||
|
pub struct CustomHighlightsChunks<'a> {
|
||||||
|
buffer_chunks: MultiBufferChunks<'a>,
|
||||||
|
buffer_chunk: Option<Chunk<'a>>,
|
||||||
|
offset: usize,
|
||||||
|
multibuffer_snapshot: &'a MultiBufferSnapshot,
|
||||||
|
|
||||||
|
highlight_endpoints: Peekable<vec::IntoIter<HighlightEndpoint>>,
|
||||||
|
active_highlights: BTreeMap<TypeId, HighlightStyle>,
|
||||||
|
text_highlights: Option<&'a TreeMap<TypeId, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
struct HighlightEndpoint {
|
||||||
|
offset: usize,
|
||||||
|
is_start: bool,
|
||||||
|
tag: TypeId,
|
||||||
|
style: HighlightStyle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CustomHighlightsChunks<'a> {
|
||||||
|
pub fn new(
|
||||||
|
range: Range<usize>,
|
||||||
|
language_aware: bool,
|
||||||
|
text_highlights: Option<&'a TreeMap<TypeId, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>>,
|
||||||
|
multibuffer_snapshot: &'a MultiBufferSnapshot,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
buffer_chunks: multibuffer_snapshot.chunks(range.clone(), language_aware),
|
||||||
|
buffer_chunk: None,
|
||||||
|
offset: range.start,
|
||||||
|
|
||||||
|
text_highlights,
|
||||||
|
highlight_endpoints: create_highlight_endpoints(
|
||||||
|
&range,
|
||||||
|
text_highlights,
|
||||||
|
multibuffer_snapshot,
|
||||||
|
),
|
||||||
|
active_highlights: Default::default(),
|
||||||
|
multibuffer_snapshot,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seek(&mut self, new_range: Range<usize>) {
|
||||||
|
self.highlight_endpoints =
|
||||||
|
create_highlight_endpoints(&new_range, self.text_highlights, self.multibuffer_snapshot);
|
||||||
|
self.offset = new_range.start;
|
||||||
|
self.buffer_chunks.seek(new_range);
|
||||||
|
self.buffer_chunk.take();
|
||||||
|
self.active_highlights.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_highlight_endpoints(
|
||||||
|
range: &Range<usize>,
|
||||||
|
text_highlights: Option<&TreeMap<TypeId, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>>,
|
||||||
|
buffer: &MultiBufferSnapshot,
|
||||||
|
) -> iter::Peekable<vec::IntoIter<HighlightEndpoint>> {
|
||||||
|
let mut highlight_endpoints = Vec::new();
|
||||||
|
if let Some(text_highlights) = text_highlights {
|
||||||
|
let start = buffer.anchor_after(range.start);
|
||||||
|
let end = buffer.anchor_after(range.end);
|
||||||
|
for (&tag, text_highlights) in text_highlights.iter() {
|
||||||
|
let style = text_highlights.0;
|
||||||
|
let ranges = &text_highlights.1;
|
||||||
|
|
||||||
|
let start_ix = match ranges.binary_search_by(|probe| {
|
||||||
|
let cmp = probe.end.cmp(&start, &buffer);
|
||||||
|
if cmp.is_gt() {
|
||||||
|
cmp::Ordering::Greater
|
||||||
|
} else {
|
||||||
|
cmp::Ordering::Less
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Ok(i) | Err(i) => i,
|
||||||
|
};
|
||||||
|
|
||||||
|
for range in &ranges[start_ix..] {
|
||||||
|
if range.start.cmp(&end, &buffer).is_ge() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
highlight_endpoints.push(HighlightEndpoint {
|
||||||
|
offset: range.start.to_offset(&buffer),
|
||||||
|
is_start: true,
|
||||||
|
tag,
|
||||||
|
style,
|
||||||
|
});
|
||||||
|
highlight_endpoints.push(HighlightEndpoint {
|
||||||
|
offset: range.end.to_offset(&buffer),
|
||||||
|
is_start: false,
|
||||||
|
tag,
|
||||||
|
style,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
highlight_endpoints.sort();
|
||||||
|
}
|
||||||
|
highlight_endpoints.into_iter().peekable()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for CustomHighlightsChunks<'a> {
|
||||||
|
type Item = Chunk<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let mut next_highlight_endpoint = usize::MAX;
|
||||||
|
while let Some(endpoint) = self.highlight_endpoints.peek().copied() {
|
||||||
|
if endpoint.offset <= self.offset {
|
||||||
|
if endpoint.is_start {
|
||||||
|
self.active_highlights.insert(endpoint.tag, endpoint.style);
|
||||||
|
} else {
|
||||||
|
self.active_highlights.remove(&endpoint.tag);
|
||||||
|
}
|
||||||
|
self.highlight_endpoints.next();
|
||||||
|
} else {
|
||||||
|
next_highlight_endpoint = endpoint.offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let chunk = self
|
||||||
|
.buffer_chunk
|
||||||
|
.get_or_insert_with(|| self.buffer_chunks.next().unwrap());
|
||||||
|
if chunk.text.is_empty() {
|
||||||
|
*chunk = self.buffer_chunks.next().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let (prefix, suffix) = chunk
|
||||||
|
.text
|
||||||
|
.split_at(chunk.text.len().min(next_highlight_endpoint - self.offset));
|
||||||
|
|
||||||
|
chunk.text = suffix;
|
||||||
|
self.offset += prefix.len();
|
||||||
|
let mut prefix = Chunk {
|
||||||
|
text: prefix,
|
||||||
|
..chunk.clone()
|
||||||
|
};
|
||||||
|
if !self.active_highlights.is_empty() {
|
||||||
|
let mut highlight_style = HighlightStyle::default();
|
||||||
|
for active_highlight in self.active_highlights.values() {
|
||||||
|
highlight_style.highlight(*active_highlight);
|
||||||
|
}
|
||||||
|
prefix.highlight_style = Some(highlight_style);
|
||||||
|
}
|
||||||
|
Some(prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for HighlightEndpoint {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for HighlightEndpoint {
|
||||||
|
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||||
|
self.offset
|
||||||
|
.cmp(&other.offset)
|
||||||
|
.then_with(|| other.is_start.cmp(&self.is_start))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,22 +1,15 @@
|
||||||
use crate::{HighlightStyles, InlayId};
|
use crate::{HighlightStyles, InlayId};
|
||||||
use collections::{BTreeMap, BTreeSet};
|
use collections::BTreeSet;
|
||||||
use gpui::HighlightStyle;
|
|
||||||
use language::{Chunk, Edit, Point, TextSummary};
|
use language::{Chunk, Edit, Point, TextSummary};
|
||||||
use multi_buffer::{
|
use multi_buffer::{Anchor, MultiBufferRow, MultiBufferRows, MultiBufferSnapshot, ToOffset};
|
||||||
Anchor, MultiBufferChunks, MultiBufferRow, MultiBufferRows, MultiBufferSnapshot, ToOffset,
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
|
||||||
cmp,
|
cmp,
|
||||||
iter::Peekable,
|
|
||||||
ops::{Add, AddAssign, Range, Sub, SubAssign},
|
ops::{Add, AddAssign, Range, Sub, SubAssign},
|
||||||
sync::Arc,
|
|
||||||
vec,
|
|
||||||
};
|
};
|
||||||
use sum_tree::{Bias, Cursor, SumTree, TreeMap};
|
use sum_tree::{Bias, Cursor, SumTree};
|
||||||
use text::{Patch, Rope};
|
use text::{Patch, Rope};
|
||||||
|
|
||||||
use super::Highlights;
|
use super::{custom_highlights::CustomHighlightsChunks, Highlights};
|
||||||
|
|
||||||
/// Decides where the [`Inlay`]s should be displayed.
|
/// Decides where the [`Inlay`]s should be displayed.
|
||||||
///
|
///
|
||||||
|
@ -207,39 +200,15 @@ pub struct InlayBufferRows<'a> {
|
||||||
max_buffer_row: MultiBufferRow,
|
max_buffer_row: MultiBufferRow,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
struct HighlightEndpoint {
|
|
||||||
offset: InlayOffset,
|
|
||||||
is_start: bool,
|
|
||||||
tag: TypeId,
|
|
||||||
style: HighlightStyle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for HighlightEndpoint {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for HighlightEndpoint {
|
|
||||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
|
||||||
self.offset
|
|
||||||
.cmp(&other.offset)
|
|
||||||
.then_with(|| other.is_start.cmp(&self.is_start))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InlayChunks<'a> {
|
pub struct InlayChunks<'a> {
|
||||||
transforms: Cursor<'a, Transform, (InlayOffset, usize)>,
|
transforms: Cursor<'a, Transform, (InlayOffset, usize)>,
|
||||||
buffer_chunks: MultiBufferChunks<'a>,
|
buffer_chunks: CustomHighlightsChunks<'a>,
|
||||||
buffer_chunk: Option<Chunk<'a>>,
|
buffer_chunk: Option<Chunk<'a>>,
|
||||||
inlay_chunks: Option<text::Chunks<'a>>,
|
inlay_chunks: Option<text::Chunks<'a>>,
|
||||||
inlay_chunk: Option<&'a str>,
|
inlay_chunk: Option<&'a str>,
|
||||||
output_offset: InlayOffset,
|
output_offset: InlayOffset,
|
||||||
max_output_offset: InlayOffset,
|
max_output_offset: InlayOffset,
|
||||||
highlight_styles: HighlightStyles,
|
highlight_styles: HighlightStyles,
|
||||||
highlight_endpoints: Peekable<vec::IntoIter<HighlightEndpoint>>,
|
|
||||||
active_highlights: BTreeMap<TypeId, HighlightStyle>,
|
|
||||||
highlights: Highlights<'a>,
|
highlights: Highlights<'a>,
|
||||||
snapshot: &'a InlaySnapshot,
|
snapshot: &'a InlaySnapshot,
|
||||||
}
|
}
|
||||||
|
@ -255,22 +224,6 @@ impl<'a> InlayChunks<'a> {
|
||||||
self.buffer_chunk = None;
|
self.buffer_chunk = None;
|
||||||
self.output_offset = new_range.start;
|
self.output_offset = new_range.start;
|
||||||
self.max_output_offset = new_range.end;
|
self.max_output_offset = new_range.end;
|
||||||
|
|
||||||
let mut highlight_endpoints = Vec::new();
|
|
||||||
if let Some(text_highlights) = self.highlights.text_highlights {
|
|
||||||
if !text_highlights.is_empty() {
|
|
||||||
self.snapshot.apply_text_highlights(
|
|
||||||
&mut self.transforms,
|
|
||||||
&new_range,
|
|
||||||
text_highlights,
|
|
||||||
&mut highlight_endpoints,
|
|
||||||
);
|
|
||||||
self.transforms.seek(&new_range.start, Bias::Right, &());
|
|
||||||
highlight_endpoints.sort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.highlight_endpoints = highlight_endpoints.into_iter().peekable();
|
|
||||||
self.active_highlights.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset(&self) -> InlayOffset {
|
pub fn offset(&self) -> InlayOffset {
|
||||||
|
@ -286,21 +239,6 @@ impl<'a> Iterator for InlayChunks<'a> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut next_highlight_endpoint = InlayOffset(usize::MAX);
|
|
||||||
while let Some(endpoint) = self.highlight_endpoints.peek().copied() {
|
|
||||||
if endpoint.offset <= self.output_offset {
|
|
||||||
if endpoint.is_start {
|
|
||||||
self.active_highlights.insert(endpoint.tag, endpoint.style);
|
|
||||||
} else {
|
|
||||||
self.active_highlights.remove(&endpoint.tag);
|
|
||||||
}
|
|
||||||
self.highlight_endpoints.next();
|
|
||||||
} else {
|
|
||||||
next_highlight_endpoint = endpoint.offset;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let chunk = match self.transforms.item()? {
|
let chunk = match self.transforms.item()? {
|
||||||
Transform::Isomorphic(_) => {
|
Transform::Isomorphic(_) => {
|
||||||
let chunk = self
|
let chunk = self
|
||||||
|
@ -314,24 +252,15 @@ impl<'a> Iterator for InlayChunks<'a> {
|
||||||
chunk
|
chunk
|
||||||
.text
|
.text
|
||||||
.len()
|
.len()
|
||||||
.min(self.transforms.end(&()).0 .0 - self.output_offset.0)
|
.min(self.transforms.end(&()).0 .0 - self.output_offset.0),
|
||||||
.min(next_highlight_endpoint.0 - self.output_offset.0),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
chunk.text = suffix;
|
chunk.text = suffix;
|
||||||
self.output_offset.0 += prefix.len();
|
self.output_offset.0 += prefix.len();
|
||||||
let mut prefix = Chunk {
|
Chunk {
|
||||||
text: prefix,
|
text: prefix,
|
||||||
..chunk.clone()
|
..chunk.clone()
|
||||||
};
|
|
||||||
if !self.active_highlights.is_empty() {
|
|
||||||
let mut highlight_style = HighlightStyle::default();
|
|
||||||
for active_highlight in self.active_highlights.values() {
|
|
||||||
highlight_style.highlight(*active_highlight);
|
|
||||||
}
|
}
|
||||||
prefix.highlight_style = Some(highlight_style);
|
|
||||||
}
|
|
||||||
prefix
|
|
||||||
}
|
}
|
||||||
Transform::Inlay(inlay) => {
|
Transform::Inlay(inlay) => {
|
||||||
let mut inlay_style_and_highlight = None;
|
let mut inlay_style_and_highlight = None;
|
||||||
|
@ -393,13 +322,6 @@ impl<'a> Iterator for InlayChunks<'a> {
|
||||||
|
|
||||||
self.output_offset.0 += chunk.len();
|
self.output_offset.0 += chunk.len();
|
||||||
|
|
||||||
if !self.active_highlights.is_empty() {
|
|
||||||
for active_highlight in self.active_highlights.values() {
|
|
||||||
highlight_style
|
|
||||||
.get_or_insert(Default::default())
|
|
||||||
.highlight(*active_highlight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Chunk {
|
Chunk {
|
||||||
text: chunk,
|
text: chunk,
|
||||||
highlight_style,
|
highlight_style,
|
||||||
|
@ -1068,21 +990,13 @@ impl InlaySnapshot {
|
||||||
let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(&());
|
let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(&());
|
||||||
cursor.seek(&range.start, Bias::Right, &());
|
cursor.seek(&range.start, Bias::Right, &());
|
||||||
|
|
||||||
let mut highlight_endpoints = Vec::new();
|
|
||||||
if let Some(text_highlights) = highlights.text_highlights {
|
|
||||||
if !text_highlights.is_empty() {
|
|
||||||
self.apply_text_highlights(
|
|
||||||
&mut cursor,
|
|
||||||
&range,
|
|
||||||
text_highlights,
|
|
||||||
&mut highlight_endpoints,
|
|
||||||
);
|
|
||||||
cursor.seek(&range.start, Bias::Right, &());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
highlight_endpoints.sort();
|
|
||||||
let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end);
|
let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end);
|
||||||
let buffer_chunks = self.buffer.chunks(buffer_range, language_aware);
|
let buffer_chunks = CustomHighlightsChunks::new(
|
||||||
|
buffer_range,
|
||||||
|
language_aware,
|
||||||
|
highlights.text_highlights,
|
||||||
|
&self.buffer,
|
||||||
|
);
|
||||||
|
|
||||||
InlayChunks {
|
InlayChunks {
|
||||||
transforms: cursor,
|
transforms: cursor,
|
||||||
|
@ -1093,71 +1007,11 @@ impl InlaySnapshot {
|
||||||
output_offset: range.start,
|
output_offset: range.start,
|
||||||
max_output_offset: range.end,
|
max_output_offset: range.end,
|
||||||
highlight_styles: highlights.styles,
|
highlight_styles: highlights.styles,
|
||||||
highlight_endpoints: highlight_endpoints.into_iter().peekable(),
|
|
||||||
active_highlights: Default::default(),
|
|
||||||
highlights,
|
highlights,
|
||||||
snapshot: self,
|
snapshot: self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_text_highlights(
|
|
||||||
&self,
|
|
||||||
cursor: &mut Cursor<'_, Transform, (InlayOffset, usize)>,
|
|
||||||
range: &Range<InlayOffset>,
|
|
||||||
text_highlights: &TreeMap<TypeId, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>,
|
|
||||||
highlight_endpoints: &mut Vec<HighlightEndpoint>,
|
|
||||||
) {
|
|
||||||
while cursor.start().0 < range.end {
|
|
||||||
let transform_start = self
|
|
||||||
.buffer
|
|
||||||
.anchor_after(self.to_buffer_offset(cmp::max(range.start, cursor.start().0)));
|
|
||||||
let transform_end =
|
|
||||||
{
|
|
||||||
let overshoot = InlayOffset(range.end.0 - cursor.start().0 .0);
|
|
||||||
self.buffer.anchor_before(self.to_buffer_offset(cmp::min(
|
|
||||||
cursor.end(&()).0,
|
|
||||||
cursor.start().0 + overshoot,
|
|
||||||
)))
|
|
||||||
};
|
|
||||||
|
|
||||||
for (&tag, text_highlights) in text_highlights.iter() {
|
|
||||||
let style = text_highlights.0;
|
|
||||||
let ranges = &text_highlights.1;
|
|
||||||
|
|
||||||
let start_ix = match ranges.binary_search_by(|probe| {
|
|
||||||
let cmp = probe.end.cmp(&transform_start, &self.buffer);
|
|
||||||
if cmp.is_gt() {
|
|
||||||
cmp::Ordering::Greater
|
|
||||||
} else {
|
|
||||||
cmp::Ordering::Less
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Ok(i) | Err(i) => i,
|
|
||||||
};
|
|
||||||
for range in &ranges[start_ix..] {
|
|
||||||
if range.start.cmp(&transform_end, &self.buffer).is_ge() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
highlight_endpoints.push(HighlightEndpoint {
|
|
||||||
offset: self.to_inlay_offset(range.start.to_offset(&self.buffer)),
|
|
||||||
is_start: true,
|
|
||||||
tag,
|
|
||||||
style,
|
|
||||||
});
|
|
||||||
highlight_endpoints.push(HighlightEndpoint {
|
|
||||||
offset: self.to_inlay_offset(range.end.to_offset(&self.buffer)),
|
|
||||||
is_start: false,
|
|
||||||
tag,
|
|
||||||
style,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor.next(&());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn text(&self) -> String {
|
pub fn text(&self) -> String {
|
||||||
self.chunks(Default::default()..self.len(), false, Highlights::default())
|
self.chunks(Default::default()..self.len(), false, Highlights::default())
|
||||||
|
@ -1213,11 +1067,12 @@ mod tests {
|
||||||
hover_links::InlayHighlight,
|
hover_links::InlayHighlight,
|
||||||
InlayId, MultiBuffer,
|
InlayId, MultiBuffer,
|
||||||
};
|
};
|
||||||
use gpui::AppContext;
|
use gpui::{AppContext, HighlightStyle};
|
||||||
use project::{InlayHint, InlayHintLabel, ResolveState};
|
use project::{InlayHint, InlayHintLabel, ResolveState};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use std::{cmp::Reverse, env, sync::Arc};
|
use std::{any::TypeId, cmp::Reverse, env, sync::Arc};
|
||||||
|
use sum_tree::TreeMap;
|
||||||
use text::Patch;
|
use text::Patch;
|
||||||
use util::post_inc;
|
use util::post_inc;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue