WIP
This commit is contained in:
parent
72b9dc8216
commit
dfc7c81500
12 changed files with 8192 additions and 8211 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2648,7 +2648,7 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"lsp2",
|
"lsp2",
|
||||||
"multi_buffer",
|
"multi_buffer2",
|
||||||
"ordered-float 2.10.0",
|
"ordered-float 2.10.0",
|
||||||
"parking_lot 0.11.2",
|
"parking_lot 0.11.2",
|
||||||
"postage",
|
"postage",
|
||||||
|
|
|
@ -6,17 +6,16 @@ mod wrap_map;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
link_go_to_definition::InlayHighlight, movement::TextLayoutDetails, Anchor, AnchorRangeExt,
|
link_go_to_definition::InlayHighlight, movement::TextLayoutDetails, Anchor, AnchorRangeExt,
|
||||||
EditorStyle, InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint,
|
InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint,
|
||||||
};
|
};
|
||||||
pub use block_map::{BlockMap, BlockPoint};
|
pub use block_map::{BlockMap, BlockPoint};
|
||||||
use collections::{BTreeMap, HashMap, HashSet};
|
use collections::{BTreeMap, HashMap, HashSet};
|
||||||
use fold_map::FoldMap;
|
use fold_map::FoldMap;
|
||||||
use gpui::{FontId, HighlightStyle, Hsla, Line, Model, ModelContext, UnderlineStyle};
|
use gpui::{FontId, HighlightStyle, Hsla, Line, Model, ModelContext};
|
||||||
use inlay_map::InlayMap;
|
use inlay_map::InlayMap;
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
|
language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
|
||||||
};
|
};
|
||||||
use lsp::DiagnosticSeverity;
|
|
||||||
use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
|
use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
|
||||||
use sum_tree::{Bias, TreeMap};
|
use sum_tree::{Bias, TreeMap};
|
||||||
use tab_map::TabMap;
|
use tab_map::TabMap;
|
||||||
|
|
|
@ -73,7 +73,7 @@ impl WrapMap {
|
||||||
wrap_width: Option<f32>,
|
wrap_width: Option<f32>,
|
||||||
cx: &mut AppContext,
|
cx: &mut AppContext,
|
||||||
) -> (Model<Self>, WrapSnapshot) {
|
) -> (Model<Self>, WrapSnapshot) {
|
||||||
let handle = cx.add_model(|cx| {
|
let handle = cx.build_model(|cx| {
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
font: (font_id, font_size),
|
font: (font_id, font_size),
|
||||||
wrap_width: None,
|
wrap_width: None,
|
||||||
|
|
|
@ -19,15 +19,11 @@ pub mod selections_collection;
|
||||||
mod editor_tests;
|
mod editor_tests;
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub mod test;
|
pub mod test;
|
||||||
use ::git::diff::DiffHunk;
|
|
||||||
use aho_corasick::AhoCorasick;
|
use aho_corasick::AhoCorasick;
|
||||||
use anyhow::{anyhow, Context, Result};
|
|
||||||
use blink_manager::BlinkManager;
|
use blink_manager::BlinkManager;
|
||||||
use client::{ClickhouseEvent, Client, Collaborator, ParticipantIndex, TelemetrySettings};
|
use client::{Collaborator, ParticipantIndex};
|
||||||
use clock::{Global, ReplicaId};
|
use clock::ReplicaId;
|
||||||
use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
|
use collections::{HashMap, HashSet, VecDeque};
|
||||||
use convert_case::{Case, Casing};
|
|
||||||
use copilot::Copilot;
|
|
||||||
pub use display_map::DisplayPoint;
|
pub use display_map::DisplayPoint;
|
||||||
use display_map::*;
|
use display_map::*;
|
||||||
pub use editor_settings::EditorSettings;
|
pub use editor_settings::EditorSettings;
|
||||||
|
@ -37,43 +33,26 @@ pub use element::{
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
serde_json, AnyElement, AppContext, AsyncAppContext, ClipboardItem, Element, Entity,
|
AnyElement, AppContext, Element, EventEmitter, Model, Pixels, Render, Subscription, Task, View,
|
||||||
EventEmitter, FontWeight, HighlightStyle, Hsla, Model, Pixels, Quad, Render, Subscription,
|
ViewContext, WeakView,
|
||||||
Task, Text, View, ViewContext, WeakView, WindowContext,
|
|
||||||
};
|
};
|
||||||
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
use hover_popover::HoverState;
|
||||||
use hover_popover::{hide_hover, HoverState};
|
|
||||||
use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
|
|
||||||
pub use items::MAX_TAB_TITLE_LEN;
|
pub use items::MAX_TAB_TITLE_LEN;
|
||||||
use itertools::Itertools;
|
|
||||||
pub use language::{char_kind, CharKind};
|
pub use language::{char_kind, CharKind};
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::{self, all_language_settings, InlayHintSettings},
|
language_settings::{self, all_language_settings, InlayHintSettings},
|
||||||
point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion,
|
AutoindentMode, BracketPair, Buffer, CodeAction, Completion, CursorShape, Diagnostic, Language,
|
||||||
CursorShape, Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language,
|
OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
|
||||||
LanguageRegistry, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, Selection,
|
|
||||||
SelectionGoal, TransactionId,
|
|
||||||
};
|
};
|
||||||
use link_go_to_definition::{
|
use link_go_to_definition::LinkGoToDefinitionState;
|
||||||
hide_link_definition, show_link_definition, GoToDefinitionLink, InlayHighlight,
|
|
||||||
LinkGoToDefinitionState,
|
|
||||||
};
|
|
||||||
use log::error;
|
|
||||||
use lsp::LanguageServerId;
|
|
||||||
use movement::TextLayoutDetails;
|
|
||||||
use multi_buffer::ToOffsetUtf16;
|
|
||||||
pub use multi_buffer::{
|
pub use multi_buffer::{
|
||||||
Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
|
Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
|
||||||
ToPoint,
|
ToPoint,
|
||||||
};
|
};
|
||||||
use ordered_float::OrderedFloat;
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction};
|
use project::Project;
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rpc::proto::*;
|
||||||
use rpc::proto::{self, PeerId};
|
use scroll::{autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager};
|
||||||
use scroll::{
|
|
||||||
autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
|
|
||||||
};
|
|
||||||
use selections_collection::SelectionsCollection;
|
use selections_collection::SelectionsCollection;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
@ -1763,7 +1742,7 @@ impl InlayHintRefreshReason {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Editor {
|
impl Editor {
|
||||||
// pub fn single_line(
|
// pub fn single_line(
|
||||||
// field_editor_style: Option<Arc<GetFieldEditorTheme>>,
|
// field_editor_style: Option<Arc<GetFieldEditorTheme>>,
|
||||||
// cx: &mut ViewContext<Self>,
|
// cx: &mut ViewContext<Self>,
|
||||||
|
@ -9105,7 +9084,7 @@ pub fn buffer(&self) -> &Model<MultiBuffer> {
|
||||||
// });
|
// });
|
||||||
// supports
|
// supports
|
||||||
// }
|
// }
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub trait CollaborationHub {
|
pub trait CollaborationHub {
|
||||||
fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
|
fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
|
||||||
|
@ -10033,7 +10012,6 @@ pub fn highlight_diagnostic_message(
|
||||||
|
|
||||||
// runs
|
// runs
|
||||||
// })
|
// })
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
|
pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator<Item = &'a str> + 'a {
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
|
|
|
@ -48,106 +48,108 @@ impl FollowableItem for Editor {
|
||||||
state: &mut Option<proto::view::Variant>,
|
state: &mut Option<proto::view::Variant>,
|
||||||
cx: &mut AppContext,
|
cx: &mut AppContext,
|
||||||
) -> Option<Task<Result<View<Self>>>> {
|
) -> Option<Task<Result<View<Self>>>> {
|
||||||
let project = workspace.read(cx).project().to_owned();
|
todo!()
|
||||||
let Some(proto::view::Variant::Editor(_)) = state else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let Some(proto::view::Variant::Editor(state)) = state.take() else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
|
|
||||||
let client = project.read(cx).client();
|
|
||||||
let replica_id = project.read(cx).replica_id();
|
|
||||||
let buffer_ids = state
|
|
||||||
.excerpts
|
|
||||||
.iter()
|
|
||||||
.map(|excerpt| excerpt.buffer_id)
|
|
||||||
.collect::<HashSet<_>>();
|
|
||||||
let buffers = project.update(cx, |project, cx| {
|
|
||||||
buffer_ids
|
|
||||||
.iter()
|
|
||||||
.map(|id| project.open_buffer_by_id(*id, cx))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
});
|
|
||||||
|
|
||||||
let pane = pane.downgrade();
|
|
||||||
Some(cx.spawn(|mut cx| async move {
|
|
||||||
let mut buffers = futures::future::try_join_all(buffers).await?;
|
|
||||||
let editor = pane.read_with(&cx, |pane, cx| {
|
|
||||||
let mut editors = pane.items_of_type::<Self>();
|
|
||||||
editors.find(|editor| {
|
|
||||||
let ids_match = editor.remote_id(&client, cx) == Some(remote_id);
|
|
||||||
let singleton_buffer_matches = state.singleton
|
|
||||||
&& buffers.first()
|
|
||||||
== editor.read(cx).buffer.read(cx).as_singleton().as_ref();
|
|
||||||
ids_match || singleton_buffer_matches
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let editor = if let Some(editor) = editor {
|
|
||||||
editor
|
|
||||||
} else {
|
|
||||||
pane.update(&mut cx, |_, cx| {
|
|
||||||
let multibuffer = cx.add_model(|cx| {
|
|
||||||
let mut multibuffer;
|
|
||||||
if state.singleton && buffers.len() == 1 {
|
|
||||||
multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
|
|
||||||
} else {
|
|
||||||
multibuffer = MultiBuffer::new(replica_id);
|
|
||||||
let mut excerpts = state.excerpts.into_iter().peekable();
|
|
||||||
while let Some(excerpt) = excerpts.peek() {
|
|
||||||
let buffer_id = excerpt.buffer_id;
|
|
||||||
let buffer_excerpts = iter::from_fn(|| {
|
|
||||||
let excerpt = excerpts.peek()?;
|
|
||||||
(excerpt.buffer_id == buffer_id)
|
|
||||||
.then(|| excerpts.next().unwrap())
|
|
||||||
});
|
|
||||||
let buffer =
|
|
||||||
buffers.iter().find(|b| b.read(cx).remote_id() == buffer_id);
|
|
||||||
if let Some(buffer) = buffer {
|
|
||||||
multibuffer.push_excerpts(
|
|
||||||
buffer.clone(),
|
|
||||||
buffer_excerpts.filter_map(deserialize_excerpt_range),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
// let project = workspace.read(cx).project().to_owned();
|
||||||
};
|
// let Some(proto::view::Variant::Editor(_)) = state else {
|
||||||
|
// return None;
|
||||||
|
// };
|
||||||
|
// let Some(proto::view::Variant::Editor(state)) = state.take() else {
|
||||||
|
// unreachable!()
|
||||||
|
// };
|
||||||
|
|
||||||
if let Some(title) = &state.title {
|
// let client = project.read(cx).client();
|
||||||
multibuffer = multibuffer.with_title(title.clone())
|
// let replica_id = project.read(cx).replica_id();
|
||||||
}
|
// let buffer_ids = state
|
||||||
|
// .excerpts
|
||||||
|
// .iter()
|
||||||
|
// .map(|excerpt| excerpt.buffer_id)
|
||||||
|
// .collect::<HashSet<_>>();
|
||||||
|
// let buffers = project.update(cx, |project, cx| {
|
||||||
|
// buffer_ids
|
||||||
|
// .iter()
|
||||||
|
// .map(|id| project.open_buffer_by_id(*id, cx))
|
||||||
|
// .collect::<Vec<_>>()
|
||||||
|
// });
|
||||||
|
|
||||||
multibuffer
|
// let pane = pane.downgrade();
|
||||||
});
|
// Some(cx.spawn(|mut cx| async move {
|
||||||
|
// let mut buffers = futures::future::try_join_all(buffers).await?;
|
||||||
|
// let editor = pane.read_with(&cx, |pane, cx| {
|
||||||
|
// let mut editors = pane.items_of_type::<Self>();
|
||||||
|
// editors.find(|editor| {
|
||||||
|
// let ids_match = editor.remote_id(&client, cx) == Some(remote_id);
|
||||||
|
// let singleton_buffer_matches = state.singleton
|
||||||
|
// && buffers.first()
|
||||||
|
// == editor.read(cx).buffer.read(cx).as_singleton().as_ref();
|
||||||
|
// ids_match || singleton_buffer_matches
|
||||||
|
// })
|
||||||
|
// })?;
|
||||||
|
|
||||||
cx.add_view(|cx| {
|
// let editor = if let Some(editor) = editor {
|
||||||
let mut editor =
|
// editor
|
||||||
Editor::for_multibuffer(multibuffer, Some(project.clone()), cx);
|
// } else {
|
||||||
editor.remote_id = Some(remote_id);
|
// pane.update(&mut cx, |_, cx| {
|
||||||
editor
|
// let multibuffer = cx.add_model(|cx| {
|
||||||
})
|
// let mut multibuffer;
|
||||||
})?
|
// if state.singleton && buffers.len() == 1 {
|
||||||
};
|
// multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
|
||||||
|
// } else {
|
||||||
|
// multibuffer = MultiBuffer::new(replica_id);
|
||||||
|
// let mut excerpts = state.excerpts.into_iter().peekable();
|
||||||
|
// while let Some(excerpt) = excerpts.peek() {
|
||||||
|
// let buffer_id = excerpt.buffer_id;
|
||||||
|
// let buffer_excerpts = iter::from_fn(|| {
|
||||||
|
// let excerpt = excerpts.peek()?;
|
||||||
|
// (excerpt.buffer_id == buffer_id)
|
||||||
|
// .then(|| excerpts.next().unwrap())
|
||||||
|
// });
|
||||||
|
// let buffer =
|
||||||
|
// buffers.iter().find(|b| b.read(cx).remote_id() == buffer_id);
|
||||||
|
// if let Some(buffer) = buffer {
|
||||||
|
// multibuffer.push_excerpts(
|
||||||
|
// buffer.clone(),
|
||||||
|
// buffer_excerpts.filter_map(deserialize_excerpt_range),
|
||||||
|
// cx,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
update_editor_from_message(
|
// if let Some(title) = &state.title {
|
||||||
editor.downgrade(),
|
// multibuffer = multibuffer.with_title(title.clone())
|
||||||
project,
|
// }
|
||||||
proto::update_view::Editor {
|
|
||||||
selections: state.selections,
|
|
||||||
pending_selection: state.pending_selection,
|
|
||||||
scroll_top_anchor: state.scroll_top_anchor,
|
|
||||||
scroll_x: state.scroll_x,
|
|
||||||
scroll_y: state.scroll_y,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
&mut cx,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(editor)
|
// multibuffer
|
||||||
}))
|
// });
|
||||||
}
|
|
||||||
|
// cx.add_view(|cx| {
|
||||||
|
// let mut editor =
|
||||||
|
// Editor::for_multibuffer(multibuffer, Some(project.clone()), cx);
|
||||||
|
// editor.remote_id = Some(remote_id);
|
||||||
|
// editor
|
||||||
|
// })
|
||||||
|
// })?
|
||||||
|
// };
|
||||||
|
|
||||||
|
// update_editor_from_message(
|
||||||
|
// editor.downgrade(),
|
||||||
|
// project,
|
||||||
|
// proto::update_view::Editor {
|
||||||
|
// selections: state.selections,
|
||||||
|
// pending_selection: state.pending_selection,
|
||||||
|
// scroll_top_anchor: state.scroll_top_anchor,
|
||||||
|
// scroll_x: state.scroll_x,
|
||||||
|
// scroll_y: state.scroll_y,
|
||||||
|
// ..Default::default()
|
||||||
|
// },
|
||||||
|
// &mut cx,
|
||||||
|
// )
|
||||||
|
// .await?;
|
||||||
|
|
||||||
|
// Ok(editor)
|
||||||
|
// }))
|
||||||
|
// }
|
||||||
|
|
||||||
fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {
|
fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {
|
||||||
self.leader_peer_id = leader_peer_id;
|
self.leader_peer_id = leader_peer_id;
|
||||||
|
@ -197,8 +199,8 @@ impl FollowableItem for Editor {
|
||||||
title: (!buffer.is_singleton()).then(|| buffer.title(cx).into()),
|
title: (!buffer.is_singleton()).then(|| buffer.title(cx).into()),
|
||||||
excerpts,
|
excerpts,
|
||||||
scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.anchor)),
|
scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.anchor)),
|
||||||
scroll_x: scroll_anchor.offset.x(),
|
scroll_x: scroll_anchor.offset.x,
|
||||||
scroll_y: scroll_anchor.offset.y(),
|
scroll_y: scroll_anchor.offset.y,
|
||||||
selections: self
|
selections: self
|
||||||
.selections
|
.selections
|
||||||
.disjoint_anchors()
|
.disjoint_anchors()
|
||||||
|
@ -254,8 +256,8 @@ impl FollowableItem for Editor {
|
||||||
Event::ScrollPositionChanged { .. } => {
|
Event::ScrollPositionChanged { .. } => {
|
||||||
let scroll_anchor = self.scroll_manager.anchor();
|
let scroll_anchor = self.scroll_manager.anchor();
|
||||||
update.scroll_top_anchor = Some(serialize_anchor(&scroll_anchor.anchor));
|
update.scroll_top_anchor = Some(serialize_anchor(&scroll_anchor.anchor));
|
||||||
update.scroll_x = scroll_anchor.offset.x();
|
update.scroll_x = scroll_anchor.offset.x;
|
||||||
update.scroll_y = scroll_anchor.offset.y();
|
update.scroll_y = scroll_anchor.offset.y;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Event::SelectionsChanged { .. } => {
|
Event::SelectionsChanged { .. } => {
|
||||||
|
@ -561,8 +563,8 @@ impl Item for Editor {
|
||||||
|
|
||||||
fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<SharedString> {
|
fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<SharedString> {
|
||||||
match path_for_buffer(&self.buffer, detail, true, cx)? {
|
match path_for_buffer(&self.buffer, detail, true, cx)? {
|
||||||
Cow::Borrowed(path) => Some(path.to_string_lossy()),
|
Cow::Borrowed(path) => Some(path.to_string_lossy),
|
||||||
Cow::Owned(path) => Some(path.to_string_lossy().to_string().into()),
|
Cow::Owned(path) => Some(path.to_string_lossy.to_string().into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,7 +600,11 @@ impl Item for Editor {
|
||||||
self.buffer.read(cx).is_singleton()
|
self.buffer.read(cx).is_singleton()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone_on_split(&self, _workspace_id: WorkspaceId, cx: &mut ViewContext<Self>) -> Option<Self>
|
fn clone_on_split(
|
||||||
|
&self,
|
||||||
|
_workspace_id: WorkspaceId,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Option<View<Self>>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
|
@ -611,7 +617,8 @@ impl Item for Editor {
|
||||||
|
|
||||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
let selection = self.selections.newest_anchor();
|
let selection = self.selections.newest_anchor();
|
||||||
self.push_to_nav_history(selection.head(), None, cx);
|
todo!()
|
||||||
|
// self.push_to_nav_history(selection.head(), None, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn workspace_deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
fn workspace_deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
@ -652,7 +659,7 @@ impl Item for Editor {
|
||||||
// we simulate saving by calling `Buffer::did_save`, so that language servers or
|
// we simulate saving by calling `Buffer::did_save`, so that language servers or
|
||||||
// other downstream listeners of save events get notified.
|
// other downstream listeners of save events get notified.
|
||||||
let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| {
|
let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| {
|
||||||
buffer.read_with(&cx, |buffer, _| buffer.is_dirty() || buffer.has_conflict())
|
buffer.read_with(&cx, |buffer, _| buffer.is_dirty || buffer.has_conflict())
|
||||||
});
|
});
|
||||||
|
|
||||||
project
|
project
|
||||||
|
@ -686,9 +693,7 @@ impl Item for Editor {
|
||||||
.as_singleton()
|
.as_singleton()
|
||||||
.expect("cannot call save_as on an excerpt list");
|
.expect("cannot call save_as on an excerpt list");
|
||||||
|
|
||||||
let file_extension = abs_path
|
let file_extension = abs_path.extension().map(|a| a.to_string_lossy.to_string());
|
||||||
.extension()
|
|
||||||
.map(|a| a.to_string_lossy().to_string());
|
|
||||||
self.report_editor_event("save", file_extension, cx);
|
self.report_editor_event("save", file_extension, cx);
|
||||||
|
|
||||||
project.update(cx, |project, cx| {
|
project.update(cx, |project, cx| {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
|
||||||
use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint};
|
use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint};
|
||||||
use gpui::TextSystem;
|
use gpui::TextSystem;
|
||||||
use language::Point;
|
use language::Point;
|
||||||
use std::{ops::Range, sync::Arc};
|
use std::ops::Range;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum FindRange {
|
pub enum FindRange {
|
||||||
|
@ -444,483 +444,483 @@ pub fn split_display_range_by_lines(
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
// #[cfg(test)]
|
||||||
mod tests {
|
// mod tests {
|
||||||
use super::*;
|
// use super::*;
|
||||||
use crate::{
|
// use crate::{
|
||||||
display_map::Inlay,
|
// display_map::Inlay,
|
||||||
test::{editor_test_context::EditorTestContext, marked_display_snapshot},
|
// test::{},
|
||||||
Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer,
|
// Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer,
|
||||||
};
|
// };
|
||||||
use project::Project;
|
// use project::Project;
|
||||||
use settings::SettingsStore;
|
// use settings::SettingsStore;
|
||||||
use util::post_inc;
|
// use util::post_inc;
|
||||||
|
|
||||||
#[gpui::test]
|
// #[gpui::test]
|
||||||
fn test_previous_word_start(cx: &mut gpui::AppContext) {
|
// fn test_previous_word_start(cx: &mut gpui::AppContext) {
|
||||||
init_test(cx);
|
// init_test(cx);
|
||||||
|
|
||||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
// fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
previous_word_start(&snapshot, display_points[1]),
|
// previous_word_start(&snapshot, display_points[1]),
|
||||||
display_points[0]
|
// display_points[0]
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
assert("\nˇ ˇlorem", cx);
|
// assert("\nˇ ˇlorem", cx);
|
||||||
assert("ˇ\nˇ lorem", cx);
|
// assert("ˇ\nˇ lorem", cx);
|
||||||
assert(" ˇloremˇ", cx);
|
// assert(" ˇloremˇ", cx);
|
||||||
assert("ˇ ˇlorem", cx);
|
// assert("ˇ ˇlorem", cx);
|
||||||
assert(" ˇlorˇem", cx);
|
// assert(" ˇlorˇem", cx);
|
||||||
assert("\nlorem\nˇ ˇipsum", cx);
|
// assert("\nlorem\nˇ ˇipsum", cx);
|
||||||
assert("\n\nˇ\nˇ", cx);
|
// assert("\n\nˇ\nˇ", cx);
|
||||||
assert(" ˇlorem ˇipsum", cx);
|
// assert(" ˇlorem ˇipsum", cx);
|
||||||
assert("loremˇ-ˇipsum", cx);
|
// assert("loremˇ-ˇipsum", cx);
|
||||||
assert("loremˇ-#$@ˇipsum", cx);
|
// assert("loremˇ-#$@ˇipsum", cx);
|
||||||
assert("ˇlorem_ˇipsum", cx);
|
// assert("ˇlorem_ˇipsum", cx);
|
||||||
assert(" ˇdefγˇ", cx);
|
// assert(" ˇdefγˇ", cx);
|
||||||
assert(" ˇbcΔˇ", cx);
|
// assert(" ˇbcΔˇ", cx);
|
||||||
assert(" abˇ——ˇcd", cx);
|
// assert(" abˇ——ˇcd", cx);
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[gpui::test]
|
// #[gpui::test]
|
||||||
fn test_previous_subword_start(cx: &mut gpui::AppContext) {
|
// fn test_previous_subword_start(cx: &mut gpui::AppContext) {
|
||||||
init_test(cx);
|
// init_test(cx);
|
||||||
|
|
||||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
// fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
previous_subword_start(&snapshot, display_points[1]),
|
// previous_subword_start(&snapshot, display_points[1]),
|
||||||
display_points[0]
|
// display_points[0]
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Subword boundaries are respected
|
// // Subword boundaries are respected
|
||||||
assert("lorem_ˇipˇsum", cx);
|
// assert("lorem_ˇipˇsum", cx);
|
||||||
assert("lorem_ˇipsumˇ", cx);
|
// assert("lorem_ˇipsumˇ", cx);
|
||||||
assert("ˇlorem_ˇipsum", cx);
|
// assert("ˇlorem_ˇipsum", cx);
|
||||||
assert("lorem_ˇipsum_ˇdolor", cx);
|
// assert("lorem_ˇipsum_ˇdolor", cx);
|
||||||
assert("loremˇIpˇsum", cx);
|
// assert("loremˇIpˇsum", cx);
|
||||||
assert("loremˇIpsumˇ", cx);
|
// assert("loremˇIpsumˇ", cx);
|
||||||
|
|
||||||
// Word boundaries are still respected
|
// // Word boundaries are still respected
|
||||||
assert("\nˇ ˇlorem", cx);
|
// assert("\nˇ ˇlorem", cx);
|
||||||
assert(" ˇloremˇ", cx);
|
// assert(" ˇloremˇ", cx);
|
||||||
assert(" ˇlorˇem", cx);
|
// assert(" ˇlorˇem", cx);
|
||||||
assert("\nlorem\nˇ ˇipsum", cx);
|
// assert("\nlorem\nˇ ˇipsum", cx);
|
||||||
assert("\n\nˇ\nˇ", cx);
|
// assert("\n\nˇ\nˇ", cx);
|
||||||
assert(" ˇlorem ˇipsum", cx);
|
// assert(" ˇlorem ˇipsum", cx);
|
||||||
assert("loremˇ-ˇipsum", cx);
|
// assert("loremˇ-ˇipsum", cx);
|
||||||
assert("loremˇ-#$@ˇipsum", cx);
|
// assert("loremˇ-#$@ˇipsum", cx);
|
||||||
assert(" ˇdefγˇ", cx);
|
// assert(" ˇdefγˇ", cx);
|
||||||
assert(" bcˇΔˇ", cx);
|
// assert(" bcˇΔˇ", cx);
|
||||||
assert(" ˇbcδˇ", cx);
|
// assert(" ˇbcδˇ", cx);
|
||||||
assert(" abˇ——ˇcd", cx);
|
// assert(" abˇ——ˇcd", cx);
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[gpui::test]
|
// #[gpui::test]
|
||||||
fn test_find_preceding_boundary(cx: &mut gpui::AppContext) {
|
// fn test_find_preceding_boundary(cx: &mut gpui::AppContext) {
|
||||||
init_test(cx);
|
// init_test(cx);
|
||||||
|
|
||||||
fn assert(
|
// fn assert(
|
||||||
marked_text: &str,
|
// marked_text: &str,
|
||||||
cx: &mut gpui::AppContext,
|
// cx: &mut gpui::AppContext,
|
||||||
is_boundary: impl FnMut(char, char) -> bool,
|
// is_boundary: impl FnMut(char, char) -> bool,
|
||||||
) {
|
// ) {
|
||||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
find_preceding_boundary(
|
// find_preceding_boundary(
|
||||||
&snapshot,
|
// &snapshot,
|
||||||
display_points[1],
|
// display_points[1],
|
||||||
FindRange::MultiLine,
|
// FindRange::MultiLine,
|
||||||
is_boundary
|
// is_boundary
|
||||||
),
|
// ),
|
||||||
display_points[0]
|
// display_points[0]
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
assert("abcˇdef\ngh\nijˇk", cx, |left, right| {
|
// assert("abcˇdef\ngh\nijˇk", cx, |left, right| {
|
||||||
left == 'c' && right == 'd'
|
// left == 'c' && right == 'd'
|
||||||
});
|
// });
|
||||||
assert("abcdef\nˇgh\nijˇk", cx, |left, right| {
|
// assert("abcdef\nˇgh\nijˇk", cx, |left, right| {
|
||||||
left == '\n' && right == 'g'
|
// left == '\n' && right == 'g'
|
||||||
});
|
// });
|
||||||
let mut line_count = 0;
|
// let mut line_count = 0;
|
||||||
assert("abcdef\nˇgh\nijˇk", cx, |left, _| {
|
// assert("abcdef\nˇgh\nijˇk", cx, |left, _| {
|
||||||
if left == '\n' {
|
// if left == '\n' {
|
||||||
line_count += 1;
|
// line_count += 1;
|
||||||
line_count == 2
|
// line_count == 2
|
||||||
} else {
|
// } else {
|
||||||
false
|
// false
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[gpui::test]
|
// #[gpui::test]
|
||||||
fn test_find_preceding_boundary_with_inlays(cx: &mut gpui::AppContext) {
|
// fn test_find_preceding_boundary_with_inlays(cx: &mut gpui::AppContext) {
|
||||||
init_test(cx);
|
// init_test(cx);
|
||||||
|
|
||||||
let input_text = "abcdefghijklmnopqrstuvwxys";
|
// let input_text = "abcdefghijklmnopqrstuvwxys";
|
||||||
let family_id = cx
|
// let family_id = cx
|
||||||
.font_cache()
|
// .font_cache()
|
||||||
.load_family(&["Helvetica"], &Default::default())
|
// .load_family(&["Helvetica"], &Default::default())
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
let font_id = cx
|
// let font_id = cx
|
||||||
.font_cache()
|
// .font_cache()
|
||||||
.select_font(family_id, &Default::default())
|
// .select_font(family_id, &Default::default())
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
let font_size = 14.0;
|
// let font_size = 14.0;
|
||||||
let buffer = MultiBuffer::build_simple(input_text, cx);
|
// let buffer = MultiBuffer::build_simple(input_text, cx);
|
||||||
let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
// let buffer_snapshot = buffer.read(cx).snapshot(cx);
|
||||||
let display_map =
|
// let display_map =
|
||||||
cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
|
// cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
|
||||||
|
|
||||||
// add all kinds of inlays between two word boundaries: we should be able to cross them all, when looking for another boundary
|
// // add all kinds of inlays between two word boundaries: we should be able to cross them all, when looking for another boundary
|
||||||
let mut id = 0;
|
// let mut id = 0;
|
||||||
let inlays = (0..buffer_snapshot.len())
|
// let inlays = (0..buffer_snapshot.len())
|
||||||
.map(|offset| {
|
// .map(|offset| {
|
||||||
[
|
// [
|
||||||
Inlay {
|
// Inlay {
|
||||||
id: InlayId::Suggestion(post_inc(&mut id)),
|
// id: InlayId::Suggestion(post_inc(&mut id)),
|
||||||
position: buffer_snapshot.anchor_at(offset, Bias::Left),
|
// position: buffer_snapshot.anchor_at(offset, Bias::Left),
|
||||||
text: format!("test").into(),
|
// text: format!("test").into(),
|
||||||
},
|
// },
|
||||||
Inlay {
|
// Inlay {
|
||||||
id: InlayId::Suggestion(post_inc(&mut id)),
|
// id: InlayId::Suggestion(post_inc(&mut id)),
|
||||||
position: buffer_snapshot.anchor_at(offset, Bias::Right),
|
// position: buffer_snapshot.anchor_at(offset, Bias::Right),
|
||||||
text: format!("test").into(),
|
// text: format!("test").into(),
|
||||||
},
|
// },
|
||||||
Inlay {
|
// Inlay {
|
||||||
id: InlayId::Hint(post_inc(&mut id)),
|
// id: InlayId::Hint(post_inc(&mut id)),
|
||||||
position: buffer_snapshot.anchor_at(offset, Bias::Left),
|
// position: buffer_snapshot.anchor_at(offset, Bias::Left),
|
||||||
text: format!("test").into(),
|
// text: format!("test").into(),
|
||||||
},
|
// },
|
||||||
Inlay {
|
// Inlay {
|
||||||
id: InlayId::Hint(post_inc(&mut id)),
|
// id: InlayId::Hint(post_inc(&mut id)),
|
||||||
position: buffer_snapshot.anchor_at(offset, Bias::Right),
|
// position: buffer_snapshot.anchor_at(offset, Bias::Right),
|
||||||
text: format!("test").into(),
|
// text: format!("test").into(),
|
||||||
},
|
// },
|
||||||
]
|
// ]
|
||||||
})
|
// })
|
||||||
.flatten()
|
// .flatten()
|
||||||
.collect();
|
// .collect();
|
||||||
let snapshot = display_map.update(cx, |map, cx| {
|
// let snapshot = display_map.update(cx, |map, cx| {
|
||||||
map.splice_inlays(Vec::new(), inlays, cx);
|
// map.splice_inlays(Vec::new(), inlays, cx);
|
||||||
map.snapshot(cx)
|
// map.snapshot(cx)
|
||||||
});
|
// });
|
||||||
|
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
find_preceding_boundary(
|
// find_preceding_boundary(
|
||||||
&snapshot,
|
// &snapshot,
|
||||||
buffer_snapshot.len().to_display_point(&snapshot),
|
// buffer_snapshot.len().to_display_point(&snapshot),
|
||||||
FindRange::MultiLine,
|
// FindRange::MultiLine,
|
||||||
|left, _| left == 'e',
|
// |left, _| left == 'e',
|
||||||
),
|
// ),
|
||||||
snapshot
|
// snapshot
|
||||||
.buffer_snapshot
|
// .buffer_snapshot
|
||||||
.offset_to_point(5)
|
// .offset_to_point(5)
|
||||||
.to_display_point(&snapshot),
|
// .to_display_point(&snapshot),
|
||||||
"Should not stop at inlays when looking for boundaries"
|
// "Should not stop at inlays when looking for boundaries"
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[gpui::test]
|
// #[gpui::test]
|
||||||
fn test_next_word_end(cx: &mut gpui::AppContext) {
|
// fn test_next_word_end(cx: &mut gpui::AppContext) {
|
||||||
init_test(cx);
|
// init_test(cx);
|
||||||
|
|
||||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
// fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
next_word_end(&snapshot, display_points[0]),
|
// next_word_end(&snapshot, display_points[0]),
|
||||||
display_points[1]
|
// display_points[1]
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
assert("\nˇ loremˇ", cx);
|
// assert("\nˇ loremˇ", cx);
|
||||||
assert(" ˇloremˇ", cx);
|
// assert(" ˇloremˇ", cx);
|
||||||
assert(" lorˇemˇ", cx);
|
// assert(" lorˇemˇ", cx);
|
||||||
assert(" loremˇ ˇ\nipsum\n", cx);
|
// assert(" loremˇ ˇ\nipsum\n", cx);
|
||||||
assert("\nˇ\nˇ\n\n", cx);
|
// assert("\nˇ\nˇ\n\n", cx);
|
||||||
assert("loremˇ ipsumˇ ", cx);
|
// assert("loremˇ ipsumˇ ", cx);
|
||||||
assert("loremˇ-ˇipsum", cx);
|
// assert("loremˇ-ˇipsum", cx);
|
||||||
assert("loremˇ#$@-ˇipsum", cx);
|
// assert("loremˇ#$@-ˇipsum", cx);
|
||||||
assert("loremˇ_ipsumˇ", cx);
|
// assert("loremˇ_ipsumˇ", cx);
|
||||||
assert(" ˇbcΔˇ", cx);
|
// assert(" ˇbcΔˇ", cx);
|
||||||
assert(" abˇ——ˇcd", cx);
|
// assert(" abˇ——ˇcd", cx);
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[gpui::test]
|
// #[gpui::test]
|
||||||
fn test_next_subword_end(cx: &mut gpui::AppContext) {
|
// fn test_next_subword_end(cx: &mut gpui::AppContext) {
|
||||||
init_test(cx);
|
// init_test(cx);
|
||||||
|
|
||||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
// fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
next_subword_end(&snapshot, display_points[0]),
|
// next_subword_end(&snapshot, display_points[0]),
|
||||||
display_points[1]
|
// display_points[1]
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Subword boundaries are respected
|
// // Subword boundaries are respected
|
||||||
assert("loˇremˇ_ipsum", cx);
|
// assert("loˇremˇ_ipsum", cx);
|
||||||
assert("ˇloremˇ_ipsum", cx);
|
// assert("ˇloremˇ_ipsum", cx);
|
||||||
assert("loremˇ_ipsumˇ", cx);
|
// assert("loremˇ_ipsumˇ", cx);
|
||||||
assert("loremˇ_ipsumˇ_dolor", cx);
|
// assert("loremˇ_ipsumˇ_dolor", cx);
|
||||||
assert("loˇremˇIpsum", cx);
|
// assert("loˇremˇIpsum", cx);
|
||||||
assert("loremˇIpsumˇDolor", cx);
|
// assert("loremˇIpsumˇDolor", cx);
|
||||||
|
|
||||||
// Word boundaries are still respected
|
// // Word boundaries are still respected
|
||||||
assert("\nˇ loremˇ", cx);
|
// assert("\nˇ loremˇ", cx);
|
||||||
assert(" ˇloremˇ", cx);
|
// assert(" ˇloremˇ", cx);
|
||||||
assert(" lorˇemˇ", cx);
|
// assert(" lorˇemˇ", cx);
|
||||||
assert(" loremˇ ˇ\nipsum\n", cx);
|
// assert(" loremˇ ˇ\nipsum\n", cx);
|
||||||
assert("\nˇ\nˇ\n\n", cx);
|
// assert("\nˇ\nˇ\n\n", cx);
|
||||||
assert("loremˇ ipsumˇ ", cx);
|
// assert("loremˇ ipsumˇ ", cx);
|
||||||
assert("loremˇ-ˇipsum", cx);
|
// assert("loremˇ-ˇipsum", cx);
|
||||||
assert("loremˇ#$@-ˇipsum", cx);
|
// assert("loremˇ#$@-ˇipsum", cx);
|
||||||
assert("loremˇ_ipsumˇ", cx);
|
// assert("loremˇ_ipsumˇ", cx);
|
||||||
assert(" ˇbcˇΔ", cx);
|
// assert(" ˇbcˇΔ", cx);
|
||||||
assert(" abˇ——ˇcd", cx);
|
// assert(" abˇ——ˇcd", cx);
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[gpui::test]
|
// #[gpui::test]
|
||||||
fn test_find_boundary(cx: &mut gpui::AppContext) {
|
// fn test_find_boundary(cx: &mut gpui::AppContext) {
|
||||||
init_test(cx);
|
// init_test(cx);
|
||||||
|
|
||||||
fn assert(
|
// fn assert(
|
||||||
marked_text: &str,
|
// marked_text: &str,
|
||||||
cx: &mut gpui::AppContext,
|
// cx: &mut gpui::AppContext,
|
||||||
is_boundary: impl FnMut(char, char) -> bool,
|
// is_boundary: impl FnMut(char, char) -> bool,
|
||||||
) {
|
// ) {
|
||||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
find_boundary(
|
// find_boundary(
|
||||||
&snapshot,
|
// &snapshot,
|
||||||
display_points[0],
|
// display_points[0],
|
||||||
FindRange::MultiLine,
|
// FindRange::MultiLine,
|
||||||
is_boundary
|
// is_boundary
|
||||||
),
|
// ),
|
||||||
display_points[1]
|
// display_points[1]
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
assert("abcˇdef\ngh\nijˇk", cx, |left, right| {
|
// assert("abcˇdef\ngh\nijˇk", cx, |left, right| {
|
||||||
left == 'j' && right == 'k'
|
// left == 'j' && right == 'k'
|
||||||
});
|
// });
|
||||||
assert("abˇcdef\ngh\nˇijk", cx, |left, right| {
|
// assert("abˇcdef\ngh\nˇijk", cx, |left, right| {
|
||||||
left == '\n' && right == 'i'
|
// left == '\n' && right == 'i'
|
||||||
});
|
// });
|
||||||
let mut line_count = 0;
|
// let mut line_count = 0;
|
||||||
assert("abcˇdef\ngh\nˇijk", cx, |left, _| {
|
// assert("abcˇdef\ngh\nˇijk", cx, |left, _| {
|
||||||
if left == '\n' {
|
// if left == '\n' {
|
||||||
line_count += 1;
|
// line_count += 1;
|
||||||
line_count == 2
|
// line_count == 2
|
||||||
} else {
|
// } else {
|
||||||
false
|
// false
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[gpui::test]
|
// #[gpui::test]
|
||||||
fn test_surrounding_word(cx: &mut gpui::AppContext) {
|
// fn test_surrounding_word(cx: &mut gpui::AppContext) {
|
||||||
init_test(cx);
|
// init_test(cx);
|
||||||
|
|
||||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
// fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
// let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
surrounding_word(&snapshot, display_points[1]),
|
// surrounding_word(&snapshot, display_points[1]),
|
||||||
display_points[0]..display_points[2],
|
// display_points[0]..display_points[2],
|
||||||
"{}",
|
// "{}",
|
||||||
marked_text.to_string()
|
// marked_text.to_string()
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
assert("ˇˇloremˇ ipsum", cx);
|
// assert("ˇˇloremˇ ipsum", cx);
|
||||||
assert("ˇloˇremˇ ipsum", cx);
|
// assert("ˇloˇremˇ ipsum", cx);
|
||||||
assert("ˇloremˇˇ ipsum", cx);
|
// assert("ˇloremˇˇ ipsum", cx);
|
||||||
assert("loremˇ ˇ ˇipsum", cx);
|
// assert("loremˇ ˇ ˇipsum", cx);
|
||||||
assert("lorem\nˇˇˇ\nipsum", cx);
|
// assert("lorem\nˇˇˇ\nipsum", cx);
|
||||||
assert("lorem\nˇˇipsumˇ", cx);
|
// assert("lorem\nˇˇipsumˇ", cx);
|
||||||
assert("loremˇ,ˇˇ ipsum", cx);
|
// assert("loremˇ,ˇˇ ipsum", cx);
|
||||||
assert("ˇloremˇˇ, ipsum", cx);
|
// assert("ˇloremˇˇ, ipsum", cx);
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[gpui::test]
|
// #[gpui::test]
|
||||||
async fn test_move_up_and_down_with_excerpts(cx: &mut gpui::TestAppContext) {
|
// async fn test_move_up_and_down_with_excerpts(cx: &mut gpui::TestAppContext) {
|
||||||
cx.update(|cx| {
|
// cx.update(|cx| {
|
||||||
init_test(cx);
|
// init_test(cx);
|
||||||
});
|
// });
|
||||||
|
|
||||||
let mut cx = EditorTestContext::new(cx).await;
|
// let mut cx = EditorTestContext::new(cx).await;
|
||||||
let editor = cx.editor.clone();
|
// let editor = cx.editor.clone();
|
||||||
let window = cx.window.clone();
|
// let window = cx.window.clone();
|
||||||
cx.update_window(window, |cx| {
|
// cx.update_window(window, |cx| {
|
||||||
let text_layout_details =
|
// let text_layout_details =
|
||||||
editor.read_with(cx, |editor, cx| editor.text_layout_details(cx));
|
// editor.read_with(cx, |editor, cx| editor.text_layout_details(cx));
|
||||||
|
|
||||||
let family_id = cx
|
// let family_id = cx
|
||||||
.font_cache()
|
// .font_cache()
|
||||||
.load_family(&["Helvetica"], &Default::default())
|
// .load_family(&["Helvetica"], &Default::default())
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
let font_id = cx
|
// let font_id = cx
|
||||||
.font_cache()
|
// .font_cache()
|
||||||
.select_font(family_id, &Default::default())
|
// .select_font(family_id, &Default::default())
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
let buffer =
|
// let buffer =
|
||||||
cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "abc\ndefg\nhijkl\nmn"));
|
// cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "abc\ndefg\nhijkl\nmn"));
|
||||||
let multibuffer = cx.add_model(|cx| {
|
// let multibuffer = cx.add_model(|cx| {
|
||||||
let mut multibuffer = MultiBuffer::new(0);
|
// let mut multibuffer = MultiBuffer::new(0);
|
||||||
multibuffer.push_excerpts(
|
// multibuffer.push_excerpts(
|
||||||
buffer.clone(),
|
// buffer.clone(),
|
||||||
[
|
// [
|
||||||
ExcerptRange {
|
// ExcerptRange {
|
||||||
context: Point::new(0, 0)..Point::new(1, 4),
|
// context: Point::new(0, 0)..Point::new(1, 4),
|
||||||
primary: None,
|
// primary: None,
|
||||||
},
|
// },
|
||||||
ExcerptRange {
|
// ExcerptRange {
|
||||||
context: Point::new(2, 0)..Point::new(3, 2),
|
// context: Point::new(2, 0)..Point::new(3, 2),
|
||||||
primary: None,
|
// primary: None,
|
||||||
},
|
// },
|
||||||
],
|
// ],
|
||||||
cx,
|
// cx,
|
||||||
);
|
// );
|
||||||
multibuffer
|
// multibuffer
|
||||||
});
|
// });
|
||||||
let display_map =
|
// let display_map =
|
||||||
cx.add_model(|cx| DisplayMap::new(multibuffer, font_id, 14.0, None, 2, 2, cx));
|
// cx.add_model(|cx| DisplayMap::new(multibuffer, font_id, 14.0, None, 2, 2, cx));
|
||||||
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
// let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
|
|
||||||
assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn");
|
// assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn");
|
||||||
|
|
||||||
let col_2_x = snapshot.x_for_point(DisplayPoint::new(2, 2), &text_layout_details);
|
// let col_2_x = snapshot.x_for_point(DisplayPoint::new(2, 2), &text_layout_details);
|
||||||
|
|
||||||
// Can't move up into the first excerpt's header
|
// // Can't move up into the first excerpt's header
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
up(
|
// up(
|
||||||
&snapshot,
|
// &snapshot,
|
||||||
DisplayPoint::new(2, 2),
|
// DisplayPoint::new(2, 2),
|
||||||
SelectionGoal::HorizontalPosition(col_2_x),
|
// SelectionGoal::HorizontalPosition(col_2_x),
|
||||||
false,
|
// false,
|
||||||
&text_layout_details
|
// &text_layout_details
|
||||||
),
|
// ),
|
||||||
(
|
// (
|
||||||
DisplayPoint::new(2, 0),
|
// DisplayPoint::new(2, 0),
|
||||||
SelectionGoal::HorizontalPosition(0.0)
|
// SelectionGoal::HorizontalPosition(0.0)
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
up(
|
// up(
|
||||||
&snapshot,
|
// &snapshot,
|
||||||
DisplayPoint::new(2, 0),
|
// DisplayPoint::new(2, 0),
|
||||||
SelectionGoal::None,
|
// SelectionGoal::None,
|
||||||
false,
|
// false,
|
||||||
&text_layout_details
|
// &text_layout_details
|
||||||
),
|
// ),
|
||||||
(
|
// (
|
||||||
DisplayPoint::new(2, 0),
|
// DisplayPoint::new(2, 0),
|
||||||
SelectionGoal::HorizontalPosition(0.0)
|
// SelectionGoal::HorizontalPosition(0.0)
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
|
|
||||||
let col_4_x = snapshot.x_for_point(DisplayPoint::new(3, 4), &text_layout_details);
|
// let col_4_x = snapshot.x_for_point(DisplayPoint::new(3, 4), &text_layout_details);
|
||||||
|
|
||||||
// Move up and down within first excerpt
|
// // Move up and down within first excerpt
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
up(
|
// up(
|
||||||
&snapshot,
|
// &snapshot,
|
||||||
DisplayPoint::new(3, 4),
|
// DisplayPoint::new(3, 4),
|
||||||
SelectionGoal::HorizontalPosition(col_4_x),
|
// SelectionGoal::HorizontalPosition(col_4_x),
|
||||||
false,
|
// false,
|
||||||
&text_layout_details
|
// &text_layout_details
|
||||||
),
|
// ),
|
||||||
(
|
// (
|
||||||
DisplayPoint::new(2, 3),
|
// DisplayPoint::new(2, 3),
|
||||||
SelectionGoal::HorizontalPosition(col_4_x)
|
// SelectionGoal::HorizontalPosition(col_4_x)
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
down(
|
// down(
|
||||||
&snapshot,
|
// &snapshot,
|
||||||
DisplayPoint::new(2, 3),
|
// DisplayPoint::new(2, 3),
|
||||||
SelectionGoal::HorizontalPosition(col_4_x),
|
// SelectionGoal::HorizontalPosition(col_4_x),
|
||||||
false,
|
// false,
|
||||||
&text_layout_details
|
// &text_layout_details
|
||||||
),
|
// ),
|
||||||
(
|
// (
|
||||||
DisplayPoint::new(3, 4),
|
// DisplayPoint::new(3, 4),
|
||||||
SelectionGoal::HorizontalPosition(col_4_x)
|
// SelectionGoal::HorizontalPosition(col_4_x)
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
|
|
||||||
let col_5_x = snapshot.x_for_point(DisplayPoint::new(6, 5), &text_layout_details);
|
// let col_5_x = snapshot.x_for_point(DisplayPoint::new(6, 5), &text_layout_details);
|
||||||
|
|
||||||
// Move up and down across second excerpt's header
|
// // Move up and down across second excerpt's header
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
up(
|
// up(
|
||||||
&snapshot,
|
// &snapshot,
|
||||||
DisplayPoint::new(6, 5),
|
// DisplayPoint::new(6, 5),
|
||||||
SelectionGoal::HorizontalPosition(col_5_x),
|
// SelectionGoal::HorizontalPosition(col_5_x),
|
||||||
false,
|
// false,
|
||||||
&text_layout_details
|
// &text_layout_details
|
||||||
),
|
// ),
|
||||||
(
|
// (
|
||||||
DisplayPoint::new(3, 4),
|
// DisplayPoint::new(3, 4),
|
||||||
SelectionGoal::HorizontalPosition(col_5_x)
|
// SelectionGoal::HorizontalPosition(col_5_x)
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
down(
|
// down(
|
||||||
&snapshot,
|
// &snapshot,
|
||||||
DisplayPoint::new(3, 4),
|
// DisplayPoint::new(3, 4),
|
||||||
SelectionGoal::HorizontalPosition(col_5_x),
|
// SelectionGoal::HorizontalPosition(col_5_x),
|
||||||
false,
|
// false,
|
||||||
&text_layout_details
|
// &text_layout_details
|
||||||
),
|
// ),
|
||||||
(
|
// (
|
||||||
DisplayPoint::new(6, 5),
|
// DisplayPoint::new(6, 5),
|
||||||
SelectionGoal::HorizontalPosition(col_5_x)
|
// SelectionGoal::HorizontalPosition(col_5_x)
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
|
|
||||||
let max_point_x = snapshot.x_for_point(DisplayPoint::new(7, 2), &text_layout_details);
|
// let max_point_x = snapshot.x_for_point(DisplayPoint::new(7, 2), &text_layout_details);
|
||||||
|
|
||||||
// Can't move down off the end
|
// // Can't move down off the end
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
down(
|
// down(
|
||||||
&snapshot,
|
// &snapshot,
|
||||||
DisplayPoint::new(7, 0),
|
// DisplayPoint::new(7, 0),
|
||||||
SelectionGoal::HorizontalPosition(0.0),
|
// SelectionGoal::HorizontalPosition(0.0),
|
||||||
false,
|
// false,
|
||||||
&text_layout_details
|
// &text_layout_details
|
||||||
),
|
// ),
|
||||||
(
|
// (
|
||||||
DisplayPoint::new(7, 2),
|
// DisplayPoint::new(7, 2),
|
||||||
SelectionGoal::HorizontalPosition(max_point_x)
|
// SelectionGoal::HorizontalPosition(max_point_x)
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
down(
|
// down(
|
||||||
&snapshot,
|
// &snapshot,
|
||||||
DisplayPoint::new(7, 2),
|
// DisplayPoint::new(7, 2),
|
||||||
SelectionGoal::HorizontalPosition(max_point_x),
|
// SelectionGoal::HorizontalPosition(max_point_x),
|
||||||
false,
|
// false,
|
||||||
&text_layout_details
|
// &text_layout_details
|
||||||
),
|
// ),
|
||||||
(
|
// (
|
||||||
DisplayPoint::new(7, 2),
|
// DisplayPoint::new(7, 2),
|
||||||
SelectionGoal::HorizontalPosition(max_point_x)
|
// SelectionGoal::HorizontalPosition(max_point_x)
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn init_test(cx: &mut gpui::AppContext) {
|
// fn init_test(cx: &mut gpui::AppContext) {
|
||||||
cx.set_global(SettingsStore::test(cx));
|
// cx.set_global(SettingsStore::test(cx));
|
||||||
theme::init(cx);
|
// theme::init(cx);
|
||||||
language::init(cx);
|
// language::init(cx);
|
||||||
crate::init(cx);
|
// crate::init(cx);
|
||||||
Project::init_settings(cx);
|
// Project::init_settings(cx);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
@ -39,7 +39,7 @@ pub struct ScrollAnchor {
|
||||||
impl ScrollAnchor {
|
impl ScrollAnchor {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
offset: Point::zero(),
|
offset: gpui::Point::zero(),
|
||||||
anchor: Anchor::min(),
|
anchor: Anchor::min(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ impl ScrollAnchor {
|
||||||
let mut scroll_position = self.offset;
|
let mut scroll_position = self.offset;
|
||||||
if self.anchor != Anchor::min() {
|
if self.anchor != Anchor::min() {
|
||||||
let scroll_top = self.anchor.to_display_point(snapshot).row() as f32;
|
let scroll_top = self.anchor.to_display_point(snapshot).row() as f32;
|
||||||
scroll_position.set_y(scroll_top + scroll_position.y());
|
scroll_position.set_y(scroll_top + scroll_position.y);
|
||||||
} else {
|
} else {
|
||||||
scroll_position.set_y(0.);
|
scroll_position.set_y(0.);
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ impl OngoingScroll {
|
||||||
|
|
||||||
pub fn filter(&self, delta: &mut gpui::Point<Pixels>) -> Option<Axis> {
|
pub fn filter(&self, delta: &mut gpui::Point<Pixels>) -> Option<Axis> {
|
||||||
const UNLOCK_PERCENT: f32 = 1.9;
|
const UNLOCK_PERCENT: f32 = 1.9;
|
||||||
const UNLOCK_LOWER_BOUND: f32 = 6.;
|
const UNLOCK_LOWER_BOUND: Pixels = px(6.);
|
||||||
let mut axis = self.axis;
|
let mut axis = self.axis;
|
||||||
|
|
||||||
let x = delta.x.abs();
|
let x = delta.x.abs();
|
||||||
|
@ -116,10 +116,10 @@ impl OngoingScroll {
|
||||||
|
|
||||||
match axis {
|
match axis {
|
||||||
Some(Axis::Vertical) => {
|
Some(Axis::Vertical) => {
|
||||||
*delta = point(pk(0.), delta.y());
|
*delta = point(px(0.), delta.y);
|
||||||
}
|
}
|
||||||
Some(Axis::Horizontal) => {
|
Some(Axis::Horizontal) => {
|
||||||
*delta = point(delta.x(), px(0.));
|
*delta = point(delta.x, px(0.));
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
@ -177,14 +177,14 @@ impl ScrollManager {
|
||||||
|
|
||||||
fn set_scroll_position(
|
fn set_scroll_position(
|
||||||
&mut self,
|
&mut self,
|
||||||
scroll_position: gpui::Point<Pixels>,
|
scroll_position: gpui::Point<f32>,
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
local: bool,
|
local: bool,
|
||||||
autoscroll: bool,
|
autoscroll: bool,
|
||||||
workspace_id: Option<i64>,
|
workspace_id: Option<i64>,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
let (new_anchor, top_row) = if scroll_position.y() <= 0. {
|
let (new_anchor, top_row) = if scroll_position.y <= 0. {
|
||||||
(
|
(
|
||||||
ScrollAnchor {
|
ScrollAnchor {
|
||||||
anchor: Anchor::min(),
|
anchor: Anchor::min(),
|
||||||
|
@ -194,7 +194,7 @@ impl ScrollManager {
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let scroll_top_buffer_point =
|
let scroll_top_buffer_point =
|
||||||
DisplayPoint::new(scroll_position.y() as u32, 0).to_point(&map);
|
DisplayPoint::new(scroll_position.y as u32, 0).to_point(&map);
|
||||||
let top_anchor = map
|
let top_anchor = map
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.anchor_at(scroll_top_buffer_point, Bias::Right);
|
.anchor_at(scroll_top_buffer_point, Bias::Right);
|
||||||
|
@ -203,8 +203,8 @@ impl ScrollManager {
|
||||||
ScrollAnchor {
|
ScrollAnchor {
|
||||||
anchor: top_anchor,
|
anchor: top_anchor,
|
||||||
offset: point(
|
offset: point(
|
||||||
scroll_position.x(),
|
scroll_position.x,
|
||||||
scroll_position.y() - top_anchor.to_display_point(&map).row() as f32,
|
scroll_position.y - top_anchor.to_display_point(&map).row() as f32,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
scroll_top_buffer_point.row,
|
scroll_top_buffer_point.row,
|
||||||
|
@ -236,8 +236,8 @@ impl ScrollManager {
|
||||||
item_id,
|
item_id,
|
||||||
workspace_id,
|
workspace_id,
|
||||||
top_row,
|
top_row,
|
||||||
anchor.offset.x(),
|
anchor.offset.x,
|
||||||
anchor.offset.y(),
|
anchor.offset.y,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.log_err()
|
.log_err()
|
||||||
|
@ -277,8 +277,8 @@ impl ScrollManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
|
pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
|
||||||
if max < self.anchor.offset.x() {
|
if max < self.anchor.offset.x {
|
||||||
self.anchor.offset.set_x(max);
|
self.anchor.offset.x = max;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
|
@ -60,7 +60,7 @@ impl Editor {
|
||||||
} else {
|
} else {
|
||||||
display_map.max_point().row() as f32
|
display_map.max_point().row() as f32
|
||||||
};
|
};
|
||||||
if scroll_position.y() > max_scroll_top {
|
if scroll_position.y > max_scroll_top {
|
||||||
scroll_position.set_y(max_scroll_top);
|
scroll_position.set_y(max_scroll_top);
|
||||||
self.set_scroll_position(scroll_position, cx);
|
self.set_scroll_position(scroll_position, cx);
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ impl Editor {
|
||||||
let margin = margin.min(self.scroll_manager.vertical_scroll_margin);
|
let margin = margin.min(self.scroll_manager.vertical_scroll_margin);
|
||||||
let target_top = (target_top - margin).max(0.0);
|
let target_top = (target_top - margin).max(0.0);
|
||||||
let target_bottom = target_bottom + margin;
|
let target_bottom = target_bottom + margin;
|
||||||
let start_row = scroll_position.y();
|
let start_row = scroll_position.y;
|
||||||
let end_row = start_row + visible_lines;
|
let end_row = start_row + visible_lines;
|
||||||
|
|
||||||
let needs_scroll_up = target_top < start_row;
|
let needs_scroll_up = target_top < start_row;
|
||||||
|
@ -222,20 +222,15 @@ impl Editor {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let scroll_left = self.scroll_manager.anchor.offset.x() * max_glyph_width;
|
let scroll_left = self.scroll_manager.anchor.offset.x * max_glyph_width;
|
||||||
let scroll_right = scroll_left + viewport_width;
|
let scroll_right = scroll_left + viewport_width;
|
||||||
|
|
||||||
if target_left < scroll_left {
|
if target_left < scroll_left {
|
||||||
self.scroll_manager
|
self.scroll_manager.anchor.offset.x = (target_left / max_glyph_width);
|
||||||
.anchor
|
|
||||||
.offset
|
|
||||||
.set_x(target_left / max_glyph_width);
|
|
||||||
true
|
true
|
||||||
} else if target_right > scroll_right {
|
} else if target_right > scroll_right {
|
||||||
self.scroll_manager
|
self.scroll_manager.anchor.offset.x =
|
||||||
.anchor
|
((target_right - viewport_width) / max_glyph_width);
|
||||||
.offset
|
|
||||||
.set_x((target_right - viewport_width) / max_glyph_width);
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
|
@ -1,297 +1,297 @@
|
||||||
use std::{
|
// use std::{
|
||||||
borrow::Cow,
|
// borrow::Cow,
|
||||||
ops::{Deref, DerefMut, Range},
|
// ops::{Deref, DerefMut, Range},
|
||||||
sync::Arc,
|
// sync::Arc,
|
||||||
};
|
// };
|
||||||
|
|
||||||
use anyhow::Result;
|
// use anyhow::Result;
|
||||||
|
|
||||||
use crate::{Editor, ToPoint};
|
// use crate::{Editor, ToPoint};
|
||||||
use collections::HashSet;
|
// use collections::HashSet;
|
||||||
use futures::Future;
|
// use futures::Future;
|
||||||
use gpui::{json, View, ViewContext};
|
// use gpui::{json, View, ViewContext};
|
||||||
use indoc::indoc;
|
// use indoc::indoc;
|
||||||
use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
|
// use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
|
||||||
use lsp::{notification, request};
|
// use lsp::{notification, request};
|
||||||
use multi_buffer::ToPointUtf16;
|
// use multi_buffer::ToPointUtf16;
|
||||||
use project::Project;
|
// use project::Project;
|
||||||
use smol::stream::StreamExt;
|
// use smol::stream::StreamExt;
|
||||||
use workspace::{AppState, Workspace, WorkspaceHandle};
|
// use workspace::{AppState, Workspace, WorkspaceHandle};
|
||||||
|
|
||||||
use super::editor_test_context::EditorTestContext;
|
// use super::editor_test_context::EditorTestContext;
|
||||||
|
|
||||||
pub struct EditorLspTestContext<'a> {
|
// pub struct EditorLspTestContext<'a> {
|
||||||
pub cx: EditorTestContext<'a>,
|
// pub cx: EditorTestContext<'a>,
|
||||||
pub lsp: lsp::FakeLanguageServer,
|
// pub lsp: lsp::FakeLanguageServer,
|
||||||
pub workspace: ViewHandle<Workspace>,
|
// pub workspace: View<Workspace>,
|
||||||
pub buffer_lsp_url: lsp::Url,
|
// pub buffer_lsp_url: lsp::Url,
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<'a> EditorLspTestContext<'a> {
|
// impl<'a> EditorLspTestContext<'a> {
|
||||||
pub async fn new(
|
// pub async fn new(
|
||||||
mut language: Language,
|
// mut language: Language,
|
||||||
capabilities: lsp::ServerCapabilities,
|
// capabilities: lsp::ServerCapabilities,
|
||||||
cx: &'a mut gpui::TestAppContext,
|
// cx: &'a mut gpui::TestAppContext,
|
||||||
) -> EditorLspTestContext<'a> {
|
// ) -> EditorLspTestContext<'a> {
|
||||||
use json::json;
|
// use json::json;
|
||||||
|
|
||||||
let app_state = cx.update(AppState::test);
|
// let app_state = cx.update(AppState::test);
|
||||||
|
|
||||||
cx.update(|cx| {
|
// cx.update(|cx| {
|
||||||
language::init(cx);
|
// language::init(cx);
|
||||||
crate::init(cx);
|
// crate::init(cx);
|
||||||
workspace::init(app_state.clone(), cx);
|
// workspace::init(app_state.clone(), cx);
|
||||||
Project::init_settings(cx);
|
// Project::init_settings(cx);
|
||||||
});
|
// });
|
||||||
|
|
||||||
let file_name = format!(
|
// let file_name = format!(
|
||||||
"file.{}",
|
// "file.{}",
|
||||||
language
|
// language
|
||||||
.path_suffixes()
|
// .path_suffixes()
|
||||||
.first()
|
// .first()
|
||||||
.expect("language must have a path suffix for EditorLspTestContext")
|
// .expect("language must have a path suffix for EditorLspTestContext")
|
||||||
);
|
// );
|
||||||
|
|
||||||
let mut fake_servers = language
|
// let mut fake_servers = language
|
||||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||||
capabilities,
|
// capabilities,
|
||||||
..Default::default()
|
// ..Default::default()
|
||||||
}))
|
// }))
|
||||||
.await;
|
// .await;
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
// let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||||
project.update(cx, |project, _| project.languages().add(Arc::new(language)));
|
// project.update(cx, |project, _| project.languages().add(Arc::new(language)));
|
||||||
|
|
||||||
app_state
|
// app_state
|
||||||
.fs
|
// .fs
|
||||||
.as_fake()
|
// .as_fake()
|
||||||
.insert_tree("/root", json!({ "dir": { file_name.clone(): "" }}))
|
// .insert_tree("/root", json!({ "dir": { file_name.clone(): "" }}))
|
||||||
.await;
|
// .await;
|
||||||
|
|
||||||
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
let workspace = window.root(cx);
|
// let workspace = window.root(cx);
|
||||||
project
|
// project
|
||||||
.update(cx, |project, cx| {
|
// .update(cx, |project, cx| {
|
||||||
project.find_or_create_local_worktree("/root", true, cx)
|
// project.find_or_create_local_worktree("/root", true, cx)
|
||||||
})
|
// })
|
||||||
.await
|
// .await
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
|
// cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
|
||||||
.await;
|
// .await;
|
||||||
|
|
||||||
let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
|
// let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
|
||||||
let item = workspace
|
// let item = workspace
|
||||||
.update(cx, |workspace, cx| {
|
// .update(cx, |workspace, cx| {
|
||||||
workspace.open_path(file, None, true, cx)
|
// workspace.open_path(file, None, true, cx)
|
||||||
})
|
// })
|
||||||
.await
|
// .await
|
||||||
.expect("Could not open test file");
|
// .expect("Could not open test file");
|
||||||
|
|
||||||
let editor = cx.update(|cx| {
|
// let editor = cx.update(|cx| {
|
||||||
item.act_as::<Editor>(cx)
|
// item.act_as::<Editor>(cx)
|
||||||
.expect("Opened test file wasn't an editor")
|
// .expect("Opened test file wasn't an editor")
|
||||||
});
|
// });
|
||||||
editor.update(cx, |_, cx| cx.focus_self());
|
// editor.update(cx, |_, cx| cx.focus_self());
|
||||||
|
|
||||||
let lsp = fake_servers.next().await.unwrap();
|
// let lsp = fake_servers.next().await.unwrap();
|
||||||
|
|
||||||
Self {
|
// Self {
|
||||||
cx: EditorTestContext {
|
// cx: EditorTestContext {
|
||||||
cx,
|
// cx,
|
||||||
window: window.into(),
|
// window: window.into(),
|
||||||
editor,
|
// editor,
|
||||||
},
|
// },
|
||||||
lsp,
|
// lsp,
|
||||||
workspace,
|
// workspace,
|
||||||
buffer_lsp_url: lsp::Url::from_file_path(format!("/root/dir/{file_name}")).unwrap(),
|
// buffer_lsp_url: lsp::Url::from_file_path(format!("/root/dir/{file_name}")).unwrap(),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub async fn new_rust(
|
// pub async fn new_rust(
|
||||||
capabilities: lsp::ServerCapabilities,
|
// capabilities: lsp::ServerCapabilities,
|
||||||
cx: &'a mut gpui::TestAppContext,
|
// cx: &'a mut gpui::TestAppContext,
|
||||||
) -> EditorLspTestContext<'a> {
|
// ) -> EditorLspTestContext<'a> {
|
||||||
let language = Language::new(
|
// let language = Language::new(
|
||||||
LanguageConfig {
|
// LanguageConfig {
|
||||||
name: "Rust".into(),
|
// name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
// path_suffixes: vec!["rs".to_string()],
|
||||||
..Default::default()
|
// ..Default::default()
|
||||||
},
|
// },
|
||||||
Some(tree_sitter_rust::language()),
|
// Some(tree_sitter_rust::language()),
|
||||||
)
|
// )
|
||||||
.with_queries(LanguageQueries {
|
// .with_queries(LanguageQueries {
|
||||||
indents: Some(Cow::from(indoc! {r#"
|
// indents: Some(Cow::from(indoc! {r#"
|
||||||
[
|
// [
|
||||||
((where_clause) _ @end)
|
// ((where_clause) _ @end)
|
||||||
(field_expression)
|
// (field_expression)
|
||||||
(call_expression)
|
// (call_expression)
|
||||||
(assignment_expression)
|
// (assignment_expression)
|
||||||
(let_declaration)
|
// (let_declaration)
|
||||||
(let_chain)
|
// (let_chain)
|
||||||
(await_expression)
|
// (await_expression)
|
||||||
] @indent
|
// ] @indent
|
||||||
|
|
||||||
(_ "[" "]" @end) @indent
|
// (_ "[" "]" @end) @indent
|
||||||
(_ "<" ">" @end) @indent
|
// (_ "<" ">" @end) @indent
|
||||||
(_ "{" "}" @end) @indent
|
// (_ "{" "}" @end) @indent
|
||||||
(_ "(" ")" @end) @indent"#})),
|
// (_ "(" ")" @end) @indent"#})),
|
||||||
brackets: Some(Cow::from(indoc! {r#"
|
// brackets: Some(Cow::from(indoc! {r#"
|
||||||
("(" @open ")" @close)
|
// ("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
// ("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
// ("{" @open "}" @close)
|
||||||
("<" @open ">" @close)
|
// ("<" @open ">" @close)
|
||||||
("\"" @open "\"" @close)
|
// ("\"" @open "\"" @close)
|
||||||
(closure_parameters "|" @open "|" @close)"#})),
|
// (closure_parameters "|" @open "|" @close)"#})),
|
||||||
..Default::default()
|
// ..Default::default()
|
||||||
})
|
// })
|
||||||
.expect("Could not parse queries");
|
// .expect("Could not parse queries");
|
||||||
|
|
||||||
Self::new(language, capabilities, cx).await
|
// Self::new(language, capabilities, cx).await
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub async fn new_typescript(
|
// pub async fn new_typescript(
|
||||||
capabilities: lsp::ServerCapabilities,
|
// capabilities: lsp::ServerCapabilities,
|
||||||
cx: &'a mut gpui::TestAppContext,
|
// cx: &'a mut gpui::TestAppContext,
|
||||||
) -> EditorLspTestContext<'a> {
|
// ) -> EditorLspTestContext<'a> {
|
||||||
let mut word_characters: HashSet<char> = Default::default();
|
// let mut word_characters: HashSet<char> = Default::default();
|
||||||
word_characters.insert('$');
|
// word_characters.insert('$');
|
||||||
word_characters.insert('#');
|
// word_characters.insert('#');
|
||||||
let language = Language::new(
|
// let language = Language::new(
|
||||||
LanguageConfig {
|
// LanguageConfig {
|
||||||
name: "Typescript".into(),
|
// name: "Typescript".into(),
|
||||||
path_suffixes: vec!["ts".to_string()],
|
// path_suffixes: vec!["ts".to_string()],
|
||||||
brackets: language::BracketPairConfig {
|
// brackets: language::BracketPairConfig {
|
||||||
pairs: vec![language::BracketPair {
|
// pairs: vec![language::BracketPair {
|
||||||
start: "{".to_string(),
|
// start: "{".to_string(),
|
||||||
end: "}".to_string(),
|
// end: "}".to_string(),
|
||||||
close: true,
|
// close: true,
|
||||||
newline: true,
|
// newline: true,
|
||||||
}],
|
// }],
|
||||||
disabled_scopes_by_bracket_ix: Default::default(),
|
// disabled_scopes_by_bracket_ix: Default::default(),
|
||||||
},
|
// },
|
||||||
word_characters,
|
// word_characters,
|
||||||
..Default::default()
|
// ..Default::default()
|
||||||
},
|
// },
|
||||||
Some(tree_sitter_typescript::language_typescript()),
|
// Some(tree_sitter_typescript::language_typescript()),
|
||||||
)
|
// )
|
||||||
.with_queries(LanguageQueries {
|
// .with_queries(LanguageQueries {
|
||||||
brackets: Some(Cow::from(indoc! {r#"
|
// brackets: Some(Cow::from(indoc! {r#"
|
||||||
("(" @open ")" @close)
|
// ("(" @open ")" @close)
|
||||||
("[" @open "]" @close)
|
// ("[" @open "]" @close)
|
||||||
("{" @open "}" @close)
|
// ("{" @open "}" @close)
|
||||||
("<" @open ">" @close)
|
// ("<" @open ">" @close)
|
||||||
("\"" @open "\"" @close)"#})),
|
// ("\"" @open "\"" @close)"#})),
|
||||||
indents: Some(Cow::from(indoc! {r#"
|
// indents: Some(Cow::from(indoc! {r#"
|
||||||
[
|
// [
|
||||||
(call_expression)
|
// (call_expression)
|
||||||
(assignment_expression)
|
// (assignment_expression)
|
||||||
(member_expression)
|
// (member_expression)
|
||||||
(lexical_declaration)
|
// (lexical_declaration)
|
||||||
(variable_declaration)
|
// (variable_declaration)
|
||||||
(assignment_expression)
|
// (assignment_expression)
|
||||||
(if_statement)
|
// (if_statement)
|
||||||
(for_statement)
|
// (for_statement)
|
||||||
] @indent
|
// ] @indent
|
||||||
|
|
||||||
(_ "[" "]" @end) @indent
|
// (_ "[" "]" @end) @indent
|
||||||
(_ "<" ">" @end) @indent
|
// (_ "<" ">" @end) @indent
|
||||||
(_ "{" "}" @end) @indent
|
// (_ "{" "}" @end) @indent
|
||||||
(_ "(" ")" @end) @indent
|
// (_ "(" ")" @end) @indent
|
||||||
"#})),
|
// "#})),
|
||||||
..Default::default()
|
// ..Default::default()
|
||||||
})
|
// })
|
||||||
.expect("Could not parse queries");
|
// .expect("Could not parse queries");
|
||||||
|
|
||||||
Self::new(language, capabilities, cx).await
|
// Self::new(language, capabilities, cx).await
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Constructs lsp range using a marked string with '[', ']' range delimiters
|
// // Constructs lsp range using a marked string with '[', ']' range delimiters
|
||||||
pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range {
|
// pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range {
|
||||||
let ranges = self.ranges(marked_text);
|
// let ranges = self.ranges(marked_text);
|
||||||
self.to_lsp_range(ranges[0].clone())
|
// self.to_lsp_range(ranges[0].clone())
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range {
|
// pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range {
|
||||||
let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
// let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
||||||
let start_point = range.start.to_point(&snapshot.buffer_snapshot);
|
// let start_point = range.start.to_point(&snapshot.buffer_snapshot);
|
||||||
let end_point = range.end.to_point(&snapshot.buffer_snapshot);
|
// let end_point = range.end.to_point(&snapshot.buffer_snapshot);
|
||||||
|
|
||||||
self.editor(|editor, cx| {
|
// self.editor(|editor, cx| {
|
||||||
let buffer = editor.buffer().read(cx);
|
// let buffer = editor.buffer().read(cx);
|
||||||
let start = point_to_lsp(
|
// let start = point_to_lsp(
|
||||||
buffer
|
// buffer
|
||||||
.point_to_buffer_offset(start_point, cx)
|
// .point_to_buffer_offset(start_point, cx)
|
||||||
.unwrap()
|
// .unwrap()
|
||||||
.1
|
// .1
|
||||||
.to_point_utf16(&buffer.read(cx)),
|
// .to_point_utf16(&buffer.read(cx)),
|
||||||
);
|
// );
|
||||||
let end = point_to_lsp(
|
// let end = point_to_lsp(
|
||||||
buffer
|
// buffer
|
||||||
.point_to_buffer_offset(end_point, cx)
|
// .point_to_buffer_offset(end_point, cx)
|
||||||
.unwrap()
|
// .unwrap()
|
||||||
.1
|
// .1
|
||||||
.to_point_utf16(&buffer.read(cx)),
|
// .to_point_utf16(&buffer.read(cx)),
|
||||||
);
|
// );
|
||||||
|
|
||||||
lsp::Range { start, end }
|
// lsp::Range { start, end }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn to_lsp(&mut self, offset: usize) -> lsp::Position {
|
// pub fn to_lsp(&mut self, offset: usize) -> lsp::Position {
|
||||||
let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
// let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
||||||
let point = offset.to_point(&snapshot.buffer_snapshot);
|
// let point = offset.to_point(&snapshot.buffer_snapshot);
|
||||||
|
|
||||||
self.editor(|editor, cx| {
|
// self.editor(|editor, cx| {
|
||||||
let buffer = editor.buffer().read(cx);
|
// let buffer = editor.buffer().read(cx);
|
||||||
point_to_lsp(
|
// point_to_lsp(
|
||||||
buffer
|
// buffer
|
||||||
.point_to_buffer_offset(point, cx)
|
// .point_to_buffer_offset(point, cx)
|
||||||
.unwrap()
|
// .unwrap()
|
||||||
.1
|
// .1
|
||||||
.to_point_utf16(&buffer.read(cx)),
|
// .to_point_utf16(&buffer.read(cx)),
|
||||||
)
|
// )
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn update_workspace<F, T>(&mut self, update: F) -> T
|
// pub fn update_workspace<F, T>(&mut self, update: F) -> T
|
||||||
where
|
// where
|
||||||
F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
|
// F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
|
||||||
{
|
// {
|
||||||
self.workspace.update(self.cx.cx, update)
|
// self.workspace.update(self.cx.cx, update)
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn handle_request<T, F, Fut>(
|
// pub fn handle_request<T, F, Fut>(
|
||||||
&self,
|
// &self,
|
||||||
mut handler: F,
|
// mut handler: F,
|
||||||
) -> futures::channel::mpsc::UnboundedReceiver<()>
|
// ) -> futures::channel::mpsc::UnboundedReceiver<()>
|
||||||
where
|
// where
|
||||||
T: 'static + request::Request,
|
// T: 'static + request::Request,
|
||||||
T::Params: 'static + Send,
|
// T::Params: 'static + Send,
|
||||||
F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut,
|
// F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut,
|
||||||
Fut: 'static + Send + Future<Output = Result<T::Result>>,
|
// Fut: 'static + Send + Future<Output = Result<T::Result>>,
|
||||||
{
|
// {
|
||||||
let url = self.buffer_lsp_url.clone();
|
// let url = self.buffer_lsp_url.clone();
|
||||||
self.lsp.handle_request::<T, _, _>(move |params, cx| {
|
// self.lsp.handle_request::<T, _, _>(move |params, cx| {
|
||||||
let url = url.clone();
|
// let url = url.clone();
|
||||||
handler(url, params, cx)
|
// handler(url, params, cx)
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn notify<T: notification::Notification>(&self, params: T::Params) {
|
// pub fn notify<T: notification::Notification>(&self, params: T::Params) {
|
||||||
self.lsp.notify::<T>(params);
|
// self.lsp.notify::<T>(params);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<'a> Deref for EditorLspTestContext<'a> {
|
// impl<'a> Deref for EditorLspTestContext<'a> {
|
||||||
type Target = EditorTestContext<'a>;
|
// type Target = EditorTestContext<'a>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
// fn deref(&self) -> &Self::Target {
|
||||||
&self.cx
|
// &self.cx
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<'a> DerefMut for EditorLspTestContext<'a> {
|
// impl<'a> DerefMut for EditorLspTestContext<'a> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
// fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.cx
|
// &mut self.cx
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
@ -315,17 +315,17 @@ use util::{
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
|
// impl<'a> Deref for EditorTestContext<'a> {
|
||||||
|
// type Target = gpui::TestAppContext;
|
||||||
|
|
||||||
impl<'a> Deref for EditorTestContext<'a> {
|
// fn deref(&self) -> &Self::Target {
|
||||||
type Target = gpui::TestAppContext;
|
// self.cx
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
// impl<'a> DerefMut for EditorTestContext<'a> {
|
||||||
self.cx
|
// fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
}
|
// &mut self.cx
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
impl<'a> DerefMut for EditorTestContext<'a> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.cx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -755,6 +755,10 @@ impl Pixels {
|
||||||
pub fn pow(&self, exponent: f32) -> Self {
|
pub fn pow(&self, exponent: f32) -> Self {
|
||||||
Self(self.0.powf(exponent))
|
Self(self.0.powf(exponent))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn abs(&self) -> Self {
|
||||||
|
Self(self.0.abs())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul<Pixels> for Pixels {
|
impl Mul<Pixels> for Pixels {
|
||||||
|
|
|
@ -90,7 +90,7 @@ pub struct BreadcrumbText {
|
||||||
pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>,
|
pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Item: Render + EventEmitter + Send {
|
pub trait Item: Render + EventEmitter {
|
||||||
fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
|
fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
|
||||||
fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
|
fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
|
||||||
fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
|
fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue