mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-26 03:59:55 +00:00
Start on Project::definition
that only works locally (for now)
This commit is contained in:
parent
11a83d01c2
commit
cbbf7391e8
3 changed files with 176 additions and 12 deletions
|
@ -11,11 +11,14 @@ use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet};
|
|||
use gpui::{
|
||||
AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task,
|
||||
};
|
||||
use language::{Buffer, DiagnosticEntry, Language, LanguageRegistry};
|
||||
use language::{
|
||||
Bias, Buffer, DiagnosticEntry, File as _, Language, LanguageRegistry, ToOffset, ToPointUtf16,
|
||||
};
|
||||
use lsp::{DiagnosticSeverity, LanguageServer};
|
||||
use postage::{prelude::Stream, watch};
|
||||
use smol::block_on;
|
||||
use std::{
|
||||
ops::Range,
|
||||
path::{Path, PathBuf},
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
|
@ -83,6 +86,13 @@ pub struct DiagnosticSummary {
|
|||
pub hint_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Definition {
|
||||
pub source_range: Option<Range<language::Anchor>>,
|
||||
pub target_buffer: ModelHandle<Buffer>,
|
||||
pub target_range: Range<language::Anchor>,
|
||||
}
|
||||
|
||||
impl DiagnosticSummary {
|
||||
fn new<'a, T: 'a>(diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<T>>) -> Self {
|
||||
let mut this = Self {
|
||||
|
@ -487,7 +497,7 @@ impl Project {
|
|||
abs_path: PathBuf,
|
||||
cx: &mut ModelContext<Project>,
|
||||
) -> Task<Result<()>> {
|
||||
let worktree_task = self.worktree_for_abs_path(&abs_path, cx);
|
||||
let worktree_task = self.find_or_create_worktree_for_abs_path(&abs_path, cx);
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let (worktree, path) = worktree_task.await?;
|
||||
worktree
|
||||
|
@ -691,26 +701,180 @@ impl Project {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn worktree_for_abs_path(
|
||||
pub fn definition<T: ToOffset>(
|
||||
&self,
|
||||
source_buffer_handle: &ModelHandle<Buffer>,
|
||||
position: T,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<Vec<Definition>>> {
|
||||
let source_buffer_handle = source_buffer_handle.clone();
|
||||
let buffer = source_buffer_handle.read(cx);
|
||||
let worktree;
|
||||
let buffer_abs_path;
|
||||
if let Some(file) = File::from_dyn(buffer.file()) {
|
||||
worktree = file.worktree.clone();
|
||||
buffer_abs_path = file.abs_path();
|
||||
} else {
|
||||
return Task::ready(Err(anyhow!("buffer does not belong to any worktree")));
|
||||
};
|
||||
|
||||
if worktree.read(cx).as_local().is_some() {
|
||||
let point = buffer.offset_to_point_utf16(position.to_offset(buffer));
|
||||
let buffer_abs_path = buffer_abs_path.unwrap();
|
||||
let lang_name;
|
||||
let lang_server;
|
||||
if let Some(lang) = buffer.language() {
|
||||
lang_name = lang.name().to_string();
|
||||
if let Some(server) = self
|
||||
.language_servers
|
||||
.get(&(worktree.read(cx).id(), lang_name.clone()))
|
||||
{
|
||||
lang_server = server.clone();
|
||||
} else {
|
||||
return Task::ready(Err(anyhow!("buffer does not have a language server")));
|
||||
};
|
||||
} else {
|
||||
return Task::ready(Err(anyhow!("buffer does not have a language")));
|
||||
}
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let response = lang_server
|
||||
.request::<lsp::request::GotoDefinition>(lsp::GotoDefinitionParams {
|
||||
text_document_position_params: lsp::TextDocumentPositionParams {
|
||||
text_document: lsp::TextDocumentIdentifier::new(
|
||||
lsp::Url::from_file_path(&buffer_abs_path).unwrap(),
|
||||
),
|
||||
position: lsp::Position::new(point.row, point.column),
|
||||
},
|
||||
work_done_progress_params: Default::default(),
|
||||
partial_result_params: Default::default(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut definitions = Vec::new();
|
||||
if let Some(response) = response {
|
||||
let mut unresolved_locations = Vec::new();
|
||||
match response {
|
||||
lsp::GotoDefinitionResponse::Scalar(loc) => {
|
||||
unresolved_locations.push((None, loc.uri, loc.range));
|
||||
}
|
||||
lsp::GotoDefinitionResponse::Array(locs) => {
|
||||
unresolved_locations
|
||||
.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
|
||||
}
|
||||
lsp::GotoDefinitionResponse::Link(links) => {
|
||||
unresolved_locations.extend(links.into_iter().map(|l| {
|
||||
(
|
||||
l.origin_selection_range,
|
||||
l.target_uri,
|
||||
l.target_selection_range,
|
||||
)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
for (source_range, target_uri, target_range) in unresolved_locations {
|
||||
let abs_path = target_uri
|
||||
.to_file_path()
|
||||
.map_err(|_| anyhow!("invalid target path"))?;
|
||||
|
||||
let (worktree, relative_path) = if let Some(result) = this
|
||||
.read_with(&cx, |this, cx| {
|
||||
this.find_worktree_for_abs_path(&abs_path, cx)
|
||||
}) {
|
||||
result
|
||||
} else {
|
||||
let (worktree, relative_path) = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.create_worktree_for_abs_path(&abs_path, cx)
|
||||
})
|
||||
.await?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.language_servers.insert(
|
||||
(worktree.read(cx).id(), lang_name.clone()),
|
||||
lang_server.clone(),
|
||||
);
|
||||
});
|
||||
(worktree, relative_path)
|
||||
};
|
||||
|
||||
let project_path = ProjectPath {
|
||||
worktree_id: worktree.read_with(&cx, |worktree, _| worktree.id()),
|
||||
path: relative_path.into(),
|
||||
};
|
||||
let target_buffer_handle = this
|
||||
.update(&mut cx, |this, cx| this.open_buffer(project_path, cx))
|
||||
.await?;
|
||||
cx.read(|cx| {
|
||||
let source_buffer = source_buffer_handle.read(cx);
|
||||
let target_buffer = target_buffer_handle.read(cx);
|
||||
let source_range = source_range.map(|range| {
|
||||
let start = source_buffer
|
||||
.clip_point_utf16(range.start.to_point_utf16(), Bias::Left);
|
||||
let end = source_buffer
|
||||
.clip_point_utf16(range.end.to_point_utf16(), Bias::Left);
|
||||
source_buffer.anchor_after(start)..source_buffer.anchor_before(end)
|
||||
});
|
||||
let target_start = target_buffer
|
||||
.clip_point_utf16(target_range.start.to_point_utf16(), Bias::Left);
|
||||
let target_end = target_buffer
|
||||
.clip_point_utf16(target_range.end.to_point_utf16(), Bias::Left);
|
||||
definitions.push(Definition {
|
||||
source_range,
|
||||
target_buffer: target_buffer_handle,
|
||||
target_range: target_buffer.anchor_after(target_start)
|
||||
..target_buffer.anchor_before(target_end),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(definitions)
|
||||
})
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_or_create_worktree_for_abs_path(
|
||||
&self,
|
||||
abs_path: &Path,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
|
||||
if let Some((tree, relative_path)) = self.find_worktree_for_abs_path(abs_path, cx) {
|
||||
Task::ready(Ok((tree.clone(), relative_path.into())))
|
||||
} else {
|
||||
self.create_worktree_for_abs_path(abs_path, cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_worktree_for_abs_path(
|
||||
&self,
|
||||
abs_path: &Path,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<(ModelHandle<Worktree>, PathBuf)>> {
|
||||
let worktree = self.add_local_worktree(abs_path, cx);
|
||||
cx.background().spawn(async move {
|
||||
let worktree = worktree.await?;
|
||||
Ok((worktree, PathBuf::new()))
|
||||
})
|
||||
}
|
||||
|
||||
fn find_worktree_for_abs_path(
|
||||
&self,
|
||||
abs_path: &Path,
|
||||
cx: &AppContext,
|
||||
) -> Option<(ModelHandle<Worktree>, PathBuf)> {
|
||||
for tree in &self.worktrees {
|
||||
if let Some(relative_path) = tree
|
||||
.read(cx)
|
||||
.as_local()
|
||||
.and_then(|t| abs_path.strip_prefix(t.abs_path()).ok())
|
||||
{
|
||||
return Task::ready(Ok((tree.clone(), relative_path.into())));
|
||||
return Some((tree.clone(), relative_path.into()));
|
||||
}
|
||||
}
|
||||
|
||||
let worktree = self.add_local_worktree(abs_path, cx);
|
||||
cx.background().spawn(async move {
|
||||
let worktree = worktree.await?;
|
||||
Ok((worktree, PathBuf::new()))
|
||||
})
|
||||
None
|
||||
}
|
||||
|
||||
pub fn is_shared(&self) -> bool {
|
||||
|
|
|
@ -1947,7 +1947,7 @@ impl fmt::Debug for Snapshot {
|
|||
#[derive(Clone, PartialEq)]
|
||||
pub struct File {
|
||||
entry_id: Option<usize>,
|
||||
worktree: ModelHandle<Worktree>,
|
||||
pub worktree: ModelHandle<Worktree>,
|
||||
worktree_path: Arc<Path>,
|
||||
pub path: Arc<Path>,
|
||||
pub mtime: SystemTime,
|
||||
|
|
|
@ -684,7 +684,7 @@ impl Workspace {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<ProjectPath>> {
|
||||
let entry = self.project().update(cx, |project, cx| {
|
||||
project.worktree_for_abs_path(abs_path, cx)
|
||||
project.find_or_create_worktree_for_abs_path(abs_path, cx)
|
||||
});
|
||||
cx.spawn(|_, cx| async move {
|
||||
let (worktree, path) = entry.await?;
|
||||
|
|
Loading…
Reference in a new issue