use crate::error::Result;
use crate::lsp::LanguageServerManager;
use crate::tools::{ensure_document_open, make_position};
use lsp_types::{GotoDefinitionResponse, Location, LocationLink};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Serialize, Deserialize)]
pub struct LocationInfo {
pub file: String,
pub line: u32,
pub character: u32,
pub end_line: Option<u32>,
pub end_character: Option<u32>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DefinitionResult {
pub locations: Vec<LocationInfo>,
}
pub fn goto_definition(
manager: &LanguageServerManager,
file_path: &str,
line: u32,
character: u32,
) -> Result<DefinitionResult> {
let path = PathBuf::from(file_path);
let (client, uri) = ensure_document_open(manager, &path)?;
let position = make_position(line, character);
let response = client.goto_definition(&uri, position)?;
let locations = match response {
Some(GotoDefinitionResponse::Scalar(loc)) => vec![convert_location(&loc)],
Some(GotoDefinitionResponse::Array(locs)) => {
locs.iter().map(convert_location).collect()
}
Some(GotoDefinitionResponse::Link(links)) => {
links.iter().map(convert_location_link).collect()
}
None => vec![],
};
Ok(DefinitionResult { locations })
}
fn convert_location(loc: &Location) -> LocationInfo {
LocationInfo {
file: uri_to_path(&loc.uri),
line: loc.range.start.line,
character: loc.range.start.character,
end_line: Some(loc.range.end.line),
end_character: Some(loc.range.end.character),
}
}
fn convert_location_link(link: &LocationLink) -> LocationInfo {
LocationInfo {
file: uri_to_path(&link.target_uri),
line: link.target_selection_range.start.line,
character: link.target_selection_range.start.character,
end_line: Some(link.target_selection_range.end.line),
end_character: Some(link.target_selection_range.end.character),
}
}
fn uri_to_path(uri: &url::Url) -> String {
uri.to_file_path()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|_| uri.to_string())
}