-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
6,620 additions
and
200 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Empty file.
Binary file not shown.
File renamed without changes.
File renamed without changes.
Binary file not shown.
Large diffs are not rendered by default.
Oops, something went wrong.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
use fjall::PartitionCreateOptions; | ||
|
||
use crate::{lsp_client::RAClient, Identifier, IdentifierReference}; | ||
|
||
pub struct CallResolver<'a> { | ||
client: &'a mut RAClient, | ||
fjall: &'a fjall::Keyspace, | ||
handle: fjall::PartitionHandle, | ||
} | ||
|
||
impl<'a> CallResolver<'a> { | ||
pub fn new(client: &'a mut RAClient, fjall: &'a fjall::Keyspace) -> Self { | ||
let handle = fjall | ||
.open_partition("links", PartitionCreateOptions::default()) | ||
.unwrap(); | ||
Self { | ||
client, | ||
fjall, | ||
handle, | ||
} | ||
} | ||
|
||
pub fn cached(&self) -> usize { | ||
self.handle.len().unwrap() | ||
} | ||
|
||
pub fn cleared(mut self) -> Self { | ||
self.fjall.delete_partition(self.handle).unwrap(); | ||
self.handle = self | ||
.fjall | ||
.open_partition("links", PartitionCreateOptions::default()) | ||
.unwrap(); | ||
self | ||
} | ||
|
||
pub fn resolve(&mut self, ident: &Identifier) -> Vec<IdentifierReference> { | ||
if let Some(data) = self.handle.get(ident.to_string()).unwrap() { | ||
tracing::info!("skipping {}", ident); | ||
return bincode::deserialize(&data).unwrap(); | ||
}; | ||
|
||
tracing::info!("checking {}", ident); | ||
|
||
let mut count = 0; | ||
let _response = loop { | ||
let response = self.client.request(lsp_server::Request { | ||
id: 1.into(), | ||
method: "textDocument/prepareCallHierarchy".to_string(), | ||
params: serde_json::to_value(&lsp_types::CallHierarchyPrepareParams { | ||
text_document_position_params: lsp_types::TextDocumentPositionParams { | ||
position: ident.range.start, | ||
text_document: lsp_types::TextDocumentIdentifier { | ||
uri: lsp_types::Url::from_file_path(&ident.path).unwrap(), | ||
}, | ||
}, | ||
work_done_progress_params: lsp_types::WorkDoneProgressParams { | ||
work_done_token: Some(lsp_types::ProgressToken::String( | ||
"prepare".to_string(), | ||
)), | ||
}, | ||
}) | ||
.unwrap(), | ||
}); | ||
if let Some(Some(value)) = response.result.as_ref().map(|r| r.as_array()) { | ||
if !value.is_empty() { | ||
break value.to_owned(); | ||
} | ||
count += 1; | ||
} | ||
|
||
// textDocument/prepareCallHierarchy will sometimes return an empty array so try | ||
// at most 5 times | ||
if count > 5 { | ||
tracing::warn!("discovered isolated task {}", ident); | ||
break vec![]; | ||
} | ||
|
||
std::thread::sleep(std::time::Duration::from_secs(1)); | ||
}; | ||
|
||
// callHierarchy/incomingCalls | ||
let response = self.client.request(lsp_server::Request { | ||
id: 1.into(), | ||
method: "callHierarchy/incomingCalls".to_string(), | ||
params: serde_json::to_value(lsp_types::CallHierarchyIncomingCallsParams { | ||
partial_result_params: lsp_types::PartialResultParams::default(), | ||
item: lsp_types::CallHierarchyItem { | ||
name: ident.name.to_owned(), | ||
kind: lsp_types::SymbolKind::FUNCTION, | ||
data: None, | ||
tags: None, | ||
detail: None, | ||
uri: lsp_types::Url::from_file_path(&ident.path).unwrap(), | ||
range: ident.range, | ||
selection_range: ident.range, | ||
}, | ||
work_done_progress_params: lsp_types::WorkDoneProgressParams { | ||
work_done_token: Some(lsp_types::ProgressToken::String("prepare".to_string())), | ||
}, | ||
}) | ||
.unwrap(), | ||
}); | ||
|
||
let links = if let Some(e) = response.error { | ||
tracing::warn!("unable to resolve {}: {:?}", ident, e); | ||
vec![] | ||
} else { | ||
let response: Result<Vec<lsp_types::CallHierarchyIncomingCall>, _> = | ||
serde_path_to_error::deserialize(response.result.unwrap()); | ||
|
||
response | ||
.unwrap() | ||
.into_iter() | ||
.map(|i| i.into()) | ||
.collect::<Vec<IdentifierReference>>() | ||
}; | ||
|
||
let data = bincode::serialize(&links).unwrap(); | ||
|
||
tracing::debug!("links: {:?}", links); | ||
|
||
self.handle.insert(ident.to_string(), data).unwrap(); | ||
links | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
use std::{fs, path::PathBuf}; | ||
|
||
use lsp_types::{CallHierarchyIncomingCall, CallHierarchyItem, Range}; | ||
|
||
/// A task that references another, with the range of the reference | ||
#[derive(Hash, PartialEq, Eq, serde::Deserialize, serde::Serialize, Clone, Debug)] | ||
pub struct IdentifierReference { | ||
pub identifier: Identifier, | ||
pub references: Vec<Range>, // the places where this identifier is used | ||
} | ||
|
||
/// identifies a task by its file, and range in the file | ||
#[derive(Hash, PartialEq, Eq, serde::Deserialize, serde::Serialize, Clone)] | ||
pub struct Identifier { | ||
pub path: String, | ||
// technically you can derive this from the name and range but it's easier to just store it | ||
pub name: String, | ||
// post_transform_name: Option<String>, | ||
pub range: lsp_types::Range, | ||
} | ||
|
||
impl Identifier { | ||
/// check the span matches and the text matches | ||
/// | ||
/// `same_location` is used to check if the location of the identifier is | ||
/// the same as the other | ||
pub fn equals_ident(&self, other: &syn::Ident, match_location: bool) -> bool { | ||
*other == self.name | ||
&& (!match_location | ||
|| (self.range.start.line == other.span().start().line as u32 | ||
&& self.range.start.character == other.span().start().column as u32)) | ||
} | ||
|
||
fn get_name(item: &CallHierarchyItem) -> String { | ||
// open file, find range inside, extract text | ||
let file = fs::read_to_string(item.uri.path()).unwrap(); | ||
let start = item.selection_range.start; | ||
let end = item.selection_range.end; | ||
file.lines().nth(start.line as usize) | ||
.unwrap() | ||
.chars() | ||
.skip(start.character as usize) | ||
.take(end.character as usize - start.character as usize) | ||
.collect() | ||
} | ||
} | ||
|
||
impl From<(PathBuf, syn::Ident)> for Identifier { | ||
fn from((path, ident): (PathBuf, syn::Ident)) -> Self { | ||
Self { | ||
path: path.display().to_string(), | ||
name: ident.to_string(), | ||
// post_transform_name: None, | ||
range: Range { | ||
start: lsp_types::Position { | ||
line: ident.span().start().line as u32 - 1, | ||
character: ident.span().start().column as u32, | ||
}, | ||
end: lsp_types::Position { | ||
line: ident.span().end().line as u32 - 1, | ||
character: ident.span().end().column as u32, | ||
}, | ||
}, | ||
} | ||
} | ||
} | ||
|
||
impl From<CallHierarchyIncomingCall> for IdentifierReference { | ||
fn from(item: CallHierarchyIncomingCall) -> Self { | ||
Self { | ||
identifier: Identifier { | ||
name: Identifier::get_name(&item.from), | ||
// post_transform_name: Some(item.from.name), | ||
path: item.from.uri.path().to_owned(), | ||
range: item.from.selection_range, | ||
}, | ||
references: item.from_ranges, | ||
} | ||
} | ||
} | ||
|
||
impl std::fmt::Debug for Identifier { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
std::fmt::Display::fmt(self, f) | ||
} | ||
} | ||
|
||
impl std::fmt::Display for Identifier { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!( | ||
f, | ||
"{}:{}#{}", | ||
self.path, | ||
self.range.start.line, | ||
self.name.to_string(), | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.