mirror of
https://github.com/denoland/deno.git
synced 2025-01-18 03:44:05 -05:00
refactor(lsp): add LspClientUrl
(#18376)
This will make it a bit harder to accidentally use a client url in the wrong place. I don't fully understand why we do this mapping, but this will help prevent bugs like #18373 Closes #18374
This commit is contained in:
parent
a3529d0232
commit
6e5a631fe0
4 changed files with 92 additions and 39 deletions
|
@ -17,6 +17,7 @@ use super::config::SpecifierSettings;
|
||||||
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 {
|
||||||
|
@ -98,18 +99,23 @@ impl OutsideLockClient {
|
||||||
|
|
||||||
pub async fn specifier_configurations(
|
pub async fn specifier_configurations(
|
||||||
&self,
|
&self,
|
||||||
specifiers: Vec<lsp::Url>,
|
specifiers: Vec<LspClientUrl>,
|
||||||
) -> Result<Vec<Result<SpecifierSettings, AnyError>>, AnyError> {
|
) -> Result<Vec<Result<SpecifierSettings, AnyError>>, AnyError> {
|
||||||
self.0.specifier_configurations(specifiers).await
|
self
|
||||||
|
.0
|
||||||
|
.specifier_configurations(
|
||||||
|
specifiers.into_iter().map(|s| s.into_url()).collect(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn specifier_configuration(
|
pub async fn specifier_configuration(
|
||||||
&self,
|
&self,
|
||||||
specifier: &lsp::Url,
|
specifier: &LspClientUrl,
|
||||||
) -> Result<SpecifierSettings, AnyError> {
|
) -> Result<SpecifierSettings, AnyError> {
|
||||||
let values = self
|
let values = self
|
||||||
.0
|
.0
|
||||||
.specifier_configurations(vec![specifier.clone()])
|
.specifier_configurations(vec![specifier.as_url().clone()])
|
||||||
.await?;
|
.await?;
|
||||||
if let Some(value) = values.into_iter().next() {
|
if let Some(value) = values.into_iter().next() {
|
||||||
value.map_err(|err| {
|
value.map_err(|err| {
|
||||||
|
|
|
@ -59,6 +59,7 @@ use super::tsc::Assets;
|
||||||
use super::tsc::AssetsSnapshot;
|
use super::tsc::AssetsSnapshot;
|
||||||
use super::tsc::TsServer;
|
use super::tsc::TsServer;
|
||||||
use super::urls;
|
use super::urls;
|
||||||
|
use super::urls::LspClientUrl;
|
||||||
use crate::args::get_root_cert_store;
|
use crate::args::get_root_cert_store;
|
||||||
use crate::args::package_json;
|
use crate::args::package_json;
|
||||||
use crate::args::resolve_import_map_from_specifier;
|
use crate::args::resolve_import_map_from_specifier;
|
||||||
|
@ -338,7 +339,10 @@ impl LanguageServer {
|
||||||
match &ls.config.workspace_folders {
|
match &ls.config.workspace_folders {
|
||||||
Some(entry) => {
|
Some(entry) => {
|
||||||
for (specifier, folder) in entry {
|
for (specifier, folder) in entry {
|
||||||
specifiers.insert(specifier.clone(), folder.uri.clone());
|
specifiers.insert(
|
||||||
|
specifier.clone(),
|
||||||
|
LspClientUrl::new(folder.uri.clone()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -2868,9 +2872,10 @@ impl tower_lsp::LanguageServer for LanguageServer {
|
||||||
let (client, client_uri, specifier, had_specifier_settings) = {
|
let (client, client_uri, specifier, had_specifier_settings) = {
|
||||||
let mut inner = self.0.write().await;
|
let mut inner = self.0.write().await;
|
||||||
let client = inner.client.clone();
|
let client = inner.client.clone();
|
||||||
let client_uri = params.text_document.uri.clone();
|
let client_uri = LspClientUrl::new(params.text_document.uri.clone());
|
||||||
let specifier =
|
let specifier = inner
|
||||||
inner.url_map.normalize_url(&client_uri, LspUrlKind::File);
|
.url_map
|
||||||
|
.normalize_url(client_uri.as_url(), LspUrlKind::File);
|
||||||
let document = inner.did_open(&specifier, params).await;
|
let document = inner.did_open(&specifier, params).await;
|
||||||
let has_specifier_settings =
|
let has_specifier_settings =
|
||||||
inner.config.has_specifier_settings(&specifier);
|
inner.config.has_specifier_settings(&specifier);
|
||||||
|
|
|
@ -14,6 +14,7 @@ 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::LspClientUrl;
|
||||||
use super::urls::LspUrlMap;
|
use super::urls::LspUrlMap;
|
||||||
use super::urls::INVALID_SPECIFIER;
|
use super::urls::INVALID_SPECIFIER;
|
||||||
|
|
||||||
|
@ -36,7 +37,6 @@ use deno_core::serde::Serialize;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
use deno_core::serde_json::json;
|
use deno_core::serde_json::json;
|
||||||
use deno_core::serde_json::Value;
|
use deno_core::serde_json::Value;
|
||||||
use deno_core::url::Url;
|
|
||||||
use deno_core::JsRuntime;
|
use deno_core::JsRuntime;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
use deno_core::OpState;
|
use deno_core::OpState;
|
||||||
|
@ -842,7 +842,7 @@ impl DocumentSpan {
|
||||||
};
|
};
|
||||||
let link = lsp::LocationLink {
|
let link = lsp::LocationLink {
|
||||||
origin_selection_range,
|
origin_selection_range,
|
||||||
target_uri,
|
target_uri: target_uri.into_url(),
|
||||||
target_range,
|
target_range,
|
||||||
target_selection_range,
|
target_selection_range,
|
||||||
};
|
};
|
||||||
|
@ -864,7 +864,8 @@ impl DocumentSpan {
|
||||||
let mut target = language_server
|
let mut target = language_server
|
||||||
.url_map
|
.url_map
|
||||||
.normalize_specifier(&specifier)
|
.normalize_specifier(&specifier)
|
||||||
.ok()?;
|
.ok()?
|
||||||
|
.into_url();
|
||||||
target.set_fragment(Some(&format!(
|
target.set_fragment(Some(&format!(
|
||||||
"L{},{}",
|
"L{},{}",
|
||||||
range.start.line + 1,
|
range.start.line + 1,
|
||||||
|
@ -915,7 +916,10 @@ impl NavigateToItem {
|
||||||
.normalize_specifier(&specifier)
|
.normalize_specifier(&specifier)
|
||||||
.ok()?;
|
.ok()?;
|
||||||
let range = self.text_span.to_range(line_index);
|
let range = self.text_span.to_range(line_index);
|
||||||
let location = lsp::Location { uri, range };
|
let location = lsp::Location {
|
||||||
|
uri: uri.into_url(),
|
||||||
|
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);
|
||||||
|
@ -1160,9 +1164,11 @@ impl ImplementationLocation {
|
||||||
let uri = language_server
|
let uri = language_server
|
||||||
.url_map
|
.url_map
|
||||||
.normalize_specifier(&specifier)
|
.normalize_specifier(&specifier)
|
||||||
.unwrap_or_else(|_| ModuleSpecifier::parse("deno://invalid").unwrap());
|
.unwrap_or_else(|_| {
|
||||||
|
LspClientUrl::new(ModuleSpecifier::parse("deno://invalid").unwrap())
|
||||||
|
});
|
||||||
lsp::Location {
|
lsp::Location {
|
||||||
uri,
|
uri: uri.into_url(),
|
||||||
range: self.document_span.text_span.to_range(line_index),
|
range: self.document_span.text_span.to_range(line_index),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1196,8 +1202,10 @@ impl RenameLocations {
|
||||||
new_name: &str,
|
new_name: &str,
|
||||||
language_server: &language_server::Inner,
|
language_server: &language_server::Inner,
|
||||||
) -> Result<lsp::WorkspaceEdit, AnyError> {
|
) -> Result<lsp::WorkspaceEdit, AnyError> {
|
||||||
let mut text_document_edit_map: HashMap<Url, lsp::TextDocumentEdit> =
|
let mut text_document_edit_map: HashMap<
|
||||||
HashMap::new();
|
LspClientUrl,
|
||||||
|
lsp::TextDocumentEdit,
|
||||||
|
> = HashMap::new();
|
||||||
for location in self.locations.iter() {
|
for location in self.locations.iter() {
|
||||||
let specifier = normalize_specifier(&location.document_span.file_name)?;
|
let specifier = normalize_specifier(&location.document_span.file_name)?;
|
||||||
let uri = language_server.url_map.normalize_specifier(&specifier)?;
|
let uri = language_server.url_map.normalize_specifier(&specifier)?;
|
||||||
|
@ -1209,7 +1217,7 @@ impl RenameLocations {
|
||||||
uri.clone(),
|
uri.clone(),
|
||||||
lsp::TextDocumentEdit {
|
lsp::TextDocumentEdit {
|
||||||
text_document: lsp::OptionalVersionedTextDocumentIdentifier {
|
text_document: lsp::OptionalVersionedTextDocumentIdentifier {
|
||||||
uri: uri.clone(),
|
uri: uri.as_url().clone(),
|
||||||
version: asset_or_doc.document_lsp_version(),
|
version: asset_or_doc.document_lsp_version(),
|
||||||
},
|
},
|
||||||
edits:
|
edits:
|
||||||
|
@ -1698,9 +1706,9 @@ impl ReferenceEntry {
|
||||||
.unwrap_or_else(|_| INVALID_SPECIFIER.clone());
|
.unwrap_or_else(|_| INVALID_SPECIFIER.clone());
|
||||||
let uri = url_map
|
let uri = url_map
|
||||||
.normalize_specifier(&specifier)
|
.normalize_specifier(&specifier)
|
||||||
.unwrap_or_else(|_| INVALID_SPECIFIER.clone());
|
.unwrap_or_else(|_| LspClientUrl::new(INVALID_SPECIFIER.clone()));
|
||||||
lsp::Location {
|
lsp::Location {
|
||||||
uri,
|
uri: uri.into_url(),
|
||||||
range: self.document_span.text_span.to_range(line_index),
|
range: self.document_span.text_span.to_range(line_index),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1748,11 +1756,11 @@ impl CallHierarchyItem {
|
||||||
let uri = language_server
|
let uri = language_server
|
||||||
.url_map
|
.url_map
|
||||||
.normalize_specifier(&target_specifier)
|
.normalize_specifier(&target_specifier)
|
||||||
.unwrap_or_else(|_| INVALID_SPECIFIER.clone());
|
.unwrap_or_else(|_| LspClientUrl::new(INVALID_SPECIFIER.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.scheme() == "file" {
|
let maybe_file_path = if uri.as_url().scheme() == "file" {
|
||||||
specifier_to_file_path(&uri).ok()
|
specifier_to_file_path(uri.as_url()).ok()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -1760,7 +1768,7 @@ impl CallHierarchyItem {
|
||||||
if let Some(file_path) = maybe_file_path.as_ref() {
|
if let Some(file_path) = maybe_file_path.as_ref() {
|
||||||
file_path.file_name().unwrap().to_string_lossy().to_string()
|
file_path.file_name().unwrap().to_string_lossy().to_string()
|
||||||
} else {
|
} else {
|
||||||
uri.to_string()
|
uri.as_str().to_string()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.name.clone()
|
self.name.clone()
|
||||||
|
@ -1796,7 +1804,7 @@ impl CallHierarchyItem {
|
||||||
lsp::CallHierarchyItem {
|
lsp::CallHierarchyItem {
|
||||||
name,
|
name,
|
||||||
tags,
|
tags,
|
||||||
uri,
|
uri: uri.into_url(),
|
||||||
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()),
|
||||||
|
|
|
@ -59,19 +59,50 @@ 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()])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 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, Url>,
|
specifier_to_url: HashMap<ModuleSpecifier, LspClientUrl>,
|
||||||
url_to_specifier: HashMap<Url, ModuleSpecifier>,
|
url_to_specifier: HashMap<Url, ModuleSpecifier>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LspUrlMapInner {
|
impl LspUrlMapInner {
|
||||||
fn put(&mut self, specifier: ModuleSpecifier, url: Url) {
|
fn put(&mut self, specifier: ModuleSpecifier, url: LspClientUrl) {
|
||||||
self.specifier_to_url.insert(specifier.clone(), url.clone());
|
self
|
||||||
self.url_to_specifier.insert(url, specifier);
|
.url_to_specifier
|
||||||
|
.insert(url.as_url().clone(), specifier.clone());
|
||||||
|
self.specifier_to_url.insert(specifier, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_url(&self, specifier: &ModuleSpecifier) -> Option<&Url> {
|
fn get_url(&self, specifier: &ModuleSpecifier) -> Option<&LspClientUrl> {
|
||||||
self.specifier_to_url.get(specifier)
|
self.specifier_to_url.get(specifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,13 +129,13 @@ impl LspUrlMap {
|
||||||
pub fn normalize_specifier(
|
pub fn normalize_specifier(
|
||||||
&self,
|
&self,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
) -> Result<Url, AnyError> {
|
) -> Result<LspClientUrl, AnyError> {
|
||||||
let mut inner = self.0.lock();
|
let mut inner = self.0.lock();
|
||||||
if let Some(url) = inner.get_url(specifier).cloned() {
|
if let Some(url) = inner.get_url(specifier).cloned() {
|
||||||
Ok(url)
|
Ok(url)
|
||||||
} else {
|
} else {
|
||||||
let url = if specifier.scheme() == "file" {
|
let url = if specifier.scheme() == "file" {
|
||||||
specifier.clone()
|
LspClientUrl(specifier.clone())
|
||||||
} else {
|
} else {
|
||||||
let specifier_str = if specifier.scheme() == "asset" {
|
let specifier_str = if specifier.scheme() == "asset" {
|
||||||
format!("deno:/asset{}", specifier.path())
|
format!("deno:/asset{}", specifier.path())
|
||||||
|
@ -136,7 +167,7 @@ impl LspUrlMap {
|
||||||
path.push_str(&parts.join("/"));
|
path.push_str(&parts.join("/"));
|
||||||
format!("deno:/{path}")
|
format!("deno:/{path}")
|
||||||
};
|
};
|
||||||
let url = Url::parse(&specifier_str)?;
|
let url = LspClientUrl(Url::parse(&specifier_str)?);
|
||||||
inner.put(specifier.clone(), url.clone());
|
inner.put(specifier.clone(), url.clone());
|
||||||
url
|
url
|
||||||
};
|
};
|
||||||
|
@ -165,7 +196,7 @@ impl LspUrlMap {
|
||||||
} else {
|
} else {
|
||||||
url.clone()
|
url.clone()
|
||||||
};
|
};
|
||||||
inner.put(specifier.clone(), url.clone());
|
inner.put(specifier.clone(), LspClientUrl(url.clone()));
|
||||||
specifier
|
specifier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,9 +226,10 @@ mod tests {
|
||||||
.expect("could not handle specifier");
|
.expect("could not handle specifier");
|
||||||
let expected_url =
|
let expected_url =
|
||||||
Url::parse("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap();
|
Url::parse("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap();
|
||||||
assert_eq!(actual_url, expected_url);
|
assert_eq!(actual_url.as_url(), &expected_url);
|
||||||
|
|
||||||
let actual_specifier = map.normalize_url(&actual_url, LspUrlKind::File);
|
let actual_specifier =
|
||||||
|
map.normalize_url(actual_url.as_url(), LspUrlKind::File);
|
||||||
assert_eq!(actual_specifier, fixture);
|
assert_eq!(actual_specifier, fixture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,9 +242,10 @@ mod tests {
|
||||||
.normalize_specifier(&fixture)
|
.normalize_specifier(&fixture)
|
||||||
.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();
|
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_url, expected_url);
|
assert_eq!(actual_url.as_url(), &expected_url);
|
||||||
|
|
||||||
let actual_specifier = map.normalize_url(&actual_url, LspUrlKind::File);
|
let actual_specifier =
|
||||||
|
map.normalize_url(actual_url.as_url(), LspUrlKind::File);
|
||||||
assert_eq!(actual_specifier, fixture);
|
assert_eq!(actual_specifier, fixture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,9 +257,10 @@ mod tests {
|
||||||
.normalize_specifier(&fixture)
|
.normalize_specifier(&fixture)
|
||||||
.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, expected_url);
|
assert_eq!(actual_url.as_url(), &expected_url);
|
||||||
|
|
||||||
let actual_specifier = map.normalize_url(&actual_url, LspUrlKind::File);
|
let actual_specifier =
|
||||||
|
map.normalize_url(actual_url.as_url(), LspUrlKind::File);
|
||||||
assert_eq!(actual_specifier, fixture);
|
assert_eq!(actual_specifier, fixture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue