mirror of
https://github.com/denoland/deno.git
synced 2024-11-26 16:09:27 -05:00
fix(lsp): handle data URLs properly (#9522)
Fixes #9514 Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
This commit is contained in:
parent
78e34d4912
commit
2225e83da2
16 changed files with 374 additions and 125 deletions
|
@ -22,13 +22,38 @@ implement these in order to have a fully functioning client that integrates well
|
||||||
with Deno:
|
with Deno:
|
||||||
|
|
||||||
- `deno/cache` - This command will instruct Deno to attempt to cache a module
|
- `deno/cache` - This command will instruct Deno to attempt to cache a module
|
||||||
and all of its dependencies. It expects an argument of
|
and all of its dependencies. If a `referrer` only is passed, then all
|
||||||
`{ textDocument: TextDocumentIdentifier }` to be passed.
|
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
|
- `deno/performance` - Requests the return of the timing averages for the
|
||||||
internal instrumentation of Deno.
|
internal instrumentation of Deno.
|
||||||
|
|
||||||
|
It does not expect any parameters.
|
||||||
- `deno/virtualTextDocument` - Requests a virtual text document from the LSP,
|
- `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
|
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
|
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
|
TypeScript library files built into Deno. The Deno language server will encode
|
||||||
`deno:/status.md` which provides a markdown formatted text document that
|
all internal files under the custom schema `deno:`, so clients should route
|
||||||
contains details about the status of the LSP for display to a user.
|
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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -456,11 +456,14 @@ impl CodeActionCollection {
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
if let Some(data) = diagnostic.data.clone() {
|
if let Some(data) = diagnostic.data.clone() {
|
||||||
let fix_data: DenoFixData = serde_json::from_value(data)?;
|
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 {
|
let code_action = lsp::CodeAction {
|
||||||
title: format!(
|
title,
|
||||||
"Cache \"{}\" and its dependencies.",
|
|
||||||
fix_data.specifier
|
|
||||||
),
|
|
||||||
kind: Some(lsp::CodeActionKind::QUICKFIX),
|
kind: Some(lsp::CodeActionKind::QUICKFIX),
|
||||||
diagnostics: Some(vec![diagnostic.clone()]),
|
diagnostics: Some(vec![diagnostic.clone()]),
|
||||||
edit: None,
|
edit: None,
|
||||||
|
|
|
@ -295,9 +295,11 @@ pub async fn generate_dependency_diagnostics(
|
||||||
}
|
}
|
||||||
ResolvedDependency::Resolved(specifier) => {
|
ResolvedDependency::Resolved(specifier) => {
|
||||||
if !(state_snapshot.documents.contains_key(&specifier) || sources.contains_key(&specifier)) {
|
if !(state_snapshot.documents.contains_key(&specifier) || sources.contains_key(&specifier)) {
|
||||||
let is_local = specifier.scheme() == "file";
|
let scheme = specifier.scheme();
|
||||||
let (code, message) = if is_local {
|
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))
|
(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 {
|
} else {
|
||||||
(Some(lsp::NumberOrString::String("no-cache".to_string())), format!("Unable to load the remote module: \"{}\".", specifier))
|
(Some(lsp::NumberOrString::String("no-cache".to_string())), format!("Unable to load the remote module: \"{}\".", specifier))
|
||||||
};
|
};
|
||||||
|
|
|
@ -53,7 +53,7 @@ use super::tsc;
|
||||||
use super::tsc::AssetDocument;
|
use super::tsc::AssetDocument;
|
||||||
use super::tsc::Assets;
|
use super::tsc::Assets;
|
||||||
use super::tsc::TsServer;
|
use super::tsc::TsServer;
|
||||||
use super::utils;
|
use super::urls;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref ABSTRACT_MODIFIER: Regex = Regex::new(r"\babstract\b").unwrap();
|
static ref ABSTRACT_MODIFIER: Regex = Regex::new(r"\babstract\b").unwrap();
|
||||||
|
@ -101,6 +101,8 @@ pub(crate) struct Inner {
|
||||||
ts_fixable_diagnostics: Vec<String>,
|
ts_fixable_diagnostics: Vec<String>,
|
||||||
/// An abstraction that handles interactions with TypeScript.
|
/// An abstraction that handles interactions with TypeScript.
|
||||||
ts_server: TsServer,
|
ts_server: TsServer,
|
||||||
|
/// A map of specifiers and URLs used to translate over the LSP.
|
||||||
|
pub url_map: urls::LspUrlMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanguageServer {
|
impl LanguageServer {
|
||||||
|
@ -131,6 +133,7 @@ impl Inner {
|
||||||
sources,
|
sources,
|
||||||
ts_fixable_diagnostics: Default::default(),
|
ts_fixable_diagnostics: Default::default(),
|
||||||
ts_server: TsServer::new(),
|
ts_server: TsServer::new(),
|
||||||
|
url_map: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -661,7 +664,7 @@ impl Inner {
|
||||||
// already managed by the language service
|
// already managed by the language service
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let specifier = utils::normalize_url(params.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
self.documents.open(
|
self.documents.open(
|
||||||
specifier.clone(),
|
specifier.clone(),
|
||||||
params.text_document.version,
|
params.text_document.version,
|
||||||
|
@ -678,7 +681,7 @@ impl Inner {
|
||||||
|
|
||||||
async fn did_change(&mut self, params: DidChangeTextDocumentParams) {
|
async fn did_change(&mut self, params: DidChangeTextDocumentParams) {
|
||||||
let mark = self.performance.mark("did_change");
|
let mark = self.performance.mark("did_change");
|
||||||
let specifier = utils::normalize_url(params.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
match self.documents.change(
|
match self.documents.change(
|
||||||
&specifier,
|
&specifier,
|
||||||
params.text_document.version,
|
params.text_document.version,
|
||||||
|
@ -704,7 +707,7 @@ impl Inner {
|
||||||
// already managed by the language service
|
// already managed by the language service
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let specifier = utils::normalize_url(params.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
self.documents.close(&specifier);
|
self.documents.close(&specifier);
|
||||||
self.navigation_trees.remove(&specifier);
|
self.navigation_trees.remove(&specifier);
|
||||||
|
|
||||||
|
@ -802,7 +805,7 @@ impl Inner {
|
||||||
params: DocumentFormattingParams,
|
params: DocumentFormattingParams,
|
||||||
) -> LspResult<Option<Vec<TextEdit>>> {
|
) -> LspResult<Option<Vec<TextEdit>>> {
|
||||||
let mark = self.performance.mark("formatting");
|
let mark = self.performance.mark("formatting");
|
||||||
let specifier = utils::normalize_url(params.text_document.uri.clone());
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
let file_text = self
|
let file_text = self
|
||||||
.documents
|
.documents
|
||||||
.content(&specifier)
|
.content(&specifier)
|
||||||
|
@ -858,9 +861,9 @@ impl Inner {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("hover");
|
let mark = self.performance.mark("hover");
|
||||||
let specifier = utils::normalize_url(
|
let specifier = self
|
||||||
params.text_document_position_params.text_document.uri,
|
.url_map
|
||||||
);
|
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
||||||
let line_index =
|
let line_index =
|
||||||
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
||||||
line_index
|
line_index
|
||||||
|
@ -898,7 +901,7 @@ impl Inner {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mark = self.performance.mark("code_action");
|
let mark = self.performance.mark("code_action");
|
||||||
let specifier = utils::normalize_url(params.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
let fixable_diagnostics: Vec<&Diagnostic> = params
|
let fixable_diagnostics: Vec<&Diagnostic> = params
|
||||||
.context
|
.context
|
||||||
.diagnostics
|
.diagnostics
|
||||||
|
@ -915,7 +918,9 @@ impl Inner {
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
"deno" => match &d.code {
|
"deno" => match &d.code {
|
||||||
Some(NumberOrString::String(code)) => code == "no-cache",
|
Some(NumberOrString::String(code)) => {
|
||||||
|
code == "no-cache" || code == "no-cache-data"
|
||||||
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -1053,7 +1058,7 @@ impl Inner {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mark = self.performance.mark("code_lens");
|
let mark = self.performance.mark("code_lens");
|
||||||
let specifier = utils::normalize_url(params.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
let line_index = self.get_line_index_sync(&specifier).unwrap();
|
let line_index = self.get_line_index_sync(&specifier).unwrap();
|
||||||
let navigation_tree =
|
let navigation_tree =
|
||||||
self.get_navigation_tree(&specifier).await.map_err(|err| {
|
self.get_navigation_tree(&specifier).await.map_err(|err| {
|
||||||
|
@ -1209,7 +1214,7 @@ impl Inner {
|
||||||
LspError::internal_error()
|
LspError::internal_error()
|
||||||
})?;
|
})?;
|
||||||
let implementation_location =
|
let implementation_location =
|
||||||
implementation.to_location(&line_index);
|
implementation.to_location(&line_index, self);
|
||||||
if !(implementation_specifier == code_lens_data.specifier
|
if !(implementation_specifier == code_lens_data.specifier
|
||||||
&& implementation_location.range.start == params.range.start)
|
&& implementation_location.range.start == params.range.start)
|
||||||
{
|
{
|
||||||
|
@ -1222,11 +1227,13 @@ impl Inner {
|
||||||
} else {
|
} else {
|
||||||
"1 implementation".to_string()
|
"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| {
|
.map_err(|err| {
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
LspError::internal_error()
|
LspError::internal_error()
|
||||||
})?;
|
})?;
|
||||||
Command {
|
Command {
|
||||||
title,
|
title,
|
||||||
command: "deno.showReferences".to_string(),
|
command: "deno.showReferences".to_string(),
|
||||||
|
@ -1300,7 +1307,7 @@ impl Inner {
|
||||||
error!("Unable to get line index: {}", err);
|
error!("Unable to get line index: {}", err);
|
||||||
LspError::internal_error()
|
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 command = if !locations.is_empty() {
|
||||||
let title = if locations.len() > 1 {
|
let title = if locations.len() > 1 {
|
||||||
|
@ -1308,11 +1315,13 @@ impl Inner {
|
||||||
} else {
|
} else {
|
||||||
"1 reference".to_string()
|
"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| {
|
.map_err(|err| {
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
LspError::internal_error()
|
LspError::internal_error()
|
||||||
})?;
|
})?;
|
||||||
Command {
|
Command {
|
||||||
title,
|
title,
|
||||||
command: "deno.showReferences".to_string(),
|
command: "deno.showReferences".to_string(),
|
||||||
|
@ -1366,9 +1375,9 @@ impl Inner {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("document_highlight");
|
let mark = self.performance.mark("document_highlight");
|
||||||
let specifier = utils::normalize_url(
|
let specifier = self
|
||||||
params.text_document_position_params.text_document.uri,
|
.url_map
|
||||||
);
|
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
||||||
let line_index =
|
let line_index =
|
||||||
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
||||||
line_index
|
line_index
|
||||||
|
@ -1412,8 +1421,9 @@ impl Inner {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("references");
|
let mark = self.performance.mark("references");
|
||||||
let specifier =
|
let specifier = self
|
||||||
utils::normalize_url(params.text_document_position.text_document.uri);
|
.url_map
|
||||||
|
.normalize_url(¶ms.text_document_position.text_document.uri);
|
||||||
let line_index =
|
let line_index =
|
||||||
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
||||||
line_index
|
line_index
|
||||||
|
@ -1444,7 +1454,7 @@ impl Inner {
|
||||||
// TODO(lucacasonato): handle error correctly
|
// TODO(lucacasonato): handle error correctly
|
||||||
let line_index =
|
let line_index =
|
||||||
self.get_line_index(reference_specifier).await.unwrap();
|
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);
|
self.performance.measure(mark);
|
||||||
|
@ -1463,9 +1473,9 @@ impl Inner {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("goto_definition");
|
let mark = self.performance.mark("goto_definition");
|
||||||
let specifier = utils::normalize_url(
|
let specifier = self
|
||||||
params.text_document_position_params.text_document.uri,
|
.url_map
|
||||||
);
|
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
||||||
let line_index =
|
let line_index =
|
||||||
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
||||||
line_index
|
line_index
|
||||||
|
@ -1503,8 +1513,9 @@ impl Inner {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("completion");
|
let mark = self.performance.mark("completion");
|
||||||
let specifier =
|
let specifier = self
|
||||||
utils::normalize_url(params.text_document_position.text_document.uri);
|
.url_map
|
||||||
|
.normalize_url(¶ms.text_document_position.text_document.uri);
|
||||||
// TODO(lucacasonato): handle error correctly
|
// TODO(lucacasonato): handle error correctly
|
||||||
let line_index =
|
let line_index =
|
||||||
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
||||||
|
@ -1548,9 +1559,9 @@ impl Inner {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("goto_implementation");
|
let mark = self.performance.mark("goto_implementation");
|
||||||
let specifier = utils::normalize_url(
|
let specifier = self
|
||||||
params.text_document_position_params.text_document.uri,
|
.url_map
|
||||||
);
|
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
||||||
let line_index =
|
let line_index =
|
||||||
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
||||||
line_index
|
line_index
|
||||||
|
@ -1605,8 +1616,9 @@ impl Inner {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("rename");
|
let mark = self.performance.mark("rename");
|
||||||
let specifier =
|
let specifier = self
|
||||||
utils::normalize_url(params.text_document_position.text_document.uri);
|
.url_map
|
||||||
|
.normalize_url(¶ms.text_document_position.text_document.uri);
|
||||||
|
|
||||||
let line_index =
|
let line_index =
|
||||||
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
||||||
|
@ -1710,9 +1722,9 @@ impl Inner {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("signature_help");
|
let mark = self.performance.mark("signature_help");
|
||||||
let specifier = utils::normalize_url(
|
let specifier = self
|
||||||
params.text_document_position_params.text_document.uri,
|
.url_map
|
||||||
);
|
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
||||||
let line_index =
|
let line_index =
|
||||||
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
if let Some(line_index) = self.get_line_index_sync(&specifier) {
|
||||||
line_index
|
line_index
|
||||||
|
@ -1930,10 +1942,10 @@ impl Inner {
|
||||||
/// in the Deno cache, including any of their dependencies.
|
/// in the Deno cache, including any of their dependencies.
|
||||||
async fn cache(&mut self, params: CacheParams) -> LspResult<bool> {
|
async fn cache(&mut self, params: CacheParams) -> LspResult<bool> {
|
||||||
let mark = self.performance.mark("cache");
|
let mark = self.performance.mark("cache");
|
||||||
let referrer = utils::normalize_url(params.referrer.uri);
|
let referrer = self.url_map.normalize_url(¶ms.referrer.uri);
|
||||||
if !params.uris.is_empty() {
|
if !params.uris.is_empty() {
|
||||||
for identifier in ¶ms.uris {
|
for identifier in ¶ms.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)
|
sources::cache(&specifier, &self.maybe_import_map)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
|
@ -1976,7 +1988,7 @@ impl Inner {
|
||||||
params: VirtualTextDocumentParams,
|
params: VirtualTextDocumentParams,
|
||||||
) -> LspResult<Option<String>> {
|
) -> LspResult<Option<String>> {
|
||||||
let mark = self.performance.mark("virtual_text_document");
|
let mark = self.performance.mark("virtual_text_document");
|
||||||
let specifier = utils::normalize_url(params.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
let contents = if specifier.as_str() == "deno:/status.md" {
|
let contents = if specifier.as_str() == "deno:/status.md" {
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
|
|
||||||
|
@ -2170,15 +2182,15 @@ mod tests {
|
||||||
("initialize_request.json", LspResponse::RequestAny),
|
("initialize_request.json", LspResponse::RequestAny),
|
||||||
("initialized_notification.json", LspResponse::None),
|
("initialized_notification.json", LspResponse::None),
|
||||||
("did_open_notification_asset.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",
|
"virtual_text_document_request.json",
|
||||||
LspResponse::RequestAny,
|
LspResponse::RequestAny,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"hover_request_asset_02.json",
|
"hover_request_asset.json",
|
||||||
LspResponse::Request(
|
LspResponse::Request(
|
||||||
4,
|
5,
|
||||||
json!({
|
json!({
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
|
@ -2807,6 +2819,7 @@ mod tests {
|
||||||
("initialize_request.json", LspResponse::RequestAny),
|
("initialize_request.json", LspResponse::RequestAny),
|
||||||
("initialized_notification.json", LspResponse::None),
|
("initialized_notification.json", LspResponse::None),
|
||||||
("did_open_notification_asset.json", LspResponse::None),
|
("did_open_notification_asset.json", LspResponse::None),
|
||||||
|
("references_request_asset.json", LspResponse::RequestAny),
|
||||||
(
|
(
|
||||||
"virtual_text_document_request.json",
|
"virtual_text_document_request.json",
|
||||||
LspResponse::RequestAny,
|
LspResponse::RequestAny,
|
||||||
|
|
|
@ -13,7 +13,7 @@ mod performance;
|
||||||
mod sources;
|
mod sources;
|
||||||
mod text;
|
mod text;
|
||||||
mod tsc;
|
mod tsc;
|
||||||
mod utils;
|
mod urls;
|
||||||
|
|
||||||
pub async fn start() -> Result<(), AnyError> {
|
pub async fn start() -> Result<(), AnyError> {
|
||||||
let stdin = tokio::io::stdin();
|
let stdin = tokio::io::stdin();
|
||||||
|
|
104
cli/lsp/tsc.rs
104
cli/lsp/tsc.rs
|
@ -7,7 +7,6 @@ use super::language_server;
|
||||||
use super::language_server::StateSnapshot;
|
use super::language_server::StateSnapshot;
|
||||||
use super::text;
|
use super::text;
|
||||||
use super::text::LineIndex;
|
use super::text::LineIndex;
|
||||||
use super::utils;
|
|
||||||
|
|
||||||
use crate::media_type::MediaType;
|
use crate::media_type::MediaType;
|
||||||
use crate::tokio_util::create_basic_runtime;
|
use crate::tokio_util::create_basic_runtime;
|
||||||
|
@ -480,40 +479,41 @@ impl DocumentSpan {
|
||||||
language_server: &mut language_server::Inner,
|
language_server: &mut language_server::Inner,
|
||||||
) -> Option<lsp::LocationLink> {
|
) -> Option<lsp::LocationLink> {
|
||||||
let target_specifier = resolve_url(&self.file_name).unwrap();
|
let target_specifier = resolve_url(&self.file_name).unwrap();
|
||||||
if let Ok(target_line_index) =
|
let target_line_index = language_server
|
||||||
language_server.get_line_index(target_specifier).await
|
.get_line_index(target_specifier.clone())
|
||||||
{
|
.await
|
||||||
let target_uri = utils::normalize_file_name(&self.file_name).unwrap();
|
.ok()?;
|
||||||
let (target_range, target_selection_range) =
|
let target_uri = language_server
|
||||||
if let Some(context_span) = &self.context_span {
|
.url_map
|
||||||
(
|
.normalize_specifier(&target_specifier)
|
||||||
context_span.to_range(&target_line_index),
|
.unwrap();
|
||||||
self.text_span.to_range(&target_line_index),
|
let (target_range, target_selection_range) =
|
||||||
)
|
if let Some(context_span) = &self.context_span {
|
||||||
} else {
|
(
|
||||||
(
|
context_span.to_range(&target_line_index),
|
||||||
self.text_span.to_range(&target_line_index),
|
self.text_span.to_range(&target_line_index),
|
||||||
self.text_span.to_range(&target_line_index),
|
)
|
||||||
)
|
} else {
|
||||||
};
|
(
|
||||||
let origin_selection_range =
|
self.text_span.to_range(&target_line_index),
|
||||||
if let Some(original_context_span) = &self.original_context_span {
|
self.text_span.to_range(&target_line_index),
|
||||||
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)
|
let origin_selection_range =
|
||||||
} else {
|
if let Some(original_context_span) = &self.original_context_span {
|
||||||
None
|
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 {
|
impl ImplementationLocation {
|
||||||
pub fn to_location(&self, line_index: &LineIndex) -> lsp::Location {
|
pub(crate) fn to_location(
|
||||||
let uri =
|
&self,
|
||||||
utils::normalize_file_name(&self.document_span.file_name).unwrap();
|
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 {
|
lsp::Location {
|
||||||
uri,
|
uri,
|
||||||
range: self.document_span.text_span.to_range(line_index),
|
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> =
|
let mut text_document_edit_map: HashMap<Url, lsp::TextDocumentEdit> =
|
||||||
HashMap::new();
|
HashMap::new();
|
||||||
for location in self.locations.iter() {
|
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 specifier = resolve_url(&location.document_span.file_name)?;
|
||||||
|
let uri = language_server.url_map.normalize_specifier(&specifier)?;
|
||||||
|
|
||||||
// ensure TextDocumentEdit for `location.file_name`.
|
// ensure TextDocumentEdit for `location.file_name`.
|
||||||
if text_document_edit_map.get(&uri).is_none() {
|
if text_document_edit_map.get(&uri).is_none() {
|
||||||
|
@ -852,9 +859,16 @@ pub struct ReferenceEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReferenceEntry {
|
impl ReferenceEntry {
|
||||||
pub fn to_location(&self, line_index: &LineIndex) -> lsp::Location {
|
pub(crate) fn to_location(
|
||||||
let uri =
|
&self,
|
||||||
utils::normalize_file_name(&self.document_span.file_name).unwrap();
|
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 {
|
lsp::Location {
|
||||||
uri,
|
uri,
|
||||||
range: self.document_span.text_span.to_range(line_index),
|
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:///") {
|
if specifier.starts_with("asset:///") {
|
||||||
resolved.push(Some((
|
resolved.push(Some((
|
||||||
specifier.clone(),
|
specifier.clone(),
|
||||||
MediaType::from(specifier).as_ts_extension(),
|
MediaType::from(specifier).as_ts_extension().into(),
|
||||||
)))
|
)))
|
||||||
} else if let Some(dependency) = dependencies.get(specifier) {
|
} else if let Some(dependency) = dependencies.get(specifier) {
|
||||||
let resolved_import =
|
let resolved_import =
|
||||||
|
@ -1259,7 +1273,7 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
||||||
let media_type = MediaType::from(&resolved_specifier);
|
let media_type = MediaType::from(&resolved_specifier);
|
||||||
resolved.push(Some((
|
resolved.push(Some((
|
||||||
resolved_specifier.to_string(),
|
resolved_specifier.to_string(),
|
||||||
media_type.as_ts_extension(),
|
media_type.as_ts_extension().into(),
|
||||||
)));
|
)));
|
||||||
} else if sources.contains_key(&resolved_specifier) {
|
} else if sources.contains_key(&resolved_specifier) {
|
||||||
let media_type = if let Some(media_type) =
|
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.push(Some((
|
||||||
resolved_specifier.to_string(),
|
resolved_specifier.to_string(),
|
||||||
media_type.as_ts_extension(),
|
media_type.as_ts_extension().into(),
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
resolved.push(None);
|
resolved.push(None);
|
||||||
|
@ -1289,7 +1303,7 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
||||||
{
|
{
|
||||||
resolved.push(Some((
|
resolved.push(Some((
|
||||||
resolved_specifier.to_string(),
|
resolved_specifier.to_string(),
|
||||||
media_type.as_ts_extension(),
|
media_type.as_ts_extension().into(),
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
resolved.push(None);
|
resolved.push(None);
|
||||||
|
|
174
cli/lsp/urls.rs
Normal file
174
cli/lsp/urls.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -121,8 +121,8 @@ impl MediaType {
|
||||||
///
|
///
|
||||||
/// *NOTE* This is defined in TypeScript as a string based enum. Changes to
|
/// *NOTE* This is defined in TypeScript as a string based enum. Changes to
|
||||||
/// that enum in TypeScript should be reflected here.
|
/// that enum in TypeScript should be reflected here.
|
||||||
pub fn as_ts_extension(&self) -> String {
|
pub fn as_ts_extension(&self) -> &str {
|
||||||
let ext = match self {
|
match self {
|
||||||
MediaType::JavaScript => ".js",
|
MediaType::JavaScript => ".js",
|
||||||
MediaType::JSX => ".jsx",
|
MediaType::JSX => ".jsx",
|
||||||
MediaType::TypeScript => ".ts",
|
MediaType::TypeScript => ".ts",
|
||||||
|
@ -138,13 +138,11 @@ impl MediaType {
|
||||||
// JS for mapping purposes, though in reality, it is unlikely to ever be
|
// JS for mapping purposes, though in reality, it is unlikely to ever be
|
||||||
// passed to the compiler.
|
// passed to the compiler.
|
||||||
MediaType::SourceMap => ".js",
|
MediaType::SourceMap => ".js",
|
||||||
// TypeScript doesn't have an "unknown", so we will treat WASM as JS for
|
// TypeScript doesn't have an "unknown", so we will treat unknowns as JS
|
||||||
// mapping purposes, though in reality, it is unlikely to ever be passed
|
// for mapping purposes, though in reality, it is unlikely to ever be
|
||||||
// to the compiler.
|
// passed to the compiler.
|
||||||
MediaType::Unknown => ".js",
|
MediaType::Unknown => ".js",
|
||||||
};
|
}
|
||||||
|
|
||||||
ext.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map the media type to a `ts.ScriptKind`
|
/// Map the media type to a `ts.ScriptKind`
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"method": "textDocument/codeLens",
|
"method": "textDocument/codeLens",
|
||||||
"params": {
|
"params": {
|
||||||
"textDocument": {
|
"textDocument": {
|
||||||
"uri": "deno:/asset//lib.es2015.symbol.wellknown.d.ts"
|
"uri": "deno:/asset//lib.deno.shared_globals.d.ts"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,17 @@
|
||||||
"params": {
|
"params": {
|
||||||
"range": {
|
"range": {
|
||||||
"start": {
|
"start": {
|
||||||
"line": 93,
|
"line": 416,
|
||||||
"character": 10
|
"character": 12
|
||||||
},
|
},
|
||||||
"end": {
|
"end": {
|
||||||
"line": 93,
|
"line": 416,
|
||||||
"character": 15
|
"character": 19
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"specifier": "asset:///lib.es2015.symbol.wellknown.d.ts",
|
"specifier": "asset:///lib.deno.shared_globals.d.ts",
|
||||||
"source": "implementations"
|
"source": "references"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 4,
|
"id": 4,
|
||||||
"method": "textDocument/hover",
|
"method": "textDocument/definition",
|
||||||
"params": {
|
"params": {
|
||||||
"textDocument": {
|
"textDocument": {
|
||||||
"uri": "file:///a/file.ts"
|
"uri": "file:///a/file.ts"
|
||||||
},
|
},
|
||||||
"position": {
|
"position": {
|
||||||
"line": 0,
|
"line": 0,
|
||||||
"character": 12
|
"character": 14
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
"uri": "file:///a/file.ts",
|
"uri": "file:///a/file.ts",
|
||||||
"languageId": "typescript",
|
"languageId": "typescript",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"text": "console.log(\"hello deno!\");\n"
|
"text": "console.log(Date.now());\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 4,
|
"id": 5,
|
||||||
"method": "textDocument/hover",
|
"method": "textDocument/hover",
|
||||||
"params": {
|
"params": {
|
||||||
"textDocument": {
|
"textDocument": {
|
17
cli/tests/lsp/references_request_asset.json
Normal file
17
cli/tests/lsp/references_request_asset.json
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 2,
|
"id": 4,
|
||||||
"method": "deno/virtualTextDocument",
|
"method": "deno/virtualTextDocument",
|
||||||
"params": {
|
"params": {
|
||||||
"textDocument": {
|
"textDocument": {
|
||||||
"uri": "deno:/asset//lib.es2015.symbol.wellknown.d.ts"
|
"uri": "deno:/asset//lib.deno.shared_globals.d.ts"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -383,7 +383,10 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
||||||
} else {
|
} else {
|
||||||
resolved_specifier.to_string()
|
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
|
// 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
|
// the source file in the graph, so we will return a fake module to
|
||||||
|
|
Loading…
Reference in a new issue