Working underline based on symbol origin
This commit is contained in:
parent
4286a9b564
commit
848445455d
11 changed files with 332 additions and 74 deletions
|
@ -1980,13 +1980,13 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||||
cx_b.read(|cx| {
|
cx_b.read(|cx| {
|
||||||
assert_eq!(definitions_1.len(), 1);
|
assert_eq!(definitions_1.len(), 1);
|
||||||
assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
|
assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
|
||||||
let target_buffer = definitions_1[0].buffer.read(cx);
|
let target_buffer = definitions_1[0].target.buffer.read(cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
target_buffer.text(),
|
target_buffer.text(),
|
||||||
"const TWO: usize = 2;\nconst THREE: usize = 3;"
|
"const TWO: usize = 2;\nconst THREE: usize = 3;"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
definitions_1[0].range.to_point(target_buffer),
|
definitions_1[0].target.range.to_point(target_buffer),
|
||||||
Point::new(0, 6)..Point::new(0, 9)
|
Point::new(0, 6)..Point::new(0, 9)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -2009,17 +2009,20 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||||
cx_b.read(|cx| {
|
cx_b.read(|cx| {
|
||||||
assert_eq!(definitions_2.len(), 1);
|
assert_eq!(definitions_2.len(), 1);
|
||||||
assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
|
assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
|
||||||
let target_buffer = definitions_2[0].buffer.read(cx);
|
let target_buffer = definitions_2[0].target.buffer.read(cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
target_buffer.text(),
|
target_buffer.text(),
|
||||||
"const TWO: usize = 2;\nconst THREE: usize = 3;"
|
"const TWO: usize = 2;\nconst THREE: usize = 3;"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
definitions_2[0].range.to_point(target_buffer),
|
definitions_2[0].target.range.to_point(target_buffer),
|
||||||
Point::new(1, 6)..Point::new(1, 11)
|
Point::new(1, 6)..Point::new(1, 11)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
assert_eq!(definitions_1[0].buffer, definitions_2[0].buffer);
|
assert_eq!(
|
||||||
|
definitions_1[0].target.buffer,
|
||||||
|
definitions_2[0].target.buffer
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test(iterations = 10)]
|
#[gpui::test(iterations = 10)]
|
||||||
|
@ -2554,7 +2557,7 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it(
|
||||||
let buffer_b2 = buffer_b2.await.unwrap();
|
let buffer_b2 = buffer_b2.await.unwrap();
|
||||||
let definitions = definitions.await.unwrap();
|
let definitions = definitions.await.unwrap();
|
||||||
assert_eq!(definitions.len(), 1);
|
assert_eq!(definitions.len(), 1);
|
||||||
assert_eq!(definitions[0].buffer, buffer_b2);
|
assert_eq!(definitions[0].target.buffer, buffer_b2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test(iterations = 10)]
|
#[gpui::test(iterations = 10)]
|
||||||
|
@ -5593,9 +5596,9 @@ impl TestClient {
|
||||||
log::info!("{}: detaching definitions request", guest_username);
|
log::info!("{}: detaching definitions request", guest_username);
|
||||||
cx.update(|cx| definitions.detach_and_log_err(cx));
|
cx.update(|cx| definitions.detach_and_log_err(cx));
|
||||||
} else {
|
} else {
|
||||||
client
|
client.buffers.extend(
|
||||||
.buffers
|
definitions.await?.into_iter().map(|loc| loc.target.buffer),
|
||||||
.extend(definitions.await?.into_iter().map(|loc| loc.buffer));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
50..=54 => {
|
50..=54 => {
|
||||||
|
|
|
@ -316,6 +316,7 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||||
cx.add_async_action(Editor::find_all_references);
|
cx.add_async_action(Editor::find_all_references);
|
||||||
|
|
||||||
hover_popover::init(cx);
|
hover_popover::init(cx);
|
||||||
|
link_go_to_definition::init(cx);
|
||||||
|
|
||||||
workspace::register_project_item::<Editor>(cx);
|
workspace::register_project_item::<Editor>(cx);
|
||||||
workspace::register_followable_item::<Editor>(cx);
|
workspace::register_followable_item::<Editor>(cx);
|
||||||
|
@ -4603,9 +4604,13 @@ impl Editor {
|
||||||
workspace.update(&mut cx, |workspace, cx| {
|
workspace.update(&mut cx, |workspace, cx| {
|
||||||
let nav_history = workspace.active_pane().read(cx).nav_history().clone();
|
let nav_history = workspace.active_pane().read(cx).nav_history().clone();
|
||||||
for definition in definitions {
|
for definition in definitions {
|
||||||
let range = definition.range.to_offset(definition.buffer.read(cx));
|
let range = definition
|
||||||
|
.target
|
||||||
|
.range
|
||||||
|
.to_offset(definition.target.buffer.read(cx));
|
||||||
|
|
||||||
let target_editor_handle = workspace.open_project_item(definition.buffer, cx);
|
let target_editor_handle =
|
||||||
|
workspace.open_project_item(definition.target.buffer, cx);
|
||||||
target_editor_handle.update(cx, |target_editor, cx| {
|
target_editor_handle.update(cx, |target_editor, cx| {
|
||||||
// When selecting a definition in a different buffer, disable the nav history
|
// When selecting a definition in a different buffer, disable the nav history
|
||||||
// to avoid creating a history entry at the previous cursor location.
|
// to avoid creating a history entry at the previous cursor location.
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
hover_popover::HoverAt,
|
hover_popover::HoverAt,
|
||||||
EditorStyle,
|
EditorStyle,
|
||||||
};
|
};
|
||||||
|
use crate::{hover_popover::HoverAt, link_go_to_definition::FetchDefinition};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use collections::{BTreeMap, HashMap};
|
use collections::{BTreeMap, HashMap};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -1417,7 +1418,7 @@ impl Element for EditorElement {
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
Event::LeftMouseUp { position, .. } => self.mouse_up(*position, cx),
|
Event::LeftMouseUp { position, .. } => self.mouse_up(*position, cx),
|
||||||
Event::LeftMouseDragged { position } => {
|
Event::LeftMouseDragged { position, .. } => {
|
||||||
self.mouse_dragged(*position, layout, paint, cx)
|
self.mouse_dragged(*position, layout, paint, cx)
|
||||||
}
|
}
|
||||||
Event::ScrollWheel {
|
Event::ScrollWheel {
|
||||||
|
@ -1426,9 +1427,26 @@ impl Element for EditorElement {
|
||||||
precise,
|
precise,
|
||||||
} => self.scroll(*position, *delta, *precise, layout, paint, cx),
|
} => self.scroll(*position, *delta, *precise, layout, paint, cx),
|
||||||
Event::KeyDown { input, .. } => self.key_down(input.as_deref(), cx),
|
Event::KeyDown { input, .. } => self.key_down(input.as_deref(), cx),
|
||||||
Event::MouseMoved { position, .. } => {
|
Event::MouseMoved { position, cmd, .. } => {
|
||||||
// This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
|
// This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
|
||||||
// Don't trigger hover popover if mouse is hovering over context menu
|
// Don't trigger hover popover if mouse is hovering over context menu
|
||||||
|
|
||||||
|
let point = if paint.text_bounds.contains_point(*position) {
|
||||||
|
let (point, overshoot) =
|
||||||
|
paint.point_for_position(&self.snapshot(cx), layout, *position);
|
||||||
|
if overshoot.is_zero() {
|
||||||
|
Some(point)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if *cmd {
|
||||||
|
cx.dispatch_action(FetchDefinition { point });
|
||||||
|
}
|
||||||
|
|
||||||
if paint
|
if paint
|
||||||
.context_menu_bounds
|
.context_menu_bounds
|
||||||
.map_or(false, |context_menu_bounds| {
|
.map_or(false, |context_menu_bounds| {
|
||||||
|
@ -1445,18 +1463,6 @@ impl Element for EditorElement {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let point = if paint.text_bounds.contains_point(*position) {
|
|
||||||
let (point, overshoot) =
|
|
||||||
paint.point_for_position(&self.snapshot(cx), layout, *position);
|
|
||||||
if overshoot.is_zero() {
|
|
||||||
Some(point)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
cx.dispatch_action(HoverAt { point });
|
cx.dispatch_action(HoverAt { point });
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@ use std::{
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
|
color::Color,
|
||||||
elements::{Flex, MouseEventHandler, Padding, Text},
|
elements::{Flex, MouseEventHandler, Padding, Text},
|
||||||
|
fonts::{HighlightStyle, Underline},
|
||||||
impl_internal_actions,
|
impl_internal_actions,
|
||||||
platform::CursorStyle,
|
platform::CursorStyle,
|
||||||
Axis, Element, ElementBox, ModelHandle, MutableAppContext, RenderContext, Task, ViewContext,
|
Axis, Element, ElementBox, ModelHandle, MutableAppContext, RenderContext, Task, ViewContext,
|
||||||
|
@ -45,9 +47,135 @@ pub struct LinkGoToDefinitionState {
|
||||||
|
|
||||||
pub fn fetch_definition(
|
pub fn fetch_definition(
|
||||||
editor: &mut Editor,
|
editor: &mut Editor,
|
||||||
FetchDefinition { point }: &FetchDefinition,
|
&FetchDefinition { point }: &FetchDefinition,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
|
if let Some(point) = point {
|
||||||
|
show_link_definition(editor, point, cx);
|
||||||
|
} else {
|
||||||
|
//TODO: Also needs to be dispatched when cmd modifier is released
|
||||||
|
hide_link_definition(editor, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_link_definition(
|
||||||
|
editor: &mut Editor,
|
||||||
|
point: DisplayPoint,
|
||||||
|
cx: &mut ViewContext<Editor>,
|
||||||
|
) {
|
||||||
|
if editor.pending_rename.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let snapshot = editor.snapshot(cx);
|
||||||
|
let multibuffer_offset = point.to_offset(&snapshot.display_snapshot, Bias::Left);
|
||||||
|
|
||||||
|
let (buffer, buffer_position) = if let Some(output) = editor
|
||||||
|
.buffer
|
||||||
|
.read(cx)
|
||||||
|
.text_anchor_for_position(multibuffer_offset, cx)
|
||||||
|
{
|
||||||
|
output
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let excerpt_id = if let Some((excerpt_id, _, _)) = editor
|
||||||
|
.buffer()
|
||||||
|
.read(cx)
|
||||||
|
.excerpt_containing(multibuffer_offset, cx)
|
||||||
|
{
|
||||||
|
excerpt_id
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let project = if let Some(project) = editor.project.clone() {
|
||||||
|
project
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get input anchor
|
||||||
|
let anchor = snapshot
|
||||||
|
.buffer_snapshot
|
||||||
|
.anchor_at(multibuffer_offset, Bias::Left);
|
||||||
|
|
||||||
|
// Don't request again if the location is the same as the previous request
|
||||||
|
if let Some(triggered_from) = &editor.link_go_to_definition_state.triggered_from {
|
||||||
|
if triggered_from
|
||||||
|
.cmp(&anchor, &snapshot.buffer_snapshot)
|
||||||
|
.is_eq()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let task = cx.spawn_weak(|this, mut cx| {
|
||||||
|
async move {
|
||||||
|
// query the LSP for definition info
|
||||||
|
let definition_request = cx.update(|cx| {
|
||||||
|
project.update(cx, |project, cx| {
|
||||||
|
project.definition(&buffer, buffer_position.clone(), cx)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let origin_range = definition_request.await.ok().and_then(|definition_result| {
|
||||||
|
definition_result
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|link| {
|
||||||
|
link.origin.map(|origin| {
|
||||||
|
let start = snapshot
|
||||||
|
.buffer_snapshot
|
||||||
|
.anchor_in_excerpt(excerpt_id.clone(), origin.range.start);
|
||||||
|
let end = snapshot
|
||||||
|
.buffer_snapshot
|
||||||
|
.anchor_in_excerpt(excerpt_id.clone(), origin.range.end);
|
||||||
|
|
||||||
|
start..end
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
if let Some(origin_range) = origin_range {
|
||||||
|
this.highlight_text::<LinkGoToDefinitionState>(
|
||||||
|
vec![origin_range],
|
||||||
|
HighlightStyle {
|
||||||
|
underline: Some(Underline {
|
||||||
|
color: Some(Color::red()),
|
||||||
|
thickness: 1.0.into(),
|
||||||
|
squiggly: false,
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok::<_, anyhow::Error>(())
|
||||||
|
}
|
||||||
|
.log_err()
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.link_go_to_definition_state.task = Some(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hide_link_definition(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
|
||||||
|
// only notify the context once
|
||||||
|
if editor.link_go_to_definition_state.symbol_range.is_some() {
|
||||||
|
editor.link_go_to_definition_state.symbol_range.take();
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.link_go_to_definition_state.task = None;
|
||||||
|
editor.link_go_to_definition_state.triggered_from = None;
|
||||||
|
|
||||||
|
editor.clear_text_highlights::<LinkGoToDefinitionState>(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn go_to_fetched_definition(
|
pub fn go_to_fetched_definition(
|
||||||
|
|
|
@ -32,6 +32,10 @@ pub enum Event {
|
||||||
},
|
},
|
||||||
LeftMouseDragged {
|
LeftMouseDragged {
|
||||||
position: Vector2F,
|
position: Vector2F,
|
||||||
|
ctrl: bool,
|
||||||
|
alt: bool,
|
||||||
|
shift: bool,
|
||||||
|
cmd: bool,
|
||||||
},
|
},
|
||||||
RightMouseDown {
|
RightMouseDown {
|
||||||
position: Vector2F,
|
position: Vector2F,
|
||||||
|
@ -61,6 +65,10 @@ pub enum Event {
|
||||||
MouseMoved {
|
MouseMoved {
|
||||||
position: Vector2F,
|
position: Vector2F,
|
||||||
left_mouse_down: bool,
|
left_mouse_down: bool,
|
||||||
|
ctrl: bool,
|
||||||
|
cmd: bool,
|
||||||
|
alt: bool,
|
||||||
|
shift: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +79,7 @@ impl Event {
|
||||||
Event::ScrollWheel { position, .. }
|
Event::ScrollWheel { position, .. }
|
||||||
| Event::LeftMouseDown { position, .. }
|
| Event::LeftMouseDown { position, .. }
|
||||||
| Event::LeftMouseUp { position, .. }
|
| Event::LeftMouseUp { position, .. }
|
||||||
| Event::LeftMouseDragged { position }
|
| Event::LeftMouseDragged { position, .. }
|
||||||
| Event::RightMouseDown { position, .. }
|
| Event::RightMouseDown { position, .. }
|
||||||
| Event::RightMouseUp { position, .. }
|
| Event::RightMouseUp { position, .. }
|
||||||
| Event::NavigateMouseDown { position, .. }
|
| Event::NavigateMouseDown { position, .. }
|
||||||
|
|
|
@ -218,14 +218,19 @@ impl Event {
|
||||||
direction,
|
direction,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
NSEventType::NSLeftMouseDragged => {
|
NSEventType::NSLeftMouseDragged => window_height.map(|window_height| {
|
||||||
window_height.map(|window_height| Self::LeftMouseDragged {
|
let modifiers = native_event.modifierFlags();
|
||||||
|
Self::LeftMouseDragged {
|
||||||
position: vec2f(
|
position: vec2f(
|
||||||
native_event.locationInWindow().x as f32,
|
native_event.locationInWindow().x as f32,
|
||||||
window_height - native_event.locationInWindow().y as f32,
|
window_height - native_event.locationInWindow().y as f32,
|
||||||
),
|
),
|
||||||
})
|
ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
|
||||||
}
|
alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
|
||||||
|
shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
|
||||||
|
cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
|
||||||
|
}
|
||||||
|
}),
|
||||||
NSEventType::NSScrollWheel => window_height.map(|window_height| Self::ScrollWheel {
|
NSEventType::NSScrollWheel => window_height.map(|window_height| Self::ScrollWheel {
|
||||||
position: vec2f(
|
position: vec2f(
|
||||||
native_event.locationInWindow().x as f32,
|
native_event.locationInWindow().x as f32,
|
||||||
|
@ -237,12 +242,19 @@ impl Event {
|
||||||
),
|
),
|
||||||
precise: native_event.hasPreciseScrollingDeltas() == YES,
|
precise: native_event.hasPreciseScrollingDeltas() == YES,
|
||||||
}),
|
}),
|
||||||
NSEventType::NSMouseMoved => window_height.map(|window_height| Self::MouseMoved {
|
NSEventType::NSMouseMoved => window_height.map(|window_height| {
|
||||||
position: vec2f(
|
let modifiers = native_event.modifierFlags();
|
||||||
native_event.locationInWindow().x as f32,
|
Self::MouseMoved {
|
||||||
window_height - native_event.locationInWindow().y as f32,
|
position: vec2f(
|
||||||
),
|
native_event.locationInWindow().x as f32,
|
||||||
left_mouse_down: NSEvent::pressedMouseButtons(nil) & 1 != 0,
|
window_height - native_event.locationInWindow().y as f32,
|
||||||
|
),
|
||||||
|
left_mouse_down: NSEvent::pressedMouseButtons(nil) & 1 != 0,
|
||||||
|
ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask),
|
||||||
|
alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask),
|
||||||
|
shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask),
|
||||||
|
cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask),
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -597,7 +597,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
|
||||||
|
|
||||||
if let Some(event) = event {
|
if let Some(event) = event {
|
||||||
match &event {
|
match &event {
|
||||||
Event::LeftMouseDragged { position } => {
|
Event::LeftMouseDragged { position, .. } => {
|
||||||
window_state_borrow.synthetic_drag_counter += 1;
|
window_state_borrow.synthetic_drag_counter += 1;
|
||||||
window_state_borrow
|
window_state_borrow
|
||||||
.executor
|
.executor
|
||||||
|
@ -805,7 +805,14 @@ async fn synthetic_drag(
|
||||||
if window_state_borrow.synthetic_drag_counter == drag_id {
|
if window_state_borrow.synthetic_drag_counter == drag_id {
|
||||||
if let Some(mut callback) = window_state_borrow.event_callback.take() {
|
if let Some(mut callback) = window_state_borrow.event_callback.take() {
|
||||||
drop(window_state_borrow);
|
drop(window_state_borrow);
|
||||||
callback(Event::LeftMouseDragged { position });
|
callback(Event::LeftMouseDragged {
|
||||||
|
// TODO: Make sure empty modifiers is correct for this
|
||||||
|
position,
|
||||||
|
shift: false,
|
||||||
|
ctrl: false,
|
||||||
|
alt: false,
|
||||||
|
cmd: false,
|
||||||
|
});
|
||||||
window_state.borrow_mut().event_callback = Some(callback);
|
window_state.borrow_mut().event_callback = Some(callback);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -294,7 +294,13 @@ impl Presenter {
|
||||||
Event::MouseMoved { .. } => {
|
Event::MouseMoved { .. } => {
|
||||||
self.last_mouse_moved_event = Some(event.clone());
|
self.last_mouse_moved_event = Some(event.clone());
|
||||||
}
|
}
|
||||||
Event::LeftMouseDragged { position } => {
|
Event::LeftMouseDragged {
|
||||||
|
position,
|
||||||
|
shift,
|
||||||
|
ctrl,
|
||||||
|
alt,
|
||||||
|
cmd,
|
||||||
|
} => {
|
||||||
if let Some((clicked_region, prev_drag_position)) = self
|
if let Some((clicked_region, prev_drag_position)) = self
|
||||||
.clicked_region
|
.clicked_region
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -308,6 +314,10 @@ impl Presenter {
|
||||||
self.last_mouse_moved_event = Some(Event::MouseMoved {
|
self.last_mouse_moved_event = Some(Event::MouseMoved {
|
||||||
position,
|
position,
|
||||||
left_mouse_down: true,
|
left_mouse_down: true,
|
||||||
|
shift,
|
||||||
|
ctrl,
|
||||||
|
alt,
|
||||||
|
cmd,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -403,6 +413,7 @@ impl Presenter {
|
||||||
if let Event::MouseMoved {
|
if let Event::MouseMoved {
|
||||||
position,
|
position,
|
||||||
left_mouse_down,
|
left_mouse_down,
|
||||||
|
..
|
||||||
} = event
|
} = event
|
||||||
{
|
{
|
||||||
if !left_mouse_down {
|
if !left_mouse_down {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::{DocumentHighlight, Hover, HoverBlock, Location, Project, ProjectTransaction};
|
use crate::{
|
||||||
|
DocumentHighlight, Hover, HoverBlock, Location, LocationLink, Project, ProjectTransaction,
|
||||||
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use client::{proto, PeerId};
|
use client::{proto, PeerId};
|
||||||
|
@ -328,7 +330,7 @@ impl LspCommand for PerformRename {
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl LspCommand for GetDefinition {
|
impl LspCommand for GetDefinition {
|
||||||
type Response = Vec<Location>;
|
type Response = Vec<LocationLink>;
|
||||||
type LspRequest = lsp::request::GotoDefinition;
|
type LspRequest = lsp::request::GotoDefinition;
|
||||||
type ProtoRequest = proto::GetDefinition;
|
type ProtoRequest = proto::GetDefinition;
|
||||||
|
|
||||||
|
@ -351,7 +353,7 @@ impl LspCommand for GetDefinition {
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
buffer: ModelHandle<Buffer>,
|
buffer: ModelHandle<Buffer>,
|
||||||
mut cx: AsyncAppContext,
|
mut cx: AsyncAppContext,
|
||||||
) -> Result<Vec<Location>> {
|
) -> Result<Vec<LocationLink>> {
|
||||||
let mut definitions = Vec::new();
|
let mut definitions = Vec::new();
|
||||||
let (lsp_adapter, language_server) = project
|
let (lsp_adapter, language_server) = project
|
||||||
.read_with(&cx, |project, cx| {
|
.read_with(&cx, |project, cx| {
|
||||||
|
@ -362,24 +364,26 @@ impl LspCommand for GetDefinition {
|
||||||
.ok_or_else(|| anyhow!("no language server found for buffer"))?;
|
.ok_or_else(|| anyhow!("no language server found for buffer"))?;
|
||||||
|
|
||||||
if let Some(message) = message {
|
if let Some(message) = message {
|
||||||
let mut unresolved_locations = Vec::new();
|
let mut unresolved_links = Vec::new();
|
||||||
match message {
|
match message {
|
||||||
lsp::GotoDefinitionResponse::Scalar(loc) => {
|
lsp::GotoDefinitionResponse::Scalar(loc) => {
|
||||||
unresolved_locations.push((loc.uri, loc.range));
|
unresolved_links.push((None, loc.uri, loc.range));
|
||||||
}
|
}
|
||||||
lsp::GotoDefinitionResponse::Array(locs) => {
|
lsp::GotoDefinitionResponse::Array(locs) => {
|
||||||
unresolved_locations.extend(locs.into_iter().map(|l| (l.uri, l.range)));
|
unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
|
||||||
}
|
}
|
||||||
lsp::GotoDefinitionResponse::Link(links) => {
|
lsp::GotoDefinitionResponse::Link(links) => {
|
||||||
unresolved_locations.extend(
|
unresolved_links.extend(links.into_iter().map(|l| {
|
||||||
links
|
(
|
||||||
.into_iter()
|
l.origin_selection_range,
|
||||||
.map(|l| (l.target_uri, l.target_selection_range)),
|
l.target_uri,
|
||||||
);
|
l.target_selection_range,
|
||||||
|
)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (target_uri, target_range) in unresolved_locations {
|
for (origin_range, target_uri, target_range) in unresolved_links {
|
||||||
let target_buffer_handle = project
|
let target_buffer_handle = project
|
||||||
.update(&mut cx, |this, cx| {
|
.update(&mut cx, |this, cx| {
|
||||||
this.open_local_buffer_via_lsp(
|
this.open_local_buffer_via_lsp(
|
||||||
|
@ -392,16 +396,34 @@ impl LspCommand for GetDefinition {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
cx.read(|cx| {
|
cx.read(|cx| {
|
||||||
|
let origin_location = origin_range.map(|origin_range| {
|
||||||
|
let origin_buffer = buffer.read(cx);
|
||||||
|
let origin_start = origin_buffer
|
||||||
|
.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
|
||||||
|
let origin_end = origin_buffer
|
||||||
|
.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
|
||||||
|
Location {
|
||||||
|
buffer: buffer.clone(),
|
||||||
|
range: origin_buffer.anchor_after(origin_start)
|
||||||
|
..origin_buffer.anchor_before(origin_end),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let target_buffer = target_buffer_handle.read(cx);
|
let target_buffer = target_buffer_handle.read(cx);
|
||||||
let target_start = target_buffer
|
let target_start = target_buffer
|
||||||
.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
|
.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
|
||||||
let target_end = target_buffer
|
let target_end = target_buffer
|
||||||
.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
|
.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
|
||||||
definitions.push(Location {
|
let target_location = Location {
|
||||||
buffer: target_buffer_handle,
|
buffer: target_buffer_handle,
|
||||||
range: target_buffer.anchor_after(target_start)
|
range: target_buffer.anchor_after(target_start)
|
||||||
..target_buffer.anchor_before(target_end),
|
..target_buffer.anchor_before(target_end),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
definitions.push(LocationLink {
|
||||||
|
origin: origin_location,
|
||||||
|
target: target_location,
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -441,24 +463,39 @@ impl LspCommand for GetDefinition {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn response_to_proto(
|
fn response_to_proto(
|
||||||
response: Vec<Location>,
|
response: Vec<LocationLink>,
|
||||||
project: &mut Project,
|
project: &mut Project,
|
||||||
peer_id: PeerId,
|
peer_id: PeerId,
|
||||||
_: &clock::Global,
|
_: &clock::Global,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> proto::GetDefinitionResponse {
|
) -> proto::GetDefinitionResponse {
|
||||||
let locations = response
|
let links = response
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|definition| {
|
.map(|definition| {
|
||||||
let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
|
let origin = definition.origin.map(|origin| {
|
||||||
proto::Location {
|
let buffer = project.serialize_buffer_for_peer(&origin.buffer, peer_id, cx);
|
||||||
start: Some(serialize_anchor(&definition.range.start)),
|
proto::Location {
|
||||||
end: Some(serialize_anchor(&definition.range.end)),
|
start: Some(serialize_anchor(&origin.range.start)),
|
||||||
|
end: Some(serialize_anchor(&origin.range.end)),
|
||||||
|
buffer: Some(buffer),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let buffer =
|
||||||
|
project.serialize_buffer_for_peer(&definition.target.buffer, peer_id, cx);
|
||||||
|
let target = proto::Location {
|
||||||
|
start: Some(serialize_anchor(&definition.target.range.start)),
|
||||||
|
end: Some(serialize_anchor(&definition.target.range.end)),
|
||||||
buffer: Some(buffer),
|
buffer: Some(buffer),
|
||||||
|
};
|
||||||
|
|
||||||
|
proto::LocationLink {
|
||||||
|
origin,
|
||||||
|
target: Some(target),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
proto::GetDefinitionResponse { locations }
|
proto::GetDefinitionResponse { links }
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn response_from_proto(
|
async fn response_from_proto(
|
||||||
|
@ -467,30 +504,60 @@ impl LspCommand for GetDefinition {
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
_: ModelHandle<Buffer>,
|
_: ModelHandle<Buffer>,
|
||||||
mut cx: AsyncAppContext,
|
mut cx: AsyncAppContext,
|
||||||
) -> Result<Vec<Location>> {
|
) -> Result<Vec<LocationLink>> {
|
||||||
let mut locations = Vec::new();
|
let mut links = Vec::new();
|
||||||
for location in message.locations {
|
for link in message.links {
|
||||||
let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
|
let origin = match link.origin {
|
||||||
|
Some(origin) => {
|
||||||
|
let buffer = origin
|
||||||
|
.buffer
|
||||||
|
.ok_or_else(|| anyhow!("missing origin buffer"))?;
|
||||||
|
let buffer = project
|
||||||
|
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
|
||||||
|
.await?;
|
||||||
|
let start = origin
|
||||||
|
.start
|
||||||
|
.and_then(deserialize_anchor)
|
||||||
|
.ok_or_else(|| anyhow!("missing origin start"))?;
|
||||||
|
let end = origin
|
||||||
|
.end
|
||||||
|
.and_then(deserialize_anchor)
|
||||||
|
.ok_or_else(|| anyhow!("missing origin end"))?;
|
||||||
|
buffer
|
||||||
|
.update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
|
||||||
|
.await;
|
||||||
|
Some(Location {
|
||||||
|
buffer,
|
||||||
|
range: start..end,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
|
||||||
|
let buffer = target.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
|
||||||
let buffer = project
|
let buffer = project
|
||||||
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
|
.update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
|
||||||
.await?;
|
.await?;
|
||||||
let start = location
|
let start = target
|
||||||
.start
|
.start
|
||||||
.and_then(deserialize_anchor)
|
.and_then(deserialize_anchor)
|
||||||
.ok_or_else(|| anyhow!("missing target start"))?;
|
.ok_or_else(|| anyhow!("missing target start"))?;
|
||||||
let end = location
|
let end = target
|
||||||
.end
|
.end
|
||||||
.and_then(deserialize_anchor)
|
.and_then(deserialize_anchor)
|
||||||
.ok_or_else(|| anyhow!("missing target end"))?;
|
.ok_or_else(|| anyhow!("missing target end"))?;
|
||||||
buffer
|
buffer
|
||||||
.update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
|
.update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
|
||||||
.await;
|
.await;
|
||||||
locations.push(Location {
|
let target = Location {
|
||||||
buffer,
|
buffer,
|
||||||
range: start..end,
|
range: start..end,
|
||||||
})
|
};
|
||||||
|
|
||||||
|
links.push(LocationLink { origin, target })
|
||||||
}
|
}
|
||||||
Ok(locations)
|
Ok(links)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
|
fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
|
||||||
|
|
|
@ -208,6 +208,12 @@ pub struct Location {
|
||||||
pub range: Range<language::Anchor>,
|
pub range: Range<language::Anchor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LocationLink {
|
||||||
|
pub origin: Option<Location>,
|
||||||
|
pub target: Location,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DocumentHighlight {
|
pub struct DocumentHighlight {
|
||||||
pub range: Range<language::Anchor>,
|
pub range: Range<language::Anchor>,
|
||||||
|
@ -2915,7 +2921,7 @@ impl Project {
|
||||||
buffer: &ModelHandle<Buffer>,
|
buffer: &ModelHandle<Buffer>,
|
||||||
position: T,
|
position: T,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Task<Result<Vec<Location>>> {
|
) -> Task<Result<Vec<LocationLink>>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
self.request_lsp(buffer.clone(), GetDefinition { position }, cx)
|
self.request_lsp(buffer.clone(), GetDefinition { position }, cx)
|
||||||
}
|
}
|
||||||
|
@ -7564,7 +7570,7 @@ mod tests {
|
||||||
assert_eq!(definitions.len(), 1);
|
assert_eq!(definitions.len(), 1);
|
||||||
let definition = definitions.pop().unwrap();
|
let definition = definitions.pop().unwrap();
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let target_buffer = definition.buffer.read(cx);
|
let target_buffer = definition.target.buffer.read(cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
target_buffer
|
target_buffer
|
||||||
.file()
|
.file()
|
||||||
|
@ -7574,7 +7580,7 @@ mod tests {
|
||||||
.abs_path(cx),
|
.abs_path(cx),
|
||||||
Path::new("/dir/a.rs"),
|
Path::new("/dir/a.rs"),
|
||||||
);
|
);
|
||||||
assert_eq!(definition.range.to_offset(target_buffer), 9..10);
|
assert_eq!(definition.target.range.to_offset(target_buffer), 9..10);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
list_worktrees(&project, cx),
|
list_worktrees(&project, cx),
|
||||||
[("/dir/b.rs".as_ref(), true), ("/dir/a.rs".as_ref(), false)]
|
[("/dir/b.rs".as_ref(), true), ("/dir/a.rs".as_ref(), false)]
|
||||||
|
|
|
@ -248,7 +248,7 @@ message GetDefinition {
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetDefinitionResponse {
|
message GetDefinitionResponse {
|
||||||
repeated Location locations = 1;
|
repeated LocationLink links = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetReferences {
|
message GetReferences {
|
||||||
|
@ -279,6 +279,11 @@ message Location {
|
||||||
Anchor end = 3;
|
Anchor end = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message LocationLink {
|
||||||
|
optional Location origin = 1;
|
||||||
|
Location target = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message DocumentHighlight {
|
message DocumentHighlight {
|
||||||
Kind kind = 1;
|
Kind kind = 1;
|
||||||
Anchor start = 2;
|
Anchor start = 2;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue