mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Add a command to expand the context for a multibuffer (#10300)
This PR adds an action to expand the excerpts lines of context in a multibuffer. Release Notes: - Added an `editor::ExpandExcerpts` action (bound to `shift-enter` by default), which can expand the excerpt the cursor is currently in by 3 lines. You can customize the number of lines by rebinding this action like so: ```json5 // In your keybindings array... { "context": "Editor && mode == full", "bindings": { "shift-enter": ["editor::ExpandExcerpts", { "lines": 5 }], } } ``` --------- Co-authored-by: Nathan <nathan@zed.dev> Co-authored-by: Max <max@zed.dev>
This commit is contained in:
parent
9d9bce08a7
commit
8a02159b82
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -6051,14 +6051,18 @@ dependencies = [
|
||||
"anyhow",
|
||||
"clock",
|
||||
"collections",
|
||||
"ctor",
|
||||
"env_logger",
|
||||
"futures 0.3.28",
|
||||
"git",
|
||||
"gpui",
|
||||
"itertools 0.11.0",
|
||||
"language",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"rand 0.8.5",
|
||||
"settings",
|
||||
"smallvec",
|
||||
"sum_tree",
|
||||
"text",
|
||||
"theme",
|
||||
|
9
assets/icons/LICENSES
Normal file
9
assets/icons/LICENSES
Normal file
@ -0,0 +1,9 @@
|
||||
Lucide License
|
||||
|
||||
ISC License
|
||||
|
||||
Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2022.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
1
assets/icons/expand_vertical.svg
Normal file
1
assets/icons/expand_vertical.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-unfold-vertical"><path d="M12 22v-6"/><path d="M12 8V2"/><path d="M4 12H2"/><path d="M10 12H8"/><path d="M16 12h-2"/><path d="M22 12h-2"/><path d="m15 19-3 3-3-3"/><path d="m15 5-3-3-3 3"/></svg>
|
After Width: | Height: | Size: 398 B |
@ -527,6 +527,7 @@
|
||||
"context": "Editor && mode == full",
|
||||
"bindings": {
|
||||
"alt-enter": "editor::OpenExcerpts",
|
||||
"shift-enter": "editor::ExpandExcerpts",
|
||||
"ctrl-k enter": "editor::OpenExcerptsSplit",
|
||||
"ctrl-f8": "editor::GoToHunk",
|
||||
"ctrl-shift-f8": "editor::GoToPrevHunk",
|
||||
|
@ -541,6 +541,7 @@
|
||||
"context": "Editor && mode == full",
|
||||
"bindings": {
|
||||
"alt-enter": "editor::OpenExcerpts",
|
||||
"shift-enter": "editor::ExpandExcerpts",
|
||||
"cmd-k enter": "editor::OpenExcerptsSplit",
|
||||
"cmd-f8": "editor::GoToHunk",
|
||||
"cmd-shift-f8": "editor::GoToPrevHunk",
|
||||
|
@ -94,12 +94,19 @@ pub struct SelectDownByLines {
|
||||
pub(super) lines: u32,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct ExpandExcerpts {
|
||||
#[serde(default)]
|
||||
pub(super) lines: u32,
|
||||
}
|
||||
|
||||
impl_actions!(
|
||||
editor,
|
||||
[
|
||||
SelectNext,
|
||||
SelectPrevious,
|
||||
SelectToBeginningOfLine,
|
||||
ExpandExcerpts,
|
||||
MovePageUp,
|
||||
MovePageDown,
|
||||
SelectToEndOfLine,
|
||||
@ -254,6 +261,6 @@ gpui::actions!(
|
||||
UndoSelection,
|
||||
UnfoldLines,
|
||||
UniqueLinesCaseSensitive,
|
||||
UniqueLinesCaseInsensitive
|
||||
UniqueLinesCaseInsensitive,
|
||||
]
|
||||
);
|
||||
|
@ -7462,6 +7462,28 @@ impl Editor {
|
||||
self.selection_history.mode = SelectionHistoryMode::Normal;
|
||||
}
|
||||
|
||||
pub fn expand_excerpts(&mut self, action: &ExpandExcerpts, cx: &mut ViewContext<Self>) {
|
||||
let selections = self.selections.disjoint_anchors();
|
||||
|
||||
let lines = if action.lines == 0 { 3 } else { action.lines };
|
||||
|
||||
self.buffer.update(cx, |buffer, cx| {
|
||||
buffer.expand_excerpts(
|
||||
selections
|
||||
.into_iter()
|
||||
.map(|selection| selection.head().excerpt_id)
|
||||
.dedup(),
|
||||
lines,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn expand_excerpt(&mut self, excerpt: ExcerptId, cx: &mut ViewContext<Self>) {
|
||||
self.buffer
|
||||
.update(cx, |buffer, cx| buffer.expand_excerpts([excerpt], 3, cx))
|
||||
}
|
||||
|
||||
fn go_to_diagnostic(&mut self, _: &GoToDiagnostic, cx: &mut ViewContext<Self>) {
|
||||
self.go_to_diagnostic_impl(Direction::Next, cx)
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ use crate::{
|
||||
mouse_context_menu::{self, MouseContextMenu},
|
||||
scroll::scroll_amount::ScrollAmount,
|
||||
CursorShape, DisplayPoint, DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode,
|
||||
EditorSettings, EditorSnapshot, EditorStyle, GutterDimensions, HalfPageDown, HalfPageUp,
|
||||
HoveredCursor, LineDown, LineUp, OpenExcerpts, PageDown, PageUp, Point, SelectPhase, Selection,
|
||||
SoftWrap, ToPoint, CURSORS_VISIBLE_FOR, MAX_LINE_LEN,
|
||||
EditorSettings, EditorSnapshot, EditorStyle, ExpandExcerpts, GutterDimensions, HalfPageDown,
|
||||
HalfPageUp, HoveredCursor, LineDown, LineUp, OpenExcerpts, PageDown, PageUp, Point,
|
||||
SelectPhase, Selection, SoftWrap, ToPoint, CURSORS_VISIBLE_FOR, MAX_LINE_LEN,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use collections::{BTreeMap, HashMap};
|
||||
@ -257,6 +257,9 @@ impl EditorElement {
|
||||
register_action(view, cx, Editor::move_to_enclosing_bracket);
|
||||
register_action(view, cx, Editor::undo_selection);
|
||||
register_action(view, cx, Editor::redo_selection);
|
||||
if !view.read(cx).is_singleton(cx) {
|
||||
register_action(view, cx, Editor::expand_excerpts);
|
||||
}
|
||||
register_action(view, cx, Editor::go_to_diagnostic);
|
||||
register_action(view, cx, Editor::go_to_prev_diagnostic);
|
||||
register_action(view, cx, Editor::go_to_hunk);
|
||||
@ -1543,6 +1546,7 @@ impl EditorElement {
|
||||
range,
|
||||
starts_new_buffer,
|
||||
height,
|
||||
id,
|
||||
..
|
||||
} => {
|
||||
let include_root = self
|
||||
@ -1700,45 +1704,38 @@ impl EditorElement {
|
||||
)
|
||||
.h_full()
|
||||
.child(
|
||||
ButtonLike::new("jump-icon")
|
||||
ButtonLike::new("expand-icon")
|
||||
.style(ButtonStyle::Transparent)
|
||||
.child(
|
||||
svg()
|
||||
.path(IconName::ArrowUpRight.path())
|
||||
.path(IconName::ExpandVertical.path())
|
||||
.size(IconSize::XSmall.rems())
|
||||
.text_color(cx.theme().colors().border)
|
||||
.group_hover("excerpt-jump-action", |style| {
|
||||
.text_color(
|
||||
cx.theme().colors().editor_line_number,
|
||||
)
|
||||
.group("")
|
||||
.hover(|style| {
|
||||
style.text_color(
|
||||
cx.theme().colors().editor_line_number,
|
||||
cx.theme()
|
||||
.colors()
|
||||
.editor_active_line_number,
|
||||
)
|
||||
}),
|
||||
)
|
||||
.when_some(jump_data.clone(), |this, jump_data| {
|
||||
this.on_click(cx.listener_for(&self.editor, {
|
||||
let path = jump_data.path.clone();
|
||||
move |editor, _, cx| {
|
||||
editor.jump(
|
||||
path.clone(),
|
||||
jump_data.position,
|
||||
jump_data.anchor,
|
||||
jump_data.line_offset_from_top,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}))
|
||||
.tooltip({
|
||||
move |cx| {
|
||||
Tooltip::for_action(
|
||||
format!(
|
||||
"Jump to {}:L{}",
|
||||
jump_data.path.path.display(),
|
||||
jump_data.position.row + 1
|
||||
),
|
||||
&OpenExcerpts,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click(cx.listener_for(&self.editor, {
|
||||
let id = *id;
|
||||
move |editor, _, cx| {
|
||||
editor.expand_excerpt(id, cx);
|
||||
}
|
||||
}))
|
||||
.tooltip({
|
||||
move |cx| {
|
||||
Tooltip::for_action(
|
||||
"Expand Excerpt",
|
||||
&ExpandExcerpts { lines: 0 },
|
||||
cx,
|
||||
)
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
@ -24,14 +24,18 @@ test-support = [
|
||||
anyhow.workspace = true
|
||||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
ctor.workspace = true
|
||||
env_logger.workspace = true
|
||||
futures.workspace = true
|
||||
git.workspace = true
|
||||
gpui.workspace = true
|
||||
itertools.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
parking_lot.workspace = true
|
||||
rand.workspace = true
|
||||
settings.workspace = true
|
||||
smallvec.workspace = true
|
||||
sum_tree.workspace = true
|
||||
text.workspace = true
|
||||
theme.workspace = true
|
||||
|
@ -7,6 +7,7 @@ use collections::{BTreeMap, Bound, HashMap, HashSet};
|
||||
use futures::{channel::mpsc, SinkExt};
|
||||
use git::diff::DiffHunk;
|
||||
use gpui::{AppContext, EventEmitter, Model, ModelContext};
|
||||
use itertools::Itertools;
|
||||
use language::{
|
||||
char_kind,
|
||||
language_settings::{language_settings, LanguageSettings},
|
||||
@ -15,6 +16,7 @@ use language::{
|
||||
Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
|
||||
ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
cell::{Ref, RefCell},
|
||||
@ -1008,12 +1010,12 @@ impl MultiBuffer {
|
||||
anchor_ranges.extend(ranges.by_ref().take(range_count).map(|range| {
|
||||
let start = Anchor {
|
||||
buffer_id: Some(buffer_id),
|
||||
excerpt_id: excerpt_id,
|
||||
excerpt_id,
|
||||
text_anchor: buffer_snapshot.anchor_after(range.start),
|
||||
};
|
||||
let end = Anchor {
|
||||
buffer_id: Some(buffer_id),
|
||||
excerpt_id: excerpt_id,
|
||||
excerpt_id,
|
||||
text_anchor: buffer_snapshot.anchor_after(range.end),
|
||||
};
|
||||
start..end
|
||||
@ -1573,6 +1575,86 @@ impl MultiBuffer {
|
||||
self.as_singleton().unwrap().read(cx).is_parsing()
|
||||
}
|
||||
|
||||
pub fn expand_excerpts(
|
||||
&mut self,
|
||||
ids: impl IntoIterator<Item = ExcerptId>,
|
||||
line_count: u32,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
if line_count == 0 {
|
||||
return;
|
||||
}
|
||||
self.sync(cx);
|
||||
|
||||
let snapshot = self.snapshot(cx);
|
||||
let locators = snapshot.excerpt_locators_for_ids(ids);
|
||||
let mut new_excerpts = SumTree::new();
|
||||
let mut cursor = snapshot.excerpts.cursor::<(Option<&Locator>, usize)>();
|
||||
let mut edits = Vec::<Edit<usize>>::new();
|
||||
|
||||
for locator in &locators {
|
||||
let prefix = cursor.slice(&Some(locator), Bias::Left, &());
|
||||
new_excerpts.append(prefix, &());
|
||||
|
||||
let mut excerpt = cursor.item().unwrap().clone();
|
||||
let old_text_len = excerpt.text_summary.len;
|
||||
|
||||
let start_row = excerpt
|
||||
.range
|
||||
.context
|
||||
.start
|
||||
.to_point(&excerpt.buffer)
|
||||
.row
|
||||
.saturating_sub(line_count);
|
||||
let start_point = Point::new(start_row, 0);
|
||||
excerpt.range.context.start = excerpt.buffer.anchor_before(start_point);
|
||||
|
||||
let end_point = excerpt.buffer.clip_point(
|
||||
excerpt.range.context.end.to_point(&excerpt.buffer) + Point::new(line_count, 0),
|
||||
Bias::Left,
|
||||
);
|
||||
excerpt.range.context.end = excerpt.buffer.anchor_after(end_point);
|
||||
excerpt.max_buffer_row = end_point.row;
|
||||
|
||||
excerpt.text_summary = excerpt
|
||||
.buffer
|
||||
.text_summary_for_range(start_point..end_point);
|
||||
|
||||
let new_start_offset = new_excerpts.summary().text.len;
|
||||
let old_start_offset = cursor.start().1;
|
||||
let edit = Edit {
|
||||
old: old_start_offset..old_start_offset + old_text_len,
|
||||
new: new_start_offset..new_start_offset + excerpt.text_summary.len,
|
||||
};
|
||||
|
||||
if let Some(last_edit) = edits.last_mut() {
|
||||
if last_edit.old.end == edit.old.start {
|
||||
last_edit.old.end = edit.old.end;
|
||||
last_edit.new.end = edit.new.end;
|
||||
} else {
|
||||
edits.push(edit);
|
||||
}
|
||||
} else {
|
||||
edits.push(edit);
|
||||
}
|
||||
|
||||
new_excerpts.push(excerpt, &());
|
||||
|
||||
cursor.next(&());
|
||||
}
|
||||
|
||||
new_excerpts.append(cursor.suffix(&()), &());
|
||||
|
||||
drop(cursor);
|
||||
self.snapshot.borrow_mut().excerpts = new_excerpts;
|
||||
|
||||
self.subscriptions.publish_mut(edits);
|
||||
cx.emit(Event::Edited {
|
||||
singleton_buffer_edited: false,
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn sync(&self, cx: &AppContext) {
|
||||
let mut snapshot = self.snapshot.borrow_mut();
|
||||
let mut excerpts_to_edit = Vec::new();
|
||||
@ -1796,6 +1878,19 @@ impl MultiBuffer {
|
||||
log::info!("Clearing multi-buffer");
|
||||
self.clear(cx);
|
||||
continue;
|
||||
} else if rng.gen_bool(0.1) && !self.excerpt_ids().is_empty() {
|
||||
let ids = self.excerpt_ids();
|
||||
let mut excerpts = HashSet::default();
|
||||
for _ in 0..rng.gen_range(0..ids.len()) {
|
||||
excerpts.extend(ids.choose(rng).copied());
|
||||
}
|
||||
|
||||
let line_count = rng.gen_range(0..5);
|
||||
|
||||
log::info!("Expanding excerpts {excerpts:?} by {line_count} lines");
|
||||
|
||||
self.expand_excerpts(excerpts.iter().cloned(), line_count, cx);
|
||||
continue;
|
||||
}
|
||||
|
||||
let excerpt_ids = self.excerpt_ids();
|
||||
@ -3361,6 +3456,39 @@ impl MultiBufferSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the locators referenced by the given excerpt ids, sorted by locator.
|
||||
fn excerpt_locators_for_ids(
|
||||
&self,
|
||||
ids: impl IntoIterator<Item = ExcerptId>,
|
||||
) -> SmallVec<[Locator; 1]> {
|
||||
let mut sorted_ids = ids.into_iter().collect::<SmallVec<[_; 1]>>();
|
||||
sorted_ids.sort_unstable();
|
||||
let mut locators = SmallVec::new();
|
||||
|
||||
while sorted_ids.last() == Some(&ExcerptId::max()) {
|
||||
sorted_ids.pop();
|
||||
locators.push(Locator::max());
|
||||
}
|
||||
|
||||
let mut sorted_ids = sorted_ids.into_iter().dedup().peekable();
|
||||
if sorted_ids.peek() == Some(&ExcerptId::min()) {
|
||||
sorted_ids.next();
|
||||
locators.push(Locator::min());
|
||||
}
|
||||
|
||||
let mut cursor = self.excerpt_ids.cursor::<ExcerptId>();
|
||||
for id in sorted_ids {
|
||||
if cursor.seek_forward(&id, Bias::Left, &()) {
|
||||
locators.push(cursor.item().unwrap().locator.clone());
|
||||
} else {
|
||||
panic!("invalid excerpt id {:?}", id);
|
||||
}
|
||||
}
|
||||
|
||||
locators.sort_unstable();
|
||||
locators
|
||||
}
|
||||
|
||||
pub fn buffer_id_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<BufferId> {
|
||||
Some(self.excerpt(excerpt_id)?.buffer_id)
|
||||
}
|
||||
@ -4286,7 +4414,8 @@ where
|
||||
.peekable();
|
||||
while let Some(range) = range_iter.next() {
|
||||
let excerpt_start = Point::new(range.start.row.saturating_sub(context_line_count), 0);
|
||||
let mut excerpt_end = Point::new(range.end.row + context_line_count, 0).min(max_point);
|
||||
// These + 1s ensure that we select the whole next line
|
||||
let mut excerpt_end = Point::new(range.end.row + 1 + context_line_count, 0).min(max_point);
|
||||
|
||||
let mut ranges_in_excerpt = 1;
|
||||
|
||||
@ -4323,6 +4452,13 @@ mod tests {
|
||||
use std::env;
|
||||
use util::test::sample_text;
|
||||
|
||||
#[ctor::ctor]
|
||||
fn init_logger() {
|
||||
if std::env::var("RUST_LOG").is_ok() {
|
||||
env_logger::init();
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_singleton(cx: &mut AppContext) {
|
||||
let buffer = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
|
||||
@ -4721,6 +4857,59 @@ mod tests {
|
||||
assert_eq!(*follower_edit_event_count.read(), 4);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_expand_excerpts(cx: &mut AppContext) {
|
||||
let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
|
||||
let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite));
|
||||
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
multibuffer.push_excerpts_with_context_lines(
|
||||
buffer.clone(),
|
||||
vec![
|
||||
// Note that in this test, this first excerpt
|
||||
// does not contain a new line
|
||||
Point::new(3, 2)..Point::new(3, 3),
|
||||
Point::new(7, 1)..Point::new(7, 3),
|
||||
Point::new(15, 0)..Point::new(15, 0),
|
||||
],
|
||||
1,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
multibuffer.expand_excerpts(multibuffer.excerpt_ids(), 1, cx)
|
||||
});
|
||||
|
||||
let snapshot = multibuffer.read(cx).snapshot(cx);
|
||||
|
||||
// Expanding context lines causes the line containing 'fff' to appear in two different excerpts.
|
||||
// We don't attempt to merge them, because removing the excerpt could create inconsistency with other layers
|
||||
// that are tracking excerpt ids.
|
||||
assert_eq!(
|
||||
snapshot.text(),
|
||||
concat!(
|
||||
"bbb\n", // Preserve newlines
|
||||
"ccc\n", //
|
||||
"ddd\n", //
|
||||
"eee\n", //
|
||||
"fff\n", // <- Same as below
|
||||
"\n", // Excerpt boundary
|
||||
"fff\n", // <- Same as above
|
||||
"ggg\n", //
|
||||
"hhh\n", //
|
||||
"iii\n", //
|
||||
"jjj\n", //
|
||||
"\n", //
|
||||
"nnn\n", //
|
||||
"ooo\n", //
|
||||
"ppp\n", //
|
||||
"qqq\n", //
|
||||
"rrr\n", //
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {
|
||||
let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
|
||||
@ -4729,6 +4918,8 @@ mod tests {
|
||||
multibuffer.push_excerpts_with_context_lines(
|
||||
buffer.clone(),
|
||||
vec![
|
||||
// Note that in this test, this first excerpt
|
||||
// does contain a new line
|
||||
Point::new(3, 2)..Point::new(4, 2),
|
||||
Point::new(7, 1)..Point::new(7, 3),
|
||||
Point::new(15, 0)..Point::new(15, 0),
|
||||
@ -4741,7 +4932,23 @@ mod tests {
|
||||
let snapshot = multibuffer.read(cx).snapshot(cx);
|
||||
assert_eq!(
|
||||
snapshot.text(),
|
||||
"bbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\n\nnnn\nooo\nppp\nqqq\n"
|
||||
concat!(
|
||||
"bbb\n", // Preserve newlines
|
||||
"ccc\n", //
|
||||
"ddd\n", //
|
||||
"eee\n", //
|
||||
"fff\n", //
|
||||
"ggg\n", //
|
||||
"hhh\n", //
|
||||
"iii\n", //
|
||||
"jjj\n", //
|
||||
"\n", //
|
||||
"nnn\n", //
|
||||
"ooo\n", //
|
||||
"ppp\n", //
|
||||
"qqq\n", //
|
||||
"rrr\n", //
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@ -4777,7 +4984,23 @@ mod tests {
|
||||
let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
|
||||
assert_eq!(
|
||||
snapshot.text(),
|
||||
"bbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\n\nnnn\nooo\nppp\nqqq\n"
|
||||
concat!(
|
||||
"bbb\n", //
|
||||
"ccc\n", //
|
||||
"ddd\n", //
|
||||
"eee\n", //
|
||||
"fff\n", //
|
||||
"ggg\n", //
|
||||
"hhh\n", //
|
||||
"iii\n", //
|
||||
"jjj\n", //
|
||||
"\n", //
|
||||
"nnn\n", //
|
||||
"ooo\n", //
|
||||
"ppp\n", //
|
||||
"qqq\n", //
|
||||
"rrr\n", //
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@ -5027,10 +5250,45 @@ mod tests {
|
||||
|
||||
for _ in 0..operations {
|
||||
match rng.gen_range(0..100) {
|
||||
0..=19 if !buffers.is_empty() => {
|
||||
0..=14 if !buffers.is_empty() => {
|
||||
let buffer = buffers.choose(&mut rng).unwrap();
|
||||
buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 5, cx));
|
||||
}
|
||||
15..=19 if !expected_excerpts.is_empty() => {
|
||||
multibuffer.update(cx, |multibuffer, cx| {
|
||||
let ids = multibuffer.excerpt_ids();
|
||||
let mut excerpts = HashSet::default();
|
||||
for _ in 0..rng.gen_range(0..ids.len()) {
|
||||
excerpts.extend(ids.choose(&mut rng).copied());
|
||||
}
|
||||
|
||||
let line_count = rng.gen_range(0..5);
|
||||
|
||||
let excerpt_ixs = excerpts
|
||||
.iter()
|
||||
.map(|id| excerpt_ids.iter().position(|i| i == id).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
log::info!("Expanding excerpts {excerpt_ixs:?} by {line_count} lines");
|
||||
multibuffer.expand_excerpts(excerpts.iter().cloned(), line_count, cx);
|
||||
|
||||
if line_count > 0 {
|
||||
for id in excerpts {
|
||||
let excerpt_ix = excerpt_ids.iter().position(|&i| i == id).unwrap();
|
||||
let (buffer, range) = &mut expected_excerpts[excerpt_ix];
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
let mut point_range = range.to_point(&snapshot);
|
||||
point_range.start =
|
||||
Point::new(point_range.start.row.saturating_sub(line_count), 0);
|
||||
point_range.end = snapshot.clip_point(
|
||||
Point::new(point_range.end.row + line_count, 0),
|
||||
Bias::Left,
|
||||
);
|
||||
*range = snapshot.anchor_before(point_range.start)
|
||||
..snapshot.anchor_after(point_range.end);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
20..=29 if !expected_excerpts.is_empty() => {
|
||||
let mut ids_to_remove = vec![];
|
||||
for _ in 0..rng.gen_range(1..=3) {
|
||||
@ -5093,8 +5351,9 @@ mod tests {
|
||||
_ => {
|
||||
let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
|
||||
let base_text = util::RandomCharIter::new(&mut rng)
|
||||
.take(10)
|
||||
.take(25)
|
||||
.collect::<String>();
|
||||
|
||||
buffers.push(cx.new_model(|cx| Buffer::local(base_text, cx)));
|
||||
buffers.last().unwrap()
|
||||
} else {
|
||||
|
@ -405,6 +405,7 @@ where
|
||||
summary.0
|
||||
}
|
||||
|
||||
/// Returns whether we found the item you where seeking for
|
||||
fn seek_internal(
|
||||
&mut self,
|
||||
target: &dyn SeekTarget<'a, T::Summary, D>,
|
||||
|
@ -47,6 +47,7 @@ pub enum IconName {
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
ChevronUp,
|
||||
ExpandVertical,
|
||||
Close,
|
||||
Collab,
|
||||
Command,
|
||||
@ -149,6 +150,7 @@ impl IconName {
|
||||
IconName::ChevronLeft => "icons/chevron_left.svg",
|
||||
IconName::ChevronRight => "icons/chevron_right.svg",
|
||||
IconName::ChevronUp => "icons/chevron_up.svg",
|
||||
IconName::ExpandVertical => "icons/expand_vertical.svg",
|
||||
IconName::Close => "icons/x.svg",
|
||||
IconName::Collab => "icons/user_group_16.svg",
|
||||
IconName::Command => "icons/command.svg",
|
||||
|
@ -289,7 +289,6 @@ define_connection! {
|
||||
sql!(
|
||||
ALTER TABLE workspaces ADD COLUMN centered_layout INTEGER; //bool
|
||||
),
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -7,10 +7,11 @@ OUTPUT_FILE="${1:-$(pwd)/assets/licenses.md}"
|
||||
> $OUTPUT_FILE
|
||||
|
||||
echo -e "# ###### THEME LICENSES ######\n" >> $OUTPUT_FILE
|
||||
|
||||
echo "Generating theme licenses"
|
||||
cat assets/themes/LICENSES >> $OUTPUT_FILE
|
||||
|
||||
echo -e "# ###### ICON LICENSES ######\n" >> $OUTPUT_FILE
|
||||
cat assets/icons/LICENSES >> $OUTPUT_FILE
|
||||
|
||||
echo -e "# ###### CODE LICENSES ######\n" >> $OUTPUT_FILE
|
||||
|
||||
[[ "$(cargo about --version)" == "cargo-about 0.6.1" ]] || cargo install cargo-about@0.6.1
|
||||
|
Loading…
Reference in New Issue
Block a user