1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-22 15:06:54 -05:00

fix(lsp): handle data URLs properly (#9522)

Fixes #9514

Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
This commit is contained in:
Kitson Kelly 2021-02-18 15:37:05 +11:00 committed by GitHub
parent 78e34d4912
commit 2225e83da2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 374 additions and 125 deletions

View file

@ -22,13 +22,38 @@ implement these in order to have a fully functioning client that integrates well
with Deno:
- `deno/cache` - This command will instruct Deno to attempt to cache a module
and all of its dependencies. It expects an argument of
`{ textDocument: TextDocumentIdentifier }` to be passed.
and all of its dependencies. If a `referrer` only is passed, then all
dependencies for the module specifier will be loaded. If there are values in
the `uris`, then only those `uris` will be cached.
It expects parameters of:
```ts
interface CacheParams {
referrer: TextDocumentIdentifier;
uris: TextDocumentIdentifier[];
}
```
- `deno/performance` - Requests the return of the timing averages for the
internal instrumentation of Deno.
It does not expect any parameters.
- `deno/virtualTextDocument` - Requests a virtual text document from the LSP,
which is a read only document that can be displayed in the client. This allows
clients to access documents in the Deno cache, like remote modules and
TypeScript library files built into Deno. It also supports a special URL of
`deno:/status.md` which provides a markdown formatted text document that
contains details about the status of the LSP for display to a user.
TypeScript library files built into Deno. The Deno language server will encode
all internal files under the custom schema `deno:`, so clients should route
all requests for the `deno:` schema back to the `deno/virtualTextDocument`
API.
It also supports a special URL of `deno:/status.md` which provides a markdown
formatted text document that contains details about the status of the LSP for
display to a user.
It expects parameters of:
```ts
interface VirtualTextDocumentParams {
textDocument: TextDocumentIdentifier;
}
```

View file

@ -456,11 +456,14 @@ impl CodeActionCollection {
) -> Result<(), AnyError> {
if let Some(data) = diagnostic.data.clone() {
let fix_data: DenoFixData = serde_json::from_value(data)?;
let title = if matches!(&diagnostic.code, Some(lsp::NumberOrString::String(code)) if code == "no-cache-data")
{
"Cache the data URL and its dependencies.".to_string()
} else {
format!("Cache \"{}\" and its dependencies.", fix_data.specifier)
};
let code_action = lsp::CodeAction {
title: format!(
"Cache \"{}\" and its dependencies.",
fix_data.specifier
),
title,
kind: Some(lsp::CodeActionKind::QUICKFIX),
diagnostics: Some(vec![diagnostic.clone()]),
edit: None,

View file

@ -295,9 +295,11 @@ pub async fn generate_dependency_diagnostics(
}
ResolvedDependency::Resolved(specifier) => {
if !(state_snapshot.documents.contains_key(&specifier) || sources.contains_key(&specifier)) {
let is_local = specifier.scheme() == "file";
let (code, message) = if is_local {
let scheme = specifier.scheme();
let (code, message) = if scheme == "file" {
(Some(lsp::NumberOrString::String("no-local".to_string())), format!("Unable to load a local module: \"{}\".\n Please check the file path.", specifier))
} else if scheme == "data" {
(Some(lsp::NumberOrString::String("no-cache-data".to_string())), "Uncached data URL.".to_string())
} else {
(Some(lsp::NumberOrString::String("no-cache".to_string())), format!("Unable to load the remote module: \"{}\".", specifier))
};

View file

@ -53,7 +53,7 @@ use super::tsc;
use super::tsc::AssetDocument;
use super::tsc::Assets;
use super::tsc::TsServer;
use super::utils;
use super::urls;
lazy_static! {
static ref ABSTRACT_MODIFIER: Regex = Regex::new(r"\babstract\b").unwrap();
@ -101,6 +101,8 @@ pub(crate) struct Inner {
ts_fixable_diagnostics: Vec<String>,
/// An abstraction that handles interactions with TypeScript.
ts_server: TsServer,
/// A map of specifiers and URLs used to translate over the LSP.
pub url_map: urls::LspUrlMap,
}
impl LanguageServer {
@ -131,6 +133,7 @@ impl Inner {
sources,
ts_fixable_diagnostics: Default::default(),
ts_server: TsServer::new(),
url_map: Default::default(),
}
}
@ -661,7 +664,7 @@ impl Inner {
// already managed by the language service
return;
}
let specifier = utils::normalize_url(params.text_document.uri);
let specifier = self.url_map.normalize_url(&params.text_document.uri);
self.documents.open(
specifier.clone(),
params.text_document.version,
@ -678,7 +681,7 @@ impl Inner {
async fn did_change(&mut self, params: DidChangeTextDocumentParams) {
let mark = self.performance.mark("did_change");
let specifier = utils::normalize_url(params.text_document.uri);
let specifier = self.url_map.normalize_url(&params.text_document.uri);
match self.documents.change(
&specifier,
params.text_document.version,
@ -704,7 +707,7 @@ impl Inner {
// already managed by the language service
return;
}
let specifier = utils::normalize_url(params.text_document.uri);
let specifier = self.url_map.normalize_url(&params.text_document.uri);
self.documents.close(&specifier);
self.navigation_trees.remove(&specifier);
@ -802,7 +805,7 @@ impl Inner {
params: DocumentFormattingParams,
) -> LspResult<Option<Vec<TextEdit>>> {
let mark = self.performance.mark("formatting");
let specifier = utils::normalize_url(params.text_document.uri.clone());
let specifier = self.url_map.normalize_url(&params.text_document.uri);
let file_text = self
.documents
.content(&specifier)
@ -858,9 +861,9 @@ impl Inner {
return Ok(None);
}
let mark = self.performance.mark("hover");
let specifier = utils::normalize_url(
params.text_document_position_params.text_document.uri,
);
let specifier = self
.url_map
.normalize_url(&params.text_document_position_params.text_document.uri);
let line_index =
if let Some(line_index) = self.get_line_index_sync(&specifier) {
line_index
@ -898,7 +901,7 @@ impl Inner {
}
let mark = self.performance.mark("code_action");
let specifier = utils::normalize_url(params.text_document.uri);
let specifier = self.url_map.normalize_url(&params.text_document.uri);
let fixable_diagnostics: Vec<&Diagnostic> = params
.context
.diagnostics
@ -915,7 +918,9 @@ impl Inner {
_ => false,
},
"deno" => match &d.code {
Some(NumberOrString::String(code)) => code == "no-cache",
Some(NumberOrString::String(code)) => {
code == "no-cache" || code == "no-cache-data"
}
_ => false,
},
_ => false,
@ -1053,7 +1058,7 @@ impl Inner {
}
let mark = self.performance.mark("code_lens");
let specifier = utils::normalize_url(params.text_document.uri);
let specifier = self.url_map.normalize_url(&params.text_document.uri);
let line_index = self.get_line_index_sync(&specifier).unwrap();
let navigation_tree =
self.get_navigation_tree(&specifier).await.map_err(|err| {
@ -1209,7 +1214,7 @@ impl Inner {
LspError::internal_error()
})?;
let implementation_location =
implementation.to_location(&line_index);
implementation.to_location(&line_index, self);
if !(implementation_specifier == code_lens_data.specifier
&& implementation_location.range.start == params.range.start)
{
@ -1222,11 +1227,13 @@ impl Inner {
} else {
"1 implementation".to_string()
};
let url = utils::normalize_specifier(&code_lens_data.specifier)
let url = self
.url_map
.normalize_specifier(&code_lens_data.specifier)
.map_err(|err| {
error!("{}", err);
LspError::internal_error()
})?;
error!("{}", err);
LspError::internal_error()
})?;
Command {
title,
command: "deno.showReferences".to_string(),
@ -1300,7 +1307,7 @@ impl Inner {
error!("Unable to get line index: {}", err);
LspError::internal_error()
})?;
locations.push(reference.to_location(&line_index));
locations.push(reference.to_location(&line_index, self));
}
let command = if !locations.is_empty() {
let title = if locations.len() > 1 {
@ -1308,11 +1315,13 @@ impl Inner {
} else {
"1 reference".to_string()
};
let url = utils::normalize_specifier(&code_lens_data.specifier)
let url = self
.url_map
.normalize_specifier(&code_lens_data.specifier)
.map_err(|err| {
error!("{}", err);
LspError::internal_error()
})?;
error!("{}", err);
LspError::internal_error()
})?;
Command {
title,
command: "deno.showReferences".to_string(),
@ -1366,9 +1375,9 @@ impl Inner {
return Ok(None);
}
let mark = self.performance.mark("document_highlight");
let specifier = utils::normalize_url(
params.text_document_position_params.text_document.uri,
);
let specifier = self
.url_map
.normalize_url(&params.text_document_position_params.text_document.uri);
let line_index =
if let Some(line_index) = self.get_line_index_sync(&specifier) {
line_index
@ -1412,8 +1421,9 @@ impl Inner {
return Ok(None);
}
let mark = self.performance.mark("references");
let specifier =
utils::normalize_url(params.text_document_position.text_document.uri);
let specifier = self
.url_map
.normalize_url(&params.text_document_position.text_document.uri);
let line_index =
if let Some(line_index) = self.get_line_index_sync(&specifier) {
line_index
@ -1444,7 +1454,7 @@ impl Inner {
// TODO(lucacasonato): handle error correctly
let line_index =
self.get_line_index(reference_specifier).await.unwrap();
results.push(reference.to_location(&line_index));
results.push(reference.to_location(&line_index, self));
}
self.performance.measure(mark);
@ -1463,9 +1473,9 @@ impl Inner {
return Ok(None);
}
let mark = self.performance.mark("goto_definition");
let specifier = utils::normalize_url(
params.text_document_position_params.text_document.uri,
);
let specifier = self
.url_map
.normalize_url(&params.text_document_position_params.text_document.uri);
let line_index =
if let Some(line_index) = self.get_line_index_sync(&specifier) {
line_index
@ -1503,8 +1513,9 @@ impl Inner {
return Ok(None);
}
let mark = self.performance.mark("completion");
let specifier =
utils::normalize_url(params.text_document_position.text_document.uri);
let specifier = self
.url_map
.normalize_url(&params.text_document_position.text_document.uri);
// TODO(lucacasonato): handle error correctly
let line_index =
if let Some(line_index) = self.get_line_index_sync(&specifier) {
@ -1548,9 +1559,9 @@ impl Inner {
return Ok(None);
}
let mark = self.performance.mark("goto_implementation");
let specifier = utils::normalize_url(
params.text_document_position_params.text_document.uri,
);
let specifier = self
.url_map
.normalize_url(&params.text_document_position_params.text_document.uri);
let line_index =
if let Some(line_index) = self.get_line_index_sync(&specifier) {
line_index
@ -1605,8 +1616,9 @@ impl Inner {
return Ok(None);
}
let mark = self.performance.mark("rename");
let specifier =
utils::normalize_url(params.text_document_position.text_document.uri);
let specifier = self
.url_map
.normalize_url(&params.text_document_position.text_document.uri);
let line_index =
if let Some(line_index) = self.get_line_index_sync(&specifier) {
@ -1710,9 +1722,9 @@ impl Inner {
return Ok(None);
}
let mark = self.performance.mark("signature_help");
let specifier = utils::normalize_url(
params.text_document_position_params.text_document.uri,
);
let specifier = self
.url_map
.normalize_url(&params.text_document_position_params.text_document.uri);
let line_index =
if let Some(line_index) = self.get_line_index_sync(&specifier) {
line_index
@ -1930,10 +1942,10 @@ impl Inner {
/// in the Deno cache, including any of their dependencies.
async fn cache(&mut self, params: CacheParams) -> LspResult<bool> {
let mark = self.performance.mark("cache");
let referrer = utils::normalize_url(params.referrer.uri);
let referrer = self.url_map.normalize_url(&params.referrer.uri);
if !params.uris.is_empty() {
for identifier in &params.uris {
let specifier = utils::normalize_url(identifier.uri.clone());
let specifier = self.url_map.normalize_url(&identifier.uri);
sources::cache(&specifier, &self.maybe_import_map)
.await
.map_err(|err| {
@ -1976,7 +1988,7 @@ impl Inner {
params: VirtualTextDocumentParams,
) -> LspResult<Option<String>> {
let mark = self.performance.mark("virtual_text_document");
let specifier = utils::normalize_url(params.text_document.uri);
let specifier = self.url_map.normalize_url(&params.text_document.uri);
let contents = if specifier.as_str() == "deno:/status.md" {
let mut contents = String::new();
@ -2170,15 +2182,15 @@ mod tests {
("initialize_request.json", LspResponse::RequestAny),
("initialized_notification.json", LspResponse::None),
("did_open_notification_asset.json", LspResponse::None),
("hover_request_asset_01.json", LspResponse::RequestAny),
("definition_request_asset.json", LspResponse::RequestAny),
(
"virtual_text_document_request.json",
LspResponse::RequestAny,
),
(
"hover_request_asset_02.json",
"hover_request_asset.json",
LspResponse::Request(
4,
5,
json!({
"contents": [
{
@ -2807,6 +2819,7 @@ mod tests {
("initialize_request.json", LspResponse::RequestAny),
("initialized_notification.json", LspResponse::None),
("did_open_notification_asset.json", LspResponse::None),
("references_request_asset.json", LspResponse::RequestAny),
(
"virtual_text_document_request.json",
LspResponse::RequestAny,

View file

@ -13,7 +13,7 @@ mod performance;
mod sources;
mod text;
mod tsc;
mod utils;
mod urls;
pub async fn start() -> Result<(), AnyError> {
let stdin = tokio::io::stdin();

View file

@ -7,7 +7,6 @@ use super::language_server;
use super::language_server::StateSnapshot;
use super::text;
use super::text::LineIndex;
use super::utils;
use crate::media_type::MediaType;
use crate::tokio_util::create_basic_runtime;
@ -480,40 +479,41 @@ impl DocumentSpan {
language_server: &mut language_server::Inner,
) -> Option<lsp::LocationLink> {
let target_specifier = resolve_url(&self.file_name).unwrap();
if let Ok(target_line_index) =
language_server.get_line_index(target_specifier).await
{
let target_uri = utils::normalize_file_name(&self.file_name).unwrap();
let (target_range, target_selection_range) =
if let Some(context_span) = &self.context_span {
(
context_span.to_range(&target_line_index),
self.text_span.to_range(&target_line_index),
)
} else {
(
self.text_span.to_range(&target_line_index),
self.text_span.to_range(&target_line_index),
)
};
let origin_selection_range =
if let Some(original_context_span) = &self.original_context_span {
Some(original_context_span.to_range(line_index))
} else if let Some(original_text_span) = &self.original_text_span {
Some(original_text_span.to_range(line_index))
} else {
None
};
let link = lsp::LocationLink {
origin_selection_range,
target_uri,
target_range,
target_selection_range,
let target_line_index = language_server
.get_line_index(target_specifier.clone())
.await
.ok()?;
let target_uri = language_server
.url_map
.normalize_specifier(&target_specifier)
.unwrap();
let (target_range, target_selection_range) =
if let Some(context_span) = &self.context_span {
(
context_span.to_range(&target_line_index),
self.text_span.to_range(&target_line_index),
)
} else {
(
self.text_span.to_range(&target_line_index),
self.text_span.to_range(&target_line_index),
)
};
Some(link)
} else {
None
}
let origin_selection_range =
if let Some(original_context_span) = &self.original_context_span {
Some(original_context_span.to_range(line_index))
} else if let Some(original_text_span) = &self.original_text_span {
Some(original_text_span.to_range(line_index))
} else {
None
};
let link = lsp::LocationLink {
origin_selection_range,
target_uri,
target_range,
target_selection_range,
};
Some(link)
}
}
@ -589,9 +589,16 @@ pub struct ImplementationLocation {
}
impl ImplementationLocation {
pub fn to_location(&self, line_index: &LineIndex) -> lsp::Location {
let uri =
utils::normalize_file_name(&self.document_span.file_name).unwrap();
pub(crate) fn to_location(
&self,
line_index: &LineIndex,
language_server: &mut language_server::Inner,
) -> lsp::Location {
let specifier = resolve_url(&self.document_span.file_name).unwrap();
let uri = language_server
.url_map
.normalize_specifier(&specifier)
.unwrap();
lsp::Location {
uri,
range: self.document_span.text_span.to_range(line_index),
@ -633,8 +640,8 @@ impl RenameLocations {
let mut text_document_edit_map: HashMap<Url, lsp::TextDocumentEdit> =
HashMap::new();
for location in self.locations.iter() {
let uri = utils::normalize_file_name(&location.document_span.file_name)?;
let specifier = resolve_url(&location.document_span.file_name)?;
let uri = language_server.url_map.normalize_specifier(&specifier)?;
// ensure TextDocumentEdit for `location.file_name`.
if text_document_edit_map.get(&uri).is_none() {
@ -852,9 +859,16 @@ pub struct ReferenceEntry {
}
impl ReferenceEntry {
pub fn to_location(&self, line_index: &LineIndex) -> lsp::Location {
let uri =
utils::normalize_file_name(&self.document_span.file_name).unwrap();
pub(crate) fn to_location(
&self,
line_index: &LineIndex,
language_server: &mut language_server::Inner,
) -> lsp::Location {
let specifier = resolve_url(&self.document_span.file_name).unwrap();
let uri = language_server
.url_map
.normalize_specifier(&specifier)
.unwrap();
lsp::Location {
uri,
range: self.document_span.text_span.to_range(line_index),
@ -1237,7 +1251,7 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
if specifier.starts_with("asset:///") {
resolved.push(Some((
specifier.clone(),
MediaType::from(specifier).as_ts_extension(),
MediaType::from(specifier).as_ts_extension().into(),
)))
} else if let Some(dependency) = dependencies.get(specifier) {
let resolved_import =
@ -1259,7 +1273,7 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
let media_type = MediaType::from(&resolved_specifier);
resolved.push(Some((
resolved_specifier.to_string(),
media_type.as_ts_extension(),
media_type.as_ts_extension().into(),
)));
} else if sources.contains_key(&resolved_specifier) {
let media_type = if let Some(media_type) =
@ -1271,7 +1285,7 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
};
resolved.push(Some((
resolved_specifier.to_string(),
media_type.as_ts_extension(),
media_type.as_ts_extension().into(),
)));
} else {
resolved.push(None);
@ -1289,7 +1303,7 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
{
resolved.push(Some((
resolved_specifier.to_string(),
media_type.as_ts_extension(),
media_type.as_ts_extension().into(),
)));
} else {
resolved.push(None);

174
cli/lsp/urls.rs Normal file
View file

@ -0,0 +1,174 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use crate::file_fetcher::map_content_type;
use crate::media_type::MediaType;
use deno_core::error::AnyError;
use deno_core::url::Url;
use deno_core::ModuleSpecifier;
use std::collections::HashMap;
/// This is a partial guess as how URLs get encoded from the LSP. We want to
/// encode URLs sent to the LSP in the same way that they would be encoded back
/// so that we have the same return encoding.
const LSP_ENCODING: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
.add(b':')
.add(b';')
.add(b'=')
.add(b'@')
.add(b'[')
.add(b'\\')
.add(b']')
.add(b'^')
.add(b'|');
fn hash_data_specifier(specifier: &ModuleSpecifier) -> String {
let mut file_name_str = specifier.path().to_string();
if let Some(query) = specifier.query() {
file_name_str.push('?');
file_name_str.push_str(query);
}
crate::checksum::gen(&[file_name_str.as_bytes()])
}
fn data_url_media_type(specifier: &ModuleSpecifier) -> MediaType {
let path = specifier.path();
let mut parts = path.splitn(2, ',');
let media_type_part =
percent_encoding::percent_decode_str(parts.next().unwrap())
.decode_utf8_lossy();
let (media_type, _) =
map_content_type(specifier, Some(media_type_part.into()));
media_type
}
/// A bi-directional map of URLs sent to the LSP client and internal module
/// specifiers. We need to map internal specifiers into `deno:` schema URLs
/// to allow the Deno language server to manage these as virtual documents.
#[derive(Debug, Default)]
pub struct LspUrlMap {
specifier_to_url: HashMap<ModuleSpecifier, Url>,
url_to_specifier: HashMap<Url, ModuleSpecifier>,
}
impl LspUrlMap {
fn put(&mut self, specifier: ModuleSpecifier, url: Url) {
self.specifier_to_url.insert(specifier.clone(), url.clone());
self.url_to_specifier.insert(url, specifier);
}
fn get_url(&self, specifier: &ModuleSpecifier) -> Option<&Url> {
self.specifier_to_url.get(specifier)
}
fn get_specifier(&self, url: &Url) -> Option<&ModuleSpecifier> {
self.url_to_specifier.get(url)
}
/// 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.
pub fn normalize_specifier(
&mut self,
specifier: &ModuleSpecifier,
) -> Result<Url, AnyError> {
if let Some(url) = self.get_url(specifier) {
Ok(url.clone())
} else {
let url = if specifier.scheme() == "file" {
specifier.clone()
} else {
let specifier_str = if specifier.scheme() == "data" {
let media_type = data_url_media_type(specifier);
let extension = if media_type == MediaType::Unknown {
""
} else {
media_type.as_ts_extension()
};
format!(
"deno:/{}/data_url{}",
hash_data_specifier(specifier),
extension
)
} else {
let path = specifier.as_str().replacen("://", "/", 1);
let path = percent_encoding::utf8_percent_encode(&path, LSP_ENCODING);
format!("deno:/{}", path)
};
let url = Url::parse(&specifier_str)?;
self.put(specifier.clone(), url.clone());
url
};
Ok(url)
}
}
/// Normalize URLs from the client, where "virtual" `deno:///` URLs are
/// converted into proper module specifiers.
pub fn normalize_url(&self, url: &Url) -> ModuleSpecifier {
if let Some(specifier) = self.get_specifier(url) {
specifier.clone()
} else {
url.clone()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use deno_core::resolve_url;
#[test]
fn test_hash_data_specifier() {
let fixture = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
let actual = hash_data_specifier(&fixture);
assert_eq!(
actual,
"c21c7fc382b2b0553dc0864aa81a3acacfb7b3d1285ab5ae76da6abec213fb37"
);
}
#[test]
fn test_data_url_media_type() {
let fixture = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
let actual = data_url_media_type(&fixture);
assert_eq!(actual, MediaType::TypeScript);
let fixture = resolve_url("data:application/javascript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
let actual = data_url_media_type(&fixture);
assert_eq!(actual, MediaType::JavaScript);
let fixture = resolve_url("data:text/plain;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
let actual = data_url_media_type(&fixture);
assert_eq!(actual, MediaType::Unknown);
}
#[test]
fn test_lsp_url_map() {
let mut map = LspUrlMap::default();
let fixture = resolve_url("https://deno.land/x/pkg@1.0.0/mod.ts").unwrap();
let actual_url = map
.normalize_specifier(&fixture)
.expect("could not handle specifier");
let expected_url =
Url::parse("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap();
assert_eq!(actual_url, expected_url);
let actual_specifier = map.normalize_url(&actual_url);
assert_eq!(actual_specifier, fixture);
}
#[test]
fn test_lsp_url_map_data() {
let mut map = LspUrlMap::default();
let fixture = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
let actual_url = map
.normalize_specifier(&fixture)
.expect("could not handle specifier");
let expected_url = Url::parse("deno:/c21c7fc382b2b0553dc0864aa81a3acacfb7b3d1285ab5ae76da6abec213fb37/data_url.ts").unwrap();
assert_eq!(actual_url, expected_url);
let actual_specifier = map.normalize_url(&actual_url);
assert_eq!(actual_specifier, fixture);
}
}

View file

@ -121,8 +121,8 @@ impl MediaType {
///
/// *NOTE* This is defined in TypeScript as a string based enum. Changes to
/// that enum in TypeScript should be reflected here.
pub fn as_ts_extension(&self) -> String {
let ext = match self {
pub fn as_ts_extension(&self) -> &str {
match self {
MediaType::JavaScript => ".js",
MediaType::JSX => ".jsx",
MediaType::TypeScript => ".ts",
@ -138,13 +138,11 @@ impl MediaType {
// JS for mapping purposes, though in reality, it is unlikely to ever be
// passed to the compiler.
MediaType::SourceMap => ".js",
// TypeScript doesn't have an "unknown", so we will treat WASM as JS for
// mapping purposes, though in reality, it is unlikely to ever be passed
// to the compiler.
// TypeScript doesn't have an "unknown", so we will treat unknowns as JS
// for mapping purposes, though in reality, it is unlikely to ever be
// passed to the compiler.
MediaType::Unknown => ".js",
};
ext.into()
}
}
/// Map the media type to a `ts.ScriptKind`

View file

@ -4,7 +4,7 @@
"method": "textDocument/codeLens",
"params": {
"textDocument": {
"uri": "deno:/asset//lib.es2015.symbol.wellknown.d.ts"
"uri": "deno:/asset//lib.deno.shared_globals.d.ts"
}
}
}

View file

@ -5,17 +5,17 @@
"params": {
"range": {
"start": {
"line": 93,
"character": 10
"line": 416,
"character": 12
},
"end": {
"line": 93,
"character": 15
"line": 416,
"character": 19
}
},
"data": {
"specifier": "asset:///lib.es2015.symbol.wellknown.d.ts",
"source": "implementations"
"specifier": "asset:///lib.deno.shared_globals.d.ts",
"source": "references"
}
}
}

View file

@ -1,14 +1,14 @@
{
"jsonrpc": "2.0",
"id": 4,
"method": "textDocument/hover",
"method": "textDocument/definition",
"params": {
"textDocument": {
"uri": "file:///a/file.ts"
},
"position": {
"line": 0,
"character": 12
"character": 14
}
}
}

View file

@ -6,7 +6,7 @@
"uri": "file:///a/file.ts",
"languageId": "typescript",
"version": 1,
"text": "console.log(\"hello deno!\");\n"
"text": "console.log(Date.now());\n"
}
}
}

View file

@ -1,6 +1,6 @@
{
"jsonrpc": "2.0",
"id": 4,
"id": 5,
"method": "textDocument/hover",
"params": {
"textDocument": {

View file

@ -0,0 +1,17 @@
{
"jsonrpc": "2.0",
"id": 2,
"method": "textDocument/references",
"params": {
"textDocument": {
"uri": "file:///a/file.ts"
},
"position": {
"line": 0,
"character": 3
},
"context": {
"includeDeclaration": true
}
}
}

View file

@ -1,10 +1,10 @@
{
"jsonrpc": "2.0",
"id": 2,
"id": 4,
"method": "deno/virtualTextDocument",
"params": {
"textDocument": {
"uri": "deno:/asset//lib.es2015.symbol.wellknown.d.ts"
"uri": "deno:/asset//lib.deno.shared_globals.d.ts"
}
}
}

View file

@ -383,7 +383,10 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
} else {
resolved_specifier.to_string()
};
resolved.push((resolved_specifier_str, media_type.as_ts_extension()));
resolved.push((
resolved_specifier_str,
media_type.as_ts_extension().into(),
));
}
// in certain situations, like certain dynamic imports, we won't have
// the source file in the graph, so we will return a fake module to