Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/ark/src/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod main_loop;
pub mod markdown;

pub mod find_references;
pub mod rename;
pub mod selection_range;
pub mod signature_help;
pub mod state;
Expand Down
28 changes: 27 additions & 1 deletion crates/ark/src/lsp/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ macro_rules! cast_response {
},
RequestResponse::Result(Err(err)) => match err {
LspError::JsonRpc(err) => Err(err),
LspError::Anyhow(err) => Err(new_jsonrpc_error(format!("{err:?}"))),
// `{err}` (Display) prints just the message. `{err:?}` (Debug)
// would include the captured backtrace when `RUST_BACKTRACE`
// is set, which then surfaces in the client's error popup.
LspError::Anyhow(err) => Err(new_jsonrpc_error(format!("{err}"))),
},
RequestResponse::Crashed(err) => {
// Notify user that the LSP has crashed and is no longer active
Expand Down Expand Up @@ -153,6 +156,8 @@ pub(crate) enum LspRequest {
GotoImplementation(GotoImplementationParams),
SelectionRange(SelectionRangeParams),
References(ReferenceParams),
PrepareRename(TextDocumentPositionParams),
Rename(RenameParams),
StatementRange(StatementRangeParams),
HelpTopic(HelpTopicParams),
OnTypeFormatting(DocumentOnTypeFormattingParams),
Expand All @@ -177,6 +182,8 @@ pub(crate) enum LspResponse {
GotoImplementation(Option<GotoImplementationResponse>),
SelectionRange(Option<Vec<SelectionRange>>),
References(Option<Vec<Location>>),
PrepareRename(Option<PrepareRenameResponse>),
Rename(Option<WorkspaceEdit>),
StatementRange(Option<StatementRangeResponse>),
HelpTopic(Option<HelpTopicResponse>),
OnTypeFormatting(Option<Vec<TextEdit>>),
Expand Down Expand Up @@ -431,6 +438,25 @@ impl LanguageServer for Backend {
)
}

async fn prepare_rename(
&self,
params: TextDocumentPositionParams,
) -> Result<Option<PrepareRenameResponse>> {
cast_response!(
self,
self.request(LspRequest::PrepareRename(params)).await,
LspResponse::PrepareRename
)
}

async fn rename(&self, params: RenameParams) -> Result<Option<WorkspaceEdit>> {
cast_response!(
self,
self.request(LspRequest::Rename(params)).await,
LspResponse::Rename
)
}

async fn on_type_formatting(
&self,
params: DocumentOnTypeFormattingParams,
Expand Down
23 changes: 23 additions & 0 deletions crates/ark/src/lsp/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ use tower_lsp::lsp_types::HoverContents;
use tower_lsp::lsp_types::HoverParams;
use tower_lsp::lsp_types::Location;
use tower_lsp::lsp_types::MessageType;
use tower_lsp::lsp_types::PrepareRenameResponse;
use tower_lsp::lsp_types::ReferenceParams;
use tower_lsp::lsp_types::Registration;
use tower_lsp::lsp_types::RenameParams;
use tower_lsp::lsp_types::SelectionRange;
use tower_lsp::lsp_types::SelectionRangeParams;
use tower_lsp::lsp_types::SignatureHelp;
use tower_lsp::lsp_types::SignatureHelpParams;
use tower_lsp::lsp_types::SymbolInformation;
use tower_lsp::lsp_types::TextDocumentPositionParams;
use tower_lsp::lsp_types::TextEdit;
use tower_lsp::lsp_types::WorkspaceEdit;
use tower_lsp::lsp_types::WorkspaceSymbolParams;
Expand All @@ -58,6 +61,7 @@ use crate::lsp::indent::indent_edit;
use crate::lsp::input_boundaries::InputBoundariesParams;
use crate::lsp::input_boundaries::InputBoundariesResponse;
use crate::lsp::main_loop::LspState;
use crate::lsp::rename;
use crate::lsp::selection_range::convert_selection_range_from_tree_sitter_to_lsp;
use crate::lsp::selection_range::selection_range;
use crate::lsp::signature_help::r_signature_help;
Expand Down Expand Up @@ -327,6 +331,25 @@ pub(crate) fn handle_references(
}
}

#[tracing::instrument(level = "info", skip_all)]
pub(crate) fn handle_prepare_rename(
params: TextDocumentPositionParams,
state: &WorldState,
) -> LspResult<Option<PrepareRenameResponse>> {
// Don't propagate errors to the frontend
Ok(rename::prepare_rename(params, state).log_err().flatten())
}

#[tracing::instrument(level = "info", skip_all)]
pub(crate) fn handle_rename(
params: RenameParams,
state: &WorldState,
) -> LspResult<Option<WorkspaceEdit>> {
// Propagate error to the frontend (unlike `prepare_rename()`) to give
// actionable feedback to the user
Ok(rename::rename(params, state)?)
}

#[tracing::instrument(level = "info", skip_all)]
pub(crate) fn handle_statement_range(
params: StatementRangeParams,
Expand Down
13 changes: 12 additions & 1 deletion crates/ark/src/lsp/main_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,12 @@ impl GlobalState {
LspRequest::References(params) => {
respond(tx, || handlers::handle_references(params, &self.world), LspResponse::References)?;
},
LspRequest::PrepareRename(params) => {
respond(tx, || handlers::handle_prepare_rename(params, &self.world), LspResponse::PrepareRename)?;
},
LspRequest::Rename(params) => {
respond(tx, || handlers::handle_rename(params, &self.world), LspResponse::Rename)?;
},
LspRequest::StatementRange(params) => {
respond(tx, || handlers::handle_statement_range(params, &self.world), LspResponse::StatementRange)?;
},
Expand Down Expand Up @@ -490,7 +496,12 @@ fn respond<T>(
let out = match response {
RequestResponse::Result(Ok(_)) => Ok(()),
RequestResponse::Result(Err(ref error)) => {
Err(anyhow!("Error while handling request:\n{error:?}"))
// The error has already been sent to the client on `response_tx`
// as a jsonrpc error, so the user sees the popup. Log here at
// info level (with `{:?}` for the full debug format including a
// backtrace) so server logs keep diagnostic context.
lsp::log_info!("Error while handling request:\n{error:?}");
Ok(())
},
RequestResponse::Crashed(ref error) => {
Err(anyhow!("Crashed while handling request:\n{error:?}"))
Expand Down
83 changes: 83 additions & 0 deletions crates/ark/src/lsp/rename.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use std::collections::HashMap;

use aether_lsp_utils::proto::from_proto;
use aether_lsp_utils::proto::to_proto;
use tower_lsp::lsp_types;
use tower_lsp::lsp_types::PrepareRenameResponse;
use tower_lsp::lsp_types::RenameParams;
use tower_lsp::lsp_types::TextDocumentPositionParams;
use tower_lsp::lsp_types::TextEdit;
use tower_lsp::lsp_types::WorkspaceEdit;

use crate::lsp::state::WorldState;

pub(crate) fn prepare_rename(
params: TextDocumentPositionParams,
state: &WorldState,
) -> anyhow::Result<Option<PrepareRenameResponse>> {
let uri = params.text_document.uri;
let position = params.position;
let document = state.get_document(&uri)?;

let offset = from_proto::offset_from_position(
position,
&document.line_index,
document.position_encoding,
)?;
let index = document.semantic_index();
let tree = document.syntax()?;
let pos = oak_ide::FileOffset { file: uri, offset };

let Some((range, placeholder)) = oak_ide::prepare_rename(&index, &tree, &pos) else {
return Ok(None);
};

let range = to_proto::range(range, &document.line_index, document.position_encoding)?;
Ok(Some(PrepareRenameResponse::RangeWithPlaceholder {
range,
placeholder,
}))
}

pub(crate) fn rename(
params: RenameParams,
state: &WorldState,
) -> anyhow::Result<Option<WorkspaceEdit>> {
let uri = params.text_document_position.text_document.uri;
let position = params.text_document_position.position;
let new_name = params.new_name;
let document = state.get_document(&uri)?;

let offset = from_proto::offset_from_position(
position,
&document.line_index,
document.position_encoding,
)?;
let index = document.semantic_index();
let root = document.syntax()?;
let pos = oak_ide::FileOffset {
file: uri.clone(),
offset,
};

let targets = oak_ide::rename(&index, &root, &pos, &new_name)?;

// All edits target the current file (intra-file rename).
let mut edits: Vec<TextEdit> = Vec::with_capacity(targets.ranges.len());
for r in targets.ranges {
let range = to_proto::range(r.range, &document.line_index, document.position_encoding)?;
edits.push(TextEdit {
range,
new_text: targets.new_text.clone(),
});
}

let mut changes: HashMap<lsp_types::Url, Vec<TextEdit>> = HashMap::new();
changes.insert(uri, edits);

Ok(Some(WorkspaceEdit {
changes: Some(changes),
document_changes: None,
change_annotations: None,
}))
}
7 changes: 7 additions & 0 deletions crates/ark/src/lsp/state_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use tower_lsp::lsp_types::InitializeParams;
use tower_lsp::lsp_types::InitializeResult;
use tower_lsp::lsp_types::OneOf;
use tower_lsp::lsp_types::RenameFilesParams;
use tower_lsp::lsp_types::RenameOptions;
use tower_lsp::lsp_types::SelectionRangeProviderCapability;
use tower_lsp::lsp_types::ServerCapabilities;
use tower_lsp::lsp_types::ServerInfo;
Expand Down Expand Up @@ -164,6 +165,12 @@ pub(crate) fn initialize(
type_definition_provider: None,
implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
references_provider: Some(OneOf::Left(true)),
rename_provider: Some(OneOf::Right(RenameOptions {
prepare_provider: Some(true),
work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: None,
},
})),
document_symbol_provider: Some(OneOf::Left(true)),
folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
workspace_symbol_provider: Some(OneOf::Left(true)),
Expand Down
2 changes: 2 additions & 0 deletions crates/ark/src/lsp/tests.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
mod find_references;
mod goto_definition;
mod rename;
mod utils;
16 changes: 2 additions & 14 deletions crates/ark/src/lsp/tests/find_references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use tower_lsp::lsp_types::Location;
use tower_lsp::lsp_types::ReferenceContext;
use tower_lsp::lsp_types::ReferenceParams;

use super::utils::make_state;
use super::utils::range;
use crate::lsp::document::Document;
use crate::lsp::find_references::find_references;
use crate::lsp::state::WorldState;
use crate::lsp::util::test_path;

fn make_params(
Expand All @@ -27,19 +28,6 @@ fn make_params(
}
}

fn make_state(uri: &lsp_types::Url, doc: &Document) -> WorldState {
let mut state = WorldState::default();
state.documents.insert(uri.clone(), doc.clone());
state
}

fn range(start: (u32, u32), end: (u32, u32)) -> lsp_types::Range {
lsp_types::Range {
start: lsp_types::Position::new(start.0, start.1),
end: lsp_types::Position::new(end.0, end.1),
}
}

#[test]
fn test_intra_file_use_and_def() {
let code = "foo <- 1\nfoo + foo\n";
Expand Down
Loading
Loading