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 = [
|
dependencies = [
|
||||||
"auto_update",
|
"auto_update",
|
||||||
"editor",
|
"editor",
|
||||||
|
"editor_extensions",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
"gpui",
|
"gpui",
|
||||||
"language",
|
"language",
|
||||||
|
@ -328,6 +329,7 @@ dependencies = [
|
||||||
"collections",
|
"collections",
|
||||||
"ctor",
|
"ctor",
|
||||||
"editor",
|
"editor",
|
||||||
|
"editor_extensions",
|
||||||
"env_logger 0.9.3",
|
"env_logger 0.9.3",
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
|
@ -1725,6 +1727,7 @@ dependencies = [
|
||||||
"db",
|
"db",
|
||||||
"drag_and_drop",
|
"drag_and_drop",
|
||||||
"editor",
|
"editor",
|
||||||
|
"editor_extensions",
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
"feedback",
|
"feedback",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
|
@ -2459,6 +2462,7 @@ dependencies = [
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
"editor",
|
"editor",
|
||||||
|
"editor_extensions",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
"gpui",
|
"gpui",
|
||||||
"language",
|
"language",
|
||||||
|
@ -2466,6 +2470,7 @@ dependencies = [
|
||||||
"lsp",
|
"lsp",
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
|
"project_types",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
|
@ -2612,6 +2617,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"async-trait",
|
||||||
"client",
|
"client",
|
||||||
"clock",
|
"clock",
|
||||||
"collections",
|
"collections",
|
||||||
|
@ -2637,6 +2643,7 @@ dependencies = [
|
||||||
"parking_lot 0.11.2",
|
"parking_lot 0.11.2",
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
|
"project_types",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rich_text",
|
"rich_text",
|
||||||
"rpc",
|
"rpc",
|
||||||
|
@ -2658,6 +2665,7 @@ dependencies = [
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
"workspace_types",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2714,6 +2722,31 @@ dependencies = [
|
||||||
"workspace2",
|
"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]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
|
@ -2910,6 +2943,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"client",
|
"client",
|
||||||
"editor",
|
"editor",
|
||||||
|
"editor_extensions",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
"gpui",
|
"gpui",
|
||||||
"human_bytes",
|
"human_bytes",
|
||||||
|
@ -2950,6 +2984,7 @@ dependencies = [
|
||||||
"collections",
|
"collections",
|
||||||
"ctor",
|
"ctor",
|
||||||
"editor",
|
"editor",
|
||||||
|
"editor_extensions",
|
||||||
"env_logger 0.9.3",
|
"env_logger 0.9.3",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
@ -2958,6 +2993,7 @@ dependencies = [
|
||||||
"picker",
|
"picker",
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
|
"project_types",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"settings",
|
"settings",
|
||||||
"text",
|
"text",
|
||||||
|
@ -4521,6 +4557,7 @@ dependencies = [
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
"editor",
|
"editor",
|
||||||
|
"editor_extensions",
|
||||||
"env_logger 0.9.3",
|
"env_logger 0.9.3",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
@ -6356,6 +6393,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"prettier",
|
"prettier",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
|
"project_types",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"regex",
|
"regex",
|
||||||
"rpc",
|
"rpc",
|
||||||
|
@ -6450,6 +6488,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"project",
|
"project",
|
||||||
|
"project_types",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
|
@ -6467,6 +6506,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"editor",
|
"editor",
|
||||||
|
"editor_extensions",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
@ -6476,6 +6516,7 @@ dependencies = [
|
||||||
"picker",
|
"picker",
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
|
"project_types",
|
||||||
"settings",
|
"settings",
|
||||||
"smol",
|
"smol",
|
||||||
"text",
|
"text",
|
||||||
|
@ -6484,6 +6525,23 @@ dependencies = [
|
||||||
"workspace",
|
"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]]
|
[[package]]
|
||||||
name = "prometheus"
|
name = "prometheus"
|
||||||
version = "0.13.3"
|
version = "0.13.3"
|
||||||
|
@ -7671,6 +7729,7 @@ dependencies = [
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
"editor",
|
"editor",
|
||||||
|
"editor_extensions",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
"gpui",
|
"gpui",
|
||||||
"language",
|
"language",
|
||||||
|
@ -7740,6 +7799,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"project",
|
"project",
|
||||||
|
"project_types",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rpc",
|
"rpc",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
|
@ -10914,6 +10974,7 @@ dependencies = [
|
||||||
"parking_lot 0.11.2",
|
"parking_lot 0.11.2",
|
||||||
"postage",
|
"postage",
|
||||||
"project",
|
"project",
|
||||||
|
"project_types",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
|
@ -10924,6 +10985,7 @@ dependencies = [
|
||||||
"theme",
|
"theme",
|
||||||
"util",
|
"util",
|
||||||
"uuid 1.4.1",
|
"uuid 1.4.1",
|
||||||
|
"workspace_types",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -10964,6 +11026,20 @@ dependencies = [
|
||||||
"uuid 1.4.1",
|
"uuid 1.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "workspace_types"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"gpui",
|
||||||
|
"language",
|
||||||
|
"lsp",
|
||||||
|
"project_types",
|
||||||
|
"rpc",
|
||||||
|
"serde",
|
||||||
|
"text",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ws2_32-sys"
|
name = "ws2_32-sys"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -11075,6 +11151,7 @@ dependencies = [
|
||||||
"db",
|
"db",
|
||||||
"diagnostics",
|
"diagnostics",
|
||||||
"editor",
|
"editor",
|
||||||
|
"editor_extensions",
|
||||||
"env_logger 0.9.3",
|
"env_logger 0.9.3",
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
"feedback",
|
"feedback",
|
||||||
|
|
|
@ -31,6 +31,7 @@ members = [
|
||||||
"crates/diagnostics",
|
"crates/diagnostics",
|
||||||
"crates/drag_and_drop",
|
"crates/drag_and_drop",
|
||||||
"crates/editor",
|
"crates/editor",
|
||||||
|
"crates/editor_extensions",
|
||||||
"crates/feature_flags",
|
"crates/feature_flags",
|
||||||
"crates/feature_flags2",
|
"crates/feature_flags2",
|
||||||
"crates/feedback",
|
"crates/feedback",
|
||||||
|
@ -76,6 +77,7 @@ members = [
|
||||||
"crates/project2",
|
"crates/project2",
|
||||||
"crates/project_panel",
|
"crates/project_panel",
|
||||||
"crates/project_symbols",
|
"crates/project_symbols",
|
||||||
|
"crates/project_types",
|
||||||
"crates/recent_projects",
|
"crates/recent_projects",
|
||||||
"crates/rope",
|
"crates/rope",
|
||||||
"crates/rpc",
|
"crates/rpc",
|
||||||
|
|
|
@ -11,6 +11,7 @@ doctest = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
auto_update = { path = "../auto_update" }
|
auto_update = { path = "../auto_update" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
|
editor_extensions = { path = "../editor_extensions" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use auto_update::{AutoUpdateStatus, AutoUpdater, DismissErrorMessage};
|
use auto_update::{AutoUpdateStatus, AutoUpdater, DismissErrorMessage};
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
|
use editor_extensions::FollowableEditor;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, anyhow,
|
actions, anyhow,
|
||||||
|
@ -103,9 +104,9 @@ impl ActivityIndicator {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
workspace.add_item(
|
workspace.add_item(
|
||||||
Box::new(
|
Box::new(cx.add_view(|cx| {
|
||||||
cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)),
|
FollowableEditor::for_buffer(buffer, project.clone(), cx)
|
||||||
),
|
})),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ ai = { path = "../ai" }
|
||||||
client = { path = "../client" }
|
client = { path = "../client" }
|
||||||
collections = { path = "../collections"}
|
collections = { path = "../collections"}
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
|
editor_extensions = { path = "../editor_extensions" }
|
||||||
fs = { path = "../fs" }
|
fs = { path = "../fs" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
|
|
|
@ -24,6 +24,7 @@ use editor::{
|
||||||
scroll::autoscroll::{Autoscroll, AutoscrollStrategy},
|
scroll::autoscroll::{Autoscroll, AutoscrollStrategy},
|
||||||
Anchor, Editor, MoveDown, MoveUp, MultiBufferSnapshot, ToOffset, ToPoint,
|
Anchor, Editor, MoveDown, MoveUp, MultiBufferSnapshot, ToOffset, ToPoint,
|
||||||
};
|
};
|
||||||
|
use editor_extensions::FollowableEditor;
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -2196,7 +2197,7 @@ struct ConversationEditor {
|
||||||
conversation: ModelHandle<Conversation>,
|
conversation: ModelHandle<Conversation>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
workspace: WeakViewHandle<Workspace>,
|
workspace: WeakViewHandle<Workspace>,
|
||||||
editor: ViewHandle<Editor>,
|
editor: ViewHandle<FollowableEditor>,
|
||||||
blocks: HashSet<BlockId>,
|
blocks: HashSet<BlockId>,
|
||||||
scroll_position: Option<ScrollPosition>,
|
scroll_position: Option<ScrollPosition>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
|
@ -2222,10 +2223,11 @@ impl ConversationEditor {
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let editor = cx.add_view(|cx| {
|
let editor = cx.add_view(|cx| {
|
||||||
let mut editor = Editor::for_buffer(conversation.read(cx).buffer.clone(), None, cx);
|
let mut editor = FollowableEditor::for_raw_buffer(conversation.read(cx).buffer.clone(), cx);
|
||||||
editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
|
editor.0.update(cx, |this, cx| {this.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
|
||||||
editor.set_show_gutter(false, cx);
|
this.set_show_gutter(false, cx);
|
||||||
editor.set_show_wrap_guides(false, cx);
|
this.set_show_wrap_guides(false, cx);
|
||||||
|
});
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2277,11 +2279,11 @@ impl ConversationEditor {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if !new_selections.is_empty() {
|
if !new_selections.is_empty() {
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
editor.change_selections(
|
editor.0.update(cx, |this, cx| this.change_selections(
|
||||||
Some(Autoscroll::Strategy(AutoscrollStrategy::Fit)),
|
Some(Autoscroll::Strategy(AutoscrollStrategy::Fit)),
|
||||||
cx,
|
cx,
|
||||||
|selections| selections.select_ranges(new_selections),
|
|selections| selections.select_ranges(new_selections),
|
||||||
);
|
));
|
||||||
});
|
});
|
||||||
// Avoid scrolling to the new cursor position so the assistant's output is stable.
|
// Avoid scrolling to the new cursor position so the assistant's output is stable.
|
||||||
cx.defer(|this, _| this.scroll_position = None);
|
cx.defer(|this, _| this.scroll_position = None);
|
||||||
|
@ -2310,7 +2312,7 @@ impl ConversationEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursors(&self, cx: &AppContext) -> Vec<usize> {
|
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
|
selections
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|selection| selection.head())
|
.map(|selection| selection.head())
|
||||||
|
@ -2339,14 +2341,14 @@ impl ConversationEditor {
|
||||||
ConversationEvent::StreamedCompletion => {
|
ConversationEvent::StreamedCompletion => {
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
if let Some(scroll_position) = self.scroll_position {
|
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 cursor_point = scroll_position.cursor.to_display_point(&snapshot);
|
||||||
let scroll_top =
|
let scroll_top =
|
||||||
cursor_point.row() as f32 - scroll_position.offset_before_cursor.y();
|
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),
|
vec2f(scroll_position.offset_before_cursor.x(), scroll_top),
|
||||||
cx,
|
cx,
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2355,7 +2357,7 @@ impl ConversationEditor {
|
||||||
|
|
||||||
fn handle_editor_event(
|
fn handle_editor_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: ViewHandle<Editor>,
|
_: ViewHandle<FollowableEditor>,
|
||||||
event: &editor::Event,
|
event: &editor::Event,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
|
@ -2377,15 +2379,15 @@ impl ConversationEditor {
|
||||||
|
|
||||||
fn cursor_scroll_position(&self, cx: &mut ViewContext<Self>) -> Option<ScrollPosition> {
|
fn cursor_scroll_position(&self, cx: &mut ViewContext<Self>) -> Option<ScrollPosition> {
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
let snapshot = editor.snapshot(cx);
|
let snapshot = editor.0.update(cx, |this, cx| this.snapshot(cx));
|
||||||
let cursor = editor.selections.newest_anchor().head();
|
let cursor = editor.0.read(cx).selections.newest_anchor().head();
|
||||||
let cursor_row = cursor.to_display_point(&snapshot.display_snapshot).row() as f32;
|
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
|
.scroll_manager
|
||||||
.anchor()
|
.anchor()
|
||||||
.scroll_position(&snapshot.display_snapshot);
|
.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) {
|
if (scroll_position.y()..scroll_bottom).contains(&cursor_row) {
|
||||||
Some(ScrollPosition {
|
Some(ScrollPosition {
|
||||||
cursor,
|
cursor,
|
||||||
|
@ -2402,7 +2404,7 @@ impl ConversationEditor {
|
||||||
|
|
||||||
fn update_message_headers(&mut self, cx: &mut ViewContext<Self>) {
|
fn update_message_headers(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
self.editor.update(cx, |editor, cx| {
|
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 excerpt_id = *buffer.as_singleton().unwrap().0;
|
||||||
let old_blocks = std::mem::take(&mut self.blocks);
|
let old_blocks = std::mem::take(&mut self.blocks);
|
||||||
let new_blocks = self
|
let new_blocks = self
|
||||||
|
@ -2505,8 +2507,10 @@ impl ConversationEditor {
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
editor.remove_blocks(old_blocks, None, cx);
|
let ids = editor.0.update(cx, |this, cx| {
|
||||||
let ids = editor.insert_blocks(new_blocks, None, cx);
|
this.remove_blocks(old_blocks, None, cx);
|
||||||
|
this.insert_blocks(new_blocks, None, cx)
|
||||||
|
});
|
||||||
self.blocks = HashSet::from_iter(ids);
|
self.blocks = HashSet::from_iter(ids);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2568,14 +2572,14 @@ impl ConversationEditor {
|
||||||
conversation.update(cx, |conversation, cx| {
|
conversation.update(cx, |conversation, cx| {
|
||||||
conversation
|
conversation
|
||||||
.editor
|
.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>) {
|
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);
|
let conversation = self.conversation.read(cx);
|
||||||
if editor.selections.count() == 1 {
|
if editor.selections.count() == 1 {
|
||||||
let selection = editor.selections.newest::<usize>(cx);
|
let selection = editor.selections.newest::<usize>(cx);
|
||||||
|
@ -2610,9 +2614,9 @@ impl ConversationEditor {
|
||||||
|
|
||||||
fn split(&mut self, _: &Split, cx: &mut ViewContext<Self>) {
|
fn split(&mut self, _: &Split, cx: &mut ViewContext<Self>) {
|
||||||
self.conversation.update(cx, |conversation, cx| {
|
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() {
|
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
|
let range = selection
|
||||||
.map(|endpoint| endpoint.to_offset(&buffer))
|
.map(|endpoint| endpoint.to_offset(&buffer))
|
||||||
.range();
|
.range();
|
||||||
|
|
|
@ -32,6 +32,7 @@ collections = { path = "../collections" }
|
||||||
context_menu = { path = "../context_menu" }
|
context_menu = { path = "../context_menu" }
|
||||||
drag_and_drop = { path = "../drag_and_drop" }
|
drag_and_drop = { path = "../drag_and_drop" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
|
editor_extensions = { path = "../editor_extensions" }
|
||||||
feedback = { path = "../feedback" }
|
feedback = { path = "../feedback" }
|
||||||
fuzzy = { path = "../fuzzy" }
|
fuzzy = { path = "../fuzzy" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
|
|
|
@ -7,6 +7,7 @@ use client::{
|
||||||
};
|
};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use editor::{CollaborationHub, Editor};
|
use editor::{CollaborationHub, Editor};
|
||||||
|
use editor_extensions::FollowableEditor;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
elements::{ChildView, Label},
|
elements::{ChildView, Label},
|
||||||
|
@ -35,7 +36,7 @@ pub fn init(cx: &mut AppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChannelView {
|
pub struct ChannelView {
|
||||||
pub editor: ViewHandle<Editor>,
|
pub editor: ViewHandle<FollowableEditor>,
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
channel_store: ModelHandle<ChannelStore>,
|
channel_store: ModelHandle<ChannelStore>,
|
||||||
channel_buffer: ModelHandle<ChannelBuffer>,
|
channel_buffer: ModelHandle<ChannelBuffer>,
|
||||||
|
@ -137,16 +138,18 @@ impl ChannelView {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let buffer = channel_buffer.read(cx).buffer();
|
let buffer = channel_buffer.read(cx).buffer();
|
||||||
let editor = cx.add_view(|cx| {
|
let editor = cx.add_view(|cx| {
|
||||||
let mut editor = Editor::for_buffer(buffer, None, cx);
|
let mut editor = FollowableEditor::for_raw_buffer(buffer, cx);
|
||||||
editor.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
|
editor.0.update(cx, |this, cx| {
|
||||||
|
this.set_collaboration_hub(Box::new(ChannelBufferCollaborationHub(
|
||||||
channel_buffer.clone(),
|
channel_buffer.clone(),
|
||||||
)));
|
)));
|
||||||
editor.set_read_only(
|
this.set_read_only(
|
||||||
!channel_buffer
|
!channel_buffer
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.channel(cx)
|
.channel(cx)
|
||||||
.is_some_and(|c| c.can_edit_notes()),
|
.is_some_and(|c| c.can_edit_notes()),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
let _editor_event_subscription = cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone()));
|
let _editor_event_subscription = cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone()));
|
||||||
|
@ -176,12 +179,12 @@ impl ChannelView {
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
ChannelBufferEvent::Disconnected => self.editor.update(cx, |editor, cx| {
|
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();
|
cx.notify();
|
||||||
}),
|
}),
|
||||||
ChannelBufferEvent::ChannelChanged => {
|
ChannelBufferEvent::ChannelChanged => {
|
||||||
self.editor.update(cx, |editor, cx| {
|
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.emit(editor::Event::TitleChanged);
|
||||||
cx.notify()
|
cx.notify()
|
||||||
});
|
});
|
||||||
|
@ -320,7 +323,7 @@ impl Item for ChannelView {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
|
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 {
|
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 {
|
fn is_project_item(&self, _cx: &AppContext) -> bool {
|
||||||
|
|
|
@ -20,6 +20,7 @@ use context_menu::{ContextMenu, ContextMenuItem};
|
||||||
use db::kvp::KEY_VALUE_STORE;
|
use db::kvp::KEY_VALUE_STORE;
|
||||||
use drag_and_drop::{DragAndDrop, Draggable};
|
use drag_and_drop::{DragAndDrop, Draggable};
|
||||||
use editor::{Cancel, Editor};
|
use editor::{Cancel, Editor};
|
||||||
|
use editor_extensions::FollowableEditor;
|
||||||
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
|
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use fuzzy::{match_strings, StringMatchCandidate};
|
use fuzzy::{match_strings, StringMatchCandidate};
|
||||||
|
@ -274,7 +275,7 @@ pub struct CollabPanel {
|
||||||
pending_serialization: Task<Option<()>>,
|
pending_serialization: Task<Option<()>>,
|
||||||
context_menu: ViewHandle<ContextMenu>,
|
context_menu: ViewHandle<ContextMenu>,
|
||||||
filter_editor: ViewHandle<Editor>,
|
filter_editor: ViewHandle<Editor>,
|
||||||
channel_name_editor: ViewHandle<Editor>,
|
channel_name_editor: ViewHandle<FollowableEditor>,
|
||||||
channel_editing_state: Option<ChannelEditingState>,
|
channel_editing_state: Option<ChannelEditingState>,
|
||||||
entries: Vec<ListEntry>,
|
entries: Vec<ListEntry>,
|
||||||
selection: Option<usize>,
|
selection: Option<usize>,
|
||||||
|
@ -409,7 +410,7 @@ impl CollabPanel {
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
let channel_name_editor = cx.add_view(|cx| {
|
let channel_name_editor = cx.add_view(|cx| {
|
||||||
Editor::single_line(
|
FollowableEditor::single_line(
|
||||||
Some(Arc::new(|theme| {
|
Some(Arc::new(|theme| {
|
||||||
theme.collab_panel.user_query_editor.clone()
|
theme.collab_panel.user_query_editor.clone()
|
||||||
})),
|
})),
|
||||||
|
@ -1433,7 +1434,7 @@ impl CollabPanel {
|
||||||
fn take_editing_state(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
fn take_editing_state(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||||
if let Some(_) = self.channel_editing_state.take() {
|
if let Some(_) = self.channel_editing_state.take() {
|
||||||
self.channel_name_editor.update(cx, |editor, cx| {
|
self.channel_name_editor.update(cx, |editor, cx| {
|
||||||
editor.set_text("", cx);
|
editor.0.update(cx, |this, cx| this.set_text("", cx));
|
||||||
});
|
});
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
@ -2822,7 +2823,7 @@ impl CollabPanel {
|
||||||
fn insert_space(&mut self, _: &InsertSpace, cx: &mut ViewContext<Self>) {
|
fn insert_space(&mut self, _: &InsertSpace, cx: &mut ViewContext<Self>) {
|
||||||
if self.channel_editing_state.is_some() {
|
if self.channel_editing_state.is_some() {
|
||||||
self.channel_name_editor.update(cx, |editor, cx| {
|
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() {
|
if pending_name.is_some() {
|
||||||
return false;
|
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());
|
*pending_name = Some(channel_name.clone());
|
||||||
|
|
||||||
|
@ -2856,7 +2857,7 @@ impl CollabPanel {
|
||||||
if pending_name.is_some() {
|
if pending_name.is_some() {
|
||||||
return false;
|
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());
|
*pending_name = Some(channel_name.clone());
|
||||||
|
|
||||||
self.channel_store
|
self.channel_store
|
||||||
|
@ -3025,8 +3026,11 @@ impl CollabPanel {
|
||||||
pending_name: None,
|
pending_name: None,
|
||||||
});
|
});
|
||||||
self.channel_name_editor.update(cx, |editor, cx| {
|
self.channel_name_editor.update(cx, |editor, cx| {
|
||||||
editor.set_text(channel.name.clone(), cx);
|
editor.0.update(cx, |this, cx| {
|
||||||
editor.select_all(&Default::default(), cx);
|
this.set_text(channel.name.clone(), cx);
|
||||||
|
this.select_all(&Default::default(), cx);
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
cx.focus(self.channel_name_editor.as_any());
|
cx.focus(self.channel_name_editor.as_any());
|
||||||
self.update_entries(false, cx);
|
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_export]
|
||||||
macro_rules! define_connection {
|
macro_rules! define_connection {
|
||||||
(pub static ref $id:ident: $t:ident<()> = $migrations:expr;) => {
|
(pub static ref $id:ident: $t:ident<()> = $migrations:expr;) => {
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<$t>);
|
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<$t>);
|
||||||
|
|
||||||
impl ::std::ops::Deref for $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;) => {
|
(pub static ref $id:ident: $t:ident<$($d:ty),+> = $migrations:expr;) => {
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<( $($d),+, $t )>);
|
pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<( $($d),+, $t )>);
|
||||||
|
|
||||||
impl ::std::ops::Deref for $t {
|
impl ::std::ops::Deref for $t {
|
||||||
|
|
|
@ -11,10 +11,12 @@ doctest = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
|
editor_extensions = { path = "../editor_extensions" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
lsp = { path = "../lsp" }
|
lsp = { path = "../lsp" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
project_types = { path = "../project_types" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
|
@ -11,6 +11,7 @@ use editor::{
|
||||||
scroll::autoscroll::Autoscroll,
|
scroll::autoscroll::Autoscroll,
|
||||||
Editor, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
Editor, ExcerptId, ExcerptRange, MultiBuffer, ToOffset,
|
||||||
};
|
};
|
||||||
|
use editor_extensions::FollowableEditor;
|
||||||
use futures::future::try_join_all;
|
use futures::future::try_join_all;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, elements::*, fonts::TextStyle, serde_json, AnyViewHandle, AppContext, Entity,
|
actions, elements::*, fonts::TextStyle, serde_json, AnyViewHandle, AppContext, Entity,
|
||||||
|
@ -21,8 +22,9 @@ use language::{
|
||||||
SelectionGoal,
|
SelectionGoal,
|
||||||
};
|
};
|
||||||
use lsp::LanguageServerId;
|
use lsp::LanguageServerId;
|
||||||
use project::{DiagnosticSummary, Project, ProjectPath};
|
use project::{DiagnosticSummary, Project};
|
||||||
use project_diagnostics_settings::ProjectDiagnosticsSettings;
|
use project_diagnostics_settings::ProjectDiagnosticsSettings;
|
||||||
|
use project_types::ProjectPath;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -58,7 +60,7 @@ type Event = editor::Event;
|
||||||
struct ProjectDiagnosticsEditor {
|
struct ProjectDiagnosticsEditor {
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
workspace: WeakViewHandle<Workspace>,
|
workspace: WeakViewHandle<Workspace>,
|
||||||
editor: ViewHandle<Editor>,
|
editor: ViewHandle<FollowableEditor>,
|
||||||
summary: DiagnosticSummary,
|
summary: DiagnosticSummary,
|
||||||
excerpts: ModelHandle<MultiBuffer>,
|
excerpts: ModelHandle<MultiBuffer>,
|
||||||
path_states: Vec<PathState>,
|
path_states: Vec<PathState>,
|
||||||
|
@ -172,7 +174,7 @@ impl ProjectDiagnosticsEditor {
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(path.clone());
|
.insert(path.clone());
|
||||||
let no_multiselections = this.editor.update(cx, |editor, cx| {
|
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) {
|
if no_multiselections && !this.is_dirty(cx) {
|
||||||
this.update_excerpts(Some(*language_server_id), 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 excerpts = cx.add_model(|cx| MultiBuffer::new(project_handle.read(cx).replica_id()));
|
||||||
let editor = cx.add_view(|cx| {
|
let editor = cx.add_view(|cx| {
|
||||||
let mut editor =
|
let mut editor =
|
||||||
Editor::for_multibuffer(excerpts.clone(), Some(project_handle.clone()), cx);
|
FollowableEditor::for_multibuffer(excerpts.clone(), project_handle.clone(), cx);
|
||||||
editor.set_vertical_scroll_margin(5, cx);
|
editor
|
||||||
|
.0
|
||||||
|
.update(cx, |this, cx| this.set_vertical_scroll_margin(5, cx));
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
let editor_event_subscription = cx.subscribe(&editor, |this, _, event, cx| {
|
let editor_event_subscription = cx.subscribe(&editor, |this, _, event, cx| {
|
||||||
|
@ -527,21 +531,25 @@ impl ProjectDiagnosticsEditor {
|
||||||
});
|
});
|
||||||
|
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
editor.remove_blocks(blocks_to_remove, None, cx);
|
editor.0.update(cx, |this, cx| {
|
||||||
let block_ids = editor.insert_blocks(
|
this.remove_blocks(blocks_to_remove, None, cx)
|
||||||
blocks_to_add.into_iter().map(|block| {
|
});
|
||||||
let (excerpt_id, text_anchor) = block.position;
|
let block_ids = editor.0.update(cx, |this, cx| {
|
||||||
BlockProperties {
|
this.insert_blocks(
|
||||||
position: excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor),
|
blocks_to_add.into_iter().map(|block| {
|
||||||
height: block.height,
|
let (excerpt_id, text_anchor) = block.position;
|
||||||
style: block.style,
|
BlockProperties {
|
||||||
render: block.render,
|
position: excerpts_snapshot.anchor_in_excerpt(excerpt_id, text_anchor),
|
||||||
disposition: block.disposition,
|
height: block.height,
|
||||||
}
|
style: block.style,
|
||||||
}),
|
render: block.render,
|
||||||
Some(Autoscroll::fit()),
|
disposition: block.disposition,
|
||||||
cx,
|
}
|
||||||
);
|
}),
|
||||||
|
Some(Autoscroll::fit()),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
let mut block_ids = block_ids.into_iter();
|
let mut block_ids = block_ids.into_iter();
|
||||||
for group_state in &mut groups_to_add {
|
for group_state in &mut groups_to_add {
|
||||||
|
@ -582,13 +590,14 @@ impl ProjectDiagnosticsEditor {
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
groups = self.path_states.get(path_ix)?.diagnostic_groups.as_slice();
|
groups = self.path_states.get(path_ix)?.diagnostic_groups.as_slice();
|
||||||
new_excerpt_ids_by_selection_id =
|
new_excerpt_ids_by_selection_id = editor.0.update(cx, |this, cx| {
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| s.refresh());
|
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.refresh())
|
||||||
selections = editor.selections.all::<usize>(cx);
|
});
|
||||||
|
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.
|
// 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 {
|
for selection in &mut selections {
|
||||||
if let Some(new_excerpt_id) = new_excerpt_ids_by_selection_id.get(&selection.id) {
|
if let Some(new_excerpt_id) = new_excerpt_ids_by_selection_id.get(&selection.id) {
|
||||||
let group_ix = match groups.binary_search_by(|probe| {
|
let group_ix = match groups.binary_search_by(|probe| {
|
||||||
|
@ -612,8 +621,10 @@ impl ProjectDiagnosticsEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
editor.change_selections(None, cx, |s| {
|
editor.0.update(cx, |this, cx| {
|
||||||
s.select(selections);
|
this.change_selections(None, cx, |s| {
|
||||||
|
s.select(selections);
|
||||||
|
})
|
||||||
});
|
});
|
||||||
Some(())
|
Some(())
|
||||||
});
|
});
|
||||||
|
@ -703,12 +714,12 @@ impl Item for ProjectDiagnosticsEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
|
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>) {
|
fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
|
||||||
self.editor.update(cx, |editor, _| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
editor.set_nav_history(Some(nav_history));
|
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>> {
|
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 {
|
fn breadcrumb_location(&self) -> ToolbarItemLocation {
|
||||||
|
|
|
@ -30,13 +30,14 @@ db = { path = "../db" }
|
||||||
drag_and_drop = { path = "../drag_and_drop" }
|
drag_and_drop = { path = "../drag_and_drop" }
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
context_menu = { path = "../context_menu" }
|
context_menu = { path = "../context_menu" }
|
||||||
|
workspace_types = {path = "../workspace_types"}
|
||||||
|
project_types = {path = "../project_types"}
|
||||||
fuzzy = { path = "../fuzzy" }
|
fuzzy = { path = "../fuzzy" }
|
||||||
git = { path = "../git" }
|
git = { path = "../git" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
lsp = { path = "../lsp" }
|
lsp = { path = "../lsp" }
|
||||||
multi_buffer = { path = "../multi_buffer" }
|
multi_buffer = { path = "../multi_buffer" }
|
||||||
project = { path = "../project" }
|
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
rich_text = { path = "../rich_text" }
|
rich_text = { path = "../rich_text" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
|
@ -46,9 +47,9 @@ text = { path = "../text" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
sqlez = { path = "../sqlez" }
|
sqlez = { path = "../sqlez" }
|
||||||
workspace = { path = "../workspace" }
|
|
||||||
|
|
||||||
aho-corasick = "1.1"
|
aho-corasick = "1.1"
|
||||||
|
async-trait.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
convert_case = "0.6.0"
|
convert_case = "0.6.0"
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub struct Inlay {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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();
|
let mut text = hint.text();
|
||||||
if hint.padding_right && !text.ends_with(' ') {
|
if hint.padding_right && !text.ends_with(' ') {
|
||||||
text.push(' ');
|
text.push(' ');
|
||||||
|
@ -1172,7 +1172,7 @@ mod tests {
|
||||||
InlayId, MultiBuffer,
|
InlayId, MultiBuffer,
|
||||||
};
|
};
|
||||||
use gpui::AppContext;
|
use gpui::AppContext;
|
||||||
use project::{InlayHint, InlayHintLabel, ResolveState};
|
use project_types::{InlayHint, InlayHintLabel, ResolveState};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use std::{cmp::Reverse, env, sync::Arc};
|
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::{
|
use language::{
|
||||||
language_settings::ShowWhitespaceSetting, Bias, CursorShape, OffsetUtf16, Selection,
|
language_settings::ShowWhitespaceSetting, Bias, CursorShape, OffsetUtf16, Selection,
|
||||||
};
|
};
|
||||||
use project::{
|
use project_types::{
|
||||||
project_settings::{GitGutterSetting, ProjectSettings},
|
project_settings::{GitGutterSetting, ProjectSettings},
|
||||||
ProjectPath,
|
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -54,7 +53,6 @@ use std::{
|
||||||
};
|
};
|
||||||
use text::Point;
|
use text::Point;
|
||||||
use theme::SelectionStyle;
|
use theme::SelectionStyle;
|
||||||
use workspace::item::Item;
|
|
||||||
|
|
||||||
enum FoldMarkers {}
|
enum FoldMarkers {}
|
||||||
|
|
||||||
|
@ -1159,7 +1157,7 @@ impl EditorElement {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
let background_ranges = editor
|
let background_ranges = editor
|
||||||
.background_highlight_row_ranges::<crate::items::BufferSearchHighlights>(
|
.background_highlight_row_ranges::<crate::BufferSearchHighlights>(
|
||||||
start_anchor..end_anchor,
|
start_anchor..end_anchor,
|
||||||
&layout.position_map.snapshot,
|
&layout.position_map.snapshot,
|
||||||
50000,
|
50000,
|
||||||
|
@ -1662,61 +1660,65 @@ impl EditorElement {
|
||||||
let include_root = editor
|
let include_root = editor
|
||||||
.project
|
.project
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
|
.map(|project| project.visible_worktrees_count(cx) > 1)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let jump_icon = project::File::from_dyn(buffer.file()).map(|file| {
|
//
|
||||||
let jump_path = ProjectPath {
|
let jump_icon =
|
||||||
worktree_id: file.worktree_id(cx),
|
editor
|
||||||
path: file.path.clone(),
|
.project
|
||||||
};
|
|
||||||
let jump_anchor = range
|
|
||||||
.primary
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(range.context.start, |primary| primary.start);
|
.zip(buffer.file())
|
||||||
let jump_position = language::ToPoint::to_point(&jump_anchor, buffer);
|
.map(|(project, buffer_file)| {
|
||||||
|
let jump_path = project.project_file(&**buffer_file);
|
||||||
|
//::from_dyn(buffer.file())
|
||||||
|
|
||||||
enum JumpIcon {}
|
let jump_anchor = range
|
||||||
MouseEventHandler::new::<JumpIcon, _>((*id).into(), cx, |state, _| {
|
.primary
|
||||||
let style = style.jump_icon.style_for(state);
|
.as_ref()
|
||||||
Svg::new("icons/arrow_up_right.svg")
|
.map_or(range.context.start, |primary| primary.start);
|
||||||
.with_color(style.color)
|
let jump_position =
|
||||||
.constrained()
|
language::ToPoint::to_point(&jump_anchor, buffer);
|
||||||
.with_width(style.icon_width)
|
|
||||||
|
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()
|
.aligned()
|
||||||
.contained()
|
.flex_float()
|
||||||
.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()
|
|
||||||
});
|
|
||||||
|
|
||||||
if *starts_new_buffer {
|
if *starts_new_buffer {
|
||||||
let editor_font_size = style.text.font_size;
|
let editor_font_size = style.text.font_size;
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
display_map::{InlayOffset, ToDisplayPoint},
|
display_map::{InlayOffset, ToDisplayPoint},
|
||||||
link_go_to_definition::{InlayHighlight, RangeInEditor},
|
link_go_to_definition::{InlayHighlight, RangeInEditor},
|
||||||
|
types::Workspace,
|
||||||
Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, EditorSnapshot, EditorStyle,
|
Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, EditorSnapshot, EditorStyle,
|
||||||
ExcerptId, RangeToAnchorExt,
|
ExcerptId, Project, RangeToAnchorExt,
|
||||||
};
|
};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
elements::{Flex, MouseEventHandler, Padding, ParentElement, Text},
|
elements::{Flex, MouseEventHandler, Padding, ParentElement, Text},
|
||||||
platform::{CursorStyle, MouseButton},
|
platform::{CursorStyle, MouseButton},
|
||||||
AnyElement, AppContext, Element, ModelHandle, Task, ViewContext, WeakViewHandle,
|
AnyElement, AppContext, Element, Task, ViewContext,
|
||||||
};
|
};
|
||||||
use language::{
|
use language::{
|
||||||
markdown, Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry, ParsedMarkdown,
|
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 std::{ops::Range, sync::Arc, time::Duration};
|
||||||
use util::TryFutureExt;
|
use util::TryFutureExt;
|
||||||
use workspace::Workspace;
|
|
||||||
|
|
||||||
pub const HOVER_DELAY_MILLIS: u64 = 350;
|
pub const HOVER_DELAY_MILLIS: u64 = 350;
|
||||||
pub const HOVER_REQUEST_DELAY_MILLIS: u64 = 200;
|
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()
|
cx.background()
|
||||||
.timer(Duration::from_millis(HOVER_DELAY_MILLIS))
|
.timer(Duration::from_millis(HOVER_DELAY_MILLIS))
|
||||||
.await;
|
.await;
|
||||||
this.update(&mut cx, |this, _| {
|
let language_registry = this.update(&mut cx, |this, cx| {
|
||||||
this.hover_state.diagnostic_popover = None;
|
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 blocks = vec![inlay_hover.tooltip];
|
||||||
let parsed_content = parse_blocks(&blocks, &language_registry, None).await;
|
let parsed_content = parse_blocks(&blocks, &language_registry, None).await;
|
||||||
|
|
||||||
|
@ -252,11 +252,7 @@ fn show_hover(
|
||||||
};
|
};
|
||||||
|
|
||||||
// query the LSP for hover info
|
// query the LSP for hover info
|
||||||
let hover_request = cx.update(|cx| {
|
let hover_request = cx.update(|cx| project.hover(&buffer, buffer_position, cx));
|
||||||
project.update(cx, |project, cx| {
|
|
||||||
project.hover(&buffer, buffer_position, cx)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(delay) = delay {
|
if let Some(delay) = delay {
|
||||||
delay.await;
|
delay.await;
|
||||||
|
@ -310,7 +306,7 @@ fn show_hover(
|
||||||
anchor..anchor
|
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 blocks = hover_result.contents;
|
||||||
let language = hover_result.language;
|
let language = hover_result.language;
|
||||||
let parsed_content = parse_blocks(&blocks, &language_registry, language).await;
|
let parsed_content = parse_blocks(&blocks, &language_registry, language).await;
|
||||||
|
@ -423,7 +419,7 @@ impl HoverState {
|
||||||
snapshot: &EditorSnapshot,
|
snapshot: &EditorSnapshot,
|
||||||
style: &EditorStyle,
|
style: &EditorStyle,
|
||||||
visible_rows: Range<u32>,
|
visible_rows: Range<u32>,
|
||||||
workspace: Option<WeakViewHandle<Workspace>>,
|
workspace: Option<Arc<dyn Workspace>>,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> Option<(DisplayPoint, Vec<AnyElement<Editor>>)> {
|
) -> Option<(DisplayPoint, Vec<AnyElement<Editor>>)> {
|
||||||
// If there is a diagnostic, position the popovers based on that.
|
// If there is a diagnostic, position the popovers based on that.
|
||||||
|
@ -462,7 +458,7 @@ impl HoverState {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct InfoPopover {
|
pub struct InfoPopover {
|
||||||
pub project: ModelHandle<Project>,
|
pub project: Arc<dyn Project>,
|
||||||
symbol_range: RangeInEditor,
|
symbol_range: RangeInEditor,
|
||||||
pub blocks: Vec<HoverBlock>,
|
pub blocks: Vec<HoverBlock>,
|
||||||
parsed_content: ParsedMarkdown,
|
parsed_content: ParsedMarkdown,
|
||||||
|
@ -472,7 +468,7 @@ impl InfoPopover {
|
||||||
pub fn render(
|
pub fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
style: &EditorStyle,
|
style: &EditorStyle,
|
||||||
workspace: Option<WeakViewHandle<Workspace>>,
|
workspace: Option<Arc<dyn Workspace>>,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> AnyElement<Editor> {
|
) -> AnyElement<Editor> {
|
||||||
MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| {
|
MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| {
|
||||||
|
|
|
@ -14,7 +14,7 @@ use futures::future;
|
||||||
use gpui::{ModelContext, ModelHandle, Task, ViewContext};
|
use gpui::{ModelContext, ModelHandle, Task, ViewContext};
|
||||||
use language::{language_settings::InlayHintKind, Buffer, BufferSnapshot};
|
use language::{language_settings::InlayHintKind, Buffer, BufferSnapshot};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use project::{InlayHint, ResolveState};
|
use project_types::{InlayHint, ResolveState};
|
||||||
|
|
||||||
use collections::{hash_map, HashMap, HashSet};
|
use collections::{hash_map, HashMap, HashSet};
|
||||||
use language::language_settings::InlayHintSettings;
|
use language::language_settings::InlayHintSettings;
|
||||||
|
@ -539,14 +539,12 @@ impl InlayHintCache {
|
||||||
.buffer(buffer_id)
|
.buffer(buffer_id)
|
||||||
.and_then(|buffer| {
|
.and_then(|buffer| {
|
||||||
let project = editor.project.as_ref()?;
|
let project = editor.project.as_ref()?;
|
||||||
Some(project.update(cx, |project, cx| {
|
Some(project.resolve_inlay_hint(
|
||||||
project.resolve_inlay_hint(
|
hint_to_resolve,
|
||||||
hint_to_resolve,
|
buffer,
|
||||||
buffer,
|
server_id,
|
||||||
server_id,
|
cx,
|
||||||
cx,
|
))
|
||||||
)
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
if let Some(resolved_hint_task) = resolved_hint_task {
|
if let Some(resolved_hint_task) = resolved_hint_task {
|
||||||
|
@ -896,9 +894,9 @@ async fn fetch_and_update_hints(
|
||||||
.buffer(query.buffer_id)
|
.buffer(query.buffer_id)
|
||||||
.and_then(|buffer| {
|
.and_then(|buffer| {
|
||||||
let project = editor.project.as_ref()?;
|
let project = editor.project.as_ref()?;
|
||||||
Some(project.update(cx, |project, cx| {
|
Some(
|
||||||
project.inlay_hints(buffer, fetch_range.clone(), cx)
|
project.inlay_hints(buffer, fetch_range.clone(), cx)
|
||||||
}))
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
use gpui::{Task, ViewContext};
|
use gpui::{Task, ViewContext};
|
||||||
use language::{Bias, ToOffset};
|
use language::{Bias, ToOffset};
|
||||||
use lsp::LanguageServerId;
|
use lsp::LanguageServerId;
|
||||||
use project::{
|
use project_types::{
|
||||||
HoverBlock, HoverBlockKind, InlayHintLabelPartTooltip, InlayHintTooltip, LocationLink,
|
HoverBlock, HoverBlockKind, InlayHintLabelPartTooltip, InlayHintTooltip, LocationLink,
|
||||||
ResolveState,
|
ResolveState,
|
||||||
};
|
};
|
||||||
|
@ -227,7 +227,7 @@ pub fn update_inlay_link_and_hover_points(
|
||||||
extra_shift_right += 1;
|
extra_shift_right += 1;
|
||||||
}
|
}
|
||||||
match cached_hint.label {
|
match cached_hint.label {
|
||||||
project::InlayHintLabel::String(_) => {
|
project_types::InlayHintLabel::String(_) => {
|
||||||
if let Some(tooltip) = cached_hint.tooltip {
|
if let Some(tooltip) = cached_hint.tooltip {
|
||||||
hover_popover::hover_at_inlay(
|
hover_popover::hover_at_inlay(
|
||||||
editor,
|
editor,
|
||||||
|
@ -257,7 +257,7 @@ pub fn update_inlay_link_and_hover_points(
|
||||||
hover_updated = true;
|
hover_updated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
project::InlayHintLabel::LabelParts(label_parts) => {
|
project_types::InlayHintLabel::LabelParts(label_parts) => {
|
||||||
let hint_start =
|
let hint_start =
|
||||||
snapshot.anchor_to_inlay_offset(hovered_hint.position);
|
snapshot.anchor_to_inlay_offset(hovered_hint.position);
|
||||||
if let Some((hovered_hint_part, part_range)) =
|
if let Some((hovered_hint_part, part_range)) =
|
||||||
|
@ -396,16 +396,14 @@ pub fn show_link_definition(
|
||||||
let result = match &trigger_point {
|
let result = match &trigger_point {
|
||||||
TriggerPoint::Text(_) => {
|
TriggerPoint::Text(_) => {
|
||||||
// query the LSP for definition info
|
// query the LSP for definition info
|
||||||
cx.update(|cx| {
|
cx.update(|cx| match definition_kind {
|
||||||
project.update(cx, |project, cx| match definition_kind {
|
LinkDefinitionKind::Symbol => {
|
||||||
LinkDefinitionKind::Symbol => {
|
project.definition(&buffer, buffer_position, cx)
|
||||||
project.definition(&buffer, buffer_position, cx)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
LinkDefinitionKind::Type => {
|
LinkDefinitionKind::Type => {
|
||||||
project.type_definition(&buffer, buffer_position, cx)
|
project.type_definition(&buffer, buffer_position, cx)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
|
|
|
@ -4,6 +4,7 @@ pub mod scroll_amount;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,14 +14,13 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use language::{Bias, Point};
|
use language::{Bias, Point};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::WorkspaceId;
|
use workspace_types::WorkspaceId;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
display_map::{DisplaySnapshot, ToDisplayPoint},
|
display_map::{DisplaySnapshot, ToDisplayPoint},
|
||||||
hover_popover::hide_hover,
|
hover_popover::hide_hover,
|
||||||
persistence::DB,
|
types, Anchor, DisplayPoint, Editor, EditorMode, Event, InlayHintRefreshReason,
|
||||||
Anchor, DisplayPoint, Editor, EditorMode, Event, InlayHintRefreshReason, MultiBufferSnapshot,
|
MultiBufferSnapshot, ToPoint,
|
||||||
ToPoint,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
|
@ -176,7 +176,7 @@ impl ScrollManager {
|
||||||
map: &DisplaySnapshot,
|
map: &DisplaySnapshot,
|
||||||
local: bool,
|
local: bool,
|
||||||
autoscroll: bool,
|
autoscroll: bool,
|
||||||
workspace_id: Option<i64>,
|
workspace_id: Option<&(Arc<dyn crate::types::Workspace>, i64)>,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
let (new_anchor, top_row) = if scroll_position.y() <= 0. {
|
let (new_anchor, top_row) = if scroll_position.y() <= 0. {
|
||||||
|
@ -215,19 +215,20 @@ impl ScrollManager {
|
||||||
top_row: u32,
|
top_row: u32,
|
||||||
local: bool,
|
local: bool,
|
||||||
autoscroll: bool,
|
autoscroll: bool,
|
||||||
workspace_id: Option<i64>,
|
workspace_id: Option<&(Arc<dyn crate::types::Workspace>, i64)>,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
self.anchor = anchor;
|
self.anchor = anchor;
|
||||||
cx.emit(Event::ScrollPositionChanged { local, autoscroll });
|
cx.emit(Event::ScrollPositionChanged { local, autoscroll });
|
||||||
self.show_scrollbar(cx);
|
self.show_scrollbar(cx);
|
||||||
self.autoscroll_request.take();
|
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 item_id = cx.view_id();
|
||||||
|
let db = workspace.db();
|
||||||
|
let workspace_id = *workspace_id;
|
||||||
cx.background()
|
cx.background()
|
||||||
.spawn(async move {
|
.spawn(async move {
|
||||||
DB.save_scroll_position(
|
db.save_scroll_position(
|
||||||
item_id,
|
item_id,
|
||||||
workspace_id,
|
workspace_id,
|
||||||
top_row,
|
top_row,
|
||||||
|
@ -324,7 +325,7 @@ impl Editor {
|
||||||
let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
|
|
||||||
hide_hover(self, 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(
|
self.scroll_manager.set_scroll_position(
|
||||||
scroll_position,
|
scroll_position,
|
||||||
&map,
|
&map,
|
||||||
|
@ -344,7 +345,7 @@ impl Editor {
|
||||||
|
|
||||||
pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
|
pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
|
||||||
hide_hover(self, cx);
|
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
|
let top_row = scroll_anchor
|
||||||
.anchor
|
.anchor
|
||||||
.to_point(&self.buffer().read(cx).snapshot(cx))
|
.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);
|
.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,
|
&mut self,
|
||||||
scroll_anchor: ScrollAnchor,
|
scroll_anchor: ScrollAnchor,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
hide_hover(self, cx);
|
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
|
let top_row = scroll_anchor
|
||||||
.anchor
|
.anchor
|
||||||
.to_point(&self.buffer().read(cx).snapshot(cx))
|
.to_point(&self.buffer().read(cx).snapshot(cx))
|
||||||
|
@ -415,11 +416,12 @@ impl Editor {
|
||||||
|
|
||||||
pub fn read_scroll_position_from_db(
|
pub fn read_scroll_position_from_db(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
db: &dyn types::Db,
|
||||||
item_id: usize,
|
item_id: usize,
|
||||||
workspace_id: WorkspaceId,
|
workspace_id: WorkspaceId,
|
||||||
cx: &mut ViewContext<Editor>,
|
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 {
|
if let Ok(Some((top_row, x, y))) = scroll_position {
|
||||||
let top_anchor = self
|
let top_anchor = self
|
||||||
.buffer()
|
.buffer()
|
||||||
|
|
|
@ -247,7 +247,7 @@ impl Editor {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn request_autoscroll_remotely(
|
pub fn request_autoscroll_remotely(
|
||||||
&mut self,
|
&mut self,
|
||||||
autoscroll: Autoscroll,
|
autoscroll: Autoscroll,
|
||||||
cx: &mut ViewContext<Self>,
|
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::{
|
use crate::persistence::DB;
|
||||||
display_map::ToDisplayPoint, link_go_to_definition::hide_link_definition,
|
use crate::{FollowableEditor, ProjectHandle, WeakWorkspaceHandle};
|
||||||
movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor,
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
|
|
||||||
};
|
|
||||||
use anyhow::{Context, Result};
|
|
||||||
use collections::HashSet;
|
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 futures::future::try_join_all;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::*,
|
elements::*,
|
||||||
|
@ -16,7 +19,8 @@ use language::{
|
||||||
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point,
|
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point,
|
||||||
SelectionGoal,
|
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 rpc::proto::{self, update_view, PeerId};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -37,15 +41,14 @@ use workspace::item::{BreadcrumbText, FollowableItemHandle, ItemHandle};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
item::{FollowableItem, Item, ItemEvent, ProjectItem},
|
item::{FollowableItem, Item, ItemEvent, ProjectItem},
|
||||||
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
|
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
|
||||||
ItemId, ItemNavHistory, Pane, StatusItemView, ToolbarItemLocation, ViewId, Workspace,
|
ItemNavHistory, Pane, StatusItemView, ToolbarItemLocation, Workspace,
|
||||||
WorkspaceId,
|
|
||||||
};
|
};
|
||||||
|
use workspace_types::*;
|
||||||
|
|
||||||
pub const MAX_TAB_TITLE_LEN: usize = 24;
|
impl FollowableItem for FollowableEditor {
|
||||||
|
|
||||||
impl FollowableItem for Editor {
|
|
||||||
fn remote_id(&self) -> Option<ViewId> {
|
fn remote_id(&self) -> Option<ViewId> {
|
||||||
self.remote_id
|
todo!();
|
||||||
|
//self.0.remote_id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_state_proto(
|
fn from_state_proto(
|
||||||
|
@ -86,13 +89,20 @@ impl FollowableItem for Editor {
|
||||||
let ids_match = editor.remote_id(&client, cx) == Some(remote_id);
|
let ids_match = editor.remote_id(&client, cx) == Some(remote_id);
|
||||||
let singleton_buffer_matches = state.singleton
|
let singleton_buffer_matches = state.singleton
|
||||||
&& buffers.first()
|
&& 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
|
ids_match || singleton_buffer_matches
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let editor = if let Some(editor) = editor {
|
let editor = if let Some(editor) = editor {
|
||||||
editor
|
editor.update(&mut cx, |this, _| this.0.clone())
|
||||||
} else {
|
} else {
|
||||||
pane.update(&mut cx, |_, cx| {
|
pane.update(&mut cx, |_, cx| {
|
||||||
let multibuffer = cx.add_model(|cx| {
|
let multibuffer = cx.add_model(|cx| {
|
||||||
|
@ -129,12 +139,20 @@ impl FollowableItem for Editor {
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.add_view(|cx| {
|
cx.add_view(|cx| {
|
||||||
let mut editor =
|
let mut editor = Editor::for_multibuffer(
|
||||||
Editor::for_multibuffer(multibuffer, Some(project.clone()), cx);
|
multibuffer,
|
||||||
|
Some(Arc::new(ProjectHandle(project.clone()))),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
editor.remote_id = Some(remote_id);
|
editor.remote_id = Some(remote_id);
|
||||||
editor
|
editor
|
||||||
})
|
})
|
||||||
})?
|
})
|
||||||
|
.ok()
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(editor) = editor else {
|
||||||
|
bail!("Failed to initialize editor")
|
||||||
};
|
};
|
||||||
|
|
||||||
update_editor_from_message(
|
update_editor_from_message(
|
||||||
|
@ -152,34 +170,37 @@ impl FollowableItem for Editor {
|
||||||
)
|
)
|
||||||
.await?;
|
.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>) {
|
fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {
|
||||||
self.leader_peer_id = leader_peer_id;
|
self.0.update(cx, |this, cx| {
|
||||||
if self.leader_peer_id.is_some() {
|
this.leader_peer_id = leader_peer_id;
|
||||||
self.buffer.update(cx, |buffer, cx| {
|
if this.leader_peer_id.is_some() {
|
||||||
buffer.remove_active_selections(cx);
|
this.buffer().update(cx, |buffer, cx| {
|
||||||
});
|
buffer.remove_active_selections(cx);
|
||||||
} else {
|
});
|
||||||
self.buffer.update(cx, |buffer, cx| {
|
} else {
|
||||||
if self.focused {
|
this.buffer().update(cx, |buffer, cx| {
|
||||||
buffer.set_active_selections(
|
if this.focused {
|
||||||
&self.selections.disjoint_anchors(),
|
buffer.set_active_selections(
|
||||||
self.selections.line_mode,
|
&this.selections.disjoint_anchors(),
|
||||||
self.cursor_shape,
|
this.selections.line_mode,
|
||||||
cx,
|
this.cursor_shape,
|
||||||
);
|
cx,
|
||||||
}
|
);
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
cx.notify();
|
}
|
||||||
|
cx.notify();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
|
fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
|
||||||
let buffer = self.buffer.read(cx);
|
let this = self.0.read(cx);
|
||||||
let scroll_anchor = self.scroll_manager.anchor();
|
let buffer = this.buffer().read(cx);
|
||||||
|
let scroll_anchor = this.scroll_manager.anchor();
|
||||||
let excerpts = buffer
|
let excerpts = buffer
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.excerpts()
|
.excerpts()
|
||||||
|
@ -206,13 +227,13 @@ impl FollowableItem for Editor {
|
||||||
scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.anchor)),
|
scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.anchor)),
|
||||||
scroll_x: scroll_anchor.offset.x(),
|
scroll_x: scroll_anchor.offset.x(),
|
||||||
scroll_y: scroll_anchor.offset.y(),
|
scroll_y: scroll_anchor.offset.y(),
|
||||||
selections: self
|
selections: this
|
||||||
.selections
|
.selections
|
||||||
.disjoint_anchors()
|
.disjoint_anchors()
|
||||||
.iter()
|
.iter()
|
||||||
.map(serialize_selection)
|
.map(serialize_selection)
|
||||||
.collect(),
|
.collect(),
|
||||||
pending_selection: self
|
pending_selection: this
|
||||||
.selections
|
.selections
|
||||||
.pending_anchor()
|
.pending_anchor()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -228,7 +249,7 @@ impl FollowableItem for Editor {
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let update =
|
let update =
|
||||||
update.get_or_insert_with(|| proto::update_view::Variant::Editor(Default::default()));
|
update.get_or_insert_with(|| proto::update_view::Variant::Editor(Default::default()));
|
||||||
|
let this = self.0.read(cx);
|
||||||
match update {
|
match update {
|
||||||
proto::update_view::Variant::Editor(update) => match event {
|
proto::update_view::Variant::Editor(update) => match event {
|
||||||
Event::ExcerptsAdded {
|
Event::ExcerptsAdded {
|
||||||
|
@ -259,20 +280,20 @@ impl FollowableItem for Editor {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Event::ScrollPositionChanged { .. } => {
|
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_top_anchor = Some(serialize_anchor(&scroll_anchor.anchor));
|
||||||
update.scroll_x = scroll_anchor.offset.x();
|
update.scroll_x = scroll_anchor.offset.x();
|
||||||
update.scroll_y = scroll_anchor.offset.y();
|
update.scroll_y = scroll_anchor.offset.y();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Event::SelectionsChanged { .. } => {
|
Event::SelectionsChanged { .. } => {
|
||||||
update.selections = self
|
update.selections = this
|
||||||
.selections
|
.selections
|
||||||
.disjoint_anchors()
|
.disjoint_anchors()
|
||||||
.iter()
|
.iter()
|
||||||
.map(serialize_selection)
|
.map(serialize_selection)
|
||||||
.collect();
|
.collect();
|
||||||
update.pending_selection = self
|
update.pending_selection = this
|
||||||
.selections
|
.selections
|
||||||
.pending_anchor()
|
.pending_anchor()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -292,8 +313,10 @@ impl FollowableItem for Editor {
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
let update_view::Variant::Editor(message) = message;
|
let update_view::Variant::Editor(message) = message;
|
||||||
let project = project.clone();
|
let project = project.clone();
|
||||||
cx.spawn(|this, mut cx| async move {
|
self.0.update(cx, |this, cx| {
|
||||||
update_editor_from_message(this, project, message, &mut cx).await
|
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.
|
// Update the editor's excerpts.
|
||||||
this.update(cx, |editor, cx| {
|
this.update(cx, |editor, cx| {
|
||||||
editor.buffer.update(cx, |multibuffer, cx| {
|
editor.buffer().update(cx, |multibuffer, cx| {
|
||||||
let mut removed_excerpt_ids = message
|
let mut removed_excerpt_ids = message
|
||||||
.deleted_excerpts
|
.deleted_excerpts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -390,7 +413,7 @@ async fn update_editor_from_message(
|
||||||
|
|
||||||
// Deserialize the editor state.
|
// Deserialize the editor state.
|
||||||
let (selections, pending_selection, scroll_top_anchor) = this.update(cx, |editor, cx| {
|
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
|
let selections = message
|
||||||
.selections
|
.selections
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -408,7 +431,7 @@ async fn update_editor_from_message(
|
||||||
// Wait until the buffer has received all of the operations referenced by
|
// Wait until the buffer has received all of the operations referenced by
|
||||||
// the editor's new state.
|
// the editor's new state.
|
||||||
this.update(cx, |editor, cx| {
|
this.update(cx, |editor, cx| {
|
||||||
editor.buffer.update(cx, |buffer, cx| {
|
editor.buffer().update(cx, |buffer, cx| {
|
||||||
buffer.wait_for_anchors(
|
buffer.wait_for_anchors(
|
||||||
selections
|
selections
|
||||||
.iter()
|
.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 {
|
fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) -> bool {
|
||||||
if let Ok(data) = data.downcast::<NavigationData>() {
|
if let Ok(data) = data.downcast::<NavigationData>() {
|
||||||
let newest_selection = self.selections.newest::<Point>(cx);
|
let this = self.0.read(cx);
|
||||||
let buffer = self.buffer.read(cx).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) {
|
let offset = if buffer.can_resolve(&data.cursor_anchor) {
|
||||||
data.cursor_anchor.to_point(&buffer)
|
data.cursor_anchor.to_point(&buffer)
|
||||||
} else {
|
} else {
|
||||||
|
@ -538,12 +562,15 @@ impl Item for Editor {
|
||||||
if newest_selection.head() == offset {
|
if newest_selection.head() == offset {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
let nav_history = self.nav_history.take();
|
self.0.update(cx, |this, cx| {
|
||||||
self.set_scroll_anchor(scroll_anchor, cx);
|
let nav_history = this.nav_history.take();
|
||||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
this.set_scroll_anchor(scroll_anchor, cx);
|
||||||
s.select_ranges([offset..offset])
|
this.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
|
s.select_ranges([offset..offset])
|
||||||
|
});
|
||||||
|
this.nav_history = nav_history;
|
||||||
});
|
});
|
||||||
self.nav_history = nav_history;
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -553,6 +580,8 @@ impl Item for Editor {
|
||||||
|
|
||||||
fn tab_tooltip_text(&self, cx: &AppContext) -> Option<Cow<str>> {
|
fn tab_tooltip_text(&self, cx: &AppContext) -> Option<Cow<str>> {
|
||||||
let file_path = self
|
let file_path = self
|
||||||
|
.0
|
||||||
|
.read(cx)
|
||||||
.buffer()
|
.buffer()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.as_singleton()?
|
.as_singleton()?
|
||||||
|
@ -567,7 +596,7 @@ impl Item for Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<Cow<str>> {
|
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::Borrowed(path) => Some(path.to_string_lossy()),
|
||||||
Cow::Owned(path) => Some(path.to_string_lossy().to_string().into()),
|
Cow::Owned(path) => Some(path.to_string_lossy().to_string().into()),
|
||||||
}
|
}
|
||||||
|
@ -579,10 +608,11 @@ impl Item for Editor {
|
||||||
style: &theme::Tab,
|
style: &theme::Tab,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> AnyElement<T> {
|
) -> AnyElement<T> {
|
||||||
|
let this = self.0.read(cx);
|
||||||
Flex::row()
|
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| {
|
.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();
|
let description = path.to_string_lossy();
|
||||||
Some(
|
Some(
|
||||||
Label::new(
|
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)) {
|
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)
|
.read(cx)
|
||||||
.for_each_buffer(|buffer| f(buffer.id(), buffer.read(cx)));
|
.for_each_buffer(|buffer| f(buffer.id(), buffer.read(cx)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_singleton(&self, cx: &AppContext) -> bool {
|
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>
|
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))
|
Some(self.clone(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
|
fn set_nav_history(&mut self, history: ItemNavHistory, cx: &mut ViewContext<Self>) {
|
||||||
self.nav_history = Some(history);
|
self.0
|
||||||
|
.update(cx, |this, cx| this.set_nav_history(Some(Box::new(history))));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
let selection = self.selections.newest_anchor();
|
self.0.update(cx, |this, cx| {
|
||||||
self.push_to_nav_history(selection.head(), None, 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>) {
|
fn workspace_deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
hide_link_definition(self, cx);
|
self.0.update(cx, |this, cx| {
|
||||||
self.link_go_to_definition_state.last_trigger_point = None;
|
hide_link_definition(this, cx);
|
||||||
|
this.link_go_to_definition_state.last_trigger_point = None
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_dirty(&self, cx: &AppContext) -> bool {
|
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 {
|
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 {
|
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() {
|
if let Some(buffer) = buffer.as_singleton() {
|
||||||
buffer.read(cx).project_path(cx).is_some()
|
buffer.read(cx).project_path(cx).is_some()
|
||||||
} else {
|
} else {
|
||||||
|
@ -651,40 +688,47 @@ impl Item for Editor {
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
self.report_editor_event("save", None, cx);
|
self.0.update(cx, |this, cx| {
|
||||||
let format = self.perform_format(project.clone(), FormatTrigger::Save, cx);
|
this.report_editor_event("save", None, cx);
|
||||||
let buffers = self.buffer().clone().read(cx).all_buffers();
|
let format = this.perform_format(
|
||||||
cx.spawn(|_, mut cx| async move {
|
Arc::new(ProjectHandle(project.clone())),
|
||||||
format.await?;
|
FormatTrigger::Save,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
let buffers = this.buffer().clone().read(cx).all_buffers();
|
||||||
|
cx.spawn(|_, mut cx| async move {
|
||||||
|
format.await?;
|
||||||
|
|
||||||
if buffers.len() == 1 {
|
if buffers.len() == 1 {
|
||||||
project
|
project
|
||||||
.update(&mut cx, |project, cx| project.save_buffers(buffers, cx))
|
.update(&mut cx, |project, cx| project.save_buffers(buffers, cx))
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
// For multi-buffers, only save those ones that contain changes. For clean buffers
|
// 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
|
// we simulate saving by calling `Buffer::did_save`, so that language servers or
|
||||||
// other downstream listeners of save events get notified.
|
// other downstream listeners of save events get notified.
|
||||||
let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| {
|
let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| {
|
||||||
buffer.read_with(&cx, |buffer, _| buffer.is_dirty() || buffer.has_conflict())
|
buffer
|
||||||
});
|
.read_with(&cx, |buffer, _| buffer.is_dirty() || buffer.has_conflict())
|
||||||
|
|
||||||
project
|
|
||||||
.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(())
|
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,
|
abs_path: PathBuf,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
let buffer = self
|
self.0.update(cx, |this, cx| {
|
||||||
.buffer()
|
let buffer = this
|
||||||
.read(cx)
|
.buffer()
|
||||||
.as_singleton()
|
.read(cx)
|
||||||
.expect("cannot call save_as on an excerpt list");
|
.as_singleton()
|
||||||
|
.expect("cannot call save_as on an excerpt list");
|
||||||
|
|
||||||
let file_extension = abs_path
|
let file_extension = abs_path
|
||||||
.extension()
|
.extension()
|
||||||
.map(|a| a.to_string_lossy().to_string());
|
.map(|a| a.to_string_lossy().to_string());
|
||||||
self.report_editor_event("save", file_extension, cx);
|
this.report_editor_event("save", file_extension, cx);
|
||||||
|
|
||||||
project.update(cx, |project, cx| {
|
project.update(cx, |project, cx| {
|
||||||
project.save_buffer_as(buffer, abs_path, cx)
|
project.save_buffer_as(buffer, abs_path, cx)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -715,23 +761,26 @@ impl Item for Editor {
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
let buffer = self.buffer().clone();
|
let this = self.0.read(cx);
|
||||||
let buffers = self.buffer.read(cx).all_buffers();
|
let buffer = this.buffer().clone();
|
||||||
|
let buffers = buffer.read(cx).all_buffers();
|
||||||
let reload_buffers =
|
let reload_buffers =
|
||||||
project.update(cx, |project, cx| project.reload_buffers(buffers, true, cx));
|
project.update(cx, |project, cx| project.reload_buffers(buffers, true, cx));
|
||||||
cx.spawn(|this, mut cx| async move {
|
self.0.update(cx, |_, cx| {
|
||||||
let transaction = reload_buffers.log_err().await;
|
cx.spawn(|this, mut cx| async move {
|
||||||
this.update(&mut cx, |editor, cx| {
|
let transaction = reload_buffers.log_err().await;
|
||||||
editor.request_autoscroll(Autoscroll::fit(), cx)
|
this.update(&mut cx, |editor, cx| {
|
||||||
})?;
|
editor.request_autoscroll(Autoscroll::fit(), cx)
|
||||||
buffer.update(&mut cx, |buffer, cx| {
|
})?;
|
||||||
if let Some(transaction) = transaction {
|
buffer.update(&mut cx, |buffer, cx| {
|
||||||
if !buffer.is_singleton() {
|
if let Some(transaction) = transaction {
|
||||||
buffer.push_transaction(&transaction.0, cx);
|
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()))
|
Some(Box::new(handle.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Vector2F> {
|
fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F> {
|
||||||
self.pixel_position_of_newest_cursor
|
self.0.read(cx).pixel_position_of_newest_cursor
|
||||||
}
|
}
|
||||||
|
|
||||||
fn breadcrumb_location(&self) -> ToolbarItemLocation {
|
fn breadcrumb_location(&self) -> ToolbarItemLocation {
|
||||||
|
@ -774,41 +823,43 @@ impl Item for Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
|
fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
|
||||||
let cursor = self.selections.newest_anchor().head();
|
self.0
|
||||||
let multibuffer = &self.buffer().read(cx);
|
.read_with(cx, |this, cx| {
|
||||||
let (buffer_id, symbols) =
|
let cursor = this.selections.newest_anchor().head();
|
||||||
multibuffer.symbols_containing(cursor, Some(&theme.editor.syntax), cx)?;
|
let multibuffer = &this.buffer().read(cx);
|
||||||
let buffer = multibuffer.buffer(buffer_id)?;
|
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 buffer = buffer.read(cx);
|
||||||
let filename = buffer
|
let filename = buffer
|
||||||
.snapshot()
|
.snapshot()
|
||||||
.resolve_file_path(
|
.resolve_file_path(
|
||||||
cx,
|
cx,
|
||||||
self.project
|
this.project
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
|
.map(|project| project.visible_worktrees_count(cx) > 1)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
)
|
)
|
||||||
.map(|path| path.to_string_lossy().to_string())
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
.unwrap_or_else(|| "untitled".to_string());
|
.unwrap_or_else(|| "untitled".to_string());
|
||||||
|
|
||||||
let mut breadcrumbs = vec![BreadcrumbText {
|
let mut breadcrumbs = vec![BreadcrumbText {
|
||||||
text: filename,
|
text: filename,
|
||||||
highlights: None,
|
highlights: None,
|
||||||
}];
|
}];
|
||||||
breadcrumbs.extend(symbols.into_iter().map(|symbol| BreadcrumbText {
|
breadcrumbs.extend(symbols.into_iter().map(|symbol| BreadcrumbText {
|
||||||
text: symbol.text,
|
text: symbol.text,
|
||||||
highlights: Some(symbol.highlight_ranges),
|
highlights: Some(symbol.highlight_ranges),
|
||||||
}));
|
}));
|
||||||
Some(breadcrumbs)
|
Some(breadcrumbs)
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
|
fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext<Self>) {
|
||||||
let workspace_id = workspace.database_id();
|
let workspace_id = workspace.database_id();
|
||||||
let item_id = cx.view_id();
|
let item_id = cx.view_id();
|
||||||
self.workspace = Some((workspace.weak_handle(), workspace.database_id()));
|
|
||||||
|
|
||||||
fn serialize(
|
fn serialize(
|
||||||
buffer: ModelHandle<Buffer>,
|
buffer: ModelHandle<Buffer>,
|
||||||
workspace_id: WorkspaceId,
|
workspace_id: WorkspaceId,
|
||||||
|
@ -828,18 +879,24 @@ impl Item for Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(buffer) = self.buffer().read(cx).as_singleton() {
|
self.0.update(cx, |this, cx| {
|
||||||
serialize(buffer.clone(), workspace_id, item_id, 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| {
|
cx.subscribe(&buffer, |this, buffer, event, cx| {
|
||||||
if let Some((_, workspace_id)) = this.workspace.as_ref() {
|
if let Some((_, workspace_id)) = this.workspace.as_ref() {
|
||||||
if let language::Event::FileHandleChanged = event {
|
if let language::Event::FileHandleChanged = event {
|
||||||
serialize(buffer, *workspace_id, cx.view_id(), cx);
|
serialize(buffer, *workspace_id, cx.view_id(), cx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
.detach();
|
||||||
.detach();
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialized_item_kind() -> Option<&'static str> {
|
fn serialized_item_kind() -> Option<&'static str> {
|
||||||
|
@ -849,7 +906,7 @@ impl Item for Editor {
|
||||||
fn deserialize(
|
fn deserialize(
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
_workspace: WeakViewHandle<Workspace>,
|
_workspace: WeakViewHandle<Workspace>,
|
||||||
workspace_id: workspace::WorkspaceId,
|
workspace_id: WorkspaceId,
|
||||||
item_id: ItemId,
|
item_id: ItemId,
|
||||||
cx: &mut ViewContext<Pane>,
|
cx: &mut ViewContext<Pane>,
|
||||||
) -> Task<Result<ViewHandle<Self>>> {
|
) -> Task<Result<ViewHandle<Self>>> {
|
||||||
|
@ -879,9 +936,20 @@ impl Item for Editor {
|
||||||
.context("Project item at stored path was not a buffer")?;
|
.context("Project item at stored path was not a buffer")?;
|
||||||
Ok(pane.update(&mut cx, |_, cx| {
|
Ok(pane.update(&mut cx, |_, cx| {
|
||||||
cx.add_view(|cx| {
|
cx.add_view(|cx| {
|
||||||
let mut editor = Editor::for_buffer(buffer, Some(project), cx);
|
FollowableEditor(cx.add_view(|cx| {
|
||||||
editor.read_scroll_position_from_db(item_id, workspace_id, cx);
|
let mut editor = Editor::for_buffer(
|
||||||
editor
|
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;
|
type Item = Buffer;
|
||||||
|
|
||||||
fn for_project_item(
|
fn for_project_item(
|
||||||
|
@ -898,23 +966,26 @@ impl ProjectItem for Editor {
|
||||||
buffer: ModelHandle<Buffer>,
|
buffer: ModelHandle<Buffer>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> 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 FollowableEditor {
|
||||||
impl SearchableItem for Editor {
|
|
||||||
type Match = Range<Anchor>;
|
type Match = Range<Anchor>;
|
||||||
|
|
||||||
fn to_search_event(
|
fn to_search_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &Self::Event,
|
event: &Self::Event,
|
||||||
_: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<SearchEvent> {
|
) -> Option<SearchEvent> {
|
||||||
match event {
|
match event {
|
||||||
Event::BufferEdited => Some(SearchEvent::MatchesInvalidated),
|
Event::BufferEdited => Some(SearchEvent::MatchesInvalidated),
|
||||||
Event::SelectionsChanged { .. } => {
|
Event::SelectionsChanged { .. } => {
|
||||||
if self.selections.disjoint_anchors().len() == 1 {
|
if self.0.read(cx).selections.disjoint_anchors().len() == 1 {
|
||||||
Some(SearchEvent::ActiveMatchChanged)
|
Some(SearchEvent::ActiveMatchChanged)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -925,20 +996,27 @@ impl SearchableItem for Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
|
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>) {
|
fn update_matches(&mut self, matches: Vec<Range<Anchor>>, cx: &mut ViewContext<Self>) {
|
||||||
self.highlight_background::<BufferSearchHighlights>(
|
self.0.update(cx, |this, cx| {
|
||||||
matches,
|
this.highlight_background::<BufferSearchHighlights>(
|
||||||
|theme| theme.search.match_background,
|
matches,
|
||||||
cx,
|
|theme| theme.search.match_background,
|
||||||
);
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
|
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String {
|
||||||
let display_map = self.snapshot(cx).display_snapshot;
|
let (display_map, selection) = self.0.update(cx, |this, cx| {
|
||||||
let selection = self.selections.newest::<usize>(cx);
|
let display_map = this.snapshot(cx).display_snapshot;
|
||||||
|
let selection = this.selections.newest::<usize>(cx);
|
||||||
|
(display_map, selection)
|
||||||
|
});
|
||||||
if selection.start == selection.end {
|
if selection.start == selection.end {
|
||||||
let point = selection.start.to_display_point(&display_map);
|
let point = selection.start.to_display_point(&display_map);
|
||||||
let range = surrounding_word(&display_map, point);
|
let range = surrounding_word(&display_map, point);
|
||||||
|
@ -964,20 +1042,24 @@ impl SearchableItem for Editor {
|
||||||
matches: Vec<Range<Anchor>>,
|
matches: Vec<Range<Anchor>>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
self.unfold_ranges([matches[index].clone()], false, true, cx);
|
self.0.update(cx, |this, cx| {
|
||||||
let range = self.range_for_match(&matches[index]);
|
this.unfold_ranges([matches[index].clone()], false, true, cx);
|
||||||
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
let range = this.range_for_match(&matches[index]);
|
||||||
s.select_ranges([range]);
|
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>) {
|
fn select_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>) {
|
||||||
self.unfold_ranges(matches.clone(), false, false, cx);
|
self.0.update(cx, |this, cx| {
|
||||||
let mut ranges = Vec::new();
|
this.unfold_ranges(matches.clone(), false, false, cx);
|
||||||
for m in &matches {
|
let mut ranges = Vec::new();
|
||||||
ranges.push(self.range_for_match(&m))
|
for m in &matches {
|
||||||
}
|
ranges.push(this.range_for_match(&m))
|
||||||
self.change_selections(None, cx, |s| s.select_ranges(ranges));
|
}
|
||||||
|
this.change_selections(None, cx, |s| s.select_ranges(ranges));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
fn replace(
|
fn replace(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -985,7 +1067,7 @@ impl SearchableItem for Editor {
|
||||||
query: &SearchQuery,
|
query: &SearchQuery,
|
||||||
cx: &mut ViewContext<Self>,
|
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.snapshot(cx);
|
||||||
let text = text.text_for_range(identifier.clone()).collect::<Vec<_>>();
|
let text = text.text_for_range(identifier.clone()).collect::<Vec<_>>();
|
||||||
let text: Cow<_> = if text.len() == 1 {
|
let text: Cow<_> = if text.len() == 1 {
|
||||||
|
@ -996,8 +1078,10 @@ impl SearchableItem for Editor {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(replacement) = query.replacement_for(&text) {
|
if let Some(replacement) = query.replacement_for(&text) {
|
||||||
self.transact(cx, |this, cx| {
|
self.0.update(cx, |this, cx| {
|
||||||
this.edit([(identifier.clone(), Arc::from(&*replacement))], cx);
|
this.transact(cx, |this, cx| {
|
||||||
|
this.edit([(identifier.clone(), Arc::from(&*replacement))], cx);
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1009,9 +1093,10 @@ impl SearchableItem for Editor {
|
||||||
count: usize,
|
count: usize,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let buffer = self.buffer().read(cx).snapshot(cx);
|
let this = self.0.read(cx);
|
||||||
let current_index_position = if self.selections.disjoint_anchors().len() == 1 {
|
let buffer = this.buffer().read(cx).snapshot(cx);
|
||||||
self.selections.newest_anchor().head()
|
let current_index_position = if this.selections.disjoint_anchors().len() == 1 {
|
||||||
|
this.selections.newest_anchor().head()
|
||||||
} else {
|
} else {
|
||||||
matches[current_index].start
|
matches[current_index].start
|
||||||
};
|
};
|
||||||
|
@ -1055,7 +1140,7 @@ impl SearchableItem for Editor {
|
||||||
query: Arc<project::search::SearchQuery>,
|
query: Arc<project::search::SearchQuery>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Task<Vec<Range<Anchor>>> {
|
) -> 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 {
|
cx.background().spawn(async move {
|
||||||
let mut ranges = Vec::new();
|
let mut ranges = Vec::new();
|
||||||
if let Some((_, _, excerpt_buffer)) = buffer.as_singleton() {
|
if let Some((_, _, excerpt_buffer)) = buffer.as_singleton() {
|
||||||
|
@ -1098,10 +1183,11 @@ impl SearchableItem for Editor {
|
||||||
matches: Vec<Range<Anchor>>,
|
matches: Vec<Range<Anchor>>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
|
let this = self.0.read(cx);
|
||||||
active_match_index(
|
active_match_index(
|
||||||
&matches,
|
&matches,
|
||||||
&self.selections.newest_anchor().head(),
|
&this.selections.newest_anchor().head(),
|
||||||
&self.buffer().read(cx).snapshot(cx),
|
&this.buffer().read(cx).snapshot(cx),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1289,11 +1375,11 @@ mod tests {
|
||||||
|
|
||||||
impl language::File for TestFile {
|
impl language::File for TestFile {
|
||||||
fn path(&self) -> &Arc<Path> {
|
fn path(&self) -> &Arc<Path> {
|
||||||
&self.path
|
&self.0.path
|
||||||
}
|
}
|
||||||
|
|
||||||
fn full_path(&self, _: &gpui::AppContext) -> PathBuf {
|
fn full_path(&self, _: &gpui::AppContext) -> PathBuf {
|
||||||
self.full_path.clone()
|
self.0.full_path.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_local(&self) -> Option<&dyn language::LocalFile> {
|
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 std::path::PathBuf;
|
||||||
|
|
||||||
use db::sqlez_macros::sql;
|
use db::sqlez_macros::sql;
|
||||||
use db::{define_connection, query};
|
use db::{define_connection, query};
|
||||||
|
|
||||||
use workspace::{ItemId, WorkspaceDb, WorkspaceId};
|
use workspace::WorkspaceDb;
|
||||||
|
use workspace_types::{ItemId, WorkspaceId};
|
||||||
|
|
||||||
define_connection!(
|
define_connection!(
|
||||||
// Current schema shape using pseudo-rust syntax:
|
// 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]
|
[dependencies]
|
||||||
client = { path = "../client" }
|
client = { path = "../client" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
|
editor_extensions = { path = "../editor_extensions" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::system_specs::SystemSpecs;
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use client::{Client, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
|
use client::{Client, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
|
||||||
use editor::{Anchor, Editor};
|
use editor::{Anchor, Editor};
|
||||||
|
use editor_extensions::FollowableEditor;
|
||||||
use futures::AsyncReadExt;
|
use futures::AsyncReadExt;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
|
@ -58,7 +59,7 @@ struct FeedbackRequestBody<'a> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct FeedbackEditor {
|
pub(crate) struct FeedbackEditor {
|
||||||
system_specs: SystemSpecs,
|
system_specs: SystemSpecs,
|
||||||
editor: ViewHandle<Editor>,
|
editor: ViewHandle<FollowableEditor>,
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
pub allow_submission: bool,
|
pub allow_submission: bool,
|
||||||
}
|
}
|
||||||
|
@ -71,8 +72,8 @@ impl FeedbackEditor {
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let editor = cx.add_view(|cx| {
|
let editor = cx.add_view(|cx| {
|
||||||
let mut editor = Editor::for_buffer(buffer, Some(project.clone()), cx);
|
let mut editor = FollowableEditor::for_buffer(buffer, project.clone(), cx);
|
||||||
editor.set_vertical_scroll_margin(5, cx);
|
editor.0.update(cx, |this, cx| this.set_vertical_scroll_margin(5, cx));
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -92,7 +93,7 @@ impl FeedbackEditor {
|
||||||
return Task::ready(Ok(()));
|
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_char_count = feedback_text.chars().count();
|
||||||
let feedback_text = feedback_text.trim().to_string();
|
let feedback_text = feedback_text.trim().to_string();
|
||||||
|
|
||||||
|
@ -339,7 +340,7 @@ impl Item for FeedbackEditor {
|
||||||
{
|
{
|
||||||
let buffer = self
|
let buffer = self
|
||||||
.editor
|
.editor
|
||||||
.read(cx)
|
.read(cx).0.read(cx)
|
||||||
.buffer()
|
.buffer()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.as_singleton()
|
.as_singleton()
|
||||||
|
@ -373,7 +374,7 @@ impl Item for FeedbackEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
|
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]
|
[dependencies]
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
|
editor_extensions = { path = "../editor_extensions" }
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
fuzzy = { path = "../fuzzy" }
|
fuzzy = { path = "../fuzzy" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
menu = { path = "../menu" }
|
menu = { path = "../menu" }
|
||||||
picker = { path = "../picker" }
|
picker = { path = "../picker" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
project_types = { path = "../project_types" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
text = { path = "../text" }
|
text = { path = "../text" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
use collections::HashMap;
|
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 fuzzy::{CharBag, PathMatch, PathMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext, WeakViewHandle,
|
actions, elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
|
use project::{PathMatchCandidateSet, Project};
|
||||||
|
use project_types::{ProjectPath, WorktreeId};
|
||||||
use std::{
|
use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{
|
sync::{
|
||||||
|
@ -636,16 +638,22 @@ impl PickerDelegate for FileFinderDelegate {
|
||||||
cx.spawn(|_, mut cx| async move {
|
cx.spawn(|_, mut cx| async move {
|
||||||
let item = open_task.await.log_err()?;
|
let item = open_task.await.log_err()?;
|
||||||
if let Some(row) = row {
|
if let Some(row) = row {
|
||||||
if let Some(active_editor) = item.downcast::<Editor>() {
|
if let Some(active_editor) = item.downcast::<FollowableEditor>() {
|
||||||
active_editor
|
active_editor
|
||||||
.downgrade()
|
.downgrade()
|
||||||
.update(&mut cx, |editor, cx| {
|
.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
|
let point = snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.clip_point(Point::new(row, col), Bias::Left);
|
.clip_point(Point::new(row, col), Bias::Left);
|
||||||
editor.change_selections(Some(Autoscroll::center()), cx, |s| {
|
editor.0.update(cx, |this, cx| {
|
||||||
s.select_ranges([point..point])
|
this.change_selections(
|
||||||
|
Some(Autoscroll::center()),
|
||||||
|
cx,
|
||||||
|
|s| s.select_ranges([point..point]),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.log_err();
|
.log_err();
|
||||||
|
|
|
@ -11,6 +11,7 @@ doctest = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
|
editor_extensions = { path = "../editor_extensions" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use collections::{HashMap, VecDeque};
|
use collections::{HashMap, VecDeque};
|
||||||
use editor::{Editor, MoveToEnd};
|
use editor::{Editor, MoveToEnd};
|
||||||
|
use editor_extensions::FollowableEditor;
|
||||||
use futures::{channel::mpsc, StreamExt};
|
use futures::{channel::mpsc, StreamExt};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
|
@ -49,7 +50,7 @@ struct LanguageServerRpcState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LspLogView {
|
pub struct LspLogView {
|
||||||
pub(crate) editor: ViewHandle<Editor>,
|
pub(crate) editor: ViewHandle<FollowableEditor>,
|
||||||
editor_subscription: Subscription,
|
editor_subscription: Subscription,
|
||||||
log_store: ModelHandle<LogStore>,
|
log_store: ModelHandle<LogStore>,
|
||||||
current_server_id: Option<LanguageServerId>,
|
current_server_id: Option<LanguageServerId>,
|
||||||
|
@ -388,9 +389,11 @@ impl LspLogView {
|
||||||
} else {
|
} else {
|
||||||
this.current_server_id = None;
|
this.current_server_id = None;
|
||||||
this.editor.update(cx, |editor, cx| {
|
this.editor.update(cx, |editor, cx| {
|
||||||
editor.set_read_only(false);
|
editor.0.update(cx, |this, cx| {
|
||||||
editor.clear(cx);
|
this.set_read_only(false);
|
||||||
editor.set_read_only(true);
|
this.clear(cx);
|
||||||
|
this.set_read_only(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
@ -417,10 +420,12 @@ impl LspLogView {
|
||||||
|| (!*is_rpc && !log_view.is_showing_rpc_trace)
|
|| (!*is_rpc && !log_view.is_showing_rpc_trace)
|
||||||
{
|
{
|
||||||
log_view.editor.update(cx, |editor, cx| {
|
log_view.editor.update(cx, |editor, cx| {
|
||||||
editor.set_read_only(false);
|
editor.0.update(cx, |this, cx| {
|
||||||
editor.handle_input(entry.trim(), cx);
|
this.set_read_only(false);
|
||||||
editor.handle_input("\n", cx);
|
this.handle_input(entry.trim(), cx);
|
||||||
editor.set_read_only(true);
|
this.handle_input("\n", cx);
|
||||||
|
this.set_read_only(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -445,13 +450,15 @@ impl LspLogView {
|
||||||
fn editor_for_logs(
|
fn editor_for_logs(
|
||||||
log_contents: String,
|
log_contents: String,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> (ViewHandle<Editor>, Subscription) {
|
) -> (ViewHandle<FollowableEditor>, Subscription) {
|
||||||
let editor = cx.add_view(|cx| {
|
let editor = cx.add_view(|cx| {
|
||||||
let mut editor = Editor::multi_line(None, cx);
|
FollowableEditor(cx.add_view(|cx| {
|
||||||
editor.set_text(log_contents, cx);
|
let mut editor = Editor::multi_line(None, cx);
|
||||||
editor.move_to_end(&MoveToEnd, cx);
|
editor.set_text(log_contents, cx);
|
||||||
editor.set_read_only(true);
|
editor.move_to_end(&MoveToEnd, cx);
|
||||||
editor
|
editor.set_read_only(true);
|
||||||
|
editor
|
||||||
|
}))
|
||||||
});
|
});
|
||||||
let editor_subscription = cx.subscribe(&editor, |_, _, event, cx| cx.emit(event.clone()));
|
let editor_subscription = cx.subscribe(&editor, |_, _, event, cx| cx.emit(event.clone()));
|
||||||
(editor, editor_subscription)
|
(editor, editor_subscription)
|
||||||
|
@ -534,6 +541,8 @@ impl LspLogView {
|
||||||
let (editor, editor_subscription) = Self::editor_for_logs(rpc_log, cx);
|
let (editor, editor_subscription) = Self::editor_for_logs(rpc_log, cx);
|
||||||
let language = self.project.read(cx).languages().language_for_name("JSON");
|
let language = self.project.read(cx).languages().language_for_name("JSON");
|
||||||
editor
|
editor
|
||||||
|
.read(cx)
|
||||||
|
.0
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.buffer()
|
.buffer()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
|
@ -620,7 +629,7 @@ impl Item for LspLogView {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SearchableItem for LspLogView {
|
impl SearchableItem for LspLogView {
|
||||||
type Match = <Editor as SearchableItem>::Match;
|
type Match = <FollowableEditor as SearchableItem>::Match;
|
||||||
|
|
||||||
fn to_search_event(
|
fn to_search_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -811,9 +820,11 @@ impl View for LspLogToolbarItemView {
|
||||||
if let Some(log_view) = this.log_view.as_ref() {
|
if let Some(log_view) = this.log_view.as_ref() {
|
||||||
log_view.update(cx, |log_view, cx| {
|
log_view.update(cx, |log_view, cx| {
|
||||||
log_view.editor.update(cx, |editor, cx| {
|
log_view.editor.update(cx, |editor, cx| {
|
||||||
editor.set_read_only(false);
|
editor.0.update(cx, |this, cx| {
|
||||||
editor.clear(cx);
|
this.set_read_only(false);
|
||||||
editor.set_read_only(true);
|
this.clear(cx);
|
||||||
|
this.set_read_only(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1015,7 +1026,7 @@ impl Entity for LogStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity for LspLogView {
|
impl Entity for LspLogView {
|
||||||
type Event = editor::Event;
|
type Event = <FollowableEditor as Entity>::Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity for LspLogToolbarItemView {
|
impl Entity for LspLogToolbarItemView {
|
||||||
|
|
|
@ -34,6 +34,7 @@ language = { path = "../language" }
|
||||||
lsp = { path = "../lsp" }
|
lsp = { path = "../lsp" }
|
||||||
node_runtime = { path = "../node_runtime" }
|
node_runtime = { path = "../node_runtime" }
|
||||||
prettier = { path = "../prettier" }
|
prettier = { path = "../prettier" }
|
||||||
|
project_types = {path = "../project_types"}
|
||||||
rpc = { path = "../rpc" }
|
rpc = { path = "../rpc" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
sum_tree = { path = "../sum_tree" }
|
sum_tree = { path = "../sum_tree" }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
mod ignore;
|
mod ignore;
|
||||||
mod lsp_command;
|
mod lsp_command;
|
||||||
pub mod project_settings;
|
pub use project_types::project_settings;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
pub mod terminals;
|
pub mod terminals;
|
||||||
pub mod worktree;
|
pub mod worktree;
|
||||||
|
@ -29,11 +29,8 @@ use gpui::{
|
||||||
executor::Background, AnyModelHandle, AppContext, AsyncAppContext, BorrowAppContext, Entity,
|
executor::Background, AnyModelHandle, AppContext, AsyncAppContext, BorrowAppContext, Entity,
|
||||||
ModelContext, ModelHandle, Task, WeakModelHandle,
|
ModelContext, ModelHandle, Task, WeakModelHandle,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::{
|
language_settings::{language_settings, FormatOnSave, Formatter, LanguageSettings},
|
||||||
language_settings, FormatOnSave, Formatter, InlayHintKind, LanguageSettings,
|
|
||||||
},
|
|
||||||
point_to_lsp,
|
point_to_lsp,
|
||||||
proto::{
|
proto::{
|
||||||
deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
|
deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
|
||||||
|
@ -47,8 +44,8 @@ use language::{
|
||||||
};
|
};
|
||||||
use log::error;
|
use log::error;
|
||||||
use lsp::{
|
use lsp::{
|
||||||
DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions,
|
DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, LanguageServer,
|
||||||
DocumentHighlightKind, LanguageServer, LanguageServerBinary, LanguageServerId, OneOf,
|
LanguageServerBinary, LanguageServerId, OneOf,
|
||||||
};
|
};
|
||||||
use lsp_command::*;
|
use lsp_command::*;
|
||||||
use node_runtime::NodeRuntime;
|
use node_runtime::NodeRuntime;
|
||||||
|
@ -56,6 +53,7 @@ use parking_lot::Mutex;
|
||||||
use postage::watch;
|
use postage::watch;
|
||||||
use prettier::Prettier;
|
use prettier::Prettier;
|
||||||
use project_settings::{LspSettings, ProjectSettings};
|
use project_settings::{LspSettings, ProjectSettings};
|
||||||
|
use project_types::*;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use search::SearchQuery;
|
use search::SearchQuery;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
@ -338,135 +336,12 @@ pub struct LanguageServerProgress {
|
||||||
pub last_update_at: Instant,
|
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)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize)]
|
||||||
pub struct DiagnosticSummary {
|
pub struct DiagnosticSummary {
|
||||||
pub error_count: usize,
|
pub error_count: usize,
|
||||||
pub warning_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 {
|
impl DiagnosticSummary {
|
||||||
fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
|
fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
|
@ -528,26 +403,11 @@ impl ProjectEntryId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum FormatTrigger {
|
|
||||||
Save,
|
|
||||||
Manual,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ProjectLspAdapterDelegate {
|
struct ProjectLspAdapterDelegate {
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
http_client: Arc<dyn HttpClient>,
|
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)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
enum SearchMatchCandidate {
|
enum SearchMatchCandidate {
|
||||||
OpenBuffer {
|
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 {
|
impl ProjectLspAdapterDelegate {
|
||||||
fn new(project: &Project, cx: &ModelContext<Project>) -> Arc<Self> {
|
fn new(project: &Project, cx: &ModelContext<Project>) -> Arc<Self> {
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
|
|
|
@ -36,6 +36,7 @@ use postage::{
|
||||||
prelude::{Sink as _, Stream as _},
|
prelude::{Sink as _, Stream as _},
|
||||||
watch,
|
watch,
|
||||||
};
|
};
|
||||||
|
use project_types::WorktreeId;
|
||||||
use smol::channel::{self, Sender};
|
use smol::channel::{self, Sender};
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
|
@ -56,10 +57,6 @@ use std::{
|
||||||
};
|
};
|
||||||
use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet};
|
use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet};
|
||||||
use util::{paths::HOME, ResultExt};
|
use util::{paths::HOME, ResultExt};
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
|
|
||||||
pub struct WorktreeId(usize);
|
|
||||||
|
|
||||||
pub enum Worktree {
|
pub enum Worktree {
|
||||||
Local(LocalWorktree),
|
Local(LocalWorktree),
|
||||||
Remote(RemoteWorktree),
|
Remote(RemoteWorktree),
|
||||||
|
@ -407,7 +404,7 @@ impl Worktree {
|
||||||
) -> ModelHandle<Self> {
|
) -> ModelHandle<Self> {
|
||||||
cx.add_model(|cx: &mut ModelContext<Self>| {
|
cx.add_model(|cx: &mut ModelContext<Self>| {
|
||||||
let snapshot = Snapshot {
|
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)),
|
abs_path: Arc::from(PathBuf::from(worktree.abs_path)),
|
||||||
root_name: worktree.root_name.clone(),
|
root_name: worktree.root_name.clone(),
|
||||||
root_char_bag: worktree
|
root_char_bag: worktree
|
||||||
|
@ -2493,30 +2490,6 @@ async fn build_gitignore(abs_path: &Path, fs: &dyn Fs) -> Result<Gitignore> {
|
||||||
Ok(builder.build()?)
|
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 {
|
impl Deref for Worktree {
|
||||||
type Target = Snapshot;
|
type Target = Snapshot;
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ editor = { path = "../editor" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
menu = { path = "../menu" }
|
menu = { path = "../menu" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
project_types = { path = "../project_types" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
|
|
@ -23,9 +23,10 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use menu::{Confirm, SelectNext, SelectPrev};
|
use menu::{Confirm, SelectNext, SelectPrev};
|
||||||
use project::{
|
use project::{
|
||||||
repository::GitFileStatus, Entry, EntryKind, Fs, Project, ProjectEntryId, ProjectPath,
|
repository::GitFileStatus, Entry, EntryKind, Fs, Project, ProjectEntryId,
|
||||||
Worktree, WorktreeId,
|
Worktree,
|
||||||
};
|
};
|
||||||
|
use project_types::{ProjectPath, WorktreeId};
|
||||||
use project_panel_settings::{ProjectPanelDockPosition, ProjectPanelSettings};
|
use project_panel_settings::{ProjectPanelDockPosition, ProjectPanelSettings};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
|
|
|
@ -10,10 +10,12 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
|
editor_extensions = { path = "../editor_extensions" }
|
||||||
fuzzy = { path = "../fuzzy" }
|
fuzzy = { path = "../fuzzy" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
picker = { path = "../picker" }
|
picker = { path = "../picker" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
project_types = { path = "../project_types" }
|
||||||
text = { path = "../text" }
|
text = { path = "../text" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
use editor::{
|
use editor::{
|
||||||
combine_syntax_and_fuzzy_match_highlights, scroll::autoscroll::Autoscroll,
|
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 fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext, WeakViewHandle,
|
actions, elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use picker::{Picker, PickerDelegate, PickerEvent};
|
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 std::{borrow::Cow, cmp::Reverse, sync::Arc};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
@ -123,15 +125,15 @@ impl PickerDelegate for ProjectSymbolsDelegate {
|
||||||
.clip_point_utf16(symbol.range.start, Bias::Left);
|
.clip_point_utf16(symbol.range.start, Bias::Left);
|
||||||
|
|
||||||
let editor = if secondary {
|
let editor = if secondary {
|
||||||
workspace.split_project_item::<Editor>(buffer, cx)
|
workspace.split_project_item::<FollowableEditor>(buffer, cx)
|
||||||
} else {
|
} else {
|
||||||
workspace.open_project_item::<Editor>(buffer, cx)
|
workspace.open_project_item::<FollowableEditor>(buffer, cx)
|
||||||
};
|
};
|
||||||
|
|
||||||
editor.update(cx, |editor, 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])
|
s.select_ranges([position..position])
|
||||||
});
|
}));
|
||||||
});
|
});
|
||||||
})?;
|
})?;
|
||||||
Ok::<_, anyhow::Error>(())
|
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"
|
bitflags = "1"
|
||||||
collections = { path = "../collections" }
|
collections = { path = "../collections" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
|
editor_extensions = { path = "../editor_extensions" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
menu = { path = "../menu" }
|
menu = { path = "../menu" }
|
||||||
|
|
|
@ -9,9 +9,10 @@ use crate::{
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use editor::{
|
use editor::{
|
||||||
items::active_match_index, scroll::autoscroll::Autoscroll, Anchor, Editor, MultiBuffer,
|
scroll::autoscroll::Autoscroll, Anchor, Editor, MultiBuffer,
|
||||||
SelectAll, MAX_TAB_TITLE_LEN,
|
SelectAll, MAX_TAB_TITLE_LEN,
|
||||||
};
|
};
|
||||||
|
use editor_extensions::{active_match_index, FollowableEditor};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
|
@ -134,7 +135,7 @@ pub struct ProjectSearchView {
|
||||||
model: ModelHandle<ProjectSearch>,
|
model: ModelHandle<ProjectSearch>,
|
||||||
query_editor: ViewHandle<Editor>,
|
query_editor: ViewHandle<Editor>,
|
||||||
replacement_editor: ViewHandle<Editor>,
|
replacement_editor: ViewHandle<Editor>,
|
||||||
results_editor: ViewHandle<Editor>,
|
results_editor: ViewHandle<FollowableEditor>,
|
||||||
semantic_state: Option<SemanticState>,
|
semantic_state: Option<SemanticState>,
|
||||||
semantic_permissioned: Option<bool>,
|
semantic_permissioned: Option<bool>,
|
||||||
search_options: SearchOptions,
|
search_options: SearchOptions,
|
||||||
|
@ -650,8 +651,8 @@ impl Item for ProjectSearchView {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
|
fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
|
||||||
self.results_editor.update(cx, |editor, _| {
|
self.results_editor.update(cx, |editor, cx| {
|
||||||
editor.set_nav_history(Some(nav_history));
|
editor.set_nav_history(nav_history,cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -665,7 +666,7 @@ impl Item for ProjectSearchView {
|
||||||
ViewEvent::UpdateTab => {
|
ViewEvent::UpdateTab => {
|
||||||
smallvec::smallvec![ItemEvent::UpdateBreadcrumbs, ItemEvent::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],
|
ViewEvent::Dismiss => smallvec::smallvec![ItemEvent::CloseItem],
|
||||||
_ => SmallVec::new(),
|
_ => SmallVec::new(),
|
||||||
}
|
}
|
||||||
|
@ -965,8 +966,8 @@ impl ProjectSearchView {
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
let results_editor = cx.add_view(|cx| {
|
let results_editor = cx.add_view(|cx| {
|
||||||
let mut editor = Editor::for_multibuffer(excerpts, Some(project.clone()), cx);
|
let mut editor = FollowableEditor::for_multibuffer(excerpts, project.clone(), cx);
|
||||||
editor.set_searchable(false);
|
editor.0.update(cx, |this, _| this.set_searchable(false));
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
cx.observe(&results_editor, |_, _, cx| cx.emit(ViewEvent::UpdateTab))
|
cx.observe(&results_editor, |_, _, cx| cx.emit(ViewEvent::UpdateTab))
|
||||||
|
@ -1100,7 +1101,7 @@ impl ProjectSearchView {
|
||||||
.or_else(|| workspace.item_of_type::<ProjectSearchView>(cx));
|
.or_else(|| workspace.item_of_type::<ProjectSearchView>(cx));
|
||||||
|
|
||||||
let query = workspace.active_item(cx).and_then(|item| {
|
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);
|
let query = editor.query_suggestion(cx);
|
||||||
if query.is_empty() {
|
if query.is_empty() {
|
||||||
None
|
None
|
||||||
|
@ -1246,11 +1247,14 @@ impl ProjectSearchView {
|
||||||
|
|
||||||
let range_to_select = match_ranges[new_index].clone();
|
let range_to_select = match_ranges[new_index].clone();
|
||||||
self.results_editor.update(cx, |editor, cx| {
|
self.results_editor.update(cx, |editor, cx| {
|
||||||
let range_to_select = editor.range_for_match(&range_to_select);
|
editor.0.update(cx, |this, cx| {
|
||||||
editor.unfold_ranges([range_to_select.clone()], false, true, cx);
|
let range_to_select = this.range_for_match(&range_to_select);
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
this.unfold_ranges([range_to_select.clone()], false, true, cx);
|
||||||
s.select_ranges([range_to_select])
|
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
|
let range_to_select = match_ranges
|
||||||
.first()
|
.first()
|
||||||
.clone()
|
.clone()
|
||||||
.map(|range| editor.range_for_match(range));
|
.map(|range| editor.0.update(cx, |this, cx| this.range_for_match(range)));
|
||||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
editor.0.update(cx, |this, cx| this.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||||
s.select_ranges(range_to_select)
|
s.select_ranges(range_to_select)
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
editor.highlight_background::<Self>(
|
editor.0.update(cx, |this, cx| this.highlight_background::<Self>(
|
||||||
match_ranges,
|
match_ranges,
|
||||||
|theme| theme.search.match_background,
|
|theme| theme.search.match_background,
|
||||||
cx,
|
cx,
|
||||||
);
|
));
|
||||||
});
|
});
|
||||||
if is_new_search && self.query_editor.is_focused(cx) {
|
if is_new_search && self.query_editor.is_focused(cx) {
|
||||||
self.focus_results_editor(cx);
|
self.focus_results_editor(cx);
|
||||||
|
@ -1315,8 +1319,8 @@ impl ProjectSearchView {
|
||||||
let results_editor = self.results_editor.read(cx);
|
let results_editor = self.results_editor.read(cx);
|
||||||
let new_index = active_match_index(
|
let new_index = active_match_index(
|
||||||
&self.model.read(cx).match_ranges,
|
&self.model.read(cx).match_ranges,
|
||||||
&results_editor.selections.newest_anchor().head(),
|
&results_editor.0.read(cx).selections.newest_anchor().head(),
|
||||||
&results_editor.buffer().read(cx).snapshot(cx),
|
&results_editor.0.read(cx).buffer().read(cx).snapshot(cx),
|
||||||
);
|
);
|
||||||
if self.active_match_index != new_index {
|
if self.active_match_index != new_index {
|
||||||
self.active_match_index = new_index;
|
self.active_match_index = new_index;
|
||||||
|
|
|
@ -14,6 +14,7 @@ collections = { path = "../collections" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
project_types = { path = "../project_types" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
picker = { path = "../picker" }
|
picker = { path = "../picker" }
|
||||||
|
|
|
@ -21,7 +21,8 @@ use ordered_float::OrderedFloat;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use parsing::{CodeContextRetriever, Span, SpanDigest, PARSEABLE_ENTIRE_FILE_TYPES};
|
use parsing::{CodeContextRetriever, Span, SpanDigest, PARSEABLE_ENTIRE_FILE_TYPES};
|
||||||
use postage::watch;
|
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 smol::channel;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Reverse,
|
cmp::Reverse,
|
||||||
|
|
|
@ -32,10 +32,12 @@ language = { path = "../language" }
|
||||||
menu = { path = "../menu" }
|
menu = { path = "../menu" }
|
||||||
node_runtime = { path = "../node_runtime" }
|
node_runtime = { path = "../node_runtime" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
project_types = {path = "../project_types"}
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
terminal = { path = "../terminal" }
|
terminal = { path = "../terminal" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
|
workspace_types = {path = "../workspace_types"}
|
||||||
|
|
||||||
async-recursion = "1.0.0"
|
async-recursion = "1.0.0"
|
||||||
itertools = "0.10"
|
itertools = "0.10"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
pane, persistence::model::ItemId, searchable::SearchableItemHandle, FollowableItemBuilders,
|
pane, searchable::SearchableItemHandle, FollowableItemBuilders, ItemNavHistory, Pane,
|
||||||
ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
|
ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
|
||||||
};
|
};
|
||||||
use crate::{AutosaveSetting, DelayedDebouncedEditAction, WorkspaceSettings};
|
use crate::{AutosaveSetting, DelayedDebouncedEditAction, WorkspaceSettings};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
@ -14,7 +14,8 @@ use gpui::{
|
||||||
fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, ModelHandle, Task, View,
|
fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, ModelHandle, Task, View,
|
||||||
ViewContext, ViewHandle, WeakViewHandle, WindowContext,
|
ViewContext, ViewHandle, WeakViewHandle, WindowContext,
|
||||||
};
|
};
|
||||||
use project::{Project, ProjectEntryId, ProjectPath};
|
use project::{Project, ProjectEntryId};
|
||||||
|
use project_types::ProjectPath;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use settings::Setting;
|
use settings::Setting;
|
||||||
|
@ -34,6 +35,7 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use theme::Theme;
|
use theme::Theme;
|
||||||
|
use workspace_types::ItemId;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct ItemSettings {
|
pub struct ItemSettings {
|
||||||
|
|
|
@ -28,7 +28,8 @@ use gpui::{
|
||||||
ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||||
WindowContext,
|
WindowContext,
|
||||||
};
|
};
|
||||||
use project::{Project, ProjectEntryId, ProjectPath};
|
use project::{Project, ProjectEntryId};
|
||||||
|
use project_types::ProjectPath;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
|
@ -1963,20 +1964,26 @@ impl View for Pane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ItemNavHistory {
|
impl workspace_types::NavigationHistorySink for ItemNavHistory {
|
||||||
pub fn push<D: 'static + Any>(&mut self, data: Option<D>, cx: &mut WindowContext) {
|
fn push_any(&mut self, data: Option<Box<dyn Any>>, cx: &mut WindowContext) {
|
||||||
self.history.push(data, self.item.clone(), cx);
|
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 {
|
impl NavHistory {
|
||||||
pub fn for_each_entry(
|
pub fn for_each_entry(
|
||||||
&self,
|
&self,
|
||||||
|
@ -2035,9 +2042,9 @@ impl NavHistory {
|
||||||
entry
|
entry
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push<D: 'static + Any>(
|
pub fn push(
|
||||||
&mut self,
|
&mut self,
|
||||||
data: Option<D>,
|
data: Option<Box<dyn Any>>,
|
||||||
item: Rc<dyn WeakItemHandle>,
|
item: Rc<dyn WeakItemHandle>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) {
|
) {
|
||||||
|
@ -2050,7 +2057,7 @@ impl NavHistory {
|
||||||
}
|
}
|
||||||
state.backward_stack.push_back(NavigationEntry {
|
state.backward_stack.push_back(NavigationEntry {
|
||||||
item,
|
item,
|
||||||
data: data.map(|data| Box::new(data) as Box<dyn Any>),
|
data,
|
||||||
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
||||||
});
|
});
|
||||||
state.forward_stack.clear();
|
state.forward_stack.clear();
|
||||||
|
@ -2061,7 +2068,7 @@ impl NavHistory {
|
||||||
}
|
}
|
||||||
state.forward_stack.push_back(NavigationEntry {
|
state.forward_stack.push_back(NavigationEntry {
|
||||||
item,
|
item,
|
||||||
data: data.map(|data| Box::new(data) as Box<dyn Any>),
|
data,
|
||||||
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2071,7 +2078,7 @@ impl NavHistory {
|
||||||
}
|
}
|
||||||
state.backward_stack.push_back(NavigationEntry {
|
state.backward_stack.push_back(NavigationEntry {
|
||||||
item,
|
item,
|
||||||
data: data.map(|data| Box::new(data) as Box<dyn Any>),
|
data,
|
||||||
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2081,7 +2088,7 @@ impl NavHistory {
|
||||||
}
|
}
|
||||||
state.closed_stack.push_back(NavigationEntry {
|
state.closed_stack.push_back(NavigationEntry {
|
||||||
item,
|
item,
|
||||||
data: data.map(|data| Box::new(data) as Box<dyn Any>),
|
data,
|
||||||
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ use gpui::{
|
||||||
AnyViewHandle, Axis, ModelHandle, ViewContext, ViewHandle,
|
AnyViewHandle, Axis, ModelHandle, ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use serde::Deserialize;
|
|
||||||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||||
use theme::Theme;
|
use theme::Theme;
|
||||||
|
use workspace_types::SplitDirection;
|
||||||
|
|
||||||
const HANDLE_HITBOX_SIZE: f32 = 4.0;
|
const HANDLE_HITBOX_SIZE: f32 = 4.0;
|
||||||
const HORIZONTAL_MIN_SIZE: f32 = 80.;
|
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 {
|
mod element {
|
||||||
use std::{cell::RefCell, iter::from_fn, ops::Range, rc::Rc};
|
use std::{cell::RefCell, iter::from_fn, ops::Range, rc::Rc};
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use workspace_types::ItemId;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct WorkspaceLocation(Arc<Vec<PathBuf>>);
|
pub struct WorkspaceLocation(Arc<Vec<PathBuf>>);
|
||||||
|
@ -284,7 +285,6 @@ impl SerializedPane {
|
||||||
|
|
||||||
pub type GroupId = i64;
|
pub type GroupId = i64;
|
||||||
pub type PaneId = i64;
|
pub type PaneId = i64;
|
||||||
pub type ItemId = usize;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct SerializedItem {
|
pub struct SerializedItem {
|
||||||
|
|
|
@ -17,6 +17,7 @@ use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
};
|
};
|
||||||
|
use workspace_types::NavigationHistorySink;
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Close,
|
Close,
|
||||||
|
@ -100,7 +101,7 @@ impl Item for SharedScreen {
|
||||||
}
|
}
|
||||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(nav_history) = self.nav_history.as_mut() {
|
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,
|
FutureExt, StreamExt,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
|
||||||
elements::*,
|
elements::*,
|
||||||
geometry::{
|
geometry::{
|
||||||
rect::RectF,
|
rect::RectF,
|
||||||
|
@ -67,12 +66,10 @@ use notifications::{NotificationHandle, NotifyResultExt};
|
||||||
pub use pane::*;
|
pub use pane::*;
|
||||||
pub use pane_group::*;
|
pub use pane_group::*;
|
||||||
use persistence::{model::SerializedItem, DB};
|
use persistence::{model::SerializedItem, DB};
|
||||||
pub use persistence::{
|
pub use persistence::{model::WorkspaceLocation, WorkspaceDb, DB as WORKSPACE_DB};
|
||||||
model::{ItemId, WorkspaceLocation},
|
|
||||||
WorkspaceDb, DB as WORKSPACE_DB,
|
|
||||||
};
|
|
||||||
use postage::prelude::Stream;
|
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 serde::Deserialize;
|
||||||
use shared_screen::SharedScreen;
|
use shared_screen::SharedScreen;
|
||||||
use status_bar::StatusBar;
|
use status_bar::StatusBar;
|
||||||
|
@ -81,6 +78,7 @@ use theme::{Theme, ThemeSettings};
|
||||||
pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
|
pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings};
|
pub use workspace_settings::{AutosaveSetting, GitGutterSetting, WorkspaceSettings};
|
||||||
|
pub use workspace_types::*;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref ZED_WINDOW_SIZE: Option<Vector2F> = env::var("ZED_WINDOW_SIZE")
|
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)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct RemoveWorktreeFromProject(pub WorktreeId);
|
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)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct OpenPaths {
|
pub struct OpenPaths {
|
||||||
pub paths: Vec<PathBuf>,
|
pub paths: Vec<PathBuf>,
|
||||||
|
@ -246,8 +214,6 @@ impl_actions!(
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
pub type WorkspaceId = i64;
|
|
||||||
|
|
||||||
pub fn init_settings(cx: &mut AppContext) {
|
pub fn init_settings(cx: &mut AppContext) {
|
||||||
settings::register::<WorkspaceSettings>(cx);
|
settings::register::<WorkspaceSettings>(cx);
|
||||||
settings::register::<item::ItemSettings>(cx);
|
settings::register::<item::ItemSettings>(cx);
|
||||||
|
@ -598,12 +564,6 @@ struct ActiveModal {
|
||||||
previously_focused_view_id: Option<usize>,
|
previously_focused_view_id: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct ViewId {
|
|
||||||
pub creator: PeerId,
|
|
||||||
pub id: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct FollowerState {
|
struct FollowerState {
|
||||||
leader_id: PeerId,
|
leader_id: PeerId,
|
||||||
|
@ -4071,24 +4031,6 @@ impl Entity for WorkspaceStore {
|
||||||
type Event = ();
|
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 {
|
pub trait WorkspaceHandle {
|
||||||
fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath>;
|
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" }
|
diagnostics = { path = "../diagnostics" }
|
||||||
db = { path = "../db" }
|
db = { path = "../db" }
|
||||||
editor = { path = "../editor" }
|
editor = { path = "../editor" }
|
||||||
|
editor_extensions = { path = "../editor_extensions" }
|
||||||
feedback = { path = "../feedback" }
|
feedback = { path = "../feedback" }
|
||||||
file_finder = { path = "../file_finder" }
|
file_finder = { path = "../file_finder" }
|
||||||
search = { path = "../search" }
|
search = { path = "../search" }
|
||||||
|
|
|
@ -93,7 +93,7 @@ fn main() {
|
||||||
if cx.has_global::<Weak<AppState>>() {
|
if cx.has_global::<Weak<AppState>>() {
|
||||||
if let Some(app_state) = cx.global::<Weak<AppState>>().upgrade() {
|
if let Some(app_state) = cx.global::<Weak<AppState>>().upgrade() {
|
||||||
workspace::open_new(&app_state, cx, |workspace, 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();
|
.detach();
|
||||||
}
|
}
|
||||||
|
@ -343,7 +343,7 @@ async fn restore_or_create_workspace(app_state: &Arc<AppState>, mut cx: AsyncApp
|
||||||
} else {
|
} else {
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
workspace::open_new(app_state, cx, |workspace, 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();
|
.detach();
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,6 +15,7 @@ use collab_ui::CollabTitlebarItem; // TODO: Add back toggle collab ui shortcut
|
||||||
use collections::VecDeque;
|
use collections::VecDeque;
|
||||||
pub use editor;
|
pub use editor;
|
||||||
use editor::{Editor, MultiBuffer};
|
use editor::{Editor, MultiBuffer};
|
||||||
|
use editor_extensions::FollowableEditor;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use feedback::{
|
use feedback::{
|
||||||
|
@ -195,7 +196,11 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
||||||
});
|
});
|
||||||
workspace.add_item(
|
workspace.add_item(
|
||||||
Box::new(cx.add_view(|cx| {
|
Box::new(cx.add_view(|cx| {
|
||||||
Editor::for_multibuffer(buffer, Some(project.clone()), cx)
|
FollowableEditor::for_multibuffer(
|
||||||
|
buffer,
|
||||||
|
project.clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
})),
|
})),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -246,7 +251,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
||||||
move |_: &NewWindow, cx: &mut AppContext| {
|
move |_: &NewWindow, cx: &mut AppContext| {
|
||||||
if let Some(app_state) = app_state.upgrade() {
|
if let Some(app_state) = app_state.upgrade() {
|
||||||
open_new(&app_state, cx, |workspace, cx| {
|
open_new(&app_state, cx, |workspace, cx| {
|
||||||
Editor::new_file(workspace, &Default::default(), cx)
|
editor_extensions::new_file(workspace, &Default::default(), cx)
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
@ -257,7 +262,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
||||||
move |_: &NewFile, cx: &mut AppContext| {
|
move |_: &NewFile, cx: &mut AppContext| {
|
||||||
if let Some(app_state) = app_state.upgrade() {
|
if let Some(app_state) = app_state.upgrade() {
|
||||||
open_new(&app_state, cx, |workspace, cx| {
|
open_new(&app_state, cx, |workspace, cx| {
|
||||||
Editor::new_file(workspace, &Default::default(), cx)
|
editor_extensions::new_file(workspace, &Default::default(), cx)
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
@ -332,7 +337,7 @@ pub fn initialize_workspace(
|
||||||
let feedback_button = cx.add_view(|_| {
|
let feedback_button = cx.add_view(|_| {
|
||||||
feedback::deploy_feedback_button::DeployFeedbackButton::new(workspace)
|
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| {
|
workspace.status_bar().update(cx, |status_bar, cx| {
|
||||||
status_bar.add_left_item(diagnostic_summary, cx);
|
status_bar.add_left_item(diagnostic_summary, cx);
|
||||||
status_bar.add_left_item(activity_indicator, 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())
|
MultiBuffer::singleton(buffer, cx).with_title("Log".into())
|
||||||
});
|
});
|
||||||
workspace.add_item(
|
workspace.add_item(
|
||||||
Box::new(
|
Box::new(cx.add_view(|cx| {
|
||||||
cx.add_view(|cx| {
|
FollowableEditor::for_multibuffer(buffer, project, cx)
|
||||||
Editor::for_multibuffer(buffer, Some(project), cx)
|
})),
|
||||||
}),
|
|
||||||
),
|
|
||||||
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())
|
MultiBuffer::singleton(buffer, cx).with_title("Telemetry Log".into())
|
||||||
});
|
});
|
||||||
workspace.add_item(
|
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,
|
cx,
|
||||||
);
|
);
|
||||||
}).log_err()?;
|
}).log_err()?;
|
||||||
|
@ -741,7 +744,7 @@ fn open_bundled_file(
|
||||||
});
|
});
|
||||||
workspace.add_item(
|
workspace.add_item(
|
||||||
Box::new(cx.add_view(|cx| {
|
Box::new(cx.add_view(|cx| {
|
||||||
Editor::for_multibuffer(buffer, Some(project.clone()), cx)
|
FollowableEditor::for_multibuffer(buffer, project.clone(), cx)
|
||||||
})),
|
})),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue