1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-28 16:20:57 -05:00

fix(lsp): panic on url_to_uri() (#25238)

This commit is contained in:
Nayeem Rahman 2024-08-28 05:15:48 +01:00 committed by GitHub
parent efcabce1c1
commit 97d1635343
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 232 additions and 251 deletions

View file

@ -751,7 +751,7 @@ impl CodeActionCollection {
.as_ref() .as_ref()
.and_then(|d| serde_json::from_value::<Vec<DataQuickFix>>(d.clone()).ok()) .and_then(|d| serde_json::from_value::<Vec<DataQuickFix>>(d.clone()).ok())
{ {
let uri = url_to_uri(specifier); let uri = url_to_uri(specifier)?;
for quick_fix in data_quick_fixes { for quick_fix in data_quick_fixes {
let mut changes = HashMap::new(); let mut changes = HashMap::new();
changes.insert( changes.insert(
@ -797,7 +797,7 @@ impl CodeActionCollection {
maybe_text_info: Option<&SourceTextInfo>, maybe_text_info: Option<&SourceTextInfo>,
maybe_parsed_source: Option<&deno_ast::ParsedSource>, maybe_parsed_source: Option<&deno_ast::ParsedSource>,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let uri = url_to_uri(specifier); let uri = url_to_uri(specifier)?;
let code = diagnostic let code = diagnostic
.code .code
.as_ref() .as_ref()

View file

@ -8,6 +8,7 @@ use deno_core::anyhow::bail;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::serde_json::json; use deno_core::serde_json::json;
use deno_core::unsync::spawn; use deno_core::unsync::spawn;
use lsp_types::Uri;
use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types as lsp;
use tower_lsp::lsp_types::ConfigurationItem; use tower_lsp::lsp_types::ConfigurationItem;
@ -17,7 +18,6 @@ use super::config::WorkspaceSettings;
use super::config::SETTINGS_SECTION; use super::config::SETTINGS_SECTION;
use super::lsp_custom; use super::lsp_custom;
use super::testing::lsp_custom as testing_lsp_custom; use super::testing::lsp_custom as testing_lsp_custom;
use super::urls::LspClientUrl;
#[derive(Debug)] #[derive(Debug)]
pub enum TestingNotification { pub enum TestingNotification {
@ -52,14 +52,11 @@ impl Client {
pub async fn publish_diagnostics( pub async fn publish_diagnostics(
&self, &self,
uri: LspClientUrl, uri: Uri,
diags: Vec<lsp::Diagnostic>, diags: Vec<lsp::Diagnostic>,
version: Option<i32>, version: Option<i32>,
) { ) {
self self.0.publish_diagnostics(uri, diags, version).await;
.0
.publish_diagnostics(uri.to_uri(), diags, version)
.await;
} }
pub fn send_registry_state_notification( pub fn send_registry_state_notification(

View file

@ -850,7 +850,7 @@ impl Config {
let mut config = Self::default(); let mut config = Self::default();
let mut folders = vec![]; let mut folders = vec![];
for root_url in root_urls { for root_url in root_urls {
let root_uri = url_to_uri(&root_url); let root_uri = url_to_uri(&root_url).unwrap();
let name = root_url.path_segments().and_then(|s| s.last()); let name = root_url.path_segments().and_then(|s| s.last());
let name = name.unwrap_or_default().to_string(); let name = name.unwrap_or_default().to_string();
folders.push(( folders.push((

View file

@ -13,7 +13,6 @@ use super::performance::Performance;
use super::tsc; use super::tsc;
use super::tsc::TsServer; use super::tsc::TsServer;
use super::urls::url_to_uri; use super::urls::url_to_uri;
use super::urls::LspClientUrl;
use super::urls::LspUrlMap; use super::urls::LspUrlMap;
use crate::graph_util; use crate::graph_util;
@ -164,15 +163,14 @@ impl DiagnosticsPublisher {
.state .state
.update(&record.specifier, version, &all_specifier_diagnostics); .update(&record.specifier, version, &all_specifier_diagnostics);
let file_referrer = documents.get_file_referrer(&record.specifier); let file_referrer = documents.get_file_referrer(&record.specifier);
let Ok(uri) =
url_map.specifier_to_uri(&record.specifier, file_referrer.as_deref())
else {
continue;
};
self self
.client .client
.publish_diagnostics( .publish_diagnostics(uri, all_specifier_diagnostics, version)
url_map
.normalize_specifier(&record.specifier, file_referrer.as_deref())
.unwrap_or(LspClientUrl::new(record.specifier)),
all_specifier_diagnostics,
version,
)
.await; .await;
messages_sent += 1; messages_sent += 1;
} }
@ -195,15 +193,14 @@ impl DiagnosticsPublisher {
// clear out any diagnostics for this specifier // clear out any diagnostics for this specifier
self.state.update(specifier, removed_value.version, &[]); self.state.update(specifier, removed_value.version, &[]);
let file_referrer = documents.get_file_referrer(specifier); let file_referrer = documents.get_file_referrer(specifier);
let Ok(uri) =
url_map.specifier_to_uri(specifier, file_referrer.as_deref())
else {
continue;
};
self self
.client .client
.publish_diagnostics( .publish_diagnostics(uri, Vec::new(), removed_value.version)
url_map
.normalize_specifier(specifier, file_referrer.as_deref())
.unwrap_or_else(|_| LspClientUrl::new(specifier.clone())),
Vec::new(),
removed_value.version,
)
.await; .await;
messages_sent += 1; messages_sent += 1;
} }
@ -1074,7 +1071,7 @@ impl DenoDiagnostic {
diagnostics: Some(vec![diagnostic.clone()]), diagnostics: Some(vec![diagnostic.clone()]),
edit: Some(lsp::WorkspaceEdit { edit: Some(lsp::WorkspaceEdit {
changes: Some(HashMap::from([( changes: Some(HashMap::from([(
url_to_uri(specifier), url_to_uri(specifier)?,
vec![lsp::TextEdit { vec![lsp::TextEdit {
new_text: format!("\"{to}\""), new_text: format!("\"{to}\""),
range: diagnostic.range, range: diagnostic.range,
@ -1091,7 +1088,7 @@ impl DenoDiagnostic {
diagnostics: Some(vec![diagnostic.clone()]), diagnostics: Some(vec![diagnostic.clone()]),
edit: Some(lsp::WorkspaceEdit { edit: Some(lsp::WorkspaceEdit {
changes: Some(HashMap::from([( changes: Some(HashMap::from([(
url_to_uri(specifier), url_to_uri(specifier)?,
vec![lsp::TextEdit { vec![lsp::TextEdit {
new_text: " with { type: \"json\" }".to_string(), new_text: " with { type: \"json\" }".to_string(),
range: lsp::Range { range: lsp::Range {
@ -1142,7 +1139,7 @@ impl DenoDiagnostic {
diagnostics: Some(vec![diagnostic.clone()]), diagnostics: Some(vec![diagnostic.clone()]),
edit: Some(lsp::WorkspaceEdit { edit: Some(lsp::WorkspaceEdit {
changes: Some(HashMap::from([( changes: Some(HashMap::from([(
url_to_uri(specifier), url_to_uri(specifier)?,
vec![lsp::TextEdit { vec![lsp::TextEdit {
new_text: format!( new_text: format!(
"\"{}\"", "\"{}\"",
@ -1168,7 +1165,7 @@ impl DenoDiagnostic {
diagnostics: Some(vec![diagnostic.clone()]), diagnostics: Some(vec![diagnostic.clone()]),
edit: Some(lsp::WorkspaceEdit { edit: Some(lsp::WorkspaceEdit {
changes: Some(HashMap::from([( changes: Some(HashMap::from([(
url_to_uri(specifier), url_to_uri(specifier)?,
vec![lsp::TextEdit { vec![lsp::TextEdit {
new_text: format!( new_text: format!(
"\"{}\"", "\"{}\"",
@ -1194,7 +1191,7 @@ impl DenoDiagnostic {
diagnostics: Some(vec![diagnostic.clone()]), diagnostics: Some(vec![diagnostic.clone()]),
edit: Some(lsp::WorkspaceEdit { edit: Some(lsp::WorkspaceEdit {
changes: Some(HashMap::from([( changes: Some(HashMap::from([(
url_to_uri(specifier), url_to_uri(specifier)?,
vec![lsp::TextEdit { vec![lsp::TextEdit {
new_text: format!("\"node:{}\"", data.specifier), new_text: format!("\"node:{}\"", data.specifier),
range: diagnostic.range, range: diagnostic.range,
@ -1642,7 +1639,7 @@ mod tests {
fn mock_config() -> Config { fn mock_config() -> Config {
let root_url = resolve_url("file:///").unwrap(); let root_url = resolve_url("file:///").unwrap();
let root_uri = url_to_uri(&root_url); let root_uri = url_to_uri(&root_url).unwrap();
Config { Config {
settings: Arc::new(Settings { settings: Arc::new(Settings {
unscoped: Arc::new(WorkspaceSettings { unscoped: Arc::new(WorkspaceSettings {

View file

@ -32,6 +32,7 @@ use std::collections::VecDeque;
use std::env; use std::env;
use std::fmt::Write as _; use std::fmt::Write as _;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::unbounded_channel;
use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedReceiver;
@ -723,7 +724,9 @@ impl Inner {
.into_iter() .into_iter()
.map(|folder| { .map(|folder| {
( (
self.url_map.normalize_url(&folder.uri, LspUrlKind::Folder), self
.url_map
.uri_to_specifier(&folder.uri, LspUrlKind::Folder),
folder, folder,
) )
}) })
@ -735,7 +738,7 @@ impl Inner {
if let Some(root_uri) = params.root_uri { if let Some(root_uri) = params.root_uri {
if !workspace_folders.iter().any(|(_, f)| f.uri == root_uri) { if !workspace_folders.iter().any(|(_, f)| f.uri == root_uri) {
let root_url = let root_url =
self.url_map.normalize_url(&root_uri, LspUrlKind::Folder); self.url_map.uri_to_specifier(&root_uri, LspUrlKind::Folder);
let name = root_url.path_segments().and_then(|s| s.last()); let name = root_url.path_segments().and_then(|s| s.last());
let name = name.unwrap_or_default().to_string(); let name = name.unwrap_or_default().to_string();
workspace_folders.insert( workspace_folders.insert(
@ -1043,7 +1046,7 @@ impl Inner {
.filter(|s| self.documents.is_valid_file_referrer(s)); .filter(|s| self.documents.is_valid_file_referrer(s));
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
let document = self.documents.open( let document = self.documents.open(
specifier.clone(), specifier.clone(),
params.text_document.version, params.text_document.version,
@ -1065,7 +1068,7 @@ impl Inner {
let mark = self.performance.mark_with_args("lsp.did_change", &params); let mark = self.performance.mark_with_args("lsp.did_change", &params);
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
match self.documents.change( match self.documents.change(
&specifier, &specifier,
params.text_document.version, params.text_document.version,
@ -1102,7 +1105,7 @@ impl Inner {
let _mark = self.performance.measure_scope("lsp.did_save"); let _mark = self.performance.measure_scope("lsp.did_save");
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
self.documents.save(&specifier); self.documents.save(&specifier);
if !self if !self
.config .config
@ -1148,7 +1151,7 @@ impl Inner {
} }
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
self.diagnostics_state.clear(&specifier); self.diagnostics_state.clear(&specifier);
if self.is_diagnosable(&specifier) { if self.is_diagnosable(&specifier) {
self.refresh_npm_specifiers().await; self.refresh_npm_specifiers().await;
@ -1202,7 +1205,7 @@ impl Inner {
let changes = params let changes = params
.changes .changes
.into_iter() .into_iter()
.map(|e| (self.url_map.normalize_url(&e.uri, LspUrlKind::File), e)) .map(|e| (self.url_map.uri_to_specifier(&e.uri, LspUrlKind::File), e))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if changes if changes
.iter() .iter()
@ -1221,7 +1224,7 @@ impl Inner {
_ => return None, _ => return None,
}; };
Some(lsp_custom::DenoConfigurationChangeEvent { Some(lsp_custom::DenoConfigurationChangeEvent {
scope_uri: url_to_uri(t.0), scope_uri: url_to_uri(t.0).ok()?,
file_uri: e.uri.clone(), file_uri: e.uri.clone(),
typ: lsp_custom::DenoConfigurationChangeType::from_file_change_type( typ: lsp_custom::DenoConfigurationChangeType::from_file_change_type(
e.typ, e.typ,
@ -1256,7 +1259,7 @@ impl Inner {
_ => return None, _ => return None,
}; };
Some(lsp_custom::DenoConfigurationChangeEvent { Some(lsp_custom::DenoConfigurationChangeEvent {
scope_uri: url_to_uri(t.0), scope_uri: url_to_uri(t.0).ok()?,
file_uri: e.uri.clone(), file_uri: e.uri.clone(),
typ: lsp_custom::DenoConfigurationChangeType::from_file_change_type( typ: lsp_custom::DenoConfigurationChangeType::from_file_change_type(
e.typ, e.typ,
@ -1282,7 +1285,7 @@ impl Inner {
) -> LspResult<Option<DocumentSymbolResponse>> { ) -> LspResult<Option<DocumentSymbolResponse>> {
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
if !self.is_diagnosable(&specifier) if !self.is_diagnosable(&specifier)
|| !self.config.specifier_enabled(&specifier) || !self.config.specifier_enabled(&specifier)
{ {
@ -1326,7 +1329,7 @@ impl Inner {
.filter(|s| self.documents.is_valid_file_referrer(s)); .filter(|s| self.documents.is_valid_file_referrer(s));
let mut specifier = self let mut specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
// skip formatting any files ignored by the config file // skip formatting any files ignored by the config file
if !self if !self
.config .config
@ -1441,7 +1444,7 @@ impl Inner {
} }
async fn hover(&self, params: HoverParams) -> LspResult<Option<Hover>> { async fn hover(&self, params: HoverParams) -> LspResult<Option<Hover>> {
let specifier = self.url_map.normalize_url( let specifier = self.url_map.uri_to_specifier(
&params.text_document_position_params.text_document.uri, &params.text_document_position_params.text_document.uri,
LspUrlKind::File, LspUrlKind::File,
); );
@ -1574,7 +1577,7 @@ impl Inner {
) -> LspResult<Option<CodeActionResponse>> { ) -> LspResult<Option<CodeActionResponse>> {
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
if !self.is_diagnosable(&specifier) if !self.is_diagnosable(&specifier)
|| !self.config.specifier_enabled(&specifier) || !self.config.specifier_enabled(&specifier)
{ {
@ -1918,7 +1921,7 @@ impl Inner {
) -> LspResult<Option<Vec<CodeLens>>> { ) -> LspResult<Option<Vec<CodeLens>>> {
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
if !self.is_diagnosable(&specifier) if !self.is_diagnosable(&specifier)
|| !self.config.specifier_enabled(&specifier) || !self.config.specifier_enabled(&specifier)
{ {
@ -2004,7 +2007,7 @@ impl Inner {
&self, &self,
params: DocumentHighlightParams, params: DocumentHighlightParams,
) -> LspResult<Option<Vec<DocumentHighlight>>> { ) -> LspResult<Option<Vec<DocumentHighlight>>> {
let specifier = self.url_map.normalize_url( let specifier = self.url_map.uri_to_specifier(
&params.text_document_position_params.text_document.uri, &params.text_document_position_params.text_document.uri,
LspUrlKind::File, LspUrlKind::File,
); );
@ -2048,7 +2051,7 @@ impl Inner {
&self, &self,
params: ReferenceParams, params: ReferenceParams,
) -> LspResult<Option<Vec<Location>>> { ) -> LspResult<Option<Vec<Location>>> {
let specifier = self.url_map.normalize_url( let specifier = self.url_map.uri_to_specifier(
&params.text_document_position.text_document.uri, &params.text_document_position.text_document.uri,
LspUrlKind::File, LspUrlKind::File,
); );
@ -2104,7 +2107,7 @@ impl Inner {
&self, &self,
params: GotoDefinitionParams, params: GotoDefinitionParams,
) -> LspResult<Option<GotoDefinitionResponse>> { ) -> LspResult<Option<GotoDefinitionResponse>> {
let specifier = self.url_map.normalize_url( let specifier = self.url_map.uri_to_specifier(
&params.text_document_position_params.text_document.uri, &params.text_document_position_params.text_document.uri,
LspUrlKind::File, LspUrlKind::File,
); );
@ -2143,7 +2146,7 @@ impl Inner {
&self, &self,
params: GotoTypeDefinitionParams, params: GotoTypeDefinitionParams,
) -> LspResult<Option<GotoTypeDefinitionResponse>> { ) -> LspResult<Option<GotoTypeDefinitionResponse>> {
let specifier = self.url_map.normalize_url( let specifier = self.url_map.uri_to_specifier(
&params.text_document_position_params.text_document.uri, &params.text_document_position_params.text_document.uri,
LspUrlKind::File, LspUrlKind::File,
); );
@ -2189,7 +2192,7 @@ impl Inner {
&self, &self,
params: CompletionParams, params: CompletionParams,
) -> LspResult<Option<CompletionResponse>> { ) -> LspResult<Option<CompletionResponse>> {
let specifier = self.url_map.normalize_url( let specifier = self.url_map.uri_to_specifier(
&params.text_document_position.text_document.uri, &params.text_document_position.text_document.uri,
LspUrlKind::File, LspUrlKind::File,
); );
@ -2378,7 +2381,7 @@ impl Inner {
&self, &self,
params: GotoImplementationParams, params: GotoImplementationParams,
) -> LspResult<Option<GotoImplementationResponse>> { ) -> LspResult<Option<GotoImplementationResponse>> {
let specifier = self.url_map.normalize_url( let specifier = self.url_map.uri_to_specifier(
&params.text_document_position_params.text_document.uri, &params.text_document_position_params.text_document.uri,
LspUrlKind::File, LspUrlKind::File,
); );
@ -2429,7 +2432,7 @@ impl Inner {
) -> LspResult<Option<Vec<FoldingRange>>> { ) -> LspResult<Option<Vec<FoldingRange>>> {
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
if !self.is_diagnosable(&specifier) if !self.is_diagnosable(&specifier)
|| !self.config.specifier_enabled(&specifier) || !self.config.specifier_enabled(&specifier)
{ {
@ -2476,7 +2479,7 @@ impl Inner {
) -> LspResult<Option<Vec<CallHierarchyIncomingCall>>> { ) -> LspResult<Option<Vec<CallHierarchyIncomingCall>>> {
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.item.uri, LspUrlKind::File); .uri_to_specifier(&params.item.uri, LspUrlKind::File);
if !self.is_diagnosable(&specifier) if !self.is_diagnosable(&specifier)
|| !self.config.specifier_enabled(&specifier) || !self.config.specifier_enabled(&specifier)
{ {
@ -2525,7 +2528,7 @@ impl Inner {
) -> LspResult<Option<Vec<CallHierarchyOutgoingCall>>> { ) -> LspResult<Option<Vec<CallHierarchyOutgoingCall>>> {
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.item.uri, LspUrlKind::File); .uri_to_specifier(&params.item.uri, LspUrlKind::File);
if !self.is_diagnosable(&specifier) if !self.is_diagnosable(&specifier)
|| !self.config.specifier_enabled(&specifier) || !self.config.specifier_enabled(&specifier)
{ {
@ -2570,7 +2573,7 @@ impl Inner {
&self, &self,
params: CallHierarchyPrepareParams, params: CallHierarchyPrepareParams,
) -> LspResult<Option<Vec<CallHierarchyItem>>> { ) -> LspResult<Option<Vec<CallHierarchyItem>>> {
let specifier = self.url_map.normalize_url( let specifier = self.url_map.uri_to_specifier(
&params.text_document_position_params.text_document.uri, &params.text_document_position_params.text_document.uri,
LspUrlKind::File, LspUrlKind::File,
); );
@ -2634,7 +2637,7 @@ impl Inner {
&self, &self,
params: RenameParams, params: RenameParams,
) -> LspResult<Option<WorkspaceEdit>> { ) -> LspResult<Option<WorkspaceEdit>> {
let specifier = self.url_map.normalize_url( let specifier = self.url_map.uri_to_specifier(
&params.text_document_position.text_document.uri, &params.text_document_position.text_document.uri,
LspUrlKind::File, LspUrlKind::File,
); );
@ -2683,7 +2686,7 @@ impl Inner {
) -> LspResult<Option<Vec<SelectionRange>>> { ) -> LspResult<Option<Vec<SelectionRange>>> {
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
if !self.is_diagnosable(&specifier) if !self.is_diagnosable(&specifier)
|| !self.config.specifier_enabled(&specifier) || !self.config.specifier_enabled(&specifier)
{ {
@ -2721,7 +2724,7 @@ impl Inner {
) -> LspResult<Option<SemanticTokensResult>> { ) -> LspResult<Option<SemanticTokensResult>> {
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
if !self.is_diagnosable(&specifier) { if !self.is_diagnosable(&specifier) {
return Ok(None); return Ok(None);
} }
@ -2774,7 +2777,7 @@ impl Inner {
) -> LspResult<Option<SemanticTokensRangeResult>> { ) -> LspResult<Option<SemanticTokensRangeResult>> {
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
if !self.is_diagnosable(&specifier) { if !self.is_diagnosable(&specifier) {
return Ok(None); return Ok(None);
} }
@ -2823,7 +2826,7 @@ impl Inner {
&self, &self,
params: SignatureHelpParams, params: SignatureHelpParams,
) -> LspResult<Option<SignatureHelp>> { ) -> LspResult<Option<SignatureHelp>> {
let specifier = self.url_map.normalize_url( let specifier = self.url_map.uri_to_specifier(
&params.text_document_position_params.text_document.uri, &params.text_document_position_params.text_document.uri,
LspUrlKind::File, LspUrlKind::File,
); );
@ -2877,8 +2880,8 @@ impl Inner {
) -> LspResult<Option<WorkspaceEdit>> { ) -> LspResult<Option<WorkspaceEdit>> {
let mut changes = vec![]; let mut changes = vec![];
for rename in params.files { for rename in params.files {
let old_specifier = self.url_map.normalize_url( let old_specifier = self.url_map.uri_to_specifier(
&url_to_uri(&resolve_url(&rename.old_uri).unwrap()), &Uri::from_str(&rename.old_uri).unwrap(),
LspUrlKind::File, LspUrlKind::File,
); );
let options = self let options = self
@ -2903,8 +2906,8 @@ impl Inner {
.get_edits_for_file_rename( .get_edits_for_file_rename(
self.snapshot(), self.snapshot(),
old_specifier, old_specifier,
self.url_map.normalize_url( self.url_map.uri_to_specifier(
&url_to_uri(&resolve_url(&rename.new_uri).unwrap()), &Uri::from_str(&rename.new_uri).unwrap(),
LspUrlKind::File, LspUrlKind::File,
), ),
format_code_settings, format_code_settings,
@ -3503,22 +3506,28 @@ impl Inner {
let mut config_events = vec![]; let mut config_events = vec![];
for (scope_url, config_data) in self.config.tree.data_by_scope().iter() { for (scope_url, config_data) in self.config.tree.data_by_scope().iter() {
let scope_uri = url_to_uri(scope_url); let Ok(scope_uri) = url_to_uri(scope_url) else {
continue;
};
if let Some(config_file) = config_data.maybe_deno_json() { if let Some(config_file) = config_data.maybe_deno_json() {
config_events.push(lsp_custom::DenoConfigurationChangeEvent { if let Ok(file_uri) = url_to_uri(&config_file.specifier) {
scope_uri: scope_uri.clone(), config_events.push(lsp_custom::DenoConfigurationChangeEvent {
file_uri: url_to_uri(&config_file.specifier), scope_uri: scope_uri.clone(),
typ: lsp_custom::DenoConfigurationChangeType::Added, file_uri,
configuration_type: lsp_custom::DenoConfigurationType::DenoJson, typ: lsp_custom::DenoConfigurationChangeType::Added,
}); configuration_type: lsp_custom::DenoConfigurationType::DenoJson,
});
}
} }
if let Some(package_json) = config_data.maybe_pkg_json() { if let Some(package_json) = config_data.maybe_pkg_json() {
config_events.push(lsp_custom::DenoConfigurationChangeEvent { if let Ok(file_uri) = url_to_uri(&package_json.specifier()) {
scope_uri, config_events.push(lsp_custom::DenoConfigurationChangeEvent {
file_uri: url_to_uri(&package_json.specifier()), scope_uri,
typ: lsp_custom::DenoConfigurationChangeType::Added, file_uri,
configuration_type: lsp_custom::DenoConfigurationType::PackageJson, typ: lsp_custom::DenoConfigurationChangeType::Added,
}); configuration_type: lsp_custom::DenoConfigurationType::PackageJson,
});
}
} }
} }
if !config_events.is_empty() { if !config_events.is_empty() {
@ -3648,7 +3657,9 @@ impl Inner {
.into_iter() .into_iter()
.map(|folder| { .map(|folder| {
( (
self.url_map.normalize_url(&folder.uri, LspUrlKind::Folder), self
.url_map
.uri_to_specifier(&folder.uri, LspUrlKind::Folder),
folder, folder,
) )
}) })
@ -3724,7 +3735,8 @@ impl Inner {
result.push(TaskDefinition { result.push(TaskDefinition {
name: name.clone(), name: name.clone(),
command: command.to_string(), command: command.to_string(),
source_uri: url_to_uri(&config_file.specifier), source_uri: url_to_uri(&config_file.specifier)
.map_err(|_| LspError::internal_error())?,
}); });
} }
}; };
@ -3735,7 +3747,8 @@ impl Inner {
result.push(TaskDefinition { result.push(TaskDefinition {
name: name.clone(), name: name.clone(),
command: command.clone(), command: command.clone(),
source_uri: url_to_uri(&package_json.specifier()), source_uri: url_to_uri(&package_json.specifier())
.map_err(|_| LspError::internal_error())?,
}); });
} }
} }
@ -3750,7 +3763,7 @@ impl Inner {
) -> LspResult<Option<Vec<InlayHint>>> { ) -> LspResult<Option<Vec<InlayHint>>> {
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
if !self.is_diagnosable(&specifier) if !self.is_diagnosable(&specifier)
|| !self.config.specifier_enabled(&specifier) || !self.config.specifier_enabled(&specifier)
|| !self.config.enabled_inlay_hints_for_specifier(&specifier) || !self.config.enabled_inlay_hints_for_specifier(&specifier)
@ -3813,7 +3826,7 @@ impl Inner {
.mark_with_args("lsp.virtual_text_document", &params); .mark_with_args("lsp.virtual_text_document", &params);
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document.uri, LspUrlKind::File); .uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
let contents = if specifier.scheme() == "deno" let contents = if specifier.scheme() == "deno"
&& specifier.path() == "/status.md" && specifier.path() == "/status.md"
{ {

View file

@ -76,7 +76,7 @@ impl ReplLanguageServer {
.initialize(InitializeParams { .initialize(InitializeParams {
process_id: None, process_id: None,
root_path: None, root_path: None,
root_uri: Some(url_to_uri(&cwd_uri)), root_uri: Some(url_to_uri(&cwd_uri).unwrap()),
initialization_options: Some( initialization_options: Some(
serde_json::to_value(get_repl_workspace_settings()).unwrap(), serde_json::to_value(get_repl_workspace_settings()).unwrap(),
), ),

View file

@ -10,6 +10,7 @@ use crate::tools::test::TestDescription;
use crate::tools::test::TestStepDescription; use crate::tools::test::TestStepDescription;
use crate::util::checksum; use crate::util::checksum;
use deno_core::error::AnyError;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use lsp::Range; use lsp::Range;
use std::collections::HashMap; use std::collections::HashMap;
@ -144,21 +145,23 @@ impl TestModule {
pub fn as_replace_notification( pub fn as_replace_notification(
&self, &self,
maybe_root_uri: Option<&ModuleSpecifier>, maybe_root_uri: Option<&ModuleSpecifier>,
) -> TestingNotification { ) -> Result<TestingNotification, AnyError> {
let label = self.label(maybe_root_uri); let label = self.label(maybe_root_uri);
TestingNotification::Module(lsp_custom::TestModuleNotificationParams { Ok(TestingNotification::Module(
text_document: lsp::TextDocumentIdentifier { lsp_custom::TestModuleNotificationParams {
uri: url_to_uri(&self.specifier), text_document: lsp::TextDocumentIdentifier {
uri: url_to_uri(&self.specifier)?,
},
kind: lsp_custom::TestModuleNotificationKind::Replace,
label,
tests: self
.defs
.iter()
.filter(|(_, def)| def.parent_id.is_none())
.map(|(id, _)| self.get_test_data(id))
.collect(),
}, },
kind: lsp_custom::TestModuleNotificationKind::Replace, ))
label,
tests: self
.defs
.iter()
.filter(|(_, def)| def.parent_id.is_none())
.map(|(id, _)| self.get_test_data(id))
.collect(),
})
} }
pub fn label(&self, maybe_root_uri: Option<&ModuleSpecifier>) -> String { pub fn label(&self, maybe_root_uri: Option<&ModuleSpecifier>) -> String {

View file

@ -186,7 +186,7 @@ impl TestRun {
self self
.queue .queue
.iter() .iter()
.map(|s| { .filter_map(|s| {
let ids = if let Some((test_module, _)) = tests.get(s) { let ids = if let Some((test_module, _)) = tests.get(s) {
if let Some(filter) = self.filters.get(s) { if let Some(filter) = self.filters.get(s) {
filter.as_ids(test_module) filter.as_ids(test_module)
@ -196,10 +196,12 @@ impl TestRun {
} else { } else {
Vec::new() Vec::new()
}; };
lsp_custom::EnqueuedTestModule { Some(lsp_custom::EnqueuedTestModule {
text_document: lsp::TextDocumentIdentifier { uri: url_to_uri(s) }, text_document: lsp::TextDocumentIdentifier {
uri: url_to_uri(s).ok()?,
},
ids, ids,
} })
}) })
.collect() .collect()
} }
@ -591,6 +593,9 @@ impl LspTestReporter {
let (test_module, _) = files let (test_module, _) = files
.entry(specifier.clone()) .entry(specifier.clone())
.or_insert_with(|| (TestModule::new(specifier), "1".to_string())); .or_insert_with(|| (TestModule::new(specifier), "1".to_string()));
let Ok(uri) = url_to_uri(&test_module.specifier) else {
return;
};
let (static_id, is_new) = test_module.register_dynamic(desc); let (static_id, is_new) = test_module.register_dynamic(desc);
self.tests.insert( self.tests.insert(
desc.id, desc.id,
@ -601,9 +606,7 @@ impl LspTestReporter {
.client .client
.send_test_notification(TestingNotification::Module( .send_test_notification(TestingNotification::Module(
lsp_custom::TestModuleNotificationParams { lsp_custom::TestModuleNotificationParams {
text_document: lsp::TextDocumentIdentifier { text_document: lsp::TextDocumentIdentifier { uri },
uri: url_to_uri(&test_module.specifier),
},
kind: lsp_custom::TestModuleNotificationKind::Insert, kind: lsp_custom::TestModuleNotificationKind::Insert,
label: test_module.label(self.maybe_root_uri.as_ref()), label: test_module.label(self.maybe_root_uri.as_ref()),
tests: vec![test_module.get_test_data(&static_id)], tests: vec![test_module.get_test_data(&static_id)],
@ -701,6 +704,9 @@ impl LspTestReporter {
let (test_module, _) = files let (test_module, _) = files
.entry(specifier.clone()) .entry(specifier.clone())
.or_insert_with(|| (TestModule::new(specifier), "1".to_string())); .or_insert_with(|| (TestModule::new(specifier), "1".to_string()));
let Ok(uri) = url_to_uri(&test_module.specifier) else {
return;
};
let (static_id, is_new) = test_module.register_step_dynamic( let (static_id, is_new) = test_module.register_step_dynamic(
desc, desc,
self.tests.get(&desc.parent_id).unwrap().static_id(), self.tests.get(&desc.parent_id).unwrap().static_id(),
@ -714,9 +720,7 @@ impl LspTestReporter {
.client .client
.send_test_notification(TestingNotification::Module( .send_test_notification(TestingNotification::Module(
lsp_custom::TestModuleNotificationParams { lsp_custom::TestModuleNotificationParams {
text_document: lsp::TextDocumentIdentifier { text_document: lsp::TextDocumentIdentifier { uri },
uri: url_to_uri(&test_module.specifier),
},
kind: lsp_custom::TestModuleNotificationKind::Insert, kind: lsp_custom::TestModuleNotificationKind::Insert,
label: test_module.label(self.maybe_root_uri.as_ref()), label: test_module.label(self.maybe_root_uri.as_ref()),
tests: vec![test_module.get_test_data(&static_id)], tests: vec![test_module.get_test_data(&static_id)],
@ -800,14 +804,14 @@ mod tests {
include: Some(vec![ include: Some(vec![
lsp_custom::TestIdentifier { lsp_custom::TestIdentifier {
text_document: lsp::TextDocumentIdentifier { text_document: lsp::TextDocumentIdentifier {
uri: url_to_uri(&specifier), uri: url_to_uri(&specifier).unwrap(),
}, },
id: None, id: None,
step_id: None, step_id: None,
}, },
lsp_custom::TestIdentifier { lsp_custom::TestIdentifier {
text_document: lsp::TextDocumentIdentifier { text_document: lsp::TextDocumentIdentifier {
uri: url_to_uri(&non_test_specifier), uri: url_to_uri(&non_test_specifier).unwrap(),
}, },
id: None, id: None,
step_id: None, step_id: None,
@ -815,7 +819,7 @@ mod tests {
]), ]),
exclude: vec![lsp_custom::TestIdentifier { exclude: vec![lsp_custom::TestIdentifier {
text_document: lsp::TextDocumentIdentifier { text_document: lsp::TextDocumentIdentifier {
uri: url_to_uri(&specifier), uri: url_to_uri(&specifier).unwrap(),
}, },
id: Some( id: Some(
"69d9fe87f64f5b66cb8b631d4fd2064e8224b8715a049be54276c42189ff8f9f" "69d9fe87f64f5b66cb8b631d4fd2064e8224b8715a049be54276c42189ff8f9f"

View file

@ -27,14 +27,16 @@ use tower_lsp::jsonrpc::Error as LspError;
use tower_lsp::jsonrpc::Result as LspResult; use tower_lsp::jsonrpc::Result as LspResult;
use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types as lsp;
fn as_delete_notification(url: ModuleSpecifier) -> TestingNotification { fn as_delete_notification(
TestingNotification::DeleteModule( url: &ModuleSpecifier,
) -> Result<TestingNotification, AnyError> {
Ok(TestingNotification::DeleteModule(
lsp_custom::TestModuleDeleteNotificationParams { lsp_custom::TestModuleDeleteNotificationParams {
text_document: lsp::TextDocumentIdentifier { text_document: lsp::TextDocumentIdentifier {
uri: url_to_uri(&url), uri: url_to_uri(url)?,
}, },
}, },
) ))
} }
pub type TestServerTests = pub type TestServerTests =
@ -126,20 +128,24 @@ impl TestServer {
.map(|tm| tm.as_ref().clone()) .map(|tm| tm.as_ref().clone())
.unwrap_or_else(|| TestModule::new(specifier.clone())); .unwrap_or_else(|| TestModule::new(specifier.clone()));
if !test_module.is_empty() { if !test_module.is_empty() {
client.send_test_notification( if let Ok(params) =
test_module.as_replace_notification(mru.as_ref()), test_module.as_replace_notification(mru.as_ref())
); {
client.send_test_notification(params);
}
} else if !was_empty { } else if !was_empty {
client.send_test_notification(as_delete_notification( if let Ok(params) = as_delete_notification(specifier) {
specifier.clone(), client.send_test_notification(params);
)); }
} }
tests tests
.insert(specifier.clone(), (test_module, script_version)); .insert(specifier.clone(), (test_module, script_version));
} }
} }
for key in keys { for key in &keys {
client.send_test_notification(as_delete_notification(key)); if let Ok(params) = as_delete_notification(key) {
client.send_test_notification(params);
}
} }
performance.measure(mark); performance.measure(mark);
} }

View file

@ -19,9 +19,10 @@ use super::refactor::EXTRACT_TYPE;
use super::semantic_tokens; use super::semantic_tokens;
use super::semantic_tokens::SemanticTokensBuilder; use super::semantic_tokens::SemanticTokensBuilder;
use super::text::LineIndex; use super::text::LineIndex;
use super::urls::uri_to_url;
use super::urls::url_to_uri; use super::urls::url_to_uri;
use super::urls::LspClientUrl;
use super::urls::INVALID_SPECIFIER; use super::urls::INVALID_SPECIFIER;
use super::urls::INVALID_URI;
use crate::args::jsr_url; use crate::args::jsr_url;
use crate::args::FmtOptionsConfig; use crate::args::FmtOptionsConfig;
@ -2047,7 +2048,7 @@ impl DocumentSpan {
let file_referrer = target_asset_or_doc.file_referrer(); let file_referrer = target_asset_or_doc.file_referrer();
let target_uri = language_server let target_uri = language_server
.url_map .url_map
.normalize_specifier(&target_specifier, file_referrer) .specifier_to_uri(&target_specifier, file_referrer)
.ok()?; .ok()?;
let (target_range, target_selection_range) = let (target_range, target_selection_range) =
if let Some(context_span) = &self.context_span { if let Some(context_span) = &self.context_span {
@ -2072,7 +2073,7 @@ impl DocumentSpan {
}; };
let link = lsp::LocationLink { let link = lsp::LocationLink {
origin_selection_range, origin_selection_range,
target_uri: target_uri.to_uri(), target_uri,
target_range, target_range,
target_selection_range, target_selection_range,
}; };
@ -2092,11 +2093,11 @@ impl DocumentSpan {
let line_index = asset_or_doc.line_index(); let line_index = asset_or_doc.line_index();
let range = self.text_span.to_range(line_index); let range = self.text_span.to_range(line_index);
let file_referrer = asset_or_doc.file_referrer(); let file_referrer = asset_or_doc.file_referrer();
let mut target = language_server let target_uri = language_server
.url_map .url_map
.normalize_specifier(&specifier, file_referrer) .specifier_to_uri(&specifier, file_referrer)
.ok()? .ok()?;
.into_url(); let mut target = uri_to_url(&target_uri);
target.set_fragment(Some(&format!( target.set_fragment(Some(&format!(
"L{},{}", "L{},{}",
range.start.line + 1, range.start.line + 1,
@ -2155,13 +2156,10 @@ impl NavigateToItem {
let file_referrer = asset_or_doc.file_referrer(); let file_referrer = asset_or_doc.file_referrer();
let uri = language_server let uri = language_server
.url_map .url_map
.normalize_specifier(&specifier, file_referrer) .specifier_to_uri(&specifier, file_referrer)
.ok()?; .ok()?;
let range = self.text_span.to_range(line_index); let range = self.text_span.to_range(line_index);
let location = lsp::Location { let location = lsp::Location { uri, range };
uri: uri.to_uri(),
range,
};
let mut tags: Option<Vec<lsp::SymbolTag>> = None; let mut tags: Option<Vec<lsp::SymbolTag>> = None;
let kind_modifiers = parse_kind_modifier(&self.kind_modifiers); let kind_modifiers = parse_kind_modifier(&self.kind_modifiers);
@ -2414,12 +2412,10 @@ impl ImplementationLocation {
let file_referrer = language_server.documents.get_file_referrer(&specifier); let file_referrer = language_server.documents.get_file_referrer(&specifier);
let uri = language_server let uri = language_server
.url_map .url_map
.normalize_specifier(&specifier, file_referrer.as_deref()) .specifier_to_uri(&specifier, file_referrer.as_deref())
.unwrap_or_else(|_| { .unwrap_or_else(|_| INVALID_URI.clone());
LspClientUrl::new(ModuleSpecifier::parse("deno://invalid").unwrap())
});
lsp::Location { lsp::Location {
uri: uri.to_uri(), uri,
range: self.document_span.text_span.to_range(line_index), range: self.document_span.text_span.to_range(line_index),
} }
} }
@ -2475,7 +2471,7 @@ impl RenameLocations {
language_server.documents.get_file_referrer(&specifier); language_server.documents.get_file_referrer(&specifier);
let uri = language_server let uri = language_server
.url_map .url_map
.normalize_specifier(&specifier, file_referrer.as_deref())?; .specifier_to_uri(&specifier, file_referrer.as_deref())?;
let asset_or_doc = language_server.get_asset_or_document(&specifier)?; let asset_or_doc = language_server.get_asset_or_document(&specifier)?;
// ensure TextDocumentEdit for `location.file_name`. // ensure TextDocumentEdit for `location.file_name`.
@ -2484,7 +2480,7 @@ impl RenameLocations {
uri.clone(), uri.clone(),
lsp::TextDocumentEdit { lsp::TextDocumentEdit {
text_document: lsp::OptionalVersionedTextDocumentIdentifier { text_document: lsp::OptionalVersionedTextDocumentIdentifier {
uri: uri.to_uri(), uri: uri.clone(),
version: asset_or_doc.document_lsp_version(), version: asset_or_doc.document_lsp_version(),
}, },
edits: edits:
@ -2686,7 +2682,7 @@ impl FileTextChanges {
.collect(); .collect();
Ok(lsp::TextDocumentEdit { Ok(lsp::TextDocumentEdit {
text_document: lsp::OptionalVersionedTextDocumentIdentifier { text_document: lsp::OptionalVersionedTextDocumentIdentifier {
uri: url_to_uri(&specifier), uri: url_to_uri(&specifier)?,
version: asset_or_doc.document_lsp_version(), version: asset_or_doc.document_lsp_version(),
}, },
edits, edits,
@ -2713,7 +2709,7 @@ impl FileTextChanges {
if self.is_new_file.unwrap_or(false) { if self.is_new_file.unwrap_or(false) {
ops.push(lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create( ops.push(lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(
lsp::CreateFile { lsp::CreateFile {
uri: url_to_uri(&specifier), uri: url_to_uri(&specifier)?,
options: Some(lsp::CreateFileOptions { options: Some(lsp::CreateFileOptions {
ignore_if_exists: Some(true), ignore_if_exists: Some(true),
overwrite: None, overwrite: None,
@ -2730,7 +2726,7 @@ impl FileTextChanges {
.collect(); .collect();
ops.push(lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit { ops.push(lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
text_document: lsp::OptionalVersionedTextDocumentIdentifier { text_document: lsp::OptionalVersionedTextDocumentIdentifier {
uri: url_to_uri(&specifier), uri: url_to_uri(&specifier)?,
version: maybe_asset_or_document.and_then(|d| d.document_lsp_version()), version: maybe_asset_or_document.and_then(|d| d.document_lsp_version()),
}, },
edits, edits,
@ -3128,10 +3124,10 @@ impl ReferenceEntry {
let file_referrer = language_server.documents.get_file_referrer(&specifier); let file_referrer = language_server.documents.get_file_referrer(&specifier);
let uri = language_server let uri = language_server
.url_map .url_map
.normalize_specifier(&specifier, file_referrer.as_deref()) .specifier_to_uri(&specifier, file_referrer.as_deref())
.unwrap_or_else(|_| LspClientUrl::new(INVALID_SPECIFIER.clone())); .unwrap_or_else(|_| INVALID_URI.clone());
lsp::Location { lsp::Location {
uri: uri.to_uri(), uri,
range: self.document_span.text_span.to_range(line_index), range: self.document_span.text_span.to_range(line_index),
} }
} }
@ -3189,12 +3185,13 @@ impl CallHierarchyItem {
.get_file_referrer(&target_specifier); .get_file_referrer(&target_specifier);
let uri = language_server let uri = language_server
.url_map .url_map
.normalize_specifier(&target_specifier, file_referrer.as_deref()) .specifier_to_uri(&target_specifier, file_referrer.as_deref())
.unwrap_or_else(|_| LspClientUrl::new(INVALID_SPECIFIER.clone())); .unwrap_or_else(|_| INVALID_URI.clone());
let use_file_name = self.is_source_file_item(); let use_file_name = self.is_source_file_item();
let maybe_file_path = if uri.as_url().scheme() == "file" { let maybe_file_path = if uri.scheme().is_some_and(|s| s.as_str() == "file")
specifier_to_file_path(uri.as_url()).ok() {
specifier_to_file_path(&uri_to_url(&uri)).ok()
} else { } else {
None None
}; };
@ -3238,7 +3235,7 @@ impl CallHierarchyItem {
lsp::CallHierarchyItem { lsp::CallHierarchyItem {
name, name,
tags, tags,
uri: uri.to_uri(), uri,
detail: Some(detail), detail: Some(detail),
kind: self.kind.clone().into(), kind: self.kind.clone().into(),
range: self.span.to_range(line_index.clone()), range: self.span.to_range(line_index.clone()),

View file

@ -13,12 +13,18 @@ use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use super::cache::LspCache; use super::cache::LspCache;
use super::logging::lsp_warn;
/// Used in situations where a default URL needs to be used where otherwise a /// Used in situations where a default URL needs to be used where otherwise a
/// panic is undesired. /// panic is undesired.
pub static INVALID_SPECIFIER: Lazy<ModuleSpecifier> = pub static INVALID_SPECIFIER: Lazy<ModuleSpecifier> =
Lazy::new(|| ModuleSpecifier::parse("deno://invalid").unwrap()); Lazy::new(|| ModuleSpecifier::parse("deno://invalid").unwrap());
/// Used in situations where a default URL needs to be used where otherwise a
/// panic is undesired.
pub static INVALID_URI: Lazy<Uri> =
Lazy::new(|| Uri::from_str("deno://invalid").unwrap());
/// Matches the `encodeURIComponent()` encoding from JavaScript, which matches /// Matches the `encodeURIComponent()` encoding from JavaScript, which matches
/// the component percent encoding set. /// the component percent encoding set.
/// ///
@ -58,7 +64,7 @@ fn hash_data_specifier(specifier: &ModuleSpecifier) -> String {
crate::util::checksum::gen(&[file_name_str.as_bytes()]) crate::util::checksum::gen(&[file_name_str.as_bytes()])
} }
fn to_deno_url(specifier: &Url) -> String { fn to_deno_uri(specifier: &Url) -> String {
let mut string = String::with_capacity(specifier.as_str().len() + 6); let mut string = String::with_capacity(specifier.as_str().len() + 6);
string.push_str("deno:/"); string.push_str("deno:/");
string.push_str(specifier.scheme()); string.push_str(specifier.scheme());
@ -95,64 +101,31 @@ fn from_deno_url(url: &Url) -> Option<Url> {
Url::parse(&string).ok() Url::parse(&string).ok()
} }
/// This exists to make it a little bit harder to accidentally use a `Url`
/// in the wrong place where a client url should be used.
#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
pub struct LspClientUrl(Url);
impl LspClientUrl {
pub fn new(url: Url) -> Self {
Self(url)
}
pub fn as_url(&self) -> &Url {
&self.0
}
pub fn into_url(self) -> Url {
self.0
}
pub fn to_uri(&self) -> Uri {
url_to_uri(&self.0)
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl std::fmt::Display for LspClientUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct LspUrlMapInner { struct LspUrlMapInner {
specifier_to_url: HashMap<ModuleSpecifier, LspClientUrl>, specifier_to_uri: HashMap<ModuleSpecifier, Uri>,
url_to_specifier: HashMap<Url, ModuleSpecifier>, uri_to_specifier: HashMap<Uri, ModuleSpecifier>,
} }
impl LspUrlMapInner { impl LspUrlMapInner {
fn put(&mut self, specifier: ModuleSpecifier, url: LspClientUrl) { fn put(&mut self, specifier: ModuleSpecifier, uri: Uri) {
self self.uri_to_specifier.insert(uri.clone(), specifier.clone());
.url_to_specifier self.specifier_to_uri.insert(specifier, uri);
.insert(url.as_url().clone(), specifier.clone());
self.specifier_to_url.insert(specifier, url);
} }
fn get_url(&self, specifier: &ModuleSpecifier) -> Option<&LspClientUrl> { fn get_uri(&self, specifier: &ModuleSpecifier) -> Option<&Uri> {
self.specifier_to_url.get(specifier) self.specifier_to_uri.get(specifier)
} }
fn get_specifier(&self, url: &Url) -> Option<&ModuleSpecifier> { fn get_specifier(&self, uri: &Uri) -> Option<&ModuleSpecifier> {
self.url_to_specifier.get(url) self.uri_to_specifier.get(uri)
} }
} }
pub fn url_to_uri(url: &Url) -> Uri { pub fn url_to_uri(url: &Url) -> Result<Uri, AnyError> {
Uri::from_str(url.as_str()).unwrap() Ok(Uri::from_str(url.as_str()).inspect_err(|err| {
lsp_warn!("Could not convert URL \"{url}\" to URI: {err}")
})?)
} }
pub fn uri_to_url(uri: &Uri) -> Url { pub fn uri_to_url(uri: &Uri) -> Url {
@ -181,24 +154,24 @@ impl LspUrlMap {
/// Normalize a specifier that is used internally within Deno (or tsc) to a /// Normalize a specifier that is used internally within Deno (or tsc) to a
/// URL that can be handled as a "virtual" document by an LSP client. /// URL that can be handled as a "virtual" document by an LSP client.
pub fn normalize_specifier( pub fn specifier_to_uri(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
) -> Result<LspClientUrl, AnyError> { ) -> Result<Uri, AnyError> {
if let Some(file_url) = if let Some(file_url) =
self.cache.vendored_specifier(specifier, file_referrer) self.cache.vendored_specifier(specifier, file_referrer)
{ {
return Ok(LspClientUrl(file_url)); return url_to_uri(&file_url);
} }
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
if let Some(url) = inner.get_url(specifier).cloned() { if let Some(uri) = inner.get_uri(specifier).cloned() {
Ok(url) Ok(uri)
} else { } else {
let url = if specifier.scheme() == "file" { let uri = if specifier.scheme() == "file" {
LspClientUrl(specifier.clone()) url_to_uri(specifier)?
} else { } else {
let specifier_str = if specifier.scheme() == "asset" { let uri_str = if specifier.scheme() == "asset" {
format!("deno:/asset{}", specifier.path()) format!("deno:/asset{}", specifier.path())
} else if specifier.scheme() == "data" { } else if specifier.scheme() == "data" {
let data_url = deno_graph::source::RawDataUrl::parse(specifier)?; let data_url = deno_graph::source::RawDataUrl::parse(specifier)?;
@ -214,13 +187,13 @@ impl LspUrlMap {
extension extension
) )
} else { } else {
to_deno_url(specifier) to_deno_uri(specifier)
}; };
let url = LspClientUrl(Url::parse(&specifier_str)?); let uri = Uri::from_str(&uri_str)?;
inner.put(specifier.clone(), url.clone()); inner.put(specifier.clone(), uri.clone());
url uri
}; };
Ok(url) Ok(uri)
} }
} }
@ -232,13 +205,17 @@ impl LspUrlMap {
/// Note: Sometimes the url provided by the client may not have a trailing slash, /// Note: Sometimes the url provided by the client may not have a trailing slash,
/// so we need to force it to in the mapping and nee to explicitly state whether /// so we need to force it to in the mapping and nee to explicitly state whether
/// this is a file or directory url. /// this is a file or directory url.
pub fn normalize_url(&self, uri: &Uri, kind: LspUrlKind) -> ModuleSpecifier { pub fn uri_to_specifier(
&self,
uri: &Uri,
kind: LspUrlKind,
) -> ModuleSpecifier {
let url = uri_to_url(uri); let url = uri_to_url(uri);
if let Some(remote_url) = self.cache.unvendored_specifier(&url) { if let Some(remote_url) = self.cache.unvendored_specifier(&url) {
return remote_url; return remote_url;
} }
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
if let Some(specifier) = inner.get_specifier(&url).cloned() { if let Some(specifier) = inner.get_specifier(uri).cloned() {
return specifier; return specifier;
} }
let mut specifier = None; let mut specifier = None;
@ -255,7 +232,7 @@ impl LspUrlMap {
specifier = Some(s); specifier = Some(s);
} }
let specifier = specifier.unwrap_or_else(|| url.clone()); let specifier = specifier.unwrap_or_else(|| url.clone());
inner.put(specifier.clone(), LspClientUrl(url)); inner.put(specifier.clone(), uri.clone());
specifier specifier
} }
} }
@ -303,15 +280,14 @@ mod tests {
fn test_lsp_url_map() { fn test_lsp_url_map() {
let map = LspUrlMap::default(); let map = LspUrlMap::default();
let fixture = resolve_url("https://deno.land/x/pkg@1.0.0/mod.ts").unwrap(); let fixture = resolve_url("https://deno.land/x/pkg@1.0.0/mod.ts").unwrap();
let actual_url = map let actual_uri = map
.normalize_specifier(&fixture, None) .specifier_to_uri(&fixture, None)
.expect("could not handle specifier"); .expect("could not handle specifier");
let expected_url = assert_eq!(
Url::parse("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap(); actual_uri.as_str(),
assert_eq!(actual_url.as_url(), &expected_url); "deno:/https/deno.land/x/pkg%401.0.0/mod.ts"
);
let actual_specifier = let actual_specifier = map.uri_to_specifier(&actual_uri, LspUrlKind::File);
map.normalize_url(&actual_url.to_uri(), LspUrlKind::File);
assert_eq!(actual_specifier, fixture); assert_eq!(actual_specifier, fixture);
} }
@ -320,17 +296,13 @@ mod tests {
let map = LspUrlMap::default(); let map = LspUrlMap::default();
let fixture = let fixture =
Uri::from_str("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap(); Uri::from_str("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap();
let actual_specifier = map.normalize_url(&fixture, LspUrlKind::File); let actual_specifier = map.uri_to_specifier(&fixture, LspUrlKind::File);
let expected_specifier = let expected_specifier =
Url::parse("https://deno.land/x/pkg@1.0.0/mod.ts").unwrap(); Url::parse("https://deno.land/x/pkg@1.0.0/mod.ts").unwrap();
assert_eq!(&actual_specifier, &expected_specifier); assert_eq!(&actual_specifier, &expected_specifier);
let actual_url = map let actual_uri = map.specifier_to_uri(&actual_specifier, None).unwrap();
.normalize_specifier(&actual_specifier, None) assert_eq!(actual_uri, fixture);
.unwrap()
.to_uri()
.clone();
assert_eq!(actual_url, fixture);
} }
#[test] #[test]
@ -338,14 +310,11 @@ mod tests {
// Test fix for #9741 - not properly encoding certain URLs // Test fix for #9741 - not properly encoding certain URLs
let map = LspUrlMap::default(); let map = LspUrlMap::default();
let fixture = resolve_url("https://cdn.skypack.dev/-/postcss@v8.2.9-E4SktPp9c0AtxrJHp8iV/dist=es2020,mode=types/lib/postcss.d.ts").unwrap(); let fixture = resolve_url("https://cdn.skypack.dev/-/postcss@v8.2.9-E4SktPp9c0AtxrJHp8iV/dist=es2020,mode=types/lib/postcss.d.ts").unwrap();
let actual_url = map let actual_uri = map
.normalize_specifier(&fixture, None) .specifier_to_uri(&fixture, None)
.expect("could not handle specifier"); .expect("could not handle specifier");
let expected_url = Url::parse("deno:/https/cdn.skypack.dev/-/postcss%40v8.2.9-E4SktPp9c0AtxrJHp8iV/dist%3Des2020%2Cmode%3Dtypes/lib/postcss.d.ts").unwrap(); assert_eq!(actual_uri.as_str(), "deno:/https/cdn.skypack.dev/-/postcss%40v8.2.9-E4SktPp9c0AtxrJHp8iV/dist%3Des2020%2Cmode%3Dtypes/lib/postcss.d.ts");
assert_eq!(actual_url.as_url(), &expected_url); let actual_specifier = map.uri_to_specifier(&actual_uri, LspUrlKind::File);
let actual_specifier =
map.normalize_url(&actual_url.to_uri(), LspUrlKind::File);
assert_eq!(actual_specifier, fixture); assert_eq!(actual_specifier, fixture);
} }
@ -353,14 +322,13 @@ mod tests {
fn test_lsp_url_map_data() { fn test_lsp_url_map_data() {
let map = LspUrlMap::default(); let map = LspUrlMap::default();
let fixture = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap(); let fixture = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
let actual_url = map let actual_uri = map
.normalize_specifier(&fixture, None) .specifier_to_uri(&fixture, None)
.expect("could not handle specifier"); .expect("could not handle specifier");
let expected_url = Url::parse("deno:/c21c7fc382b2b0553dc0864aa81a3acacfb7b3d1285ab5ae76da6abec213fb37/data_url.ts").unwrap(); let expected_url = Url::parse("deno:/c21c7fc382b2b0553dc0864aa81a3acacfb7b3d1285ab5ae76da6abec213fb37/data_url.ts").unwrap();
assert_eq!(actual_url.as_url(), &expected_url); assert_eq!(&uri_to_url(&actual_uri), &expected_url);
let actual_specifier = let actual_specifier = map.uri_to_specifier(&actual_uri, LspUrlKind::File);
map.normalize_url(&actual_url.to_uri(), LspUrlKind::File);
assert_eq!(actual_specifier, fixture); assert_eq!(actual_specifier, fixture);
} }
@ -368,15 +336,11 @@ mod tests {
fn test_lsp_url_map_host_with_port() { fn test_lsp_url_map_host_with_port() {
let map = LspUrlMap::default(); let map = LspUrlMap::default();
let fixture = resolve_url("http://localhost:8000/mod.ts").unwrap(); let fixture = resolve_url("http://localhost:8000/mod.ts").unwrap();
let actual_url = map let actual_uri = map
.normalize_specifier(&fixture, None) .specifier_to_uri(&fixture, None)
.expect("could not handle specifier"); .expect("could not handle specifier");
let expected_url = assert_eq!(actual_uri.as_str(), "deno:/http/localhost%3A8000/mod.ts");
Url::parse("deno:/http/localhost%3A8000/mod.ts").unwrap(); let actual_specifier = map.uri_to_specifier(&actual_uri, LspUrlKind::File);
assert_eq!(actual_url.as_url(), &expected_url);
let actual_specifier =
map.normalize_url(&actual_url.to_uri(), LspUrlKind::File);
assert_eq!(actual_specifier, fixture); assert_eq!(actual_specifier, fixture);
} }
@ -388,7 +352,7 @@ mod tests {
"file:///c%3A/Users/deno/Desktop/file%20with%20spaces%20in%20name.txt", "file:///c%3A/Users/deno/Desktop/file%20with%20spaces%20in%20name.txt",
) )
.unwrap(); .unwrap();
let actual = map.normalize_url(&fixture, LspUrlKind::File); let actual = map.uri_to_specifier(&fixture, LspUrlKind::File);
let expected = let expected =
Url::parse("file:///C:/Users/deno/Desktop/file with spaces in name.txt") Url::parse("file:///C:/Users/deno/Desktop/file with spaces in name.txt")
.unwrap(); .unwrap();
@ -403,7 +367,7 @@ mod tests {
"file:///Users/deno/Desktop/file%20with%20spaces%20in%20name.txt", "file:///Users/deno/Desktop/file%20with%20spaces%20in%20name.txt",
) )
.unwrap(); .unwrap();
let actual = map.normalize_url(&fixture, LspUrlKind::File); let actual = map.uri_to_specifier(&fixture, LspUrlKind::File);
let expected = let expected =
Url::parse("file:///Users/deno/Desktop/file with spaces in name.txt") Url::parse("file:///Users/deno/Desktop/file with spaces in name.txt")
.unwrap(); .unwrap();
@ -414,7 +378,7 @@ mod tests {
fn test_normalize_deno_status() { fn test_normalize_deno_status() {
let map = LspUrlMap::default(); let map = LspUrlMap::default();
let fixture = Uri::from_str("deno:/status.md").unwrap(); let fixture = Uri::from_str("deno:/status.md").unwrap();
let actual = map.normalize_url(&fixture, LspUrlKind::File); let actual = map.uri_to_specifier(&fixture, LspUrlKind::File);
assert_eq!(actual.as_str(), fixture.as_str()); assert_eq!(actual.as_str(), fixture.as_str());
} }