mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 08:33:43 -05:00
fix(lsp): handle code lenses for non-documents (#9454)
This commit is contained in:
parent
97d5ef2950
commit
d95666cae0
6 changed files with 154 additions and 67 deletions
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
use super::analysis;
|
use super::analysis;
|
||||||
use super::text::LineIndex;
|
use super::text::LineIndex;
|
||||||
use super::tsc::NavigationTree;
|
|
||||||
|
|
||||||
use crate::import_map::ImportMap;
|
use crate::import_map::ImportMap;
|
||||||
use crate::media_type::MediaType;
|
use crate::media_type::MediaType;
|
||||||
|
@ -34,7 +33,6 @@ impl IndexValid {
|
||||||
pub struct DocumentData {
|
pub struct DocumentData {
|
||||||
bytes: Option<Vec<u8>>,
|
bytes: Option<Vec<u8>>,
|
||||||
line_index: Option<LineIndex>,
|
line_index: Option<LineIndex>,
|
||||||
navigation_tree: Option<NavigationTree>,
|
|
||||||
dependencies: Option<HashMap<String, analysis::Dependency>>,
|
dependencies: Option<HashMap<String, analysis::Dependency>>,
|
||||||
version: Option<i32>,
|
version: Option<i32>,
|
||||||
}
|
}
|
||||||
|
@ -74,7 +72,6 @@ impl DocumentData {
|
||||||
} else {
|
} else {
|
||||||
Some(LineIndex::new(&content))
|
Some(LineIndex::new(&content))
|
||||||
};
|
};
|
||||||
self.navigation_tree = None;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,14 +187,6 @@ impl DocumentCache {
|
||||||
doc.line_index.clone()
|
doc.line_index.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn navigation_tree(
|
|
||||||
&self,
|
|
||||||
specifier: &ModuleSpecifier,
|
|
||||||
) -> Option<NavigationTree> {
|
|
||||||
let doc = self.docs.get(specifier)?;
|
|
||||||
doc.navigation_tree.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open(
|
pub fn open(
|
||||||
&mut self,
|
&mut self,
|
||||||
specifier: ModuleSpecifier,
|
specifier: ModuleSpecifier,
|
||||||
|
@ -229,22 +218,6 @@ impl DocumentCache {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_navigation_tree(
|
|
||||||
&mut self,
|
|
||||||
specifier: &ModuleSpecifier,
|
|
||||||
navigation_tree: NavigationTree,
|
|
||||||
) -> Result<(), AnyError> {
|
|
||||||
if let Some(mut doc) = self.docs.get_mut(specifier) {
|
|
||||||
doc.navigation_tree = Some(navigation_tree);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(custom_error(
|
|
||||||
"NotFound",
|
|
||||||
"The document \"{}\" was unexpectedly missing.",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version(&self, specifier: &ModuleSpecifier) -> Option<i32> {
|
pub fn version(&self, specifier: &ModuleSpecifier) -> Option<i32> {
|
||||||
self.docs.get(specifier).and_then(|doc| doc.version)
|
self.docs.get(specifier).and_then(|doc| doc.version)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use deno_core::error::anyhow;
|
use deno_core::error::anyhow;
|
||||||
use deno_core::error::custom_error;
|
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::serde::Deserialize;
|
use deno_core::serde::Deserialize;
|
||||||
use deno_core::serde::Serialize;
|
use deno_core::serde::Serialize;
|
||||||
|
@ -86,6 +85,8 @@ pub(crate) struct Inner {
|
||||||
maybe_import_map: Option<ImportMap>,
|
maybe_import_map: Option<ImportMap>,
|
||||||
/// The URL for the import map which is used to determine relative imports.
|
/// The URL for the import map which is used to determine relative imports.
|
||||||
maybe_import_map_uri: Option<Url>,
|
maybe_import_map_uri: Option<Url>,
|
||||||
|
/// A map of all the cached navigation trees.
|
||||||
|
navigation_trees: HashMap<ModuleSpecifier, tsc::NavigationTree>,
|
||||||
/// A collection of measurements which instrument that performance of the LSP.
|
/// A collection of measurements which instrument that performance of the LSP.
|
||||||
performance: Performance,
|
performance: Performance,
|
||||||
/// Cached sources that are read-only.
|
/// Cached sources that are read-only.
|
||||||
|
@ -119,6 +120,7 @@ impl Inner {
|
||||||
maybe_config_uri: Default::default(),
|
maybe_config_uri: Default::default(),
|
||||||
maybe_import_map: Default::default(),
|
maybe_import_map: Default::default(),
|
||||||
maybe_import_map_uri: Default::default(),
|
maybe_import_map_uri: Default::default(),
|
||||||
|
navigation_trees: Default::default(),
|
||||||
performance: Default::default(),
|
performance: Default::default(),
|
||||||
sources,
|
sources,
|
||||||
ts_fixable_diagnostics: Default::default(),
|
ts_fixable_diagnostics: Default::default(),
|
||||||
|
@ -184,33 +186,24 @@ impl Inner {
|
||||||
&mut self,
|
&mut self,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
) -> Result<tsc::NavigationTree, AnyError> {
|
) -> Result<tsc::NavigationTree, AnyError> {
|
||||||
if self.documents.contains(specifier) {
|
let mark = self.performance.mark("get_navigation_tree");
|
||||||
let mark = self.performance.mark("get_navigation_tree");
|
if let Some(navigation_tree) = self.navigation_trees.get(specifier) {
|
||||||
if let Some(navigation_tree) = self.documents.navigation_tree(specifier) {
|
self.performance.measure(mark);
|
||||||
self.performance.measure(mark);
|
Ok(navigation_tree.clone())
|
||||||
Ok(navigation_tree)
|
|
||||||
} else {
|
|
||||||
let res = self
|
|
||||||
.ts_server
|
|
||||||
.request(
|
|
||||||
self.snapshot(),
|
|
||||||
tsc::RequestMethod::GetNavigationTree(specifier.clone()),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let navigation_tree: tsc::NavigationTree =
|
|
||||||
serde_json::from_value(res).unwrap();
|
|
||||||
self
|
|
||||||
.documents
|
|
||||||
.set_navigation_tree(specifier, navigation_tree.clone())?;
|
|
||||||
self.performance.measure(mark);
|
|
||||||
Ok(navigation_tree)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(custom_error(
|
let res = self
|
||||||
"NotFound",
|
.ts_server
|
||||||
format!("The document \"{}\" was unexpectedly not found.", specifier),
|
.request(
|
||||||
))
|
self.snapshot(),
|
||||||
|
tsc::RequestMethod::GetNavigationTree(specifier.clone()),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let navigation_tree: tsc::NavigationTree = serde_json::from_value(res)?;
|
||||||
|
self
|
||||||
|
.navigation_trees
|
||||||
|
.insert(specifier.clone(), navigation_tree.clone());
|
||||||
|
self.performance.measure(mark);
|
||||||
|
Ok(navigation_tree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,6 +641,9 @@ impl Inner {
|
||||||
{
|
{
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
}
|
}
|
||||||
|
// there are scenarios where local documents with a nav tree are opened in
|
||||||
|
// the editor
|
||||||
|
self.navigation_trees.remove(&specifier);
|
||||||
|
|
||||||
self.performance.measure(mark);
|
self.performance.measure(mark);
|
||||||
// TODO(@kitsonk): how to better lazily do this?
|
// TODO(@kitsonk): how to better lazily do this?
|
||||||
|
@ -672,6 +668,7 @@ impl Inner {
|
||||||
{
|
{
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
}
|
}
|
||||||
|
self.navigation_trees.remove(&specifier);
|
||||||
|
|
||||||
self.performance.measure(mark);
|
self.performance.measure(mark);
|
||||||
// TODO(@kitsonk): how to better lazily do this?
|
// TODO(@kitsonk): how to better lazily do this?
|
||||||
|
@ -690,12 +687,13 @@ impl Inner {
|
||||||
}
|
}
|
||||||
let specifier = utils::normalize_url(params.text_document.uri);
|
let specifier = utils::normalize_url(params.text_document.uri);
|
||||||
self.documents.close(&specifier);
|
self.documents.close(&specifier);
|
||||||
|
self.navigation_trees.remove(&specifier);
|
||||||
|
|
||||||
|
self.performance.measure(mark);
|
||||||
// TODO(@kitsonk): how to better lazily do this?
|
// TODO(@kitsonk): how to better lazily do this?
|
||||||
if let Err(err) = self.prepare_diagnostics().await {
|
if let Err(err) = self.prepare_diagnostics().await {
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
}
|
}
|
||||||
self.performance.measure(mark);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn did_change_configuration(
|
async fn did_change_configuration(
|
||||||
|
@ -713,7 +711,7 @@ impl Inner {
|
||||||
.await
|
.await
|
||||||
.map(|vec| vec.get(0).cloned())
|
.map(|vec| vec.get(0).cloned())
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
error!("failed to fetch the extension settings {:?}", err);
|
error!("failed to fetch the extension settings {}", err);
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -1026,7 +1024,7 @@ impl Inner {
|
||||||
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| {
|
||||||
error!("Failed to retrieve nav tree: {:#?}", err);
|
error!("Failed to retrieve nav tree: {}", err);
|
||||||
LspError::invalid_request()
|
LspError::invalid_request()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -1191,11 +1189,16 @@ impl Inner {
|
||||||
} else {
|
} else {
|
||||||
"1 implementation".to_string()
|
"1 implementation".to_string()
|
||||||
};
|
};
|
||||||
|
let url = utils::normalize_specifier(&code_lens_data.specifier)
|
||||||
|
.map_err(|err| {
|
||||||
|
error!("{}", err);
|
||||||
|
LspError::internal_error()
|
||||||
|
})?;
|
||||||
Command {
|
Command {
|
||||||
title,
|
title,
|
||||||
command: "deno.showReferences".to_string(),
|
command: "deno.showReferences".to_string(),
|
||||||
arguments: Some(vec![
|
arguments: Some(vec![
|
||||||
serde_json::to_value(code_lens_data.specifier).unwrap(),
|
serde_json::to_value(url).unwrap(),
|
||||||
serde_json::to_value(params.range.start).unwrap(),
|
serde_json::to_value(params.range.start).unwrap(),
|
||||||
serde_json::to_value(locations).unwrap(),
|
serde_json::to_value(locations).unwrap(),
|
||||||
]),
|
]),
|
||||||
|
@ -1272,11 +1275,16 @@ impl Inner {
|
||||||
} else {
|
} else {
|
||||||
"1 reference".to_string()
|
"1 reference".to_string()
|
||||||
};
|
};
|
||||||
|
let url = utils::normalize_specifier(&code_lens_data.specifier)
|
||||||
|
.map_err(|err| {
|
||||||
|
error!("{}", err);
|
||||||
|
LspError::internal_error()
|
||||||
|
})?;
|
||||||
Command {
|
Command {
|
||||||
title,
|
title,
|
||||||
command: "deno.showReferences".to_string(),
|
command: "deno.showReferences".to_string(),
|
||||||
arguments: Some(vec![
|
arguments: Some(vec![
|
||||||
serde_json::to_value(code_lens_data.specifier).unwrap(),
|
serde_json::to_value(url).unwrap(),
|
||||||
serde_json::to_value(params.range.start).unwrap(),
|
serde_json::to_value(params.range.start).unwrap(),
|
||||||
serde_json::to_value(locations).unwrap(),
|
serde_json::to_value(locations).unwrap(),
|
||||||
]),
|
]),
|
||||||
|
@ -1531,13 +1539,13 @@ impl Inner {
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot(), req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver {:#?}", err);
|
error!("Failed to request to tsserver {}", err);
|
||||||
LspError::invalid_request()
|
LspError::invalid_request()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let maybe_implementations = serde_json::from_value::<Option<Vec<tsc::ImplementationLocation>>>(res)
|
let maybe_implementations = serde_json::from_value::<Option<Vec<tsc::ImplementationLocation>>>(res)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to deserialized tsserver response to Vec<ImplementationLocation> {:#?}", err);
|
error!("Failed to deserialized tsserver response to Vec<ImplementationLocation> {}", err);
|
||||||
LspError::internal_error()
|
LspError::internal_error()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -1596,7 +1604,7 @@ impl Inner {
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot(), req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver {:#?}", err);
|
error!("Failed to request to tsserver {}", err);
|
||||||
LspError::invalid_request()
|
LspError::invalid_request()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -1605,7 +1613,7 @@ impl Inner {
|
||||||
>(res)
|
>(res)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!(
|
error!(
|
||||||
"Failed to deserialize tsserver response to Vec<RenameLocation> {:#?}",
|
"Failed to deserialize tsserver response to Vec<RenameLocation> {}",
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
LspError::internal_error()
|
LspError::internal_error()
|
||||||
|
@ -1617,7 +1625,7 @@ impl Inner {
|
||||||
.into_workspace_edit(¶ms.new_name, self)
|
.into_workspace_edit(¶ms.new_name, self)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to get workspace edits: {:#?}", err);
|
error!("Failed to get workspace edits: {}", err);
|
||||||
LspError::internal_error()
|
LspError::internal_error()
|
||||||
})?;
|
})?;
|
||||||
self.performance.measure(mark);
|
self.performance.measure(mark);
|
||||||
|
@ -1637,7 +1645,7 @@ impl Inner {
|
||||||
"deno/cache" => match params.map(serde_json::from_value) {
|
"deno/cache" => match params.map(serde_json::from_value) {
|
||||||
Some(Ok(params)) => Ok(Some(
|
Some(Ok(params)) => Ok(Some(
|
||||||
serde_json::to_value(self.cache(params).await?).map_err(|err| {
|
serde_json::to_value(self.cache(params).await?).map_err(|err| {
|
||||||
error!("Failed to serialize cache response: {:#?}", err);
|
error!("Failed to serialize cache response: {}", err);
|
||||||
LspError::internal_error()
|
LspError::internal_error()
|
||||||
})?,
|
})?,
|
||||||
)),
|
)),
|
||||||
|
@ -1650,7 +1658,7 @@ impl Inner {
|
||||||
serde_json::to_value(self.virtual_text_document(params).await?)
|
serde_json::to_value(self.virtual_text_document(params).await?)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!(
|
error!(
|
||||||
"Failed to serialize virtual_text_document response: {:#?}",
|
"Failed to serialize virtual_text_document response: {}",
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
LspError::internal_error()
|
LspError::internal_error()
|
||||||
|
@ -2584,6 +2592,51 @@ mod tests {
|
||||||
harness.run().await;
|
harness.run().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct CodeLensResponse {
|
||||||
|
pub result: Option<Vec<CodeLens>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct CodeLensResolveResponse {
|
||||||
|
pub result: CodeLens,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_code_lens_non_doc_nav_tree() {
|
||||||
|
let mut harness = LspTestHarness::new(vec![
|
||||||
|
("initialize_request.json", LspResponse::RequestAny),
|
||||||
|
("initialized_notification.json", LspResponse::None),
|
||||||
|
("did_open_notification_asset.json", LspResponse::None),
|
||||||
|
(
|
||||||
|
"virtual_text_document_request.json",
|
||||||
|
LspResponse::RequestAny,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"code_lens_request_asset.json",
|
||||||
|
LspResponse::RequestAssert(|value| {
|
||||||
|
let resp: CodeLensResponse = serde_json::from_value(value).unwrap();
|
||||||
|
let lenses = resp.result.unwrap();
|
||||||
|
assert!(lenses.len() > 50);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"code_lens_resolve_request_asset.json",
|
||||||
|
LspResponse::RequestAssert(|value| {
|
||||||
|
let resp: CodeLensResolveResponse =
|
||||||
|
serde_json::from_value(value).unwrap();
|
||||||
|
assert!(resp.result.command.is_some());
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"shutdown_request.json",
|
||||||
|
LspResponse::Request(3, json!(null)),
|
||||||
|
),
|
||||||
|
("exit_notification.json", LspResponse::None),
|
||||||
|
]);
|
||||||
|
harness.run().await;
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_code_actions() {
|
async fn test_code_actions() {
|
||||||
let mut harness = LspTestHarness::new(vec![
|
let mut harness = LspTestHarness::new(vec![
|
||||||
|
|
|
@ -16,6 +16,19 @@ pub fn normalize_file_name(file_name: &str) -> Result<Url, AnyError> {
|
||||||
Url::parse(&specifier_str).map_err(|err| err.into())
|
Url::parse(&specifier_str).map_err(|err| err.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn normalize_specifier(
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
) -> Result<Url, AnyError> {
|
||||||
|
let url = specifier.as_url();
|
||||||
|
if url.scheme() == "file" {
|
||||||
|
Ok(url.clone())
|
||||||
|
} else {
|
||||||
|
let specifier_str =
|
||||||
|
format!("deno:///{}", url.as_str().replacen("://", "/", 1));
|
||||||
|
Url::parse(&specifier_str).map_err(|err| err.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Normalize URLs from the client, where "virtual" `deno:///` URLs are
|
/// Normalize URLs from the client, where "virtual" `deno:///` URLs are
|
||||||
/// converted into proper module specifiers.
|
/// converted into proper module specifiers.
|
||||||
pub fn normalize_url(url: Url) -> ModuleSpecifier {
|
pub fn normalize_url(url: Url) -> ModuleSpecifier {
|
||||||
|
@ -40,6 +53,23 @@ pub fn normalize_url(url: Url) -> ModuleSpecifier {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_normalize_file_name() {
|
||||||
|
let fixture = "https://deno.land/x/mod.ts";
|
||||||
|
let actual = normalize_file_name(fixture).unwrap();
|
||||||
|
let expected = Url::parse("deno:///https/deno.land/x/mod.ts").unwrap();
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_normalize_specifier() {
|
||||||
|
let fixture =
|
||||||
|
ModuleSpecifier::resolve_url("https://deno.land/x/mod.ts").unwrap();
|
||||||
|
let actual = normalize_specifier(&fixture).unwrap();
|
||||||
|
let expected = Url::parse("deno:///https/deno.land/x/mod.ts").unwrap();
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_normalize_url() {
|
fn test_normalize_url() {
|
||||||
let fixture = Url::parse("deno:///https/deno.land/x/mod.ts").unwrap();
|
let fixture = Url::parse("deno:///https/deno.land/x/mod.ts").unwrap();
|
||||||
|
|
10
cli/tests/lsp/code_lens_request_asset.json
Normal file
10
cli/tests/lsp/code_lens_request_asset.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 4,
|
||||||
|
"method": "textDocument/codeLens",
|
||||||
|
"params": {
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "deno:/asset//lib.es2015.symbol.wellknown.d.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
cli/tests/lsp/code_lens_resolve_request_asset.json
Normal file
21
cli/tests/lsp/code_lens_resolve_request_asset.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 5,
|
||||||
|
"method": "codeLens/resolve",
|
||||||
|
"params": {
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 93,
|
||||||
|
"character": 10
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 93,
|
||||||
|
"character": 15
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"specifier": "asset:///lib.es2015.symbol.wellknown.d.ts",
|
||||||
|
"source": "implementations"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(Date.now());\n"
|
"text": "console.log(\"hello deno!\");\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue