Compare commits
15 commits
main
...
editor_ren
Author | SHA1 | Date | |
---|---|---|---|
![]() |
72e42a0d89 | ||
![]() |
750f901f9c | ||
![]() |
7d6ce9432a | ||
![]() |
b381277c73 | ||
![]() |
7968891f9e | ||
![]() |
efcaf48723 | ||
![]() |
69a77e5ec8 | ||
![]() |
d17d450e2d | ||
![]() |
f3dcea3623 | ||
![]() |
a2a6d77cdb | ||
![]() |
bff46f7e55 | ||
![]() |
eae0e30277 | ||
![]() |
144d011a07 | ||
![]() |
fcb2094fae | ||
![]() |
cadf8d0160 |
58 changed files with 2321 additions and 1215 deletions
77
Cargo.lock
generated
77
Cargo.lock
generated
|
@ -8,6 +8,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"auto_update",
|
||||
"editor",
|
||||
"editor_extensions",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"language",
|
||||
|
@ -328,6 +329,7 @@ dependencies = [
|
|||
"collections",
|
||||
"ctor",
|
||||
"editor",
|
||||
"editor_extensions",
|
||||
"env_logger 0.9.3",
|
||||
"fs",
|
||||
"futures 0.3.28",
|
||||
|
@ -1725,6 +1727,7 @@ dependencies = [
|
|||
"db",
|
||||
"drag_and_drop",
|
||||
"editor",
|
||||
"editor_extensions",
|
||||
"feature_flags",
|
||||
"feedback",
|
||||
"futures 0.3.28",
|
||||
|
@ -2459,6 +2462,7 @@ dependencies = [
|
|||
"client",
|
||||
"collections",
|
||||
"editor",
|
||||
"editor_extensions",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"language",
|
||||
|
@ -2466,6 +2470,7 @@ dependencies = [
|
|||
"lsp",
|
||||
"postage",
|
||||
"project",
|
||||
"project_types",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
@ -2612,6 +2617,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"aho-corasick",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"client",
|
||||
"clock",
|
||||
"collections",
|
||||
|
@ -2637,6 +2643,7 @@ dependencies = [
|
|||
"parking_lot 0.11.2",
|
||||
"postage",
|
||||
"project",
|
||||
"project_types",
|
||||
"rand 0.8.5",
|
||||
"rich_text",
|
||||
"rpc",
|
||||
|
@ -2658,6 +2665,7 @@ dependencies = [
|
|||
"unindent",
|
||||
"util",
|
||||
"workspace",
|
||||
"workspace_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2714,6 +2722,31 @@ dependencies = [
|
|||
"workspace2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "editor_extensions"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"client",
|
||||
"collections",
|
||||
"db",
|
||||
"editor",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"language",
|
||||
"lsp",
|
||||
"project",
|
||||
"project_types",
|
||||
"rpc",
|
||||
"smallvec",
|
||||
"text",
|
||||
"theme",
|
||||
"util",
|
||||
"workspace",
|
||||
"workspace_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
|
@ -2910,6 +2943,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"client",
|
||||
"editor",
|
||||
"editor_extensions",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"human_bytes",
|
||||
|
@ -2950,6 +2984,7 @@ dependencies = [
|
|||
"collections",
|
||||
"ctor",
|
||||
"editor",
|
||||
"editor_extensions",
|
||||
"env_logger 0.9.3",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
|
@ -2958,6 +2993,7 @@ dependencies = [
|
|||
"picker",
|
||||
"postage",
|
||||
"project",
|
||||
"project_types",
|
||||
"serde_json",
|
||||
"settings",
|
||||
"text",
|
||||
|
@ -4521,6 +4557,7 @@ dependencies = [
|
|||
"client",
|
||||
"collections",
|
||||
"editor",
|
||||
"editor_extensions",
|
||||
"env_logger 0.9.3",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
|
@ -6356,6 +6393,7 @@ dependencies = [
|
|||
"postage",
|
||||
"prettier",
|
||||
"pretty_assertions",
|
||||
"project_types",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"rpc",
|
||||
|
@ -6450,6 +6488,7 @@ dependencies = [
|
|||
"postage",
|
||||
"pretty_assertions",
|
||||
"project",
|
||||
"project_types",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
@ -6467,6 +6506,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"editor",
|
||||
"editor_extensions",
|
||||
"futures 0.3.28",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
|
@ -6476,6 +6516,7 @@ dependencies = [
|
|||
"picker",
|
||||
"postage",
|
||||
"project",
|
||||
"project_types",
|
||||
"settings",
|
||||
"smol",
|
||||
"text",
|
||||
|
@ -6484,6 +6525,23 @@ dependencies = [
|
|||
"workspace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "project_types"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collections",
|
||||
"gpui",
|
||||
"itertools 0.10.5",
|
||||
"language",
|
||||
"lsp",
|
||||
"rpc",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"settings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prometheus"
|
||||
version = "0.13.3"
|
||||
|
@ -7671,6 +7729,7 @@ dependencies = [
|
|||
"client",
|
||||
"collections",
|
||||
"editor",
|
||||
"editor_extensions",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"language",
|
||||
|
@ -7740,6 +7799,7 @@ dependencies = [
|
|||
"postage",
|
||||
"pretty_assertions",
|
||||
"project",
|
||||
"project_types",
|
||||
"rand 0.8.5",
|
||||
"rpc",
|
||||
"rusqlite",
|
||||
|
@ -10914,6 +10974,7 @@ dependencies = [
|
|||
"parking_lot 0.11.2",
|
||||
"postage",
|
||||
"project",
|
||||
"project_types",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
@ -10924,6 +10985,7 @@ dependencies = [
|
|||
"theme",
|
||||
"util",
|
||||
"uuid 1.4.1",
|
||||
"workspace_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -10964,6 +11026,20 @@ dependencies = [
|
|||
"uuid 1.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "workspace_types"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gpui",
|
||||
"language",
|
||||
"lsp",
|
||||
"project_types",
|
||||
"rpc",
|
||||
"serde",
|
||||
"text",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ws2_32-sys"
|
||||
version = "0.2.1"
|
||||
|
@ -11075,6 +11151,7 @@ dependencies = [
|
|||
"db",
|
||||
"diagnostics",
|
||||
"editor",
|
||||
"editor_extensions",
|
||||
"env_logger 0.9.3",
|
||||
"feature_flags",
|
||||
"feedback",
|
||||
|
|
|
@ -31,6 +31,7 @@ members = [
|
|||
"crates/diagnostics",
|
||||
"crates/drag_and_drop",
|
||||
"crates/editor",
|
||||
"crates/editor_extensions",
|
||||
"crates/feature_flags",
|
||||
"crates/feature_flags2",
|
||||
"crates/feedback",
|
||||
|
@ -76,6 +77,7 @@ members = [
|
|||
"crates/project2",
|
||||
"crates/project_panel",
|
||||
"crates/project_symbols",
|
||||
"crates/project_types",
|
||||
"crates/recent_projects",
|
||||
"crates/rope",
|
||||
"crates/rpc",
|
||||
|
|
|
@ -11,6 +11,7 @@ doctest = false
|
|||
[dependencies]
|
||||
auto_update = { path = "../auto_update" }
|
||||
editor = { path = "../editor" }
|
||||
editor_extensions = { path = "../editor_extensions" }
|
||||
language = { path = "../language" }
|
||||
gpui = { path = "../gpui" }
|
||||
project = { path = "../project" }
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use auto_update::{AutoUpdateStatus, AutoUpdater, DismissErrorMessage};
|
||||
use editor::Editor;
|
||||
use editor_extensions::FollowableEditor;
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
actions, anyhow,
|
||||
|
@ -103,9 +104,9 @@ impl ActivityIndicator {
|
|||
);
|
||||
});
|
||||
workspace.add_item(
|
||||
Box::new(
|
||||
cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
|
||||
),
|
||||
Box::new(cx.add_view(|cx| {
|
||||
FollowableEditor::for_buffer(buffer, project.clone(), cx)
|
||||
})),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ ai = { path = "../ai" }
|
|||
client = { path = "../client" }
|
||||
collections = { path = "../collections"}
|
||||
editor = { path = "../editor" }
|
||||
editor_extensions = { path = "../editor_extensions" }
|
||||
fs = { path = "../fs" }
|
||||
gpui = { path = "../gpui" }
|
||||
language = { path = "../language" }
|
||||
|
|
|
@ -24,6 +24,7 @@ use editor::{
|
|||
scroll::autoscroll::{Autoscroll, AutoscrollStrategy},
|
||||
Anchor, Editor, MoveDown, MoveUp, MultiBufferSnapshot, ToOffset, ToPoint,
|
||||
};
|
||||
use editor_extensions::FollowableEditor;
|
||||
use fs::Fs;
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
|
@ -2196,7 +2197,7 @@ struct ConversationEditor {
|
|||
conversation: ModelHandle<Conversation>,
|
||||
fs: Arc<dyn Fs>,
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
editor: ViewHandle<Editor>,
|
||||
editor: ViewHandle<FollowableEditor>,
|
||||
blocks: HashSet<BlockId>,
|
||||
scroll_position: Option<ScrollPosition>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
|
@ -2222,10 +2223,11 @@ impl ConversationEditor {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let editor = cx.add_view(|cx| {
|
||||
let mut editor = Editor::for_buffer(conversation.read(cx).buffer.clone(), None, cx);
|
||||
editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
|
||||
editor.set_show_gutter(false, cx);
|
||||
editor.set_show_wrap_guides(false, cx);
|
||||
let mut editor = FollowableEditor::for_raw_buffer(conversation.read(cx).buffer.clone(), cx);
|
||||
editor.0.update(cx, |this, cx| {this.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
|
||||
this.set_show_gutter(false, cx);
|
||||
this.set_show_wrap_guides(false, cx);
|
||||
});
|
||||
editor
|
||||
});
|
||||
|
||||
|
@ -2277,11 +2279,11 @@ impl ConversationEditor {
|
|||
.collect::<Vec<_>>();
|
||||
if !new_selections.is_empty() {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.change_selections(
|
||||
editor.0.update(cx, |this, cx| this.change_selections(
|
||||
Some(Autoscroll::Strategy(AutoscrollStrategy::Fit)),
|
||||
cx,
|
||||
|selections| selections.select_ranges(new_selections),
|
||||
);
|
||||
));
|
||||
});
|
||||
// Avoid scrolling to the new cursor position so the assistant's output is stable.
|
||||
cx.defer(|this, _| this.scroll_position = None);
|
||||
|
@ -2310,7 +2312,7 @@ impl ConversationEditor {
|
|||
}
|
||||
|
||||
fn cursors(&self, cx: &AppContext) -> Vec<usize> {
|
||||
let selections = self.editor.read(cx).selections.all::<usize>(cx);
|
||||
let selections = self.editor.read(cx).0.read(cx).selections.all::<usize>(cx);
|
||||
selections
|
||||
.into_iter()
|
||||
.map(|selection| selection.head())
|
||||
|
@ -2339,14 +2341,14 @@ impl ConversationEditor {
|
|||
ConversationEvent::StreamedCompletion => {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
if let Some(scroll_position) = self.scroll_position {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let snapshot = editor.0.update(cx, |this, cx| this.snapshot(cx));
|
||||
let cursor_point = scroll_position.cursor.to_display_point(&snapshot);
|
||||
let scroll_top =
|
||||
cursor_point.row() as f32 - scroll_position.offset_before_cursor.y();
|
||||
editor.set_scroll_position(
|
||||
editor.0.update(cx, |this, cx| this.set_scroll_position(
|
||||
vec2f(scroll_position.offset_before_cursor.x(), scroll_top),
|
||||
cx,
|
||||
);
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2355,7 +2357,7 @@ impl ConversationEditor {
|
|||
|
||||
fn handle_editor_event(
|
||||
&mut self,
|
||||
_: ViewHandle<Editor>,
|
||||
_: ViewHandle<FollowableEditor>,
|
||||
event: &editor::Event,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
|
@ -2377,15 +2379,15 @@ impl ConversationEditor {
|
|||
|
||||
fn cursor_scroll_position(&self, cx: &mut ViewContext<Self>) -> Option<ScrollPosition> {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let cursor = editor.selections.newest_anchor().head();
|
||||
let snapshot = editor.0.update(cx, |this, cx| this.snapshot(cx));
|
||||
let cursor = editor.0.read(cx).selections.newest_anchor().head();
|
||||
let cursor_row = cursor.to_display_point(&snapshot.display_snapshot).row() as f32;
|
||||
let scroll_position = editor
|
||||
let scroll_position = editor.0.read(cx)
|
||||
.scroll_manager
|
||||
.anchor()
|
||||
.scroll_position(&snapshot.display_snapshot);
|
||||
|
||||
let scroll_bottom = scroll_position.y() + editor.visible_line_count().unwrap_or(0.);
|
||||
let scroll_bottom = scroll_position.y() + editor.0.read(cx).visible_line_count().unwrap_or(0.);
|
||||
if (scroll_position.y()..scroll_bottom).contains(&cursor_row) {
|
||||
Some(ScrollPosition {
|
||||
cursor,
|
||||
|
@ -2402,7 +2404,7 @@ impl ConversationEditor {
|
|||
|
||||
fn update_message_headers(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||
let buffer = editor.0.read(cx).buffer().read(cx).snapshot(cx);
|
||||
let excerpt_id = *buffer.as_singleton().unwrap().0;
|
||||
let old_blocks = std::mem::take(&mut self.blocks);
|
||||
let new_blocks = self
|
||||
|
@ -2505,8 +2507,10 @@ impl ConversationEditor {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
editor.remove_blocks(old_blocks, None, cx);
|
||||
let ids = editor.insert_blocks(new_blocks, None, cx);
|
||||
let ids = editor.0.update(cx, |this, cx| {
|
||||
this.remove_blocks(old_blocks, None, cx);
|
||||
this.insert_blocks(new_blocks, None, cx)
|
||||
});
|
||||
self.blocks = HashSet::from_iter(ids);
|
||||
});
|
||||
}
|
||||
|
@ -2568,14 +2572,14 @@ impl ConversationEditor {
|
|||
conversation.update(cx, |conversation, cx| {
|
||||
conversation
|
||||
.editor
|
||||
.update(cx, |editor, cx| editor.insert(&text, cx))
|
||||
.update(cx, |editor, cx| editor.0.update(cx, |this, cx| this.insert(&text, cx)))
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn copy(&mut self, _: &editor::Copy, cx: &mut ViewContext<Self>) {
|
||||
let editor = self.editor.read(cx);
|
||||
let editor = self.editor.read(cx).0.read(cx);
|
||||
let conversation = self.conversation.read(cx);
|
||||
if editor.selections.count() == 1 {
|
||||
let selection = editor.selections.newest::<usize>(cx);
|
||||
|
@ -2610,9 +2614,9 @@ impl ConversationEditor {
|
|||
|
||||
fn split(&mut self, _: &Split, cx: &mut ViewContext<Self>) {
|
||||
self.conversation.update(cx, |conversation, cx| {
|
||||
let selections = self.editor.read(cx).selections.disjoint_anchors();
|
||||
let selections = self.editor.read(cx).0.read(cx).selections.disjoint_anchors();
|
||||
for selection in selections.into_iter() {
|
||||
let buffer = self.editor.read(cx).buffer().read(cx).snapshot(cx);
|
||||
let buffer = self.editor.read(cx).0.read(cx).buffer().read(cx).snapshot(cx);
|
||||
let range = selection
|
||||
.map(|endpoint| endpoint.to_offset(&buffer))
|
||||
.range();
|
||||
|
|
|
@ -32,6 +32,7 @@ collections = { path = "../collections" }
|
|||
context_menu = { path = "../context_menu" }
|
||||
drag_and_drop = { path = "../drag_and_drop" }
|
||||
editor = { path = "../editor" }
|
||||
editor_extensions = { path = "../editor_extensions" }
|
||||
feedback = { path = "../feedback" }
|
||||
fuzzy = { path = "../fuzzy" }
|
||||
gpui = { path = "../gpui" }
|
||||
|
|
|
@ -7,6 +7,7 @@ use client::{
|
|||
};
|
||||
use collections::HashMap;
|
||||
use editor::{CollaborationHub, Editor};
|
||||
use editor_extensions::FollowableEditor;
|
||||
use gpui::{
|
||||
actions,
|
||||
elements::{ChildView, Label},
|
||||
|
@ -35,7 +36,7 @@ pub fn init(cx: &mut AppContext) {
|
|||
}
|
||||
|
||||
pub struct ChannelView {
|
||||
pub editor: ViewHandle<Editor>,
|
||||
pub editor: ViewHandle<FollowableEditor>,
|
||||
project: ModelHandle<Project>,
|
||||
channel_store: ModelHandle<ChannelStore>,
|
||||
channel_buffer: ModelHandle<ChannelBuffer>,
|
||||
|
@ -137,16 +138,18 @@ impl ChannelView {
|
|||
) -> Self {
|
||||
let buffer = channel_buffer.read(cx).buffer();
|
||||
let editor = cx.add_view(|cx| {
|
||||
let mut editor = Editor::for_buffer(buffer, None, cx);
|
||||
editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
|
||||
let mut editor = FollowableEditor::for_raw_buffer(buffer, cx);
|
||||
editor.0.update(cx, |this, cx| {
|
||||
this.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
|
||||
channel_buffer.clone(),
|
||||
)));
|
||||
editor.set_read_only(
|
||||
this.set_read_only(
|
||||
!channel_buffer
|
||||
.read(cx)
|
||||
.channel(cx)
|
||||
.is_some_and(|c| c.can_edit_notes()),
|
||||
);
|
||||
);
|
||||
});
|
||||
editor
|
||||
});
|
||||
let _editor_event_subscription = cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone()));
|
||||
|
@ -176,12 +179,12 @@ impl ChannelView {
|
|||
) {
|
||||
match event {
|
||||
ChannelBufferEvent::Disconnected => self.editor.update(cx, |editor, cx| {
|
||||
editor.set_read_only(true);
|
||||
editor.0.update(cx, |this, cx| this.set_read_only(true));
|
||||
cx.notify();
|
||||
}),
|
||||
ChannelBufferEvent::ChannelChanged => {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.set_read_only(!self.channel(cx).is_some_and(|c| c.can_edit_notes()));
|
||||
editor.0.update(cx, |this, cx| this.set_read_only(!self.channel(cx).is_some_and(|c| c.can_edit_notes())));
|
||||
cx.emit(editor::Event::TitleChanged);
|
||||
cx.notify()
|
||||
});
|
||||
|
@ -320,7 +323,7 @@ impl Item for ChannelView {
|
|||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
|
||||
editor::Editor::to_item_events(event)
|
||||
FollowableEditor::to_item_events(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,7 +433,7 @@ impl FollowableItem for ChannelView {
|
|||
}
|
||||
|
||||
fn should_unfollow_on_event(event: &Self::Event, cx: &AppContext) -> bool {
|
||||
Editor::should_unfollow_on_event(event, cx)
|
||||
FollowableEditor::should_unfollow_on_event(event, cx)
|
||||
}
|
||||
|
||||
fn is_project_item(&self, _cx: &AppContext) -> bool {
|
||||
|
|
|
@ -20,6 +20,7 @@ use context_menu::{ContextMenu, ContextMenuItem};
|
|||
use db::kvp::KEY_VALUE_STORE;
|
||||
use drag_and_drop::{DragAndDrop, Draggable};
|
||||
use editor::{Cancel, Editor};
|
||||
use editor_extensions::FollowableEditor;
|
||||
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
|
||||
use futures::StreamExt;
|
||||
use fuzzy::{match_strings, StringMatchCandidate};
|
||||
|
@ -274,7 +275,7 @@ pub struct CollabPanel {
|
|||
pending_serialization: Task<Option<()>>,
|
||||
context_menu: ViewHandle<ContextMenu>,
|
||||
filter_editor: ViewHandle<Editor>,
|
||||
channel_name_editor: ViewHandle<Editor>,
|
||||
channel_name_editor: ViewHandle<FollowableEditor>,
|
||||
channel_editing_state: Option<ChannelEditingState>,
|
||||
entries: Vec<ListEntry>,
|
||||
selection: Option<usize>,
|
||||
|
@ -409,7 +410,7 @@ impl CollabPanel {
|
|||
.detach();
|
||||
|
||||
let channel_name_editor = cx.add_view(|cx| {
|
||||
Editor::single_line(
|
||||
FollowableEditor::single_line(
|
||||
Some(Arc::new(|theme| {
|
||||
theme.collab_panel.user_query_editor.clone()
|
||||
})),
|
||||
|
@ -1433,7 +1434,7 @@ impl CollabPanel {
|
|||
fn take_editing_state(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||
if let Some(_) = self.channel_editing_state.take() {
|
||||
self.channel_name_editor.update(cx, |editor, cx| {
|
||||
editor.set_text("", cx);
|
||||
editor.0.update(cx, |this, cx| this.set_text("", cx));
|
||||
});
|
||||
true
|
||||
} else {
|
||||
|
@ -2822,7 +2823,7 @@ impl CollabPanel {
|
|||
fn insert_space(&mut self, _: &InsertSpace, cx: &mut ViewContext<Self>) {
|
||||
if self.channel_editing_state.is_some() {
|
||||
self.channel_name_editor.update(cx, |editor, cx| {
|
||||
editor.insert(" ", cx);
|
||||
editor.0.update(cx, |this, cx| this.insert(" ", cx));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2838,7 +2839,7 @@ impl CollabPanel {
|
|||
if pending_name.is_some() {
|
||||
return false;
|
||||
}
|
||||
let channel_name = self.channel_name_editor.read(cx).text(cx);
|
||||
let channel_name = self.channel_name_editor.read(cx).0.read(cx).text(cx);
|
||||
|
||||
*pending_name = Some(channel_name.clone());
|
||||
|
||||
|
@ -2856,7 +2857,7 @@ impl CollabPanel {
|
|||
if pending_name.is_some() {
|
||||
return false;
|
||||
}
|
||||
let channel_name = self.channel_name_editor.read(cx).text(cx);
|
||||
let channel_name = self.channel_name_editor.read(cx).0.read(cx).text(cx);
|
||||
*pending_name = Some(channel_name.clone());
|
||||
|
||||
self.channel_store
|
||||
|
@ -3025,8 +3026,11 @@ impl CollabPanel {
|
|||
pending_name: None,
|
||||
});
|
||||
self.channel_name_editor.update(cx, |editor, cx| {
|
||||
editor.set_text(channel.name.clone(), cx);
|
||||
editor.select_all(&Default::default(), cx);
|
||||
editor.0.update(cx, |this, cx| {
|
||||
this.set_text(channel.name.clone(), cx);
|
||||
this.select_all(&Default::default(), cx);
|
||||
})
|
||||
|
||||
});
|
||||
cx.focus(self.channel_name_editor.as_any());
|
||||
self.update_entries(false, cx);
|
||||
|
|
|
@ -118,6 +118,7 @@ pub async fn open_test_db<M: Migrator>(db_name: &str) -> ThreadSafeConnection<M>
|
|||
#[macro_export]
|
||||
macro_rules! define_connection {
|
||||
(pub static ref $id:ident: $t:ident<()> = $migrations:expr;) => {
|
||||
#[derive(Clone)]
|
||||
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<$t>);
|
||||
|
||||
impl ::std::ops::Deref for $t {
|
||||
|
@ -149,6 +150,7 @@ macro_rules! define_connection {
|
|||
}
|
||||
};
|
||||
(pub static ref $id:ident: $t:ident<$($d:ty),+> = $migrations:expr;) => {
|
||||
#[derive(Clone)]
|
||||
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<( $($d),+, $t )>);
|
||||
|
||||
impl ::std::ops::Deref for $t {
|
||||
|
|
|
@ -11,10 +11,12 @@ doctest = false
|
|||
[dependencies]
|
||||
collections = { path = "../collections" }
|
||||
editor = { path = "../editor" }
|
||||
editor_extensions = { path = "../editor_extensions" }
|
||||
gpui = { path = "../gpui" }
|
||||
language = { path = "../language" }
|
||||
lsp = { path = "../lsp" }
|
||||
project = { path = "../project" }
|
||||
project_types = { path = "../project_types" }
|
||||
settings = { path = "../settings" }
|
||||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
|
|
|
@ -11,6 +11,7 @@ use editor::{
|
|||
scroll::autoscroll::Autoscroll,
|
||||
Editor, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
||||
};
|
||||
use editor_extensions::FollowableEditor;
|
||||
use futures::future::try_join_all;
|
||||
use gpui::{
|
||||
actions, elements::*, fonts::TextStyle, serde_json, AnyViewHandle, AppContext, Entity,
|
||||
|
@ -21,8 +22,9 @@ use language::{
|
|||
SelectionGoal,
|
||||
};
|
||||
use lsp::LanguageServerId;
|
||||
use project::{DiagnosticSummary, Project, ProjectPath};
|
||||
use project::{DiagnosticSummary, Project};
|
||||
use project_diagnostics_settings::ProjectDiagnosticsSettings;
|
||||
use project_types::ProjectPath;
|
||||
use serde_json::json;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
|
@ -58,7 +60,7 @@ type Event = editor::Event;
|
|||
struct ProjectDiagnosticsEditor {
|
||||
project: ModelHandle<Project>,
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
editor: ViewHandle<Editor>,
|
||||
editor: ViewHandle<FollowableEditor>,
|
||||
summary: DiagnosticSummary,
|
||||
excerpts: ModelHandle<MultiBuffer>,
|
||||
path_states: Vec<PathState>,
|
||||
|
@ -172,7 +174,7 @@ impl ProjectDiagnosticsEditor {
|
|||
.or_default()
|
||||
.insert(path.clone());
|
||||
let no_multiselections = this.editor.update(cx, |editor, cx| {
|
||||
editor.selections.all::<usize>(cx).len() <= 1
|
||||
editor.0.read(cx).selections.all::<usize>(cx).len() <= 1
|
||||
});
|
||||
if no_multiselections && !this.is_dirty(cx) {
|
||||
this.update_excerpts(Some(*language_server_id), cx);
|
||||
|
@ -184,8 +186,10 @@ impl ProjectDiagnosticsEditor {
|
|||
let excerpts = cx.add_model(|cx| MultiBuffer::new(project_handle.read(cx).replica_id()));
|
||||
let editor = cx.add_view(|cx| {
|
||||
let mut editor =
|
||||
Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), cx);
|
||||
editor.set_vertical_scroll_margin(5, cx);
|
||||
FollowableEditor::for_multibuffer(excerpts.clone(), project_handle.clone(), cx);
|
||||
editor
|
||||
.0
|
||||
.update(cx, |this, cx| this.set_vertical_scroll_margin(5, cx));
|
||||
editor
|
||||
});
|
||||
let editor_event_subscription = cx.subscribe(&editor, |this, _, event, cx| {
|
||||
|
@ -527,21 +531,25 @@ impl ProjectDiagnosticsEditor {
|
|||
});
|
||||
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.remove_blocks(blocks_to_remove, None, cx);
|
||||
let block_ids = editor.insert_blocks(
|
||||
blocks_to_add.into_iter().map(|block| {
|
||||
let (excerpt_id, text_anchor) = block.position;
|
||||
BlockProperties {
|
||||
position: excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor),
|
||||
height: block.height,
|
||||
style: block.style,
|
||||
render: block.render,
|
||||
disposition: block.disposition,
|
||||
}
|
||||
}),
|
||||
Some(Autoscroll::fit()),
|
||||
cx,
|
||||
);
|
||||
editor.0.update(cx, |this, cx| {
|
||||
this.remove_blocks(blocks_to_remove, None, cx)
|
||||
});
|
||||
let block_ids = editor.0.update(cx, |this, cx| {
|
||||
this.insert_blocks(
|
||||
blocks_to_add.into_iter().map(|block| {
|
||||
let (excerpt_id, text_anchor) = block.position;
|
||||
BlockProperties {
|
||||
position: excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor),
|
||||
height: block.height,
|
||||
style: block.style,
|
||||
render: block.render,
|
||||
disposition: block.disposition,
|
||||
}
|
||||
}),
|
||||
Some(Autoscroll::fit()),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
let mut block_ids = block_ids.into_iter();
|
||||
for group_state in &mut groups_to_add {
|
||||
|
@ -582,13 +590,14 @@ impl ProjectDiagnosticsEditor {
|
|||
}];
|
||||
} else {
|
||||
groups = self.path_states.get(path_ix)?.diagnostic_groups.as_slice();
|
||||
new_excerpt_ids_by_selection_id =
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| s.refresh());
|
||||
selections = editor.selections.all::<usize>(cx);
|
||||
new_excerpt_ids_by_selection_id = editor.0.update(cx, |this, cx| {
|
||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.refresh())
|
||||
});
|
||||
selections = editor.0.read(cx).selections.all::<usize>(cx);
|
||||
}
|
||||
|
||||
// If any selection has lost its position, move it to start of the next primary diagnostic.
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let snapshot = editor.0.update(cx, |this, cx| this.snapshot(cx));
|
||||
for selection in &mut selections {
|
||||
if let Some(new_excerpt_id) = new_excerpt_ids_by_selection_id.get(&selection.id) {
|
||||
let group_ix = match groups.binary_search_by(|probe| {
|
||||
|
@ -612,8 +621,10 @@ impl ProjectDiagnosticsEditor {
|
|||
}
|
||||
}
|
||||
}
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.select(selections);
|
||||
editor.0.update(cx, |this, cx| {
|
||||
this.change_selections(None, cx, |s| {
|
||||
s.select(selections);
|
||||
})
|
||||
});
|
||||
Some(())
|
||||
});
|
||||
|
@ -703,12 +714,12 @@ impl Item for ProjectDiagnosticsEditor {
|
|||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
|
||||
Editor::to_item_events(event)
|
||||
FollowableEditor::to_item_events(event)
|
||||
}
|
||||
|
||||
fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
|
||||
self.editor.update(cx, |editor, _| {
|
||||
editor.set_nav_history(Some(nav_history));
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.set_nav_history(nav_history, cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -751,7 +762,7 @@ impl Item for ProjectDiagnosticsEditor {
|
|||
}
|
||||
|
||||
fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
|
||||
self.editor.breadcrumbs(theme, cx)
|
||||
self.editor.read(cx).breadcrumbs(theme, cx)
|
||||
}
|
||||
|
||||
fn breadcrumb_location(&self) -> ToolbarItemLocation {
|
||||
|
|
|
@ -30,13 +30,14 @@ db = { path = "../db" }
|
|||
drag_and_drop = { path = "../drag_and_drop" }
|
||||
collections = { path = "../collections" }
|
||||
context_menu = { path = "../context_menu" }
|
||||
workspace_types = {path = "../workspace_types"}
|
||||
project_types = {path = "../project_types"}
|
||||
fuzzy = { path = "../fuzzy" }
|
||||
git = { path = "../git" }
|
||||
gpui = { path = "../gpui" }
|
||||
language = { path = "../language" }
|
||||
lsp = { path = "../lsp" }
|
||||
multi_buffer = { path = "../multi_buffer" }
|
||||
project = { path = "../project" }
|
||||
rpc = { path = "../rpc" }
|
||||
rich_text = { path = "../rich_text" }
|
||||
settings = { path = "../settings" }
|
||||
|
@ -46,9 +47,9 @@ text = { path = "../text" }
|
|||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
sqlez = { path = "../sqlez" }
|
||||
workspace = { path = "../workspace" }
|
||||
|
||||
aho-corasick = "1.1"
|
||||
async-trait.workspace = true
|
||||
anyhow.workspace = true
|
||||
convert_case = "0.6.0"
|
||||
futures.workspace = true
|
||||
|
|
|
@ -42,7 +42,7 @@ pub struct Inlay {
|
|||
}
|
||||
|
||||
impl Inlay {
|
||||
pub fn hint(id: usize, position: Anchor, hint: &project::InlayHint) -> Self {
|
||||
pub fn hint(id: usize, position: Anchor, hint: &project_types::InlayHint) -> Self {
|
||||
let mut text = hint.text();
|
||||
if hint.padding_right && !text.ends_with(' ') {
|
||||
text.push(' ');
|
||||
|
@ -1172,7 +1172,7 @@ mod tests {
|
|||
InlayId, MultiBuffer,
|
||||
};
|
||||
use gpui::AppContext;
|
||||
use project::{InlayHint, InlayHintLabel, ResolveState};
|
||||
use project_types::{InlayHint, InlayHintLabel, ResolveState};
|
||||
use rand::prelude::*;
|
||||
use settings::SettingsStore;
|
||||
use std::{cmp::Reverse, env, sync::Arc};
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -39,9 +39,8 @@ use json::json;
|
|||
use language::{
|
||||
language_settings::ShowWhitespaceSetting, Bias, CursorShape, OffsetUtf16, Selection,
|
||||
};
|
||||
use project::{
|
||||
use project_types::{
|
||||
project_settings::{GitGutterSetting, ProjectSettings},
|
||||
ProjectPath,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
|
@ -54,7 +53,6 @@ use std::{
|
|||
};
|
||||
use text::Point;
|
||||
use theme::SelectionStyle;
|
||||
use workspace::item::Item;
|
||||
|
||||
enum FoldMarkers {}
|
||||
|
||||
|
@ -1159,7 +1157,7 @@ impl EditorElement {
|
|||
})
|
||||
};
|
||||
let background_ranges = editor
|
||||
.background_highlight_row_ranges::<crate::items::BufferSearchHighlights>(
|
||||
.background_highlight_row_ranges::<crate::BufferSearchHighlights>(
|
||||
start_anchor..end_anchor,
|
||||
&layout.position_map.snapshot,
|
||||
50000,
|
||||
|
@ -1662,61 +1660,65 @@ impl EditorElement {
|
|||
let include_root = editor
|
||||
.project
|
||||
.as_ref()
|
||||
.map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
|
||||
.map(|project| project.visible_worktrees_count(cx) > 1)
|
||||
.unwrap_or_default();
|
||||
let jump_icon = project::File::from_dyn(buffer.file()).map(|file| {
|
||||
let jump_path = ProjectPath {
|
||||
worktree_id: file.worktree_id(cx),
|
||||
path: file.path.clone(),
|
||||
};
|
||||
let jump_anchor = range
|
||||
.primary
|
||||
//
|
||||
let jump_icon =
|
||||
editor
|
||||
.project
|
||||
.as_ref()
|
||||
.map_or(range.context.start, |primary| primary.start);
|
||||
let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
|
||||
.zip(buffer.file())
|
||||
.map(|(project, buffer_file)| {
|
||||
let jump_path = project.project_file(&**buffer_file);
|
||||
//::from_dyn(buffer.file())
|
||||
|
||||
enum JumpIcon {}
|
||||
MouseEventHandler::new::<JumpIcon, _>((*id).into(), cx, |state, _| {
|
||||
let style = style.jump_icon.style_for(state);
|
||||
Svg::new("icons/arrow_up_right.svg")
|
||||
.with_color(style.color)
|
||||
.constrained()
|
||||
.with_width(style.icon_width)
|
||||
let jump_anchor = range
|
||||
.primary
|
||||
.as_ref()
|
||||
.map_or(range.context.start, |primary| primary.start);
|
||||
let jump_position =
|
||||
language::ToPoint::to_point(&jump_anchor, buffer);
|
||||
|
||||
enum JumpIcon {}
|
||||
MouseEventHandler::new::<JumpIcon, _>(
|
||||
(*id).into(),
|
||||
cx,
|
||||
|state, _| {
|
||||
let style = style.jump_icon.style_for(state);
|
||||
Svg::new("icons/arrow_up_right.svg")
|
||||
.with_color(style.color)
|
||||
.constrained()
|
||||
.with_width(style.icon_width)
|
||||
.aligned()
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
.constrained()
|
||||
.with_width(style.button_width)
|
||||
.with_height(style.button_width)
|
||||
},
|
||||
)
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_click(MouseButton::Left, move |_, editor, cx| {
|
||||
if let Some((workspace, _)) = editor.workspace.as_mut() {
|
||||
Editor::jump(
|
||||
&**workspace,
|
||||
jump_path.clone(),
|
||||
jump_position,
|
||||
jump_anchor,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
})
|
||||
.with_tooltip::<JumpIcon>(
|
||||
(*id).into(),
|
||||
"Jump to Buffer".to_string(),
|
||||
Some(Box::new(crate::OpenExcerpts)),
|
||||
tooltip_style.clone(),
|
||||
cx,
|
||||
)
|
||||
.aligned()
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
.constrained()
|
||||
.with_width(style.button_width)
|
||||
.with_height(style.button_width)
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_click(MouseButton::Left, move |_, editor, cx| {
|
||||
if let Some(workspace) = editor
|
||||
.workspace
|
||||
.as_ref()
|
||||
.and_then(|(workspace, _)| workspace.upgrade(cx))
|
||||
{
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
Editor::jump(
|
||||
workspace,
|
||||
jump_path.clone(),
|
||||
jump_position,
|
||||
jump_anchor,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
.with_tooltip::<JumpIcon>(
|
||||
(*id).into(),
|
||||
"Jump to Buffer".to_string(),
|
||||
Some(Box::new(crate::OpenExcerpts)),
|
||||
tooltip_style.clone(),
|
||||
cx,
|
||||
)
|
||||
.aligned()
|
||||
.flex_float()
|
||||
});
|
||||
.flex_float()
|
||||
});
|
||||
|
||||
if *starts_new_buffer {
|
||||
let editor_font_size = style.text.font_size;
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
use crate::{
|
||||
display_map::{InlayOffset, ToDisplayPoint},
|
||||
link_go_to_definition::{InlayHighlight, RangeInEditor},
|
||||
types::Workspace,
|
||||
Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, EditorSnapshot, EditorStyle,
|
||||
ExcerptId, RangeToAnchorExt,
|
||||
ExcerptId, Project, RangeToAnchorExt,
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use gpui::{
|
||||
actions,
|
||||
elements::{Flex, MouseEventHandler, Padding, ParentElement, Text},
|
||||
platform::{CursorStyle, MouseButton},
|
||||
AnyElement, AppContext, Element, ModelHandle, Task, ViewContext, WeakViewHandle,
|
||||
AnyElement, AppContext, Element, Task, ViewContext,
|
||||
};
|
||||
use language::{
|
||||
markdown, Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry, ParsedMarkdown,
|
||||
};
|
||||
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart, Project};
|
||||
use project_types::{HoverBlock, HoverBlockKind, InlayHintLabelPart};
|
||||
use std::{ops::Range, sync::Arc, time::Duration};
|
||||
use util::TryFutureExt;
|
||||
use workspace::Workspace;
|
||||
|
||||
pub const HOVER_DELAY_MILLIS: u64 = 350;
|
||||
pub const HOVER_REQUEST_DELAY_MILLIS: u64 = 200;
|
||||
|
@ -103,11 +103,11 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie
|
|||
cx.background()
|
||||
.timer(Duration::from_millis(HOVER_DELAY_MILLIS))
|
||||
.await;
|
||||
this.update(&mut cx, |this, _| {
|
||||
let language_registry = this.update(&mut cx, |this, cx| {
|
||||
this.hover_state.diagnostic_popover = None;
|
||||
project.languages(cx)
|
||||
})?;
|
||||
|
||||
let language_registry = project.update(&mut cx, |p, _| p.languages().clone());
|
||||
let blocks = vec![inlay_hover.tooltip];
|
||||
let parsed_content = parse_blocks(&blocks, &language_registry, None).await;
|
||||
|
||||
|
@ -252,11 +252,7 @@ fn show_hover(
|
|||
};
|
||||
|
||||
// query the LSP for hover info
|
||||
let hover_request = cx.update(|cx| {
|
||||
project.update(cx, |project, cx| {
|
||||
project.hover(&buffer, buffer_position, cx)
|
||||
})
|
||||
});
|
||||
let hover_request = cx.update(|cx| project.hover(&buffer, buffer_position, cx));
|
||||
|
||||
if let Some(delay) = delay {
|
||||
delay.await;
|
||||
|
@ -310,7 +306,7 @@ fn show_hover(
|
|||
anchor..anchor
|
||||
};
|
||||
|
||||
let language_registry = project.update(&mut cx, |p, _| p.languages().clone());
|
||||
let language_registry = cx.update(|cx| project.languages(cx));
|
||||
let blocks = hover_result.contents;
|
||||
let language = hover_result.language;
|
||||
let parsed_content = parse_blocks(&blocks, &language_registry, language).await;
|
||||
|
@ -423,7 +419,7 @@ impl HoverState {
|
|||
snapshot: &EditorSnapshot,
|
||||
style: &EditorStyle,
|
||||
visible_rows: Range<u32>,
|
||||
workspace: Option<WeakViewHandle<Workspace>>,
|
||||
workspace: Option<Arc<dyn Workspace>>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> Option<(DisplayPoint, Vec<AnyElement<Editor>>)> {
|
||||
// If there is a diagnostic, position the popovers based on that.
|
||||
|
@ -462,7 +458,7 @@ impl HoverState {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InfoPopover {
|
||||
pub project: ModelHandle<Project>,
|
||||
pub project: Arc<dyn Project>,
|
||||
symbol_range: RangeInEditor,
|
||||
pub blocks: Vec<HoverBlock>,
|
||||
parsed_content: ParsedMarkdown,
|
||||
|
@ -472,7 +468,7 @@ impl InfoPopover {
|
|||
pub fn render(
|
||||
&mut self,
|
||||
style: &EditorStyle,
|
||||
workspace: Option<WeakViewHandle<Workspace>>,
|
||||
workspace: Option<Arc<dyn Workspace>>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> AnyElement<Editor> {
|
||||
MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| {
|
||||
|
|
|
@ -14,7 +14,7 @@ use futures::future;
|
|||
use gpui::{ModelContext, ModelHandle, Task, ViewContext};
|
||||
use language::{language_settings::InlayHintKind, Buffer, BufferSnapshot};
|
||||
use parking_lot::RwLock;
|
||||
use project::{InlayHint, ResolveState};
|
||||
use project_types::{InlayHint, ResolveState};
|
||||
|
||||
use collections::{hash_map, HashMap, HashSet};
|
||||
use language::language_settings::InlayHintSettings;
|
||||
|
@ -539,14 +539,12 @@ impl InlayHintCache {
|
|||
.buffer(buffer_id)
|
||||
.and_then(|buffer| {
|
||||
let project = editor.project.as_ref()?;
|
||||
Some(project.update(cx, |project, cx| {
|
||||
project.resolve_inlay_hint(
|
||||
hint_to_resolve,
|
||||
buffer,
|
||||
server_id,
|
||||
cx,
|
||||
)
|
||||
}))
|
||||
Some(project.resolve_inlay_hint(
|
||||
hint_to_resolve,
|
||||
buffer,
|
||||
server_id,
|
||||
cx,
|
||||
))
|
||||
})
|
||||
})?;
|
||||
if let Some(resolved_hint_task) = resolved_hint_task {
|
||||
|
@ -896,9 +894,9 @@ async fn fetch_and_update_hints(
|
|||
.buffer(query.buffer_id)
|
||||
.and_then(|buffer| {
|
||||
let project = editor.project.as_ref()?;
|
||||
Some(project.update(cx, |project, cx| {
|
||||
Some(
|
||||
project.inlay_hints(buffer, fetch_range.clone(), cx)
|
||||
}))
|
||||
)
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
|||
use gpui::{Task, ViewContext};
|
||||
use language::{Bias, ToOffset};
|
||||
use lsp::LanguageServerId;
|
||||
use project::{
|
||||
use project_types::{
|
||||
HoverBlock, HoverBlockKind, InlayHintLabelPartTooltip, InlayHintTooltip, LocationLink,
|
||||
ResolveState,
|
||||
};
|
||||
|
@ -227,7 +227,7 @@ pub fn update_inlay_link_and_hover_points(
|
|||
extra_shift_right += 1;
|
||||
}
|
||||
match cached_hint.label {
|
||||
project::InlayHintLabel::String(_) => {
|
||||
project_types::InlayHintLabel::String(_) => {
|
||||
if let Some(tooltip) = cached_hint.tooltip {
|
||||
hover_popover::hover_at_inlay(
|
||||
editor,
|
||||
|
@ -257,7 +257,7 @@ pub fn update_inlay_link_and_hover_points(
|
|||
hover_updated = true;
|
||||
}
|
||||
}
|
||||
project::InlayHintLabel::LabelParts(label_parts) => {
|
||||
project_types::InlayHintLabel::LabelParts(label_parts) => {
|
||||
let hint_start =
|
||||
snapshot.anchor_to_inlay_offset(hovered_hint.position);
|
||||
if let Some((hovered_hint_part, part_range)) =
|
||||
|
@ -396,16 +396,14 @@ pub fn show_link_definition(
|
|||
let result = match &trigger_point {
|
||||
TriggerPoint::Text(_) => {
|
||||
// query the LSP for definition info
|
||||
cx.update(|cx| {
|
||||
project.update(cx, |project, cx| match definition_kind {
|
||||
LinkDefinitionKind::Symbol => {
|
||||
project.definition(&buffer, buffer_position, cx)
|
||||
}
|
||||
cx.update(|cx| match definition_kind {
|
||||
LinkDefinitionKind::Symbol => {
|
||||
project.definition(&buffer, buffer_position, cx)
|
||||
}
|
||||
|
||||
LinkDefinitionKind::Type => {
|
||||
project.type_definition(&buffer, buffer_position, cx)
|
||||
}
|
||||
})
|
||||
LinkDefinitionKind::Type => {
|
||||
project.type_definition(&buffer, buffer_position, cx)
|
||||
}
|
||||
})
|
||||
.await
|
||||
.ok()
|
||||
|
|
|
@ -4,6 +4,7 @@ pub mod scroll_amount;
|
|||
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
|
@ -13,14 +14,13 @@ use gpui::{
|
|||
};
|
||||
use language::{Bias, Point};
|
||||
use util::ResultExt;
|
||||
use workspace::WorkspaceId;
|
||||
use workspace_types::WorkspaceId;
|
||||
|
||||
use crate::{
|
||||
display_map::{DisplaySnapshot, ToDisplayPoint},
|
||||
hover_popover::hide_hover,
|
||||
persistence::DB,
|
||||
Anchor, DisplayPoint, Editor, EditorMode, Event, InlayHintRefreshReason, MultiBufferSnapshot,
|
||||
ToPoint,
|
||||
types, Anchor, DisplayPoint, Editor, EditorMode, Event, InlayHintRefreshReason,
|
||||
MultiBufferSnapshot, ToPoint,
|
||||
};
|
||||
|
||||
use self::{
|
||||
|
@ -176,7 +176,7 @@ impl ScrollManager {
|
|||
map: &DisplaySnapshot,
|
||||
local: bool,
|
||||
autoscroll: bool,
|
||||
workspace_id: Option<i64>,
|
||||
workspace_id: Option<&(Arc<dyn crate::types::Workspace>, i64)>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
let (new_anchor, top_row) = if scroll_position.y() <= 0. {
|
||||
|
@ -215,19 +215,20 @@ impl ScrollManager {
|
|||
top_row: u32,
|
||||
local: bool,
|
||||
autoscroll: bool,
|
||||
workspace_id: Option<i64>,
|
||||
workspace_id: Option<&(Arc<dyn crate::types::Workspace>, i64)>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
self.anchor = anchor;
|
||||
cx.emit(Event::ScrollPositionChanged { local, autoscroll });
|
||||
self.show_scrollbar(cx);
|
||||
self.autoscroll_request.take();
|
||||
if let Some(workspace_id) = workspace_id {
|
||||
if let Some((workspace, workspace_id)) = workspace_id {
|
||||
let item_id = cx.view_id();
|
||||
|
||||
let db = workspace.db();
|
||||
let workspace_id = *workspace_id;
|
||||
cx.background()
|
||||
.spawn(async move {
|
||||
DB.save_scroll_position(
|
||||
db.save_scroll_position(
|
||||
item_id,
|
||||
workspace_id,
|
||||
top_row,
|
||||
|
@ -324,7 +325,7 @@ impl Editor {
|
|||
let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
|
||||
hide_hover(self, cx);
|
||||
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
|
||||
let workspace_id = self.workspace.as_ref();
|
||||
self.scroll_manager.set_scroll_position(
|
||||
scroll_position,
|
||||
&map,
|
||||
|
@ -344,7 +345,7 @@ impl Editor {
|
|||
|
||||
pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
|
||||
hide_hover(self, cx);
|
||||
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
|
||||
let workspace_id = self.workspace.as_ref();
|
||||
let top_row = scroll_anchor
|
||||
.anchor
|
||||
.to_point(&self.buffer().read(cx).snapshot(cx))
|
||||
|
@ -353,13 +354,13 @@ impl Editor {
|
|||
.set_anchor(scroll_anchor, top_row, true, false, workspace_id, cx);
|
||||
}
|
||||
|
||||
pub(crate) fn set_scroll_anchor_remote(
|
||||
pub fn set_scroll_anchor_remote(
|
||||
&mut self,
|
||||
scroll_anchor: ScrollAnchor,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
hide_hover(self, cx);
|
||||
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
|
||||
let workspace_id = self.workspace.as_ref();
|
||||
let top_row = scroll_anchor
|
||||
.anchor
|
||||
.to_point(&self.buffer().read(cx).snapshot(cx))
|
||||
|
@ -415,11 +416,12 @@ impl Editor {
|
|||
|
||||
pub fn read_scroll_position_from_db(
|
||||
&mut self,
|
||||
db: &dyn types::Db,
|
||||
item_id: usize,
|
||||
workspace_id: WorkspaceId,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
let scroll_position = DB.get_scroll_position(item_id, workspace_id);
|
||||
let scroll_position = db.get_scroll_position(item_id, workspace_id);
|
||||
if let Ok(Some((top_row, x, y))) = scroll_position {
|
||||
let top_anchor = self
|
||||
.buffer()
|
||||
|
|
|
@ -247,7 +247,7 @@ impl Editor {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn request_autoscroll_remotely(
|
||||
pub fn request_autoscroll_remotely(
|
||||
&mut self,
|
||||
autoscroll: Autoscroll,
|
||||
cx: &mut ViewContext<Self>,
|
||||
|
|
229
crates/editor/src/types.rs
Normal file
229
crates/editor/src/types.rs
Normal file
|
@ -0,0 +1,229 @@
|
|||
use crate::Editor;
|
||||
use client::Client;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use client::{proto::PeerId, Collaborator, ParticipantIndex};
|
||||
use collections::{HashMap, HashSet};
|
||||
use gpui::geometry::vector::Vector2F;
|
||||
use gpui::WeakViewHandle;
|
||||
use gpui::{AppContext, ModelHandle, Subscription, Task, ViewContext, ViewHandle};
|
||||
use language::{
|
||||
Buffer, CachedLspAdapter, CodeAction, Completion, LanguageRegistry, LanguageServerName,
|
||||
};
|
||||
use lsp::{LanguageServer, LanguageServerId};
|
||||
use project_types::ProjectPath;
|
||||
use project_types::{FormatTrigger, ProjectTransaction};
|
||||
use std::ops::Range;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use workspace_types::{ItemId, SplitDirection, WorkspaceId};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait Db: 'static + Send + Sync {
|
||||
async fn save_scroll_position(
|
||||
&self,
|
||||
item_id: ItemId,
|
||||
workspace_id: WorkspaceId,
|
||||
top_row: u32,
|
||||
vertical_offset: f32,
|
||||
horizontal_offset: f32,
|
||||
) -> Result<()>;
|
||||
fn get_scroll_position(
|
||||
&self,
|
||||
item_id: ItemId,
|
||||
workspace_id: WorkspaceId,
|
||||
) -> Result<Option<(u32, f32, f32)>>;
|
||||
async fn save_path(
|
||||
&self,
|
||||
item_id: ItemId,
|
||||
workspace_id: WorkspaceId,
|
||||
path: PathBuf,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
pub type DisableUpdateHistoryGuard = Box<dyn DisableUpdateHistory>;
|
||||
|
||||
pub trait DisableUpdateHistory {
|
||||
fn release(self, cx: &mut AppContext);
|
||||
}
|
||||
|
||||
pub trait CollaborationHub {
|
||||
fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator>;
|
||||
fn user_participant_indices<'a>(
|
||||
&self,
|
||||
cx: &'a AppContext,
|
||||
) -> &'a HashMap<u64, ParticipantIndex>;
|
||||
}
|
||||
|
||||
pub trait Workspace: 'static {
|
||||
fn db(&self) -> Arc<dyn Db>;
|
||||
fn open_abs_path(&self, abs_path: PathBuf, visible: bool, cx: &mut AppContext);
|
||||
fn open_path(
|
||||
&self,
|
||||
path: ProjectPath,
|
||||
focus_item: bool,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<ViewHandle<Editor>>>;
|
||||
fn active_editor(&self, cx: &mut AppContext) -> Option<ViewHandle<Editor>>;
|
||||
fn project(&self, cx: &mut AppContext) -> Arc<dyn Project>;
|
||||
fn disable_update_history_for_current_pane(
|
||||
&self,
|
||||
cx: &mut AppContext,
|
||||
) -> Option<DisableUpdateHistoryGuard>;
|
||||
fn split_buffer(
|
||||
&self,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
cx: &mut AppContext,
|
||||
) -> ViewHandle<crate::Editor>;
|
||||
fn open_buffer(
|
||||
&self,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
cx: &mut AppContext,
|
||||
) -> ViewHandle<crate::Editor>;
|
||||
fn add_item(&self, item: Box<ViewHandle<Editor>>, cx: &mut AppContext);
|
||||
fn split_item(
|
||||
&self,
|
||||
split_direction: SplitDirection,
|
||||
item: Box<ViewHandle<Editor>>,
|
||||
cx: &mut AppContext,
|
||||
);
|
||||
}
|
||||
|
||||
pub trait Project: 'static + std::fmt::Debug {
|
||||
fn apply_code_action(
|
||||
&self,
|
||||
buffer_handle: ModelHandle<Buffer>,
|
||||
action: CodeAction,
|
||||
push_to_history: bool,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<ProjectTransaction>>;
|
||||
fn inlay_hints(
|
||||
&self,
|
||||
buffer_handle: ModelHandle<Buffer>,
|
||||
range: Range<text::Anchor>,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<anyhow::Result<Vec<project_types::InlayHint>>>;
|
||||
fn visible_worktrees_count(&self, cx: &AppContext) -> usize;
|
||||
fn resolve_inlay_hint(
|
||||
&self,
|
||||
hint: project_types::InlayHint,
|
||||
buffer_handle: ModelHandle<Buffer>,
|
||||
server_id: LanguageServerId,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<anyhow::Result<project_types::InlayHint>>;
|
||||
fn languages(&self, cx: &AppContext) -> Arc<LanguageRegistry>;
|
||||
fn hover(
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Option<project_types::Hover>>>;
|
||||
fn definition(
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<project_types::LocationLink>>>;
|
||||
|
||||
fn type_definition(
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<project_types::LocationLink>>>;
|
||||
fn completions(
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<Completion>>>;
|
||||
fn as_hub(&self) -> Box<dyn CollaborationHub>;
|
||||
fn is_remote(&self, cx: &AppContext) -> bool;
|
||||
fn is_local(&self, cx: &AppContext) -> bool {
|
||||
!self.is_remote(cx)
|
||||
}
|
||||
fn remote_id(&self, cx: &AppContext) -> Option<u64>;
|
||||
fn language_servers_for_buffer(
|
||||
&self,
|
||||
buffer: &Buffer,
|
||||
cx: &AppContext,
|
||||
) -> Vec<(Arc<CachedLspAdapter>, Arc<LanguageServer>)>;
|
||||
fn on_type_format(
|
||||
&self,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
trigger: String,
|
||||
push_to_history: bool,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Option<text::Transaction>>>;
|
||||
fn client(&self, cx: &AppContext) -> Arc<Client>;
|
||||
fn language_server_for_id(
|
||||
&self,
|
||||
id: LanguageServerId,
|
||||
cx: &AppContext,
|
||||
) -> Option<Arc<LanguageServer>>;
|
||||
|
||||
fn code_actions(
|
||||
&self,
|
||||
buffer_handle: &ModelHandle<Buffer>,
|
||||
range: Range<text::Anchor>,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<CodeAction>>>;
|
||||
fn document_highlights(
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<project_types::DocumentHighlight>>>;
|
||||
fn format(
|
||||
&self,
|
||||
buffers: HashSet<ModelHandle<Buffer>>,
|
||||
push_to_history: bool,
|
||||
trigger: FormatTrigger,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<anyhow::Result<ProjectTransaction>>;
|
||||
fn restart_language_servers_for_buffers(
|
||||
&self,
|
||||
buffers: HashSet<ModelHandle<Buffer>>,
|
||||
cx: &mut AppContext,
|
||||
) -> Option<()>;
|
||||
fn prepare_rename(
|
||||
&self,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
position: usize,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Option<Range<text::Anchor>>>>;
|
||||
fn references(
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<project_types::Location>>>;
|
||||
fn apply_additional_edits_for_completion(
|
||||
&self,
|
||||
buffer_handle: ModelHandle<Buffer>,
|
||||
completion: Completion,
|
||||
push_to_history: bool,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Option<text::Transaction>>>;
|
||||
fn language_server_for_buffer<'a>(
|
||||
&self,
|
||||
buffer: &Buffer,
|
||||
server_id: LanguageServerId,
|
||||
cx: &'a AppContext,
|
||||
) -> Option<(&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)>;
|
||||
fn open_local_buffer_via_lsp(
|
||||
&self,
|
||||
abs_path: lsp::Url,
|
||||
language_server_id: LanguageServerId,
|
||||
language_server_name: LanguageServerName,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<ModelHandle<Buffer>>>;
|
||||
fn subscribe(
|
||||
&self,
|
||||
is_singleton: bool,
|
||||
cx: &mut ViewContext<crate::Editor>,
|
||||
) -> Vec<Subscription>;
|
||||
fn project_file(&self, file: &dyn language::File) -> ProjectPath;
|
||||
}
|
27
crates/editor_extensions/Cargo.toml
Normal file
27
crates/editor_extensions/Cargo.toml
Normal file
|
@ -0,0 +1,27 @@
|
|||
[package]
|
||||
name = "editor_extensions"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
collections = {path = "../collections"}
|
||||
client = {path = "../client"}
|
||||
db = {path = "../db"}
|
||||
editor = {path = "../editor"}
|
||||
gpui = {path = "../gpui"}
|
||||
lsp = {path = "../lsp"}
|
||||
language = {path = "../language"}
|
||||
project = {path = "../project"}
|
||||
project_types = {path = "../project_types"}
|
||||
text = {path = "../text"}
|
||||
workspace = {path = "../workspace"}
|
||||
workspace_types = {path = "../workspace_types"}
|
||||
util = {path = "../util"}
|
||||
theme = {path = "../theme"}
|
||||
smallvec.workspace = true
|
||||
rpc = {path = "../rpc"}
|
||||
futures.workspace = true
|
||||
async-trait.workspace = true
|
|
@ -1,10 +1,13 @@
|
|||
use crate::{
|
||||
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
|
||||
movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor,
|
||||
Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use crate::persistence::DB;
|
||||
use crate::{FollowableEditor, ProjectHandle, WeakWorkspaceHandle};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use collections::HashSet;
|
||||
use editor::{
|
||||
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
|
||||
movement::surrounding_word, scroll::ScrollAnchor, Anchor, Autoscroll, Editor, Event, ExcerptId,
|
||||
ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
|
||||
};
|
||||
use editor::{BufferSearchHighlights, MAX_TAB_TITLE_LEN};
|
||||
use futures::future::try_join_all;
|
||||
use gpui::{
|
||||
elements::*,
|
||||
|
@ -16,7 +19,8 @@ use language::{
|
|||
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point,
|
||||
SelectionGoal,
|
||||
};
|
||||
use project::{search::SearchQuery, FormatTrigger, Item as _, Project, ProjectPath};
|
||||
use project::{search::SearchQuery, Item as _, Project};
|
||||
use project_types::{FormatTrigger, ProjectPath};
|
||||
use rpc::proto::{self, update_view, PeerId};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
|
@ -37,15 +41,14 @@ use workspace::item::{BreadcrumbText, FollowableItemHandle, ItemHandle};
|
|||
use workspace::{
|
||||
item::{FollowableItem, Item, ItemEvent, ProjectItem},
|
||||
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
|
||||
ItemId, ItemNavHistory, Pane, StatusItemView, ToolbarItemLocation, ViewId, Workspace,
|
||||
WorkspaceId,
|
||||
ItemNavHistory, Pane, StatusItemView, ToolbarItemLocation, Workspace,
|
||||
};
|
||||
use workspace_types::*;
|
||||
|
||||
pub const MAX_TAB_TITLE_LEN: usize = 24;
|
||||
|
||||
impl FollowableItem for Editor {
|
||||
impl FollowableItem for FollowableEditor {
|
||||
fn remote_id(&self) -> Option<ViewId> {
|
||||
self.remote_id
|
||||
todo!();
|
||||
//self.0.remote_id
|
||||
}
|
||||
|
||||
fn from_state_proto(
|
||||
|
@ -86,13 +89,20 @@ impl FollowableItem for 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();
|
||||
== editor
|
||||
.read(cx)
|
||||
.0
|
||||
.read(cx)
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.as_ref();
|
||||
ids_match || singleton_buffer_matches
|
||||
})
|
||||
})?;
|
||||
|
||||
let editor = if let Some(editor) = editor {
|
||||
editor
|
||||
editor.update(&mut cx, |this, _| this.0.clone())
|
||||
} else {
|
||||
pane.update(&mut cx, |_, cx| {
|
||||
let multibuffer = cx.add_model(|cx| {
|
||||
|
@ -129,12 +139,20 @@ impl FollowableItem for Editor {
|
|||
});
|
||||
|
||||
cx.add_view(|cx| {
|
||||
let mut editor =
|
||||
Editor::for_multibuffer(multibuffer, Some(project.clone()), cx);
|
||||
let mut editor = Editor::for_multibuffer(
|
||||
multibuffer,
|
||||
Some(Arc::new(ProjectHandle(project.clone()))),
|
||||
cx,
|
||||
);
|
||||
editor.remote_id = Some(remote_id);
|
||||
editor
|
||||
})
|
||||
})?
|
||||
})
|
||||
.ok()
|
||||
};
|
||||
|
||||
let Some(editor) = editor else {
|
||||
bail!("Failed to initialize editor")
|
||||
};
|
||||
|
||||
update_editor_from_message(
|
||||
|
@ -152,34 +170,37 @@ impl FollowableItem for Editor {
|
|||
)
|
||||
.await?;
|
||||
|
||||
Ok(editor)
|
||||
pane.update(&mut cx, |_, cx| cx.add_view(|_| FollowableEditor(editor)))
|
||||
}))
|
||||
}
|
||||
|
||||
fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {
|
||||
self.leader_peer_id = leader_peer_id;
|
||||
if self.leader_peer_id.is_some() {
|
||||
self.buffer.update(cx, |buffer, cx| {
|
||||
buffer.remove_active_selections(cx);
|
||||
});
|
||||
} else {
|
||||
self.buffer.update(cx, |buffer, cx| {
|
||||
if self.focused {
|
||||
buffer.set_active_selections(
|
||||
&self.selections.disjoint_anchors(),
|
||||
self.selections.line_mode,
|
||||
self.cursor_shape,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
cx.notify();
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.leader_peer_id = leader_peer_id;
|
||||
if this.leader_peer_id.is_some() {
|
||||
this.buffer().update(cx, |buffer, cx| {
|
||||
buffer.remove_active_selections(cx);
|
||||
});
|
||||
} else {
|
||||
this.buffer().update(cx, |buffer, cx| {
|
||||
if this.focused {
|
||||
buffer.set_active_selections(
|
||||
&this.selections.disjoint_anchors(),
|
||||
this.selections.line_mode,
|
||||
this.cursor_shape,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
|
||||
fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
|
||||
let buffer = self.buffer.read(cx);
|
||||
let scroll_anchor = self.scroll_manager.anchor();
|
||||
let this = self.0.read(cx);
|
||||
let buffer = this.buffer().read(cx);
|
||||
let scroll_anchor = this.scroll_manager.anchor();
|
||||
let excerpts = buffer
|
||||
.read(cx)
|
||||
.excerpts()
|
||||
|
@ -206,13 +227,13 @@ impl FollowableItem for Editor {
|
|||
scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.anchor)),
|
||||
scroll_x: scroll_anchor.offset.x(),
|
||||
scroll_y: scroll_anchor.offset.y(),
|
||||
selections: self
|
||||
selections: this
|
||||
.selections
|
||||
.disjoint_anchors()
|
||||
.iter()
|
||||
.map(serialize_selection)
|
||||
.collect(),
|
||||
pending_selection: self
|
||||
pending_selection: this
|
||||
.selections
|
||||
.pending_anchor()
|
||||
.as_ref()
|
||||
|
@ -228,7 +249,7 @@ impl FollowableItem for Editor {
|
|||
) -> bool {
|
||||
let update =
|
||||
update.get_or_insert_with(|| proto::update_view::Variant::Editor(Default::default()));
|
||||
|
||||
let this = self.0.read(cx);
|
||||
match update {
|
||||
proto::update_view::Variant::Editor(update) => match event {
|
||||
Event::ExcerptsAdded {
|
||||
|
@ -259,20 +280,20 @@ impl FollowableItem for Editor {
|
|||
true
|
||||
}
|
||||
Event::ScrollPositionChanged { .. } => {
|
||||
let scroll_anchor = self.scroll_manager.anchor();
|
||||
let scroll_anchor = this.scroll_manager.anchor();
|
||||
update.scroll_top_anchor = Some(serialize_anchor(&scroll_anchor.anchor));
|
||||
update.scroll_x = scroll_anchor.offset.x();
|
||||
update.scroll_y = scroll_anchor.offset.y();
|
||||
true
|
||||
}
|
||||
Event::SelectionsChanged { .. } => {
|
||||
update.selections = self
|
||||
update.selections = this
|
||||
.selections
|
||||
.disjoint_anchors()
|
||||
.iter()
|
||||
.map(serialize_selection)
|
||||
.collect();
|
||||
update.pending_selection = self
|
||||
update.pending_selection = this
|
||||
.selections
|
||||
.pending_anchor()
|
||||
.as_ref()
|
||||
|
@ -292,8 +313,10 @@ impl FollowableItem for Editor {
|
|||
) -> Task<Result<()>> {
|
||||
let update_view::Variant::Editor(message) = message;
|
||||
let project = project.clone();
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
update_editor_from_message(this, project, message, &mut cx).await
|
||||
self.0.update(cx, |this, cx| {
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
update_editor_from_message(this, project, message, &mut cx).await
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -333,7 +356,7 @@ async fn update_editor_from_message(
|
|||
|
||||
// Update the editor's excerpts.
|
||||
this.update(cx, |editor, cx| {
|
||||
editor.buffer.update(cx, |multibuffer, cx| {
|
||||
editor.buffer().update(cx, |multibuffer, cx| {
|
||||
let mut removed_excerpt_ids = message
|
||||
.deleted_excerpts
|
||||
.into_iter()
|
||||
|
@ -390,7 +413,7 @@ async fn update_editor_from_message(
|
|||
|
||||
// Deserialize the editor state.
|
||||
let (selections, pending_selection, scroll_top_anchor) = this.update(cx, |editor, cx| {
|
||||
let buffer = editor.buffer.read(cx).read(cx);
|
||||
let buffer = editor.buffer().read(cx).read(cx);
|
||||
let selections = message
|
||||
.selections
|
||||
.into_iter()
|
||||
|
@ -408,7 +431,7 @@ async fn update_editor_from_message(
|
|||
// Wait until the buffer has received all of the operations referenced by
|
||||
// the editor's new state.
|
||||
this.update(cx, |editor, cx| {
|
||||
editor.buffer.update(cx, |buffer, cx| {
|
||||
editor.buffer().update(cx, |buffer, cx| {
|
||||
buffer.wait_for_anchors(
|
||||
selections
|
||||
.iter()
|
||||
|
@ -515,11 +538,12 @@ fn deserialize_anchor(buffer: &MultiBufferSnapshot, anchor: proto::EditorAnchor)
|
|||
})
|
||||
}
|
||||
|
||||
impl Item for Editor {
|
||||
impl Item for FollowableEditor {
|
||||
fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) -> bool {
|
||||
if let Ok(data) = data.downcast::<NavigationData>() {
|
||||
let newest_selection = self.selections.newest::<Point>(cx);
|
||||
let buffer = self.buffer.read(cx).read(cx);
|
||||
let this = self.0.read(cx);
|
||||
let newest_selection = this.selections.newest::<Point>(cx);
|
||||
let buffer = this.buffer().read(cx).read(cx);
|
||||
let offset = if buffer.can_resolve(&data.cursor_anchor) {
|
||||
data.cursor_anchor.to_point(&buffer)
|
||||
} else {
|
||||
|
@ -538,12 +562,15 @@ impl Item for Editor {
|
|||
if newest_selection.head() == offset {
|
||||
false
|
||||
} else {
|
||||
let nav_history = self.nav_history.take();
|
||||
self.set_scroll_anchor(scroll_anchor, cx);
|
||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select_ranges([offset..offset])
|
||||
self.0.update(cx, |this, cx| {
|
||||
let nav_history = this.nav_history.take();
|
||||
this.set_scroll_anchor(scroll_anchor, cx);
|
||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select_ranges([offset..offset])
|
||||
});
|
||||
this.nav_history = nav_history;
|
||||
});
|
||||
self.nav_history = nav_history;
|
||||
|
||||
true
|
||||
}
|
||||
} else {
|
||||
|
@ -553,6 +580,8 @@ impl Item for Editor {
|
|||
|
||||
fn tab_tooltip_text(&self, cx: &AppContext) -> Option<Cow<str>> {
|
||||
let file_path = self
|
||||
.0
|
||||
.read(cx)
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.as_singleton()?
|
||||
|
@ -567,7 +596,7 @@ impl Item for Editor {
|
|||
}
|
||||
|
||||
fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<Cow<str>> {
|
||||
match path_for_buffer(&self.buffer, detail, true, cx)? {
|
||||
match path_for_buffer(&self.0.read(cx).buffer(), detail, true, cx)? {
|
||||
Cow::Borrowed(path) => Some(path.to_string_lossy()),
|
||||
Cow::Owned(path) => Some(path.to_string_lossy().to_string().into()),
|
||||
}
|
||||
|
@ -579,10 +608,11 @@ impl Item for Editor {
|
|||
style: &theme::Tab,
|
||||
cx: &AppContext,
|
||||
) -> AnyElement<T> {
|
||||
let this = self.0.read(cx);
|
||||
Flex::row()
|
||||
.with_child(Label::new(self.title(cx).to_string(), style.label.clone()).into_any())
|
||||
.with_child(Label::new(this.title(cx).to_string(), style.label.clone()).into_any())
|
||||
.with_children(detail.and_then(|detail| {
|
||||
let path = path_for_buffer(&self.buffer, detail, false, cx)?;
|
||||
let path = path_for_buffer(&this.buffer(), detail, false, cx)?;
|
||||
let description = path.to_string_lossy();
|
||||
Some(
|
||||
Label::new(
|
||||
|
@ -599,13 +629,15 @@ impl Item for Editor {
|
|||
}
|
||||
|
||||
fn for_each_project_item(&self, cx: &AppContext, f: &mut dyn FnMut(usize, &dyn project::Item)) {
|
||||
self.buffer
|
||||
self.0
|
||||
.read(cx)
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.for_each_buffer(|buffer| f(buffer.id(), buffer.read(cx)));
|
||||
}
|
||||
|
||||
fn is_singleton(&self, cx: &AppContext) -> bool {
|
||||
self.buffer.read(cx).is_singleton()
|
||||
self.0.read(cx).buffer().read(cx).is_singleton()
|
||||
}
|
||||
|
||||
fn clone_on_split(&self, _workspace_id: WorkspaceId, cx: &mut ViewContext<Self>) -> Option<Self>
|
||||
|
@ -615,30 +647,35 @@ impl Item for Editor {
|
|||
Some(self.clone(cx))
|
||||
}
|
||||
|
||||
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
|
||||
self.nav_history = Some(history);
|
||||
fn set_nav_history(&mut self, history: ItemNavHistory, cx: &mut ViewContext<Self>) {
|
||||
self.0
|
||||
.update(cx, |this, cx| this.set_nav_history(Some(Box::new(history))));
|
||||
}
|
||||
|
||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
let selection = self.selections.newest_anchor();
|
||||
self.push_to_nav_history(selection.head(), None, cx);
|
||||
self.0.update(cx, |this, cx| {
|
||||
let selection = this.selections.newest_anchor();
|
||||
this.push_to_nav_history(selection.head(), None, cx)
|
||||
});
|
||||
}
|
||||
|
||||
fn workspace_deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
hide_link_definition(self, cx);
|
||||
self.link_go_to_definition_state.last_trigger_point = None;
|
||||
self.0.update(cx, |this, cx| {
|
||||
hide_link_definition(this, cx);
|
||||
this.link_go_to_definition_state.last_trigger_point = None
|
||||
});
|
||||
}
|
||||
|
||||
fn is_dirty(&self, cx: &AppContext) -> bool {
|
||||
self.buffer().read(cx).read(cx).is_dirty()
|
||||
self.0.read(cx).buffer().read(cx).read(cx).is_dirty()
|
||||
}
|
||||
|
||||
fn has_conflict(&self, cx: &AppContext) -> bool {
|
||||
self.buffer().read(cx).read(cx).has_conflict()
|
||||
self.0.read(cx).buffer().read(cx).read(cx).has_conflict()
|
||||
}
|
||||
|
||||
fn can_save(&self, cx: &AppContext) -> bool {
|
||||
let buffer = &self.buffer().read(cx);
|
||||
let buffer = &self.0.read(cx).buffer().read(cx);
|
||||
if let Some(buffer) = buffer.as_singleton() {
|
||||
buffer.read(cx).project_path(cx).is_some()
|
||||
} else {
|
||||
|
@ -651,40 +688,47 @@ impl Item for Editor {
|
|||
project: ModelHandle<Project>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
self.report_editor_event("save", None, cx);
|
||||
let format = self.perform_format(project.clone(), FormatTrigger::Save, cx);
|
||||
let buffers = self.buffer().clone().read(cx).all_buffers();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
format.await?;
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.report_editor_event("save", None, cx);
|
||||
let format = this.perform_format(
|
||||
Arc::new(ProjectHandle(project.clone())),
|
||||
FormatTrigger::Save,
|
||||
cx,
|
||||
);
|
||||
let buffers = this.buffer().clone().read(cx).all_buffers();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
format.await?;
|
||||
|
||||
if buffers.len() == 1 {
|
||||
project
|
||||
.update(&mut cx, |project, cx| project.save_buffers(buffers, cx))
|
||||
.await?;
|
||||
} else {
|
||||
// For multi-buffers, only save those ones that contain changes. For clean buffers
|
||||
// we simulate saving by calling `Buffer::did_save`, so that language servers or
|
||||
// other downstream listeners of save events get notified.
|
||||
let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| {
|
||||
buffer.read_with(&cx, |buffer, _| buffer.is_dirty() || buffer.has_conflict())
|
||||
});
|
||||
|
||||
project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.save_buffers(dirty_buffers, cx)
|
||||
})
|
||||
.await?;
|
||||
for buffer in clean_buffers {
|
||||
buffer.update(&mut cx, |buffer, cx| {
|
||||
let version = buffer.saved_version().clone();
|
||||
let fingerprint = buffer.saved_version_fingerprint();
|
||||
let mtime = buffer.saved_mtime();
|
||||
buffer.did_save(version, fingerprint, mtime, cx);
|
||||
if buffers.len() == 1 {
|
||||
project
|
||||
.update(&mut cx, |project, cx| project.save_buffers(buffers, cx))
|
||||
.await?;
|
||||
} else {
|
||||
// For multi-buffers, only save those ones that contain changes. For clean buffers
|
||||
// we simulate saving by calling `Buffer::did_save`, so that language servers or
|
||||
// other downstream listeners of save events get notified.
|
||||
let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| {
|
||||
buffer
|
||||
.read_with(&cx, |buffer, _| buffer.is_dirty() || buffer.has_conflict())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.save_buffers(dirty_buffers, cx)
|
||||
})
|
||||
.await?;
|
||||
for buffer in clean_buffers {
|
||||
buffer.update(&mut cx, |buffer, cx| {
|
||||
let version = buffer.saved_version().clone();
|
||||
let fingerprint = buffer.saved_version_fingerprint();
|
||||
let mtime = buffer.saved_mtime();
|
||||
buffer.did_save(version, fingerprint, mtime, cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -694,19 +738,21 @@ impl Item for Editor {
|
|||
abs_path: PathBuf,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let buffer = self
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.expect("cannot call save_as on an excerpt list");
|
||||
self.0.update(cx, |this, cx| {
|
||||
let buffer = this
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.expect("cannot call save_as on an excerpt list");
|
||||
|
||||
let file_extension = abs_path
|
||||
.extension()
|
||||
.map(|a| a.to_string_lossy().to_string());
|
||||
self.report_editor_event("save", file_extension, cx);
|
||||
let file_extension = abs_path
|
||||
.extension()
|
||||
.map(|a| a.to_string_lossy().to_string());
|
||||
this.report_editor_event("save", file_extension, cx);
|
||||
|
||||
project.update(cx, |project, cx| {
|
||||
project.save_buffer_as(buffer, abs_path, cx)
|
||||
project.update(cx, |project, cx| {
|
||||
project.save_buffer_as(buffer, abs_path, cx)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -715,23 +761,26 @@ impl Item for Editor {
|
|||
project: ModelHandle<Project>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let buffer = self.buffer().clone();
|
||||
let buffers = self.buffer.read(cx).all_buffers();
|
||||
let this = self.0.read(cx);
|
||||
let buffer = this.buffer().clone();
|
||||
let buffers = buffer.read(cx).all_buffers();
|
||||
let reload_buffers =
|
||||
project.update(cx, |project, cx| project.reload_buffers(buffers, true, cx));
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let transaction = reload_buffers.log_err().await;
|
||||
this.update(&mut cx, |editor, cx| {
|
||||
editor.request_autoscroll(Autoscroll::fit(), cx)
|
||||
})?;
|
||||
buffer.update(&mut cx, |buffer, cx| {
|
||||
if let Some(transaction) = transaction {
|
||||
if !buffer.is_singleton() {
|
||||
buffer.push_transaction(&transaction.0, cx);
|
||||
self.0.update(cx, |_, cx| {
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let transaction = reload_buffers.log_err().await;
|
||||
this.update(&mut cx, |editor, cx| {
|
||||
editor.request_autoscroll(Autoscroll::fit(), cx)
|
||||
})?;
|
||||
buffer.update(&mut cx, |buffer, cx| {
|
||||
if let Some(transaction) = transaction {
|
||||
if !buffer.is_singleton() {
|
||||
buffer.push_transaction(&transaction.0, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -765,8 +814,8 @@ impl Item for Editor {
|
|||
Some(Box::new(handle.clone()))
|
||||
}
|
||||
|
||||
fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Vector2F> {
|
||||
self.pixel_position_of_newest_cursor
|
||||
fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F> {
|
||||
self.0.read(cx).pixel_position_of_newest_cursor
|
||||
}
|
||||
|
||||
fn breadcrumb_location(&self) -> ToolbarItemLocation {
|
||||
|
@ -774,41 +823,43 @@ impl Item for Editor {
|
|||
}
|
||||
|
||||
fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
|
||||
let cursor = self.selections.newest_anchor().head();
|
||||
let multibuffer = &self.buffer().read(cx);
|
||||
let (buffer_id, symbols) =
|
||||
multibuffer.symbols_containing(cursor, Some(&theme.editor.syntax), cx)?;
|
||||
let buffer = multibuffer.buffer(buffer_id)?;
|
||||
self.0
|
||||
.read_with(cx, |this, cx| {
|
||||
let cursor = this.selections.newest_anchor().head();
|
||||
let multibuffer = &this.buffer().read(cx);
|
||||
let (buffer_id, symbols) =
|
||||
multibuffer.symbols_containing(cursor, Some(&theme.editor.syntax), cx)?;
|
||||
let buffer = multibuffer.buffer(buffer_id)?;
|
||||
|
||||
let buffer = buffer.read(cx);
|
||||
let filename = buffer
|
||||
.snapshot()
|
||||
.resolve_file_path(
|
||||
cx,
|
||||
self.project
|
||||
.as_ref()
|
||||
.map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.map(|path| path.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|| "untitled".to_string());
|
||||
let buffer = buffer.read(cx);
|
||||
let filename = buffer
|
||||
.snapshot()
|
||||
.resolve_file_path(
|
||||
cx,
|
||||
this.project
|
||||
.as_ref()
|
||||
.map(|project| project.visible_worktrees_count(cx) > 1)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.map(|path| path.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|| "untitled".to_string());
|
||||
|
||||
let mut breadcrumbs = vec![BreadcrumbText {
|
||||
text: filename,
|
||||
highlights: None,
|
||||
}];
|
||||
breadcrumbs.extend(symbols.into_iter().map(|symbol| BreadcrumbText {
|
||||
text: symbol.text,
|
||||
highlights: Some(symbol.highlight_ranges),
|
||||
}));
|
||||
Some(breadcrumbs)
|
||||
let mut breadcrumbs = vec![BreadcrumbText {
|
||||
text: filename,
|
||||
highlights: None,
|
||||
}];
|
||||
breadcrumbs.extend(symbols.into_iter().map(|symbol| BreadcrumbText {
|
||||
text: symbol.text,
|
||||
highlights: Some(symbol.highlight_ranges),
|
||||
}));
|
||||
Some(breadcrumbs)
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
|
||||
let workspace_id = workspace.database_id();
|
||||
let item_id = cx.view_id();
|
||||
self.workspace = Some((workspace.weak_handle(), workspace.database_id()));
|
||||
|
||||
fn serialize(
|
||||
buffer: ModelHandle<Buffer>,
|
||||
workspace_id: WorkspaceId,
|
||||
|
@ -828,18 +879,24 @@ impl Item for Editor {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(buffer) = self.buffer().read(cx).as_singleton() {
|
||||
serialize(buffer.clone(), workspace_id, item_id, cx);
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.workspace = Some((
|
||||
Arc::new(WeakWorkspaceHandle(workspace.weak_handle())),
|
||||
workspace.database_id(),
|
||||
));
|
||||
if let Some(buffer) = this.buffer().read(cx).as_singleton() {
|
||||
serialize(buffer.clone(), workspace_id, item_id, cx);
|
||||
|
||||
cx.subscribe(&buffer, |this, buffer, event, cx| {
|
||||
if let Some((_, workspace_id)) = this.workspace.as_ref() {
|
||||
if let language::Event::FileHandleChanged = event {
|
||||
serialize(buffer, *workspace_id, cx.view_id(), cx);
|
||||
cx.subscribe(&buffer, |this, buffer, event, cx| {
|
||||
if let Some((_, workspace_id)) = this.workspace.as_ref() {
|
||||
if let language::Event::FileHandleChanged = event {
|
||||
serialize(buffer, *workspace_id, cx.view_id(), cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn serialized_item_kind() -> Option<&'static str> {
|
||||
|
@ -849,7 +906,7 @@ impl Item for Editor {
|
|||
fn deserialize(
|
||||
project: ModelHandle<Project>,
|
||||
_workspace: WeakViewHandle<Workspace>,
|
||||
workspace_id: workspace::WorkspaceId,
|
||||
workspace_id: WorkspaceId,
|
||||
item_id: ItemId,
|
||||
cx: &mut ViewContext<Pane>,
|
||||
) -> Task<Result<ViewHandle<Self>>> {
|
||||
|
@ -879,9 +936,20 @@ impl Item for Editor {
|
|||
.context("Project item at stored path was not a buffer")?;
|
||||
Ok(pane.update(&mut cx, |_, cx| {
|
||||
cx.add_view(|cx| {
|
||||
let mut editor = Editor::for_buffer(buffer, Some(project), cx);
|
||||
editor.read_scroll_position_from_db(item_id, workspace_id, cx);
|
||||
editor
|
||||
FollowableEditor(cx.add_view(|cx| {
|
||||
let mut editor = Editor::for_buffer(
|
||||
buffer,
|
||||
Some(Arc::new(ProjectHandle(project))),
|
||||
cx,
|
||||
);
|
||||
editor.read_scroll_position_from_db(
|
||||
&*DB,
|
||||
item_id,
|
||||
workspace_id,
|
||||
cx,
|
||||
);
|
||||
editor
|
||||
}))
|
||||
})
|
||||
})?)
|
||||
})
|
||||
|
@ -890,7 +958,7 @@ impl Item for Editor {
|
|||
}
|
||||
}
|
||||
|
||||
impl ProjectItem for Editor {
|
||||
impl ProjectItem for FollowableEditor {
|
||||
type Item = Buffer;
|
||||
|
||||
fn for_project_item(
|
||||
|
@ -898,23 +966,26 @@ impl ProjectItem for Editor {
|
|||
buffer: ModelHandle<Buffer>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
Self::for_buffer(buffer, Some(project), cx)
|
||||
Self(
|
||||
cx.add_view(|cx| {
|
||||
Editor::for_buffer(buffer, Some(Arc::new(ProjectHandle(project))), cx)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum BufferSearchHighlights {}
|
||||
impl SearchableItem for Editor {
|
||||
impl SearchableItem for FollowableEditor {
|
||||
type Match = Range<Anchor>;
|
||||
|
||||
fn to_search_event(
|
||||
&mut self,
|
||||
event: &Self::Event,
|
||||
_: &mut ViewContext<Self>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<SearchEvent> {
|
||||
match event {
|
||||
Event::BufferEdited => Some(SearchEvent::MatchesInvalidated),
|
||||
Event::SelectionsChanged { .. } => {
|
||||
if self.selections.disjoint_anchors().len() == 1 {
|
||||
if self.0.read(cx).selections.disjoint_anchors().len() == 1 {
|
||||
Some(SearchEvent::ActiveMatchChanged)
|
||||
} else {
|
||||
None
|
||||
|
@ -925,20 +996,27 @@ impl SearchableItem for Editor {
|
|||
}
|
||||
|
||||
fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.clear_background_highlights::<BufferSearchHighlights>(cx);
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.clear_background_highlights::<BufferSearchHighlights>(cx)
|
||||
});
|
||||
}
|
||||
|
||||
fn update_matches(&mut self, matches: Vec<Range<Anchor>>, cx: &mut ViewContext<Self>) {
|
||||
self.highlight_background::<BufferSearchHighlights>(
|
||||
matches,
|
||||
|theme| theme.search.match_background,
|
||||
cx,
|
||||
);
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.highlight_background::<BufferSearchHighlights>(
|
||||
matches,
|
||||
|theme| theme.search.match_background,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
|
||||
let display_map = self.snapshot(cx).display_snapshot;
|
||||
let selection = self.selections.newest::<usize>(cx);
|
||||
let (display_map, selection) = self.0.update(cx, |this, cx| {
|
||||
let display_map = this.snapshot(cx).display_snapshot;
|
||||
let selection = this.selections.newest::<usize>(cx);
|
||||
(display_map, selection)
|
||||
});
|
||||
if selection.start == selection.end {
|
||||
let point = selection.start.to_display_point(&display_map);
|
||||
let range = surrounding_word(&display_map, point);
|
||||
|
@ -964,20 +1042,24 @@ impl SearchableItem for Editor {
|
|||
matches: Vec<Range<Anchor>>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.unfold_ranges([matches[index].clone()], false, true, cx);
|
||||
let range = self.range_for_match(&matches[index]);
|
||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select_ranges([range]);
|
||||
})
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.unfold_ranges([matches[index].clone()], false, true, cx);
|
||||
let range = this.range_for_match(&matches[index]);
|
||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select_ranges([range]);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
|
||||
self.unfold_ranges(matches.clone(), false, false, cx);
|
||||
let mut ranges = Vec::new();
|
||||
for m in &matches {
|
||||
ranges.push(self.range_for_match(&m))
|
||||
}
|
||||
self.change_selections(None, cx, |s| s.select_ranges(ranges));
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.unfold_ranges(matches.clone(), false, false, cx);
|
||||
let mut ranges = Vec::new();
|
||||
for m in &matches {
|
||||
ranges.push(this.range_for_match(&m))
|
||||
}
|
||||
this.change_selections(None, cx, |s| s.select_ranges(ranges));
|
||||
});
|
||||
}
|
||||
fn replace(
|
||||
&mut self,
|
||||
|
@ -985,7 +1067,7 @@ impl SearchableItem for Editor {
|
|||
query: &SearchQuery,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let text = self.buffer.read(cx);
|
||||
let text = self.0.read(cx).buffer().read(cx);
|
||||
let text = text.snapshot(cx);
|
||||
let text = text.text_for_range(identifier.clone()).collect::<Vec<_>>();
|
||||
let text: Cow<_> = if text.len() == 1 {
|
||||
|
@ -996,8 +1078,10 @@ impl SearchableItem for Editor {
|
|||
};
|
||||
|
||||
if let Some(replacement) = query.replacement_for(&text) {
|
||||
self.transact(cx, |this, cx| {
|
||||
this.edit([(identifier.clone(), Arc::from(&*replacement))], cx);
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.transact(cx, |this, cx| {
|
||||
this.edit([(identifier.clone(), Arc::from(&*replacement))], cx);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1009,9 +1093,10 @@ impl SearchableItem for Editor {
|
|||
count: usize,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> usize {
|
||||
let buffer = self.buffer().read(cx).snapshot(cx);
|
||||
let current_index_position = if self.selections.disjoint_anchors().len() == 1 {
|
||||
self.selections.newest_anchor().head()
|
||||
let this = self.0.read(cx);
|
||||
let buffer = this.buffer().read(cx).snapshot(cx);
|
||||
let current_index_position = if this.selections.disjoint_anchors().len() == 1 {
|
||||
this.selections.newest_anchor().head()
|
||||
} else {
|
||||
matches[current_index].start
|
||||
};
|
||||
|
@ -1055,7 +1140,7 @@ impl SearchableItem for Editor {
|
|||
query: Arc<project::search::SearchQuery>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Vec<Range<Anchor>>> {
|
||||
let buffer = self.buffer().read(cx).snapshot(cx);
|
||||
let buffer = self.0.read(cx).buffer().read(cx).snapshot(cx);
|
||||
cx.background().spawn(async move {
|
||||
let mut ranges = Vec::new();
|
||||
if let Some((_, _, excerpt_buffer)) = buffer.as_singleton() {
|
||||
|
@ -1098,10 +1183,11 @@ impl SearchableItem for Editor {
|
|||
matches: Vec<Range<Anchor>>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<usize> {
|
||||
let this = self.0.read(cx);
|
||||
active_match_index(
|
||||
&matches,
|
||||
&self.selections.newest_anchor().head(),
|
||||
&self.buffer().read(cx).snapshot(cx),
|
||||
&this.selections.newest_anchor().head(),
|
||||
&this.buffer().read(cx).snapshot(cx),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1289,11 +1375,11 @@ mod tests {
|
|||
|
||||
impl language::File for TestFile {
|
||||
fn path(&self) -> &Arc<Path> {
|
||||
&self.path
|
||||
&self.0.path
|
||||
}
|
||||
|
||||
fn full_path(&self, _: &gpui::AppContext) -> PathBuf {
|
||||
self.full_path.clone()
|
||||
self.0.full_path.clone()
|
||||
}
|
||||
|
||||
fn as_local(&self) -> Option<&dyn language::LocalFile> {
|
703
crates/editor_extensions/src/lib.rs
Normal file
703
crates/editor_extensions/src/lib.rs
Normal file
|
@ -0,0 +1,703 @@
|
|||
mod items;
|
||||
mod persistence;
|
||||
|
||||
use editor::MultiBuffer;
|
||||
use gpui::elements::ChildView;
|
||||
use gpui::elements::ParentElement;
|
||||
use gpui::keymap_matcher::KeymapContext;
|
||||
use gpui::WindowContext;
|
||||
use gpui::{
|
||||
AnyViewHandle, AppContext, Element, ModelHandle, Subscription, Task, ViewContext, ViewHandle,
|
||||
WeakViewHandle,
|
||||
};
|
||||
pub use items::*;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use client::{Client, Collaborator, ParticipantIndex};
|
||||
use collections::{HashMap, HashSet};
|
||||
use editor::scroll::autoscroll::Autoscroll;
|
||||
use editor::{
|
||||
CollaborationHub, ConfirmRename, Editor, Event, InlayHintRefreshReason, OpenExcerpts, Project,
|
||||
};
|
||||
use language::{
|
||||
Buffer, CachedLspAdapter, CodeAction, Completion, LanguageRegistry, LanguageServerName,
|
||||
};
|
||||
use lsp::{LanguageServer, LanguageServerId};
|
||||
use project_types::*;
|
||||
use rpc::proto::PeerId;
|
||||
use std::mem;
|
||||
use std::ops::Range;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use util::ResultExt;
|
||||
use workspace::item::ItemHandle;
|
||||
use workspace::Workspace;
|
||||
use workspace_types::*;
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
cx.add_action(new_file);
|
||||
cx.add_action(new_file_in_direction);
|
||||
cx.add_action(open_excerpts);
|
||||
cx.add_action(confirm_rename);
|
||||
workspace::register_project_item::<FollowableEditor>(cx);
|
||||
workspace::register_followable_item::<FollowableEditor>(cx);
|
||||
workspace::register_deserializable_item::<FollowableEditor>(cx);
|
||||
}
|
||||
|
||||
impl Project for ProjectHandle {
|
||||
fn apply_code_action(
|
||||
&self,
|
||||
buffer_handle: ModelHandle<Buffer>,
|
||||
mut action: CodeAction,
|
||||
push_to_history: bool,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<ProjectTransaction>> {
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.apply_code_action(buffer_handle, action, push_to_history, cx)
|
||||
})
|
||||
}
|
||||
|
||||
fn inlay_hints(
|
||||
&self,
|
||||
buffer_handle: ModelHandle<Buffer>,
|
||||
range: Range<text::Anchor>,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<anyhow::Result<Vec<InlayHint>>> {
|
||||
self.0
|
||||
.update(cx, |this, cx| this.inlay_hints(buffer_handle, range, cx))
|
||||
}
|
||||
fn visible_worktrees_count(&self, cx: &AppContext) -> usize {
|
||||
self.0.read(cx).visible_worktrees(cx).count()
|
||||
}
|
||||
fn resolve_inlay_hint(
|
||||
&self,
|
||||
hint: InlayHint,
|
||||
buffer_handle: ModelHandle<Buffer>,
|
||||
server_id: LanguageServerId,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<anyhow::Result<InlayHint>> {
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
|
||||
})
|
||||
}
|
||||
fn languages(&self, cx: &AppContext) -> Arc<LanguageRegistry> {
|
||||
self.0.read(cx).languages().clone()
|
||||
}
|
||||
fn hover(
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Option<Hover>>> {
|
||||
self.0
|
||||
.update(cx, |this, cx| this.hover(buffer, position, cx))
|
||||
}
|
||||
fn definition(
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<LocationLink>>> {
|
||||
self.0
|
||||
.update(cx, |this, cx| this.definition(buffer, position, cx))
|
||||
}
|
||||
|
||||
fn type_definition(
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<LocationLink>>> {
|
||||
self.0
|
||||
.update(cx, |this, cx| this.type_definition(buffer, position, cx))
|
||||
}
|
||||
|
||||
fn completions(
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<Completion>>> {
|
||||
self.0
|
||||
.update(cx, |this, cx| this.completions(buffer, position, cx))
|
||||
}
|
||||
|
||||
fn as_hub(&self) -> Box<dyn CollaborationHub> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn is_remote(&self, cx: &AppContext) -> bool {
|
||||
self.0.read(cx).is_remote()
|
||||
}
|
||||
fn remote_id(&self, cx: &AppContext) -> Option<u64> {
|
||||
self.0.read(cx).remote_id()
|
||||
}
|
||||
fn language_servers_for_buffer(
|
||||
&self,
|
||||
buffer: &Buffer,
|
||||
cx: &AppContext,
|
||||
) -> Vec<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
|
||||
self.0
|
||||
.read(cx)
|
||||
.language_servers_for_buffer(buffer, cx)
|
||||
.map(|(adapter, server)| (adapter.clone(), server.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn on_type_format(
|
||||
&self,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
trigger: String,
|
||||
push_to_history: bool,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Option<text::Transaction>>> {
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.on_type_format(buffer, position, trigger, push_to_history, cx)
|
||||
})
|
||||
}
|
||||
fn client(&self, cx: &AppContext) -> Arc<Client> {
|
||||
self.0.read(cx).client().clone()
|
||||
}
|
||||
|
||||
fn language_server_for_id(
|
||||
&self,
|
||||
id: LanguageServerId,
|
||||
cx: &AppContext,
|
||||
) -> Option<Arc<LanguageServer>> {
|
||||
self.0.read(cx).language_server_for_id(id).clone()
|
||||
}
|
||||
|
||||
fn code_actions(
|
||||
&self,
|
||||
buffer_handle: &ModelHandle<Buffer>,
|
||||
range: Range<text::Anchor>,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<CodeAction>>> {
|
||||
self.0
|
||||
.update(cx, |this, cx| this.code_actions(buffer_handle, range, cx))
|
||||
}
|
||||
fn document_highlights(
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<DocumentHighlight>>> {
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.document_highlights(buffer, position, cx)
|
||||
})
|
||||
}
|
||||
|
||||
fn format(
|
||||
&self,
|
||||
buffers: HashSet<ModelHandle<Buffer>>,
|
||||
push_to_history: bool,
|
||||
trigger: FormatTrigger,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<anyhow::Result<ProjectTransaction>> {
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.format(buffers, push_to_history, trigger, cx)
|
||||
})
|
||||
}
|
||||
|
||||
fn restart_language_servers_for_buffers(
|
||||
&self,
|
||||
buffers: HashSet<ModelHandle<Buffer>>,
|
||||
cx: &mut AppContext,
|
||||
) -> Option<()> {
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.restart_language_servers_for_buffers(buffers, cx)
|
||||
})
|
||||
}
|
||||
|
||||
fn prepare_rename(
|
||||
&self,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
position: usize,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Option<Range<text::Anchor>>>> {
|
||||
self.0
|
||||
.update(cx, |this, cx| this.prepare_rename(buffer, position, cx))
|
||||
}
|
||||
|
||||
fn references(
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
position: text::Anchor,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Vec<Location>>> {
|
||||
self.0
|
||||
.update(cx, |this, cx| this.references(buffer, position, cx))
|
||||
}
|
||||
|
||||
fn apply_additional_edits_for_completion(
|
||||
&self,
|
||||
buffer_handle: ModelHandle<Buffer>,
|
||||
completion: Completion,
|
||||
push_to_history: bool,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<Option<text::Transaction>>> {
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.apply_additional_edits_for_completion(
|
||||
buffer_handle,
|
||||
completion,
|
||||
push_to_history,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn language_server_for_buffer<'a>(
|
||||
&self,
|
||||
buffer: &Buffer,
|
||||
server_id: LanguageServerId,
|
||||
cx: &'a AppContext,
|
||||
) -> Option<(&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
|
||||
self.0
|
||||
.read(cx)
|
||||
.language_server_for_buffer(buffer, server_id, cx)
|
||||
}
|
||||
|
||||
fn open_local_buffer_via_lsp(
|
||||
&self,
|
||||
abs_path: lsp::Url,
|
||||
language_server_id: LanguageServerId,
|
||||
language_server_name: LanguageServerName,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<ModelHandle<Buffer>>> {
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.open_local_buffer_via_lsp(abs_path, language_server_id, language_server_name, cx)
|
||||
})
|
||||
}
|
||||
|
||||
fn subscribe(&self, is_singleton: bool, cx: &mut ViewContext<Editor>) -> Vec<Subscription> {
|
||||
let mut project_subscriptions = Vec::with_capacity(2);
|
||||
if is_singleton {
|
||||
project_subscriptions.push(cx.observe(&self.0, |_, _, cx| {
|
||||
cx.emit(Event::TitleChanged);
|
||||
}));
|
||||
}
|
||||
project_subscriptions.push(cx.subscribe(&self.0, |editor, _, event, cx| {
|
||||
if let project::Event::RefreshInlayHints = event {
|
||||
editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
|
||||
};
|
||||
}));
|
||||
project_subscriptions
|
||||
}
|
||||
|
||||
fn project_file(&self, file: &dyn language::File) -> ProjectPath {
|
||||
todo!()
|
||||
//project::File::from_dyn(file).map
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct ProjectHandle(pub ModelHandle<project::Project>);
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct WeakWorkspaceHandle(pub WeakViewHandle<workspace::Workspace>);
|
||||
|
||||
impl CollaborationHub for ProjectHandle {
|
||||
fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
|
||||
self.0.read(cx).collaborators()
|
||||
}
|
||||
|
||||
fn user_participant_indices<'a>(
|
||||
&self,
|
||||
cx: &'a AppContext,
|
||||
) -> &'a HashMap<u64, ParticipantIndex> {
|
||||
self.0.read(cx).user_store().read(cx).participant_indices()
|
||||
}
|
||||
}
|
||||
|
||||
impl editor::Workspace for WeakWorkspaceHandle {
|
||||
fn open_abs_path(&self, abs_path: PathBuf, visible: bool, cx: &mut AppContext) {
|
||||
self.0
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.open_abs_path(abs_path, visible, cx)
|
||||
})
|
||||
.map_or_else(|err| Task::ready(Err(err)), |ok| ok)
|
||||
.detach_and_log_err(cx)
|
||||
}
|
||||
fn open_path(
|
||||
&self,
|
||||
path: ProjectPath,
|
||||
focus_item: bool,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<Result<ViewHandle<Editor>>> {
|
||||
let open_task = self
|
||||
.0
|
||||
.update(cx, |this, cx| this.open_path(path, None, focus_item, cx));
|
||||
cx.spawn(move |mut cx| async move {
|
||||
Ok(open_task?
|
||||
.await?
|
||||
.downcast::<Editor>()
|
||||
.ok_or_else(|| anyhow!("opened item was not an editor"))?)
|
||||
})
|
||||
}
|
||||
|
||||
fn active_editor(&self, cx: &mut AppContext) -> Option<ViewHandle<Editor>> {
|
||||
self.0
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace
|
||||
.active_item(cx)
|
||||
.and_then(|item| item.act_as::<Editor>(cx))
|
||||
})
|
||||
.log_err()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn project(&self, cx: &mut AppContext) -> Arc<dyn Project> {
|
||||
Arc::new(ProjectHandle(
|
||||
self.0
|
||||
.update(cx, |this, cx| this.project().clone())
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
fn disable_update_history_for_current_pane(
|
||||
&self,
|
||||
cx: &mut AppContext,
|
||||
) -> Option<editor::DisableUpdateHistoryGuard> {
|
||||
let pane = self
|
||||
.0
|
||||
.update(cx, |this, cx| this.active_pane().clone())
|
||||
.log_err()?;
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.disable_history();
|
||||
pane.enable_history()
|
||||
});
|
||||
let pane = pane.downgrade();
|
||||
struct Guard {
|
||||
pane: WeakViewHandle<workspace::Pane>,
|
||||
}
|
||||
impl editor::DisableUpdateHistory for Guard {
|
||||
fn release(self, cx: &mut AppContext) {
|
||||
self.pane.update(cx, |this, _| this.enable_history());
|
||||
}
|
||||
}
|
||||
Some(Box::new(Guard { pane }))
|
||||
}
|
||||
fn split_buffer(&self, buffer: ModelHandle<Buffer>, cx: &mut AppContext) -> ViewHandle<Editor> {
|
||||
self.0
|
||||
.update(cx, |this, cx| {
|
||||
this.split_project_item::<FollowableEditor>(buffer, cx)
|
||||
})
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.0
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn open_buffer(&self, buffer: ModelHandle<Buffer>, cx: &mut AppContext) -> ViewHandle<Editor> {
|
||||
self.0
|
||||
.update(cx, |this, cx| {
|
||||
this.open_project_item::<FollowableEditor>(buffer, cx)
|
||||
})
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.0
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn add_item(&self, item: Box<ViewHandle<Editor>>, cx: &mut AppContext) {
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.add_item(Box::new(cx.add_view(|_| FollowableEditor(*item))), cx)
|
||||
});
|
||||
}
|
||||
|
||||
fn split_item(
|
||||
&self,
|
||||
split_direction: SplitDirection,
|
||||
item: Box<ViewHandle<Editor>>,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.split_item(
|
||||
split_direction,
|
||||
Box::new(cx.add_view(|_| FollowableEditor(*item))),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
fn db(&self) -> Arc<dyn editor::Db> {
|
||||
Arc::new(persistence::DB.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FollowableEditor(pub ViewHandle<editor::Editor>);
|
||||
|
||||
impl gpui::Entity for FollowableEditor {
|
||||
type Event = <Editor as gpui::Entity>::Event;
|
||||
}
|
||||
|
||||
impl FollowableEditor {
|
||||
pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
|
||||
Self(cx.add_view(|cx| self.0.update(cx, |this, cx| this.clone(cx))))
|
||||
}
|
||||
pub fn for_multibuffer(
|
||||
buffer: ModelHandle<MultiBuffer>,
|
||||
project: ModelHandle<project::Project>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
Self(cx.add_view(|cx| {
|
||||
Editor::for_multibuffer(buffer, Some(Arc::new(ProjectHandle(project))), cx)
|
||||
}))
|
||||
}
|
||||
pub fn for_buffer(
|
||||
buffer: ModelHandle<Buffer>,
|
||||
project: ModelHandle<project::Project>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
Self(
|
||||
cx.add_view(|cx| {
|
||||
Editor::for_buffer(buffer, Some(Arc::new(ProjectHandle(project))), cx)
|
||||
}),
|
||||
)
|
||||
}
|
||||
pub fn for_raw_buffer(
|
||||
buffer: ModelHandle<Buffer>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
Self(
|
||||
cx.add_view(|cx| {
|
||||
Editor::for_buffer(buffer, None, cx)
|
||||
}),
|
||||
)
|
||||
}
|
||||
pub fn single_line(
|
||||
field_editor_style: Option<Arc<editor::GetFieldEditorTheme>>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
Self(
|
||||
cx.add_view(|cx| {
|
||||
Editor::single_line(field_editor_style, cx)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl gpui::View for FollowableEditor {
|
||||
fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
|
||||
ChildView::new(&self.0, cx).into_any()
|
||||
}
|
||||
fn ui_name() -> &'static str {
|
||||
Editor::ui_name()
|
||||
}
|
||||
|
||||
fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||
self.0.update(cx, |this, cx| this.focus_in(focused, cx))
|
||||
}
|
||||
|
||||
fn focus_out(&mut self, handle: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||
self.0.update(cx, |this, cx| this.focus_out(handle, cx))
|
||||
}
|
||||
|
||||
fn modifiers_changed(
|
||||
&mut self,
|
||||
event: &gpui::platform::ModifiersChangedEvent,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> bool {
|
||||
self.0
|
||||
.update(cx, |this, cx| this.modifiers_changed(event, cx))
|
||||
}
|
||||
|
||||
fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
|
||||
self.0
|
||||
.read_with(cx, |this, cx| this.update_keymap_context(keymap, cx));
|
||||
}
|
||||
|
||||
fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
|
||||
self.0
|
||||
.read_with(cx, |this, cx| this.text_for_range(range_utf16, cx))
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
|
||||
self.0
|
||||
.read_with(cx, |this, cx| this.selected_text_range(cx))
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
|
||||
self.0
|
||||
.read_with(cx, |this, cx| this.marked_text_range(cx))
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn unmark_text(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.0.update(cx, |this, cx| this.unmark_text(cx));
|
||||
}
|
||||
|
||||
fn replace_text_in_range(
|
||||
&mut self,
|
||||
range_utf16: Option<Range<usize>>,
|
||||
text: &str,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.replace_text_in_range(range_utf16, text, cx)
|
||||
})
|
||||
}
|
||||
|
||||
fn replace_and_mark_text_in_range(
|
||||
&mut self,
|
||||
range_utf16: Option<Range<usize>>,
|
||||
text: &str,
|
||||
new_selected_range_utf16: Option<Range<usize>>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.0.update(cx, |this, cx| {
|
||||
this.replace_and_mark_text_in_range(range_utf16, text, new_selected_range_utf16, cx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_file(
|
||||
workspace: &mut Workspace,
|
||||
_: &workspace_types::NewFile,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
let project = workspace.project().clone();
|
||||
if project.read(cx).is_remote() {
|
||||
cx.propagate_action();
|
||||
} else if let Some(buffer) = project
|
||||
.update(cx, |project, cx| project.create_buffer("", None, cx))
|
||||
.log_err()
|
||||
{
|
||||
workspace.add_item(
|
||||
Box::new(cx.add_view(|cx| {
|
||||
FollowableEditor(cx.add_view(|cx| {
|
||||
Editor::for_buffer(buffer, Some(Arc::new(ProjectHandle(project.clone()))), cx)
|
||||
}))
|
||||
})),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_file_in_direction(
|
||||
workspace: &mut Workspace,
|
||||
action: &workspace::NewFileInDirection,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
let project = workspace.project().clone();
|
||||
if project.read(cx).is_remote() {
|
||||
cx.propagate_action();
|
||||
} else if let Some(buffer) = project
|
||||
.update(cx, |project, cx| project.create_buffer("", None, cx))
|
||||
.log_err()
|
||||
{
|
||||
workspace.split_item(
|
||||
action.0,
|
||||
Box::new(cx.add_view(|cx| {
|
||||
FollowableEditor(cx.add_view(|cx| {
|
||||
Editor::for_buffer(buffer, Some(Arc::new(ProjectHandle(project.clone()))), cx)
|
||||
}))
|
||||
})),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
|
||||
let active_item = workspace.active_item(cx);
|
||||
let editor_handle = if let Some(editor) = active_item
|
||||
.as_ref()
|
||||
.and_then(|item| item.act_as::<Editor>(cx))
|
||||
{
|
||||
editor
|
||||
} else {
|
||||
cx.propagate_action();
|
||||
return;
|
||||
};
|
||||
|
||||
let editor = editor_handle.read(cx);
|
||||
let buffer = editor.buffer().read(cx);
|
||||
if buffer.is_singleton() {
|
||||
cx.propagate_action();
|
||||
return;
|
||||
}
|
||||
|
||||
let mut new_selections_by_buffer = HashMap::default();
|
||||
for selection in editor.selections.all::<usize>(cx) {
|
||||
for (buffer, mut range, _) in
|
||||
buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
|
||||
{
|
||||
if selection.reversed {
|
||||
mem::swap(&mut range.start, &mut range.end);
|
||||
}
|
||||
new_selections_by_buffer
|
||||
.entry(buffer)
|
||||
.or_insert(Vec::new())
|
||||
.push(range)
|
||||
}
|
||||
}
|
||||
|
||||
editor_handle.update(cx, |editor, cx| {
|
||||
editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
|
||||
});
|
||||
let pane = workspace.active_pane().clone();
|
||||
pane.update(cx, |pane, _| pane.disable_history());
|
||||
|
||||
// We defer the pane interaction because we ourselves are a workspace item
|
||||
// and activating a new item causes the pane to call a method on us reentrantly,
|
||||
// which panics if we're on the stack.
|
||||
cx.defer(move |workspace, cx| {
|
||||
for (buffer, ranges) in new_selections_by_buffer.into_iter() {
|
||||
let editor = workspace.open_project_item::<FollowableEditor>(buffer, cx);
|
||||
editor.update(cx, |this, cx| {
|
||||
this.0.update(cx, |editor, cx| {
|
||||
editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
|
||||
s.select_ranges(ranges);
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pane.update(cx, |pane, _| pane.enable_history());
|
||||
});
|
||||
}
|
||||
|
||||
pub fn confirm_rename(
|
||||
workspace: &mut Workspace,
|
||||
_: &ConfirmRename,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
|
||||
|
||||
let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
|
||||
let rename = editor.take_rename(false, cx)?;
|
||||
let buffer = editor.buffer().read(cx);
|
||||
let (start_buffer, start) =
|
||||
buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
|
||||
let (end_buffer, end) = buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
|
||||
if start_buffer == end_buffer {
|
||||
let new_name = rename.editor.read(cx).text(cx);
|
||||
Some((start_buffer, start..end, rename.old_name, new_name))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})?;
|
||||
|
||||
let rename = workspace.project().clone().update(cx, |project, cx| {
|
||||
project.perform_rename(buffer.clone(), range.start, new_name.clone(), true, cx)
|
||||
});
|
||||
|
||||
let editor = editor.downgrade();
|
||||
Some(cx.spawn(|workspace, mut cx| async move {
|
||||
let project_transaction = rename.await?;
|
||||
Editor::open_project_transaction(
|
||||
&editor,
|
||||
&WeakWorkspaceHandle(workspace),
|
||||
project_transaction,
|
||||
format!("Rename: {old_name} → {new_name}"),
|
||||
cx.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
editor.update(&mut cx, |editor, cx| {
|
||||
editor.refresh_document_highlights(cx);
|
||||
})?;
|
||||
Ok(())
|
||||
}))
|
||||
}
|
|
@ -1,9 +1,12 @@
|
|||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use db::sqlez_macros::sql;
|
||||
use db::{define_connection, query};
|
||||
|
||||
use workspace::{ItemId, WorkspaceDb, WorkspaceId};
|
||||
use workspace::WorkspaceDb;
|
||||
use workspace_types::{ItemId, WorkspaceId};
|
||||
|
||||
define_connection!(
|
||||
// Current schema shape using pseudo-rust syntax:
|
||||
|
@ -81,3 +84,39 @@ impl EditorDb {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl editor::Db for EditorDb {
|
||||
async fn save_scroll_position(
|
||||
&self,
|
||||
item_id: ItemId,
|
||||
workspace_id: WorkspaceId,
|
||||
top_row: u32,
|
||||
vertical_offset: f32,
|
||||
horizontal_offset: f32,
|
||||
) -> Result<()> {
|
||||
self.save_scroll_position(
|
||||
item_id,
|
||||
workspace_id,
|
||||
top_row,
|
||||
vertical_offset,
|
||||
horizontal_offset,
|
||||
)
|
||||
.await
|
||||
}
|
||||
fn get_scroll_position(
|
||||
&self,
|
||||
item_id: ItemId,
|
||||
workspace_id: WorkspaceId,
|
||||
) -> Result<Option<(u32, f32, f32)>> {
|
||||
self.get_scroll_position(item_id, workspace_id)
|
||||
}
|
||||
async fn save_path(
|
||||
&self,
|
||||
item_id: ItemId,
|
||||
workspace_id: WorkspaceId,
|
||||
path: PathBuf,
|
||||
) -> Result<()> {
|
||||
self.save_path(item_id, workspace_id, path).await
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ test-support = []
|
|||
[dependencies]
|
||||
client = { path = "../client" }
|
||||
editor = { path = "../editor" }
|
||||
editor_extensions = { path = "../editor_extensions" }
|
||||
language = { path = "../language" }
|
||||
gpui = { path = "../gpui" }
|
||||
project = { path = "../project" }
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::system_specs::SystemSpecs;
|
|||
use anyhow::bail;
|
||||
use client::{Client, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
|
||||
use editor::{Anchor, Editor};
|
||||
use editor_extensions::FollowableEditor;
|
||||
use futures::AsyncReadExt;
|
||||
use gpui::{
|
||||
actions,
|
||||
|
@ -58,7 +59,7 @@ struct FeedbackRequestBody<'a> {
|
|||
#[derive(Clone)]
|
||||
pub(crate) struct FeedbackEditor {
|
||||
system_specs: SystemSpecs,
|
||||
editor: ViewHandle<Editor>,
|
||||
editor: ViewHandle<FollowableEditor>,
|
||||
project: ModelHandle<Project>,
|
||||
pub allow_submission: bool,
|
||||
}
|
||||
|
@ -71,8 +72,8 @@ impl FeedbackEditor {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let editor = cx.add_view(|cx| {
|
||||
let mut editor = Editor::for_buffer(buffer, Some(project.clone()), cx);
|
||||
editor.set_vertical_scroll_margin(5, cx);
|
||||
let mut editor = FollowableEditor::for_buffer(buffer, project.clone(), cx);
|
||||
editor.0.update(cx, |this, cx| this.set_vertical_scroll_margin(5, cx));
|
||||
editor
|
||||
});
|
||||
|
||||
|
@ -92,7 +93,7 @@ impl FeedbackEditor {
|
|||
return Task::ready(Ok(()));
|
||||
}
|
||||
|
||||
let feedback_text = self.editor.read(cx).text(cx);
|
||||
let feedback_text = self.editor.read(cx).0.read(cx).text(cx);
|
||||
let feedback_char_count = feedback_text.chars().count();
|
||||
let feedback_text = feedback_text.trim().to_string();
|
||||
|
||||
|
@ -339,7 +340,7 @@ impl Item for FeedbackEditor {
|
|||
{
|
||||
let buffer = self
|
||||
.editor
|
||||
.read(cx)
|
||||
.read(cx).0.read(cx)
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
|
@ -373,7 +374,7 @@ impl Item for FeedbackEditor {
|
|||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
|
||||
Editor::to_item_events(event)
|
||||
FollowableEditor::to_item_events(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,12 +10,14 @@ doctest = false
|
|||
|
||||
[dependencies]
|
||||
editor = { path = "../editor" }
|
||||
editor_extensions = { path = "../editor_extensions" }
|
||||
collections = { path = "../collections" }
|
||||
fuzzy = { path = "../fuzzy" }
|
||||
gpui = { path = "../gpui" }
|
||||
menu = { path = "../menu" }
|
||||
picker = { path = "../picker" }
|
||||
project = { path = "../project" }
|
||||
project_types = { path = "../project_types" }
|
||||
settings = { path = "../settings" }
|
||||
text = { path = "../text" }
|
||||
util = { path = "../util" }
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use collections::HashMap;
|
||||
use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
|
||||
use editor::{scroll::autoscroll::Autoscroll, Bias};
|
||||
use editor_extensions::FollowableEditor;
|
||||
use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
|
||||
use gpui::{
|
||||
actions, elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext, WeakViewHandle,
|
||||
};
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
|
||||
use project::{PathMatchCandidateSet, Project};
|
||||
use project_types::{ProjectPath, WorktreeId};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::{
|
||||
|
@ -636,16 +638,22 @@ impl PickerDelegate for FileFinderDelegate {
|
|||
cx.spawn(|_, mut cx| async move {
|
||||
let item = open_task.await.log_err()?;
|
||||
if let Some(row) = row {
|
||||
if let Some(active_editor) = item.downcast::<Editor>() {
|
||||
if let Some(active_editor) = item.downcast::<FollowableEditor>() {
|
||||
active_editor
|
||||
.downgrade()
|
||||
.update(&mut cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(cx).display_snapshot;
|
||||
let snapshot = editor
|
||||
.0
|
||||
.update(cx, |this, cx| this.snapshot(cx).display_snapshot);
|
||||
let point = snapshot
|
||||
.buffer_snapshot
|
||||
.clip_point(Point::new(row, col), Bias::Left);
|
||||
editor.change_selections(Some(Autoscroll::center()), cx, |s| {
|
||||
s.select_ranges([point..point])
|
||||
editor.0.update(cx, |this, cx| {
|
||||
this.change_selections(
|
||||
Some(Autoscroll::center()),
|
||||
cx,
|
||||
|s| s.select_ranges([point..point]),
|
||||
)
|
||||
});
|
||||
})
|
||||
.log_err();
|
||||
|
|
|
@ -11,6 +11,7 @@ doctest = false
|
|||
[dependencies]
|
||||
collections = { path = "../collections" }
|
||||
editor = { path = "../editor" }
|
||||
editor_extensions = { path = "../editor_extensions" }
|
||||
settings = { path = "../settings" }
|
||||
theme = { path = "../theme" }
|
||||
language = { path = "../language" }
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use collections::{HashMap, VecDeque};
|
||||
use editor::{Editor, MoveToEnd};
|
||||
use editor_extensions::FollowableEditor;
|
||||
use futures::{channel::mpsc, StreamExt};
|
||||
use gpui::{
|
||||
actions,
|
||||
|
@ -49,7 +50,7 @@ struct LanguageServerRpcState {
|
|||
}
|
||||
|
||||
pub struct LspLogView {
|
||||
pub(crate) editor: ViewHandle<Editor>,
|
||||
pub(crate) editor: ViewHandle<FollowableEditor>,
|
||||
editor_subscription: Subscription,
|
||||
log_store: ModelHandle<LogStore>,
|
||||
current_server_id: Option<LanguageServerId>,
|
||||
|
@ -388,9 +389,11 @@ impl LspLogView {
|
|||
} else {
|
||||
this.current_server_id = None;
|
||||
this.editor.update(cx, |editor, cx| {
|
||||
editor.set_read_only(false);
|
||||
editor.clear(cx);
|
||||
editor.set_read_only(true);
|
||||
editor.0.update(cx, |this, cx| {
|
||||
this.set_read_only(false);
|
||||
this.clear(cx);
|
||||
this.set_read_only(true);
|
||||
});
|
||||
});
|
||||
cx.notify();
|
||||
}
|
||||
|
@ -417,10 +420,12 @@ impl LspLogView {
|
|||
|| (!*is_rpc && !log_view.is_showing_rpc_trace)
|
||||
{
|
||||
log_view.editor.update(cx, |editor, cx| {
|
||||
editor.set_read_only(false);
|
||||
editor.handle_input(entry.trim(), cx);
|
||||
editor.handle_input("\n", cx);
|
||||
editor.set_read_only(true);
|
||||
editor.0.update(cx, |this, cx| {
|
||||
this.set_read_only(false);
|
||||
this.handle_input(entry.trim(), cx);
|
||||
this.handle_input("\n", cx);
|
||||
this.set_read_only(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -445,13 +450,15 @@ impl LspLogView {
|
|||
fn editor_for_logs(
|
||||
log_contents: String,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> (ViewHandle<Editor>, Subscription) {
|
||||
) -> (ViewHandle<FollowableEditor>, Subscription) {
|
||||
let editor = cx.add_view(|cx| {
|
||||
let mut editor = Editor::multi_line(None, cx);
|
||||
editor.set_text(log_contents, cx);
|
||||
editor.move_to_end(&MoveToEnd, cx);
|
||||
editor.set_read_only(true);
|
||||
editor
|
||||
FollowableEditor(cx.add_view(|cx| {
|
||||
let mut editor = Editor::multi_line(None, cx);
|
||||
editor.set_text(log_contents, cx);
|
||||
editor.move_to_end(&MoveToEnd, cx);
|
||||
editor.set_read_only(true);
|
||||
editor
|
||||
}))
|
||||
});
|
||||
let editor_subscription = cx.subscribe(&editor, |_, _, event, cx| cx.emit(event.clone()));
|
||||
(editor, editor_subscription)
|
||||
|
@ -534,6 +541,8 @@ impl LspLogView {
|
|||
let (editor, editor_subscription) = Self::editor_for_logs(rpc_log, cx);
|
||||
let language = self.project.read(cx).languages().language_for_name("JSON");
|
||||
editor
|
||||
.read(cx)
|
||||
.0
|
||||
.read(cx)
|
||||
.buffer()
|
||||
.read(cx)
|
||||
|
@ -620,7 +629,7 @@ impl Item for LspLogView {
|
|||
}
|
||||
|
||||
impl SearchableItem for LspLogView {
|
||||
type Match = <Editor as SearchableItem>::Match;
|
||||
type Match = <FollowableEditor as SearchableItem>::Match;
|
||||
|
||||
fn to_search_event(
|
||||
&mut self,
|
||||
|
@ -811,9 +820,11 @@ impl View for LspLogToolbarItemView {
|
|||
if let Some(log_view) = this.log_view.as_ref() {
|
||||
log_view.update(cx, |log_view, cx| {
|
||||
log_view.editor.update(cx, |editor, cx| {
|
||||
editor.set_read_only(false);
|
||||
editor.clear(cx);
|
||||
editor.set_read_only(true);
|
||||
editor.0.update(cx, |this, cx| {
|
||||
this.set_read_only(false);
|
||||
this.clear(cx);
|
||||
this.set_read_only(true);
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
|
@ -1015,7 +1026,7 @@ impl Entity for LogStore {
|
|||
}
|
||||
|
||||
impl Entity for LspLogView {
|
||||
type Event = editor::Event;
|
||||
type Event = <FollowableEditor as Entity>::Event;
|
||||
}
|
||||
|
||||
impl Entity for LspLogToolbarItemView {
|
||||
|
|
|
@ -34,6 +34,7 @@ language = { path = "../language" }
|
|||
lsp = { path = "../lsp" }
|
||||
node_runtime = { path = "../node_runtime" }
|
||||
prettier = { path = "../prettier" }
|
||||
project_types = {path = "../project_types"}
|
||||
rpc = { path = "../rpc" }
|
||||
settings = { path = "../settings" }
|
||||
sum_tree = { path = "../sum_tree" }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
mod ignore;
|
||||
mod lsp_command;
|
||||
pub mod project_settings;
|
||||
pub use project_types::project_settings;
|
||||
pub mod search;
|
||||
pub mod terminals;
|
||||
pub mod worktree;
|
||||
|
@ -29,11 +29,8 @@ use gpui::{
|
|||
executor::Background, AnyModelHandle, AppContext, AsyncAppContext, BorrowAppContext, Entity,
|
||||
ModelContext, ModelHandle, Task, WeakModelHandle,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use language::{
|
||||
language_settings::{
|
||||
language_settings, FormatOnSave, Formatter, InlayHintKind, LanguageSettings,
|
||||
},
|
||||
language_settings::{language_settings, FormatOnSave, Formatter, LanguageSettings},
|
||||
point_to_lsp,
|
||||
proto::{
|
||||
deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
|
||||
|
@ -47,8 +44,8 @@ use language::{
|
|||
};
|
||||
use log::error;
|
||||
use lsp::{
|
||||
DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions,
|
||||
DocumentHighlightKind, LanguageServer, LanguageServerBinary, LanguageServerId, OneOf,
|
||||
DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, LanguageServer,
|
||||
LanguageServerBinary, LanguageServerId, OneOf,
|
||||
};
|
||||
use lsp_command::*;
|
||||
use node_runtime::NodeRuntime;
|
||||
|
@ -56,6 +53,7 @@ use parking_lot::Mutex;
|
|||
use postage::watch;
|
||||
use prettier::Prettier;
|
||||
use project_settings::{LspSettings, ProjectSettings};
|
||||
use project_types::*;
|
||||
use rand::prelude::*;
|
||||
use search::SearchQuery;
|
||||
use serde::Serialize;
|
||||
|
@ -338,135 +336,12 @@ pub struct LanguageServerProgress {
|
|||
pub last_update_at: Instant,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
||||
pub struct ProjectPath {
|
||||
pub worktree_id: WorktreeId,
|
||||
pub path: Arc<Path>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize)]
|
||||
pub struct DiagnosticSummary {
|
||||
pub error_count: usize,
|
||||
pub warning_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Location {
|
||||
pub buffer: ModelHandle<Buffer>,
|
||||
pub range: Range<language::Anchor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InlayHint {
|
||||
pub position: language::Anchor,
|
||||
pub label: InlayHintLabel,
|
||||
pub kind: Option<InlayHintKind>,
|
||||
pub padding_left: bool,
|
||||
pub padding_right: bool,
|
||||
pub tooltip: Option<InlayHintTooltip>,
|
||||
pub resolve_state: ResolveState,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ResolveState {
|
||||
Resolved,
|
||||
CanResolve(LanguageServerId, Option<lsp::LSPAny>),
|
||||
Resolving,
|
||||
}
|
||||
|
||||
impl InlayHint {
|
||||
pub fn text(&self) -> String {
|
||||
match &self.label {
|
||||
InlayHintLabel::String(s) => s.to_owned(),
|
||||
InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &part.value).join(""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InlayHintLabel {
|
||||
String(String),
|
||||
LabelParts(Vec<InlayHintLabelPart>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InlayHintLabelPart {
|
||||
pub value: String,
|
||||
pub tooltip: Option<InlayHintLabelPartTooltip>,
|
||||
pub location: Option<(LanguageServerId, lsp::Location)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InlayHintTooltip {
|
||||
String(String),
|
||||
MarkupContent(MarkupContent),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InlayHintLabelPartTooltip {
|
||||
String(String),
|
||||
MarkupContent(MarkupContent),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MarkupContent {
|
||||
pub kind: HoverBlockKind,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocationLink {
|
||||
pub origin: Option<Location>,
|
||||
pub target: Location,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DocumentHighlight {
|
||||
pub range: Range<language::Anchor>,
|
||||
pub kind: DocumentHighlightKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Symbol {
|
||||
pub language_server_name: LanguageServerName,
|
||||
pub source_worktree_id: WorktreeId,
|
||||
pub path: ProjectPath,
|
||||
pub label: CodeLabel,
|
||||
pub name: String,
|
||||
pub kind: lsp::SymbolKind,
|
||||
pub range: Range<Unclipped<PointUtf16>>,
|
||||
pub signature: [u8; 32],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct HoverBlock {
|
||||
pub text: String,
|
||||
pub kind: HoverBlockKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum HoverBlockKind {
|
||||
PlainText,
|
||||
Markdown,
|
||||
Code { language: String },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Hover {
|
||||
pub contents: Vec<HoverBlock>,
|
||||
pub range: Option<Range<language::Anchor>>,
|
||||
pub language: Option<Arc<Language>>,
|
||||
}
|
||||
|
||||
impl Hover {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.contents.iter().all(|block| block.text.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ProjectTransaction(pub HashMap<ModelHandle<Buffer>, language::Transaction>);
|
||||
|
||||
impl DiagnosticSummary {
|
||||
fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
|
||||
let mut this = Self {
|
||||
|
@ -528,26 +403,11 @@ impl ProjectEntryId {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FormatTrigger {
|
||||
Save,
|
||||
Manual,
|
||||
}
|
||||
|
||||
struct ProjectLspAdapterDelegate {
|
||||
project: ModelHandle<Project>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
}
|
||||
|
||||
impl FormatTrigger {
|
||||
fn from_proto(value: i32) -> FormatTrigger {
|
||||
match value {
|
||||
0 => FormatTrigger::Save,
|
||||
1 => FormatTrigger::Manual,
|
||||
_ => FormatTrigger::Save,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum SearchMatchCandidate {
|
||||
OpenBuffer {
|
||||
|
@ -9021,15 +8881,6 @@ impl Entity for Project {
|
|||
}
|
||||
}
|
||||
|
||||
impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
|
||||
fn from((worktree_id, path): (WorktreeId, P)) -> Self {
|
||||
Self {
|
||||
worktree_id,
|
||||
path: path.as_ref().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProjectLspAdapterDelegate {
|
||||
fn new(project: &Project, cx: &ModelContext<Project>) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
|
|
|
@ -36,6 +36,7 @@ use postage::{
|
|||
prelude::{Sink as _, Stream as _},
|
||||
watch,
|
||||
};
|
||||
use project_types::WorktreeId;
|
||||
use smol::channel::{self, Sender};
|
||||
use std::{
|
||||
any::Any,
|
||||
|
@ -56,10 +57,6 @@ use std::{
|
|||
};
|
||||
use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet};
|
||||
use util::{paths::HOME, ResultExt};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
|
||||
pub struct WorktreeId(usize);
|
||||
|
||||
pub enum Worktree {
|
||||
Local(LocalWorktree),
|
||||
Remote(RemoteWorktree),
|
||||
|
@ -407,7 +404,7 @@ impl Worktree {
|
|||
) -> ModelHandle<Self> {
|
||||
cx.add_model(|cx: &mut ModelContext<Self>| {
|
||||
let snapshot = Snapshot {
|
||||
id: WorktreeId(worktree.id as usize),
|
||||
id: WorktreeId::from_usize(worktree.id as usize),
|
||||
abs_path: Arc::from(PathBuf::from(worktree.abs_path)),
|
||||
root_name: worktree.root_name.clone(),
|
||||
root_char_bag: worktree
|
||||
|
@ -2493,30 +2490,6 @@ async fn build_gitignore(abs_path: &Path, fs: &dyn Fs) -> Result<Gitignore> {
|
|||
Ok(builder.build()?)
|
||||
}
|
||||
|
||||
impl WorktreeId {
|
||||
pub fn from_usize(handle_id: usize) -> Self {
|
||||
Self(handle_id)
|
||||
}
|
||||
|
||||
pub(crate) fn from_proto(id: u64) -> Self {
|
||||
Self(id as usize)
|
||||
}
|
||||
|
||||
pub fn to_proto(&self) -> u64 {
|
||||
self.0 as u64
|
||||
}
|
||||
|
||||
pub fn to_usize(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for WorktreeId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Worktree {
|
||||
type Target = Snapshot;
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ editor = { path = "../editor" }
|
|||
gpui = { path = "../gpui" }
|
||||
menu = { path = "../menu" }
|
||||
project = { path = "../project" }
|
||||
project_types = { path = "../project_types" }
|
||||
settings = { path = "../settings" }
|
||||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
|
|
|
@ -23,9 +23,10 @@ use gpui::{
|
|||
};
|
||||
use menu::{Confirm, SelectNext, SelectPrev};
|
||||
use project::{
|
||||
repository::GitFileStatus, Entry, EntryKind, Fs, Project, ProjectEntryId, ProjectPath,
|
||||
Worktree, WorktreeId,
|
||||
repository::GitFileStatus, Entry, EntryKind, Fs, Project, ProjectEntryId,
|
||||
Worktree,
|
||||
};
|
||||
use project_types::{ProjectPath, WorktreeId};
|
||||
use project_panel_settings::{ProjectPanelDockPosition, ProjectPanelSettings};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::SettingsStore;
|
||||
|
|
|
@ -10,10 +10,12 @@ doctest = false
|
|||
|
||||
[dependencies]
|
||||
editor = { path = "../editor" }
|
||||
editor_extensions = { path = "../editor_extensions" }
|
||||
fuzzy = { path = "../fuzzy" }
|
||||
gpui = { path = "../gpui" }
|
||||
picker = { path = "../picker" }
|
||||
project = { path = "../project" }
|
||||
project_types = { path = "../project_types" }
|
||||
text = { path = "../text" }
|
||||
settings = { path = "../settings" }
|
||||
workspace = { path = "../workspace" }
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
use editor::{
|
||||
combine_syntax_and_fuzzy_match_highlights, scroll::autoscroll::Autoscroll,
|
||||
styled_runs_for_code_label, Bias, Editor,
|
||||
styled_runs_for_code_label, Bias,
|
||||
};
|
||||
use editor_extensions::FollowableEditor;
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions, elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext, WeakViewHandle,
|
||||
};
|
||||
use ordered_float::OrderedFloat;
|
||||
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||
use project::{Project, Symbol};
|
||||
use project::{Project};
|
||||
use project_types::Symbol;
|
||||
use std::{borrow::Cow, cmp::Reverse, sync::Arc};
|
||||
use util::ResultExt;
|
||||
use workspace::Workspace;
|
||||
|
@ -123,15 +125,15 @@ impl PickerDelegate for ProjectSymbolsDelegate {
|
|||
.clip_point_utf16(symbol.range.start, Bias::Left);
|
||||
|
||||
let editor = if secondary {
|
||||
workspace.split_project_item::<Editor>(buffer, cx)
|
||||
workspace.split_project_item::<FollowableEditor>(buffer, cx)
|
||||
} else {
|
||||
workspace.open_project_item::<Editor>(buffer, cx)
|
||||
workspace.open_project_item::<FollowableEditor>(buffer, cx)
|
||||
};
|
||||
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.change_selections(Some(Autoscroll::center()), cx, |s| {
|
||||
editor.0.update(cx, |this, cx| this.change_selections(Some(Autoscroll::center()), cx, |s| {
|
||||
s.select_ranges([position..position])
|
||||
});
|
||||
}));
|
||||
});
|
||||
})?;
|
||||
Ok::<_, anyhow::Error>(())
|
||||
|
|
19
crates/project_types/Cargo.toml
Normal file
19
crates/project_types/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "project_types"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
gpui = {path = "../gpui"}
|
||||
lsp = {path = "../lsp"}
|
||||
language = {path = "../language"}
|
||||
itertools = "0.10"
|
||||
settings = {path = "../settings"}
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
schemars.workspace = true
|
||||
collections = {path = "../collections"}
|
||||
rpc = {path = "../rpc"}
|
185
crates/project_types/src/lib.rs
Normal file
185
crates/project_types/src/lib.rs
Normal file
|
@ -0,0 +1,185 @@
|
|||
pub mod project_settings;
|
||||
|
||||
use collections::HashMap;
|
||||
use gpui::ModelHandle;
|
||||
use itertools::Itertools;
|
||||
use language::{
|
||||
language_settings::InlayHintKind, Buffer, CodeLabel, Language, LanguageServerId,
|
||||
LanguageServerName, PointUtf16, Unclipped,
|
||||
};
|
||||
use lsp::DocumentHighlightKind;
|
||||
use std::{ops::Range, path::Path, sync::Arc};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
|
||||
pub struct WorktreeId(usize);
|
||||
|
||||
impl WorktreeId {
|
||||
pub fn from_usize(handle_id: usize) -> Self {
|
||||
Self(handle_id)
|
||||
}
|
||||
|
||||
pub fn from_proto(id: u64) -> Self {
|
||||
Self(id as usize)
|
||||
}
|
||||
|
||||
pub fn to_proto(&self) -> u64 {
|
||||
self.0 as u64
|
||||
}
|
||||
|
||||
pub fn to_usize(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for WorktreeId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
||||
pub struct ProjectPath {
|
||||
pub worktree_id: WorktreeId,
|
||||
pub path: Arc<Path>,
|
||||
}
|
||||
|
||||
impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
|
||||
fn from((worktree_id, path): (WorktreeId, P)) -> Self {
|
||||
Self {
|
||||
worktree_id,
|
||||
path: path.as_ref().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Location {
|
||||
pub buffer: ModelHandle<Buffer>,
|
||||
pub range: Range<language::Anchor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InlayHint {
|
||||
pub position: language::Anchor,
|
||||
pub label: InlayHintLabel,
|
||||
pub kind: Option<InlayHintKind>,
|
||||
pub padding_left: bool,
|
||||
pub padding_right: bool,
|
||||
pub tooltip: Option<InlayHintTooltip>,
|
||||
pub resolve_state: ResolveState,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ResolveState {
|
||||
Resolved,
|
||||
CanResolve(LanguageServerId, Option<lsp::LSPAny>),
|
||||
Resolving,
|
||||
}
|
||||
|
||||
impl InlayHint {
|
||||
pub fn text(&self) -> String {
|
||||
match &self.label {
|
||||
InlayHintLabel::String(s) => s.to_owned(),
|
||||
InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &part.value).join(""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InlayHintLabel {
|
||||
String(String),
|
||||
LabelParts(Vec<InlayHintLabelPart>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InlayHintLabelPart {
|
||||
pub value: String,
|
||||
pub tooltip: Option<InlayHintLabelPartTooltip>,
|
||||
pub location: Option<(LanguageServerId, lsp::Location)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InlayHintTooltip {
|
||||
String(String),
|
||||
MarkupContent(MarkupContent),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InlayHintLabelPartTooltip {
|
||||
String(String),
|
||||
MarkupContent(MarkupContent),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MarkupContent {
|
||||
pub kind: HoverBlockKind,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocationLink {
|
||||
pub origin: Option<Location>,
|
||||
pub target: Location,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DocumentHighlight {
|
||||
pub range: Range<language::Anchor>,
|
||||
pub kind: DocumentHighlightKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Symbol {
|
||||
pub language_server_name: LanguageServerName,
|
||||
pub source_worktree_id: WorktreeId,
|
||||
pub path: ProjectPath,
|
||||
pub label: CodeLabel,
|
||||
pub name: String,
|
||||
pub kind: lsp::SymbolKind,
|
||||
pub range: Range<Unclipped<PointUtf16>>,
|
||||
pub signature: [u8; 32],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct HoverBlock {
|
||||
pub text: String,
|
||||
pub kind: HoverBlockKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum HoverBlockKind {
|
||||
PlainText,
|
||||
Markdown,
|
||||
Code { language: String },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Hover {
|
||||
pub contents: Vec<HoverBlock>,
|
||||
pub range: Option<Range<language::Anchor>>,
|
||||
pub language: Option<Arc<Language>>,
|
||||
}
|
||||
|
||||
impl Hover {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.contents.iter().all(|block| block.text.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ProjectTransaction(pub HashMap<ModelHandle<Buffer>, language::Transaction>);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FormatTrigger {
|
||||
Save,
|
||||
Manual,
|
||||
}
|
||||
impl FormatTrigger {
|
||||
pub fn from_proto(value: i32) -> FormatTrigger {
|
||||
match value {
|
||||
0 => FormatTrigger::Save,
|
||||
1 => FormatTrigger::Manual,
|
||||
_ => FormatTrigger::Save,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ doctest = false
|
|||
bitflags = "1"
|
||||
collections = { path = "../collections" }
|
||||
editor = { path = "../editor" }
|
||||
editor_extensions = { path = "../editor_extensions" }
|
||||
gpui = { path = "../gpui" }
|
||||
language = { path = "../language" }
|
||||
menu = { path = "../menu" }
|
||||
|
|
|
@ -9,9 +9,10 @@ use crate::{
|
|||
use anyhow::{Context, Result};
|
||||
use collections::HashMap;
|
||||
use editor::{
|
||||
items::active_match_index, scroll::autoscroll::Autoscroll, Anchor, Editor, MultiBuffer,
|
||||
scroll::autoscroll::Autoscroll, Anchor, Editor, MultiBuffer,
|
||||
SelectAll, MAX_TAB_TITLE_LEN,
|
||||
};
|
||||
use editor_extensions::{active_match_index, FollowableEditor};
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
actions,
|
||||
|
@ -134,7 +135,7 @@ pub struct ProjectSearchView {
|
|||
model: ModelHandle<ProjectSearch>,
|
||||
query_editor: ViewHandle<Editor>,
|
||||
replacement_editor: ViewHandle<Editor>,
|
||||
results_editor: ViewHandle<Editor>,
|
||||
results_editor: ViewHandle<FollowableEditor>,
|
||||
semantic_state: Option<SemanticState>,
|
||||
semantic_permissioned: Option<bool>,
|
||||
search_options: SearchOptions,
|
||||
|
@ -650,8 +651,8 @@ impl Item for ProjectSearchView {
|
|||
}
|
||||
|
||||
fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
|
||||
self.results_editor.update(cx, |editor, _| {
|
||||
editor.set_nav_history(Some(nav_history));
|
||||
self.results_editor.update(cx, |editor, cx| {
|
||||
editor.set_nav_history(nav_history,cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -665,7 +666,7 @@ impl Item for ProjectSearchView {
|
|||
ViewEvent::UpdateTab => {
|
||||
smallvec::smallvec![ItemEvent::UpdateBreadcrumbs, ItemEvent::UpdateTab]
|
||||
}
|
||||
ViewEvent::EditorEvent(editor_event) => Editor::to_item_events(editor_event),
|
||||
ViewEvent::EditorEvent(editor_event) => FollowableEditor::to_item_events(editor_event),
|
||||
ViewEvent::Dismiss => smallvec::smallvec![ItemEvent::CloseItem],
|
||||
_ => SmallVec::new(),
|
||||
}
|
||||
|
@ -965,8 +966,8 @@ impl ProjectSearchView {
|
|||
editor
|
||||
});
|
||||
let results_editor = cx.add_view(|cx| {
|
||||
let mut editor = Editor::for_multibuffer(excerpts, Some(project.clone()), cx);
|
||||
editor.set_searchable(false);
|
||||
let mut editor = FollowableEditor::for_multibuffer(excerpts, project.clone(), cx);
|
||||
editor.0.update(cx, |this, _| this.set_searchable(false));
|
||||
editor
|
||||
});
|
||||
cx.observe(&results_editor, |_, _, cx| cx.emit(ViewEvent::UpdateTab))
|
||||
|
@ -1100,7 +1101,7 @@ impl ProjectSearchView {
|
|||
.or_else(|| workspace.item_of_type::<ProjectSearchView>(cx));
|
||||
|
||||
let query = workspace.active_item(cx).and_then(|item| {
|
||||
let editor = item.act_as::<Editor>(cx)?;
|
||||
let editor = item.act_as::<FollowableEditor>(cx)?;
|
||||
let query = editor.query_suggestion(cx);
|
||||
if query.is_empty() {
|
||||
None
|
||||
|
@ -1246,11 +1247,14 @@ impl ProjectSearchView {
|
|||
|
||||
let range_to_select = match_ranges[new_index].clone();
|
||||
self.results_editor.update(cx, |editor, cx| {
|
||||
let range_to_select = editor.range_for_match(&range_to_select);
|
||||
editor.unfold_ranges([range_to_select.clone()], false, true, cx);
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select_ranges([range_to_select])
|
||||
});
|
||||
editor.0.update(cx, |this, cx| {
|
||||
let range_to_select = this.range_for_match(&range_to_select);
|
||||
this.unfold_ranges([range_to_select.clone()], false, true, cx);
|
||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select_ranges([range_to_select])
|
||||
});
|
||||
})
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1291,16 +1295,16 @@ impl ProjectSearchView {
|
|||
let range_to_select = match_ranges
|
||||
.first()
|
||||
.clone()
|
||||
.map(|range| editor.range_for_match(range));
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
.map(|range| editor.0.update(cx, |this, cx| this.range_for_match(range)));
|
||||
editor.0.update(cx, |this, cx| this.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select_ranges(range_to_select)
|
||||
});
|
||||
}));
|
||||
}
|
||||
editor.highlight_background::<Self>(
|
||||
editor.0.update(cx, |this, cx| this.highlight_background::<Self>(
|
||||
match_ranges,
|
||||
|theme| theme.search.match_background,
|
||||
cx,
|
||||
);
|
||||
));
|
||||
});
|
||||
if is_new_search && self.query_editor.is_focused(cx) {
|
||||
self.focus_results_editor(cx);
|
||||
|
@ -1315,8 +1319,8 @@ impl ProjectSearchView {
|
|||
let results_editor = self.results_editor.read(cx);
|
||||
let new_index = active_match_index(
|
||||
&self.model.read(cx).match_ranges,
|
||||
&results_editor.selections.newest_anchor().head(),
|
||||
&results_editor.buffer().read(cx).snapshot(cx),
|
||||
&results_editor.0.read(cx).selections.newest_anchor().head(),
|
||||
&results_editor.0.read(cx).buffer().read(cx).snapshot(cx),
|
||||
);
|
||||
if self.active_match_index != new_index {
|
||||
self.active_match_index = new_index;
|
||||
|
|
|
@ -14,6 +14,7 @@ collections = { path = "../collections" }
|
|||
gpui = { path = "../gpui" }
|
||||
language = { path = "../language" }
|
||||
project = { path = "../project" }
|
||||
project_types = { path = "../project_types" }
|
||||
workspace = { path = "../workspace" }
|
||||
util = { path = "../util" }
|
||||
picker = { path = "../picker" }
|
||||
|
|
|
@ -21,7 +21,8 @@ use ordered_float::OrderedFloat;
|
|||
use parking_lot::Mutex;
|
||||
use parsing::{CodeContextRetriever, Span, SpanDigest, PARSEABLE_ENTIRE_FILE_TYPES};
|
||||
use postage::watch;
|
||||
use project::{Fs, PathChange, Project, ProjectEntryId, Worktree, WorktreeId};
|
||||
use project::{Fs, PathChange, Project, ProjectEntryId, Worktree};
|
||||
use project_types::WorktreeId;
|
||||
use smol::channel;
|
||||
use std::{
|
||||
cmp::Reverse,
|
||||
|
|
|
@ -32,10 +32,12 @@ language = { path = "../language" }
|
|||
menu = { path = "../menu" }
|
||||
node_runtime = { path = "../node_runtime" }
|
||||
project = { path = "../project" }
|
||||
project_types = {path = "../project_types"}
|
||||
settings = { path = "../settings" }
|
||||
terminal = { path = "../terminal" }
|
||||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
workspace_types = {path = "../workspace_types"}
|
||||
|
||||
async-recursion = "1.0.0"
|
||||
itertools = "0.10"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
pane, persistence::model::ItemId, searchable::SearchableItemHandle, FollowableItemBuilders,
|
||||
ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
|
||||
pane, searchable::SearchableItemHandle, FollowableItemBuilders, ItemNavHistory, Pane,
|
||||
ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
|
||||
};
|
||||
use crate::{AutosaveSetting, DelayedDebouncedEditAction, WorkspaceSettings};
|
||||
use anyhow::Result;
|
||||
|
@ -14,7 +14,8 @@ use gpui::{
|
|||
fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, ModelHandle, Task, View,
|
||||
ViewContext, ViewHandle, WeakViewHandle, WindowContext,
|
||||
};
|
||||
use project::{Project, ProjectEntryId, ProjectPath};
|
||||
use project::{Project, ProjectEntryId};
|
||||
use project_types::ProjectPath;
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use settings::Setting;
|
||||
|
@ -34,6 +35,7 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
use theme::Theme;
|
||||
use workspace_types::ItemId;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ItemSettings {
|
||||
|
|
|
@ -28,7 +28,8 @@ use gpui::{
|
|||
ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||
WindowContext,
|
||||
};
|
||||
use project::{Project, ProjectEntryId, ProjectPath};
|
||||
use project::{Project, ProjectEntryId};
|
||||
use project_types::ProjectPath;
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
any::Any,
|
||||
|
@ -1963,20 +1964,26 @@ impl View for Pane {
|
|||
}
|
||||
}
|
||||
|
||||
impl ItemNavHistory {
|
||||
pub fn push<D: 'static + Any>(&mut self, data: Option<D>, cx: &mut WindowContext) {
|
||||
impl workspace_types::NavigationHistorySink for ItemNavHistory {
|
||||
fn push_any(&mut self, data: Option<Box<dyn Any>>, cx: &mut WindowContext) {
|
||||
self.history.push(data, self.item.clone(), cx);
|
||||
}
|
||||
|
||||
pub fn pop_backward(&mut self, cx: &mut WindowContext) -> Option<NavigationEntry> {
|
||||
self.history.pop(NavigationMode::GoingBack, cx)
|
||||
}
|
||||
|
||||
pub fn pop_forward(&mut self, cx: &mut WindowContext) -> Option<NavigationEntry> {
|
||||
self.history.pop(NavigationMode::GoingForward, cx)
|
||||
}
|
||||
}
|
||||
|
||||
trait NavigationHistorySource {
|
||||
fn pop_forward(&mut self, cx: &mut WindowContext) -> Option<NavigationEntry>;
|
||||
fn pop_backward(&mut self, cx: &mut WindowContext) -> Option<NavigationEntry>;
|
||||
}
|
||||
|
||||
impl NavigationHistorySource for ItemNavHistory {
|
||||
fn pop_forward(&mut self, cx: &mut WindowContext) -> Option<NavigationEntry> {
|
||||
self.history.pop(NavigationMode::GoingForward, cx)
|
||||
}
|
||||
|
||||
fn pop_backward(&mut self, cx: &mut WindowContext) -> Option<NavigationEntry> {
|
||||
self.history.pop(NavigationMode::GoingBack, cx)
|
||||
}
|
||||
}
|
||||
impl NavHistory {
|
||||
pub fn for_each_entry(
|
||||
&self,
|
||||
|
@ -2035,9 +2042,9 @@ impl NavHistory {
|
|||
entry
|
||||
}
|
||||
|
||||
pub fn push<D: 'static + Any>(
|
||||
pub fn push(
|
||||
&mut self,
|
||||
data: Option<D>,
|
||||
data: Option<Box<dyn Any>>,
|
||||
item: Rc<dyn WeakItemHandle>,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
|
@ -2050,7 +2057,7 @@ impl NavHistory {
|
|||
}
|
||||
state.backward_stack.push_back(NavigationEntry {
|
||||
item,
|
||||
data: data.map(|data| Box::new(data) as Box<dyn Any>),
|
||||
data,
|
||||
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
||||
});
|
||||
state.forward_stack.clear();
|
||||
|
@ -2061,7 +2068,7 @@ impl NavHistory {
|
|||
}
|
||||
state.forward_stack.push_back(NavigationEntry {
|
||||
item,
|
||||
data: data.map(|data| Box::new(data) as Box<dyn Any>),
|
||||
data,
|
||||
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
||||
});
|
||||
}
|
||||
|
@ -2071,7 +2078,7 @@ impl NavHistory {
|
|||
}
|
||||
state.backward_stack.push_back(NavigationEntry {
|
||||
item,
|
||||
data: data.map(|data| Box::new(data) as Box<dyn Any>),
|
||||
data,
|
||||
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
||||
});
|
||||
}
|
||||
|
@ -2081,7 +2088,7 @@ impl NavHistory {
|
|||
}
|
||||
state.closed_stack.push_back(NavigationEntry {
|
||||
item,
|
||||
data: data.map(|data| Box::new(data) as Box<dyn Any>),
|
||||
data,
|
||||
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ use gpui::{
|
|||
AnyViewHandle, Axis, ModelHandle, ViewContext, ViewHandle,
|
||||
};
|
||||
use project::Project;
|
||||
use serde::Deserialize;
|
||||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||
use theme::Theme;
|
||||
use workspace_types::SplitDirection;
|
||||
|
||||
const HANDLE_HITBOX_SIZE: f32 = 4.0;
|
||||
const HORIZONTAL_MIN_SIZE: f32 = 80.;
|
||||
|
@ -534,59 +534,6 @@ impl PaneAxis {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
||||
pub enum SplitDirection {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl SplitDirection {
|
||||
pub fn all() -> [Self; 4] {
|
||||
[Self::Up, Self::Down, Self::Left, Self::Right]
|
||||
}
|
||||
|
||||
pub fn edge(&self, rect: RectF) -> f32 {
|
||||
match self {
|
||||
Self::Up => rect.min_y(),
|
||||
Self::Down => rect.max_y(),
|
||||
Self::Left => rect.min_x(),
|
||||
Self::Right => rect.max_x(),
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a new rectangle which shares an edge in SplitDirection and has `size` along SplitDirection
|
||||
pub fn along_edge(&self, rect: RectF, size: f32) -> RectF {
|
||||
match self {
|
||||
Self::Up => RectF::new(rect.origin(), Vector2F::new(rect.width(), size)),
|
||||
Self::Down => RectF::new(
|
||||
rect.lower_left() - Vector2F::new(0., size),
|
||||
Vector2F::new(rect.width(), size),
|
||||
),
|
||||
Self::Left => RectF::new(rect.origin(), Vector2F::new(size, rect.height())),
|
||||
Self::Right => RectF::new(
|
||||
rect.upper_right() - Vector2F::new(size, 0.),
|
||||
Vector2F::new(size, rect.height()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn axis(&self) -> Axis {
|
||||
match self {
|
||||
Self::Up | Self::Down => Axis::Vertical,
|
||||
Self::Left | Self::Right => Axis::Horizontal,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increasing(&self) -> bool {
|
||||
match self {
|
||||
Self::Left | Self::Up => false,
|
||||
Self::Down | Self::Right => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod element {
|
||||
use std::{cell::RefCell, iter::from_fn, ops::Range, rc::Rc};
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ use std::{
|
|||
};
|
||||
use util::ResultExt;
|
||||
use uuid::Uuid;
|
||||
use workspace_types::ItemId;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct WorkspaceLocation(Arc<Vec<PathBuf>>);
|
||||
|
@ -284,7 +285,6 @@ impl SerializedPane {
|
|||
|
||||
pub type GroupId = i64;
|
||||
pub type PaneId = i64;
|
||||
pub type ItemId = usize;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct SerializedItem {
|
||||
|
|
|
@ -17,6 +17,7 @@ use std::{
|
|||
borrow::Cow,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
use workspace_types::NavigationHistorySink;
|
||||
|
||||
pub enum Event {
|
||||
Close,
|
||||
|
@ -100,7 +101,7 @@ impl Item for SharedScreen {
|
|||
}
|
||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if let Some(nav_history) = self.nav_history.as_mut() {
|
||||
nav_history.push::<()>(None, cx);
|
||||
nav_history.push_any(None, cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ use futures::{
|
|||
FutureExt, StreamExt,
|
||||
};
|
||||
use gpui::{
|
||||
actions,
|
||||
elements::*,
|
||||
geometry::{
|
||||
rect::RectF,
|
||||
|
@ -67,12 +66,10 @@ use notifications::{NotificationHandle, NotifyResultExt};
|
|||
pub use pane::*;
|
||||
pub use pane_group::*;
|
||||
use persistence::{model::SerializedItem, DB};
|
||||
pub use persistence::{
|
||||
model::{ItemId, WorkspaceLocation},
|
||||
WorkspaceDb, DB as WORKSPACE_DB,
|
||||
};
|
||||
pub use persistence::{model::WorkspaceLocation, WorkspaceDb, DB as WORKSPACE_DB};
|
||||
use postage::prelude::Stream;
|
||||
use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
|
||||
use project::{Project, ProjectEntryId, Worktree};
|
||||
use project_types::{ProjectPath, WorktreeId};
|
||||
use serde::Deserialize;
|
||||
use shared_screen::SharedScreen;
|
||||
use status_bar::StatusBar;
|
||||
|
@ -81,6 +78,7 @@ use theme::{Theme, ThemeSettings};
|
|||
pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
|
||||
use util::ResultExt;
|
||||
pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings};
|
||||
pub use workspace_types::*;
|
||||
|
||||
lazy_static! {
|
||||
static ref ZED_WINDOW_SIZE: Option<Vector2F> = env::var("ZED_WINDOW_SIZE")
|
||||
|
@ -116,36 +114,6 @@ impl<T: Modal> ModalHandle for ViewHandle<T> {
|
|||
#[derive(Clone, PartialEq)]
|
||||
pub struct RemoveWorktreeFromProject(pub WorktreeId);
|
||||
|
||||
actions!(
|
||||
workspace,
|
||||
[
|
||||
Open,
|
||||
NewFile,
|
||||
NewWindow,
|
||||
CloseWindow,
|
||||
CloseInactiveTabsAndPanes,
|
||||
AddFolderToProject,
|
||||
Unfollow,
|
||||
SaveAs,
|
||||
ReloadActiveItem,
|
||||
ActivatePreviousPane,
|
||||
ActivateNextPane,
|
||||
FollowNextCollaborator,
|
||||
NewTerminal,
|
||||
NewCenterTerminal,
|
||||
ToggleTerminalFocus,
|
||||
NewSearch,
|
||||
Feedback,
|
||||
Restart,
|
||||
Welcome,
|
||||
ToggleZoom,
|
||||
ToggleLeftDock,
|
||||
ToggleRightDock,
|
||||
ToggleBottomDock,
|
||||
CloseAllDocks,
|
||||
]
|
||||
);
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct OpenPaths {
|
||||
pub paths: Vec<PathBuf>,
|
||||
|
@ -246,8 +214,6 @@ impl_actions!(
|
|||
]
|
||||
);
|
||||
|
||||
pub type WorkspaceId = i64;
|
||||
|
||||
pub fn init_settings(cx: &mut AppContext) {
|
||||
settings::register::<WorkspaceSettings>(cx);
|
||||
settings::register::<item::ItemSettings>(cx);
|
||||
|
@ -598,12 +564,6 @@ struct ActiveModal {
|
|||
previously_focused_view_id: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ViewId {
|
||||
pub creator: PeerId,
|
||||
pub id: u64,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct FollowerState {
|
||||
leader_id: PeerId,
|
||||
|
@ -4071,24 +4031,6 @@ impl Entity for WorkspaceStore {
|
|||
type Event = ();
|
||||
}
|
||||
|
||||
impl ViewId {
|
||||
pub(crate) fn from_proto(message: proto::ViewId) -> Result<Self> {
|
||||
Ok(Self {
|
||||
creator: message
|
||||
.creator
|
||||
.ok_or_else(|| anyhow!("creator is missing"))?,
|
||||
id: message.id,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn to_proto(&self) -> proto::ViewId {
|
||||
proto::ViewId {
|
||||
creator: Some(self.creator),
|
||||
id: self.id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WorkspaceHandle {
|
||||
fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath>;
|
||||
}
|
||||
|
|
16
crates/workspace_types/Cargo.toml
Normal file
16
crates/workspace_types/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "workspace_types"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
gpui = {path = "../gpui"}
|
||||
text = {path = "../text"}
|
||||
lsp = {path = "../lsp"}
|
||||
rpc = {path = "../rpc"}
|
||||
language = {path = "../language"}
|
||||
project_types = {path = "../project_types"}
|
||||
serde.workspace = true
|
131
crates/workspace_types/src/lib.rs
Normal file
131
crates/workspace_types/src/lib.rs
Normal file
|
@ -0,0 +1,131 @@
|
|||
use std::any::Any;
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use gpui::{
|
||||
actions,
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
Axis, WindowContext,
|
||||
};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
actions!(
|
||||
workspace,
|
||||
[
|
||||
Open,
|
||||
NewFile,
|
||||
NewWindow,
|
||||
CloseWindow,
|
||||
CloseInactiveTabsAndPanes,
|
||||
AddFolderToProject,
|
||||
Unfollow,
|
||||
SaveAs,
|
||||
ReloadActiveItem,
|
||||
ActivatePreviousPane,
|
||||
ActivateNextPane,
|
||||
FollowNextCollaborator,
|
||||
NewTerminal,
|
||||
NewCenterTerminal,
|
||||
ToggleTerminalFocus,
|
||||
NewSearch,
|
||||
Feedback,
|
||||
Restart,
|
||||
Welcome,
|
||||
ToggleZoom,
|
||||
ToggleLeftDock,
|
||||
ToggleRightDock,
|
||||
ToggleBottomDock,
|
||||
CloseAllDocks,
|
||||
]
|
||||
);
|
||||
pub type WorkspaceId = i64;
|
||||
|
||||
pub type ItemId = usize;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
||||
pub enum SplitDirection {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl SplitDirection {
|
||||
pub fn all() -> [Self; 4] {
|
||||
[Self::Up, Self::Down, Self::Left, Self::Right]
|
||||
}
|
||||
|
||||
pub fn edge(&self, rect: RectF) -> f32 {
|
||||
match self {
|
||||
Self::Up => rect.min_y(),
|
||||
Self::Down => rect.max_y(),
|
||||
Self::Left => rect.min_x(),
|
||||
Self::Right => rect.max_x(),
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a new rectangle which shares an edge in SplitDirection and has `size` along SplitDirection
|
||||
pub fn along_edge(&self, rect: RectF, size: f32) -> RectF {
|
||||
match self {
|
||||
Self::Up => RectF::new(rect.origin(), Vector2F::new(rect.width(), size)),
|
||||
Self::Down => RectF::new(
|
||||
rect.lower_left() - Vector2F::new(0., size),
|
||||
Vector2F::new(rect.width(), size),
|
||||
),
|
||||
Self::Left => RectF::new(rect.origin(), Vector2F::new(size, rect.height())),
|
||||
Self::Right => RectF::new(
|
||||
rect.upper_right() - Vector2F::new(size, 0.),
|
||||
Vector2F::new(size, rect.height()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn axis(&self) -> Axis {
|
||||
match self {
|
||||
Self::Up | Self::Down => Axis::Vertical,
|
||||
Self::Left | Self::Right => Axis::Horizontal,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increasing(&self) -> bool {
|
||||
match self {
|
||||
Self::Left | Self::Up => false,
|
||||
Self::Down | Self::Right => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ViewId {
|
||||
pub creator: rpc::proto::PeerId,
|
||||
pub id: u64,
|
||||
}
|
||||
|
||||
impl ViewId {
|
||||
pub fn from_proto(message: rpc::proto::ViewId) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
creator: message
|
||||
.creator
|
||||
.ok_or_else(|| anyhow!("creator is missing"))?,
|
||||
id: message.id,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_proto(&self) -> rpc::proto::ViewId {
|
||||
rpc::proto::ViewId {
|
||||
creator: Some(self.creator),
|
||||
id: self.id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NavigationHistorySink {
|
||||
fn push_any(&mut self, data: Option<Box<dyn Any>>, cx: &mut WindowContext);
|
||||
}
|
||||
|
||||
impl dyn NavigationHistorySink {
|
||||
pub fn push<D: 'static + Any>(&mut self, data: Option<D>, cx: &mut WindowContext) {
|
||||
self.push_any(data.map(|data| Box::new(data) as _), cx)
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@ copilot_button = { path = "../copilot_button" }
|
|||
diagnostics = { path = "../diagnostics" }
|
||||
db = { path = "../db" }
|
||||
editor = { path = "../editor" }
|
||||
editor_extensions = { path = "../editor_extensions" }
|
||||
feedback = { path = "../feedback" }
|
||||
file_finder = { path = "../file_finder" }
|
||||
search = { path = "../search" }
|
||||
|
|
|
@ -93,7 +93,7 @@ fn main() {
|
|||
if cx.has_global::<Weak<AppState>>() {
|
||||
if let Some(app_state) = cx.global::<Weak<AppState>>().upgrade() {
|
||||
workspace::open_new(&app_state, cx, |workspace, cx| {
|
||||
Editor::new_file(workspace, &Default::default(), cx)
|
||||
editor_extensions::new_file(workspace, &Default::default(), cx)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
@ -343,7 +343,7 @@ async fn restore_or_create_workspace(app_state: &Arc<AppState>, mut cx: AsyncApp
|
|||
} else {
|
||||
cx.update(|cx| {
|
||||
workspace::open_new(app_state, cx, |workspace, cx| {
|
||||
Editor::new_file(workspace, &Default::default(), cx)
|
||||
editor_extensions::new_file(workspace, &Default::default(), cx)
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
|
|
|
@ -15,6 +15,7 @@ use collab_ui::CollabTitlebarItem; // TODO: Add back toggle collab ui shortcut
|
|||
use collections::VecDeque;
|
||||
pub use editor;
|
||||
use editor::{Editor, MultiBuffer};
|
||||
use editor_extensions::FollowableEditor;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use feedback::{
|
||||
|
@ -195,7 +196,11 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
|||
});
|
||||
workspace.add_item(
|
||||
Box::new(cx.add_view(|cx| {
|
||||
Editor::for_multibuffer(buffer, Some(project.clone()), cx)
|
||||
FollowableEditor::for_multibuffer(
|
||||
buffer,
|
||||
project.clone(),
|
||||
cx,
|
||||
)
|
||||
})),
|
||||
cx,
|
||||
);
|
||||
|
@ -246,7 +251,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
|||
move |_: &NewWindow, cx: &mut AppContext| {
|
||||
if let Some(app_state) = app_state.upgrade() {
|
||||
open_new(&app_state, cx, |workspace, cx| {
|
||||
Editor::new_file(workspace, &Default::default(), cx)
|
||||
editor_extensions::new_file(workspace, &Default::default(), cx)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
@ -257,7 +262,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
|||
move |_: &NewFile, cx: &mut AppContext| {
|
||||
if let Some(app_state) = app_state.upgrade() {
|
||||
open_new(&app_state, cx, |workspace, cx| {
|
||||
Editor::new_file(workspace, &Default::default(), cx)
|
||||
editor_extensions::new_file(workspace, &Default::default(), cx)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
@ -332,7 +337,7 @@ pub fn initialize_workspace(
|
|||
let feedback_button = cx.add_view(|_| {
|
||||
feedback::deploy_feedback_button::DeployFeedbackButton::new(workspace)
|
||||
});
|
||||
let cursor_position = cx.add_view(|_| editor::items::CursorPosition::new());
|
||||
let cursor_position = cx.add_view(|_| editor_extensions::CursorPosition::new());
|
||||
workspace.status_bar().update(cx, |status_bar, cx| {
|
||||
status_bar.add_left_item(diagnostic_summary, cx);
|
||||
status_bar.add_left_item(activity_indicator, cx);
|
||||
|
@ -535,11 +540,9 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
|
|||
MultiBuffer::singleton(buffer, cx).with_title("Log".into())
|
||||
});
|
||||
workspace.add_item(
|
||||
Box::new(
|
||||
cx.add_view(|cx| {
|
||||
Editor::for_multibuffer(buffer, Some(project), cx)
|
||||
}),
|
||||
),
|
||||
Box::new(cx.add_view(|cx| {
|
||||
FollowableEditor::for_multibuffer(buffer, project, cx)
|
||||
})),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
|
@ -706,7 +709,7 @@ fn open_telemetry_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Works
|
|||
MultiBuffer::singleton(buffer, cx).with_title("Telemetry Log".into())
|
||||
});
|
||||
workspace.add_item(
|
||||
Box::new(cx.add_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx))),
|
||||
Box::new(cx.add_view(|cx| FollowableEditor::for_multibuffer(buffer, project, cx))),
|
||||
cx,
|
||||
);
|
||||
}).log_err()?;
|
||||
|
@ -741,7 +744,7 @@ fn open_bundled_file(
|
|||
});
|
||||
workspace.add_item(
|
||||
Box::new(cx.add_view(|cx| {
|
||||
Editor::for_multibuffer(buffer, Some(project.clone()), cx)
|
||||
FollowableEditor::for_multibuffer(buffer, project.clone(), cx)
|
||||
})),
|
||||
cx,
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue