mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -05:00
feat(lsp): support the unstable setting (#8851)
This commit is contained in:
parent
b091c6c8c9
commit
097c3379ba
3 changed files with 224 additions and 84 deletions
|
@ -89,49 +89,34 @@ impl LanguageServer {
|
|||
config.settings.enable
|
||||
}
|
||||
|
||||
pub async fn update_import_map(&self) -> Result<(), AnyError> {
|
||||
let (maybe_import_map, maybe_root_uri) = {
|
||||
let config = self.config.read().unwrap();
|
||||
(config.settings.import_map.clone(), config.root_uri.clone())
|
||||
};
|
||||
if let Some(import_map_str) = &maybe_import_map {
|
||||
info!("update import map");
|
||||
let import_map_url = if let Ok(url) = Url::from_file_path(import_map_str)
|
||||
pub async fn get_line_index(
|
||||
&self,
|
||||
specifier: ModuleSpecifier,
|
||||
) -> Result<Vec<u32>, AnyError> {
|
||||
let line_index = if specifier.as_url().scheme() == "asset" {
|
||||
let state_snapshot = self.snapshot();
|
||||
if let Some(source) =
|
||||
tsc::get_asset(&specifier, &self.ts_server, &state_snapshot).await?
|
||||
{
|
||||
Ok(url)
|
||||
} else if let Some(root_uri) = &maybe_root_uri {
|
||||
let root_path = root_uri
|
||||
.to_file_path()
|
||||
.map_err(|_| anyhow!("Bad root_uri: {}", root_uri))?;
|
||||
let import_map_path = root_path.join(import_map_str);
|
||||
Url::from_file_path(import_map_path).map_err(|_| {
|
||||
anyhow!("Bad file path for import map: {:?}", import_map_str)
|
||||
})
|
||||
text::index_lines(&source)
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"The path to the import map (\"{}\") is not resolvable.",
|
||||
import_map_str
|
||||
))
|
||||
}?;
|
||||
let import_map_path = import_map_url
|
||||
.to_file_path()
|
||||
.map_err(|_| anyhow!("Bad file path."))?;
|
||||
let import_map_json =
|
||||
fs::read_to_string(import_map_path).await.map_err(|err| {
|
||||
anyhow!(
|
||||
"Failed to load the import map at: {}. [{}]",
|
||||
import_map_url,
|
||||
err
|
||||
)
|
||||
})?;
|
||||
let import_map =
|
||||
ImportMap::from_json(&import_map_url.to_string(), &import_map_json)?;
|
||||
*self.maybe_import_map_uri.write().unwrap() = Some(import_map_url);
|
||||
*self.maybe_import_map.write().unwrap() = Some(import_map);
|
||||
return Err(anyhow!("asset source missing: {}", specifier));
|
||||
}
|
||||
} else {
|
||||
*self.maybe_import_map.write().unwrap() = None;
|
||||
}
|
||||
Ok(())
|
||||
let file_cache = self.file_cache.read().unwrap();
|
||||
if let Some(file_id) = file_cache.lookup(&specifier) {
|
||||
let file_text = file_cache.get_contents(file_id)?;
|
||||
text::index_lines(&file_text)
|
||||
} else {
|
||||
let mut sources = self.sources.write().unwrap();
|
||||
if let Some(line_index) = sources.get_line_index(&specifier) {
|
||||
line_index
|
||||
} else {
|
||||
return Err(anyhow!("source for specifier not found: {}", specifier));
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(line_index)
|
||||
}
|
||||
|
||||
async fn prepare_diagnostics(&self) -> Result<(), AnyError> {
|
||||
|
@ -260,34 +245,76 @@ impl LanguageServer {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn get_line_index(
|
||||
&self,
|
||||
specifier: ModuleSpecifier,
|
||||
) -> Result<Vec<u32>, AnyError> {
|
||||
let line_index = if specifier.as_url().scheme() == "asset" {
|
||||
let state_snapshot = self.snapshot();
|
||||
if let Some(source) =
|
||||
tsc::get_asset(&specifier, &self.ts_server, &state_snapshot).await?
|
||||
{
|
||||
text::index_lines(&source)
|
||||
} else {
|
||||
return Err(anyhow!("asset source missing: {}", specifier));
|
||||
}
|
||||
} else {
|
||||
let file_cache = self.file_cache.read().unwrap();
|
||||
if let Some(file_id) = file_cache.lookup(&specifier) {
|
||||
let file_text = file_cache.get_contents(file_id)?;
|
||||
text::index_lines(&file_text)
|
||||
} else {
|
||||
let mut sources = self.sources.write().unwrap();
|
||||
if let Some(line_index) = sources.get_line_index(&specifier) {
|
||||
line_index
|
||||
} else {
|
||||
return Err(anyhow!("source for specifier not found: {}", specifier));
|
||||
}
|
||||
}
|
||||
pub async fn update_import_map(&self) -> Result<(), AnyError> {
|
||||
let (maybe_import_map, maybe_root_uri) = {
|
||||
let config = self.config.read().unwrap();
|
||||
(config.settings.import_map.clone(), config.root_uri.clone())
|
||||
};
|
||||
Ok(line_index)
|
||||
if let Some(import_map_str) = &maybe_import_map {
|
||||
info!("update import map");
|
||||
let import_map_url = if let Ok(url) = Url::from_file_path(import_map_str)
|
||||
{
|
||||
Ok(url)
|
||||
} else if let Some(root_uri) = &maybe_root_uri {
|
||||
let root_path = root_uri
|
||||
.to_file_path()
|
||||
.map_err(|_| anyhow!("Bad root_uri: {}", root_uri))?;
|
||||
let import_map_path = root_path.join(import_map_str);
|
||||
Url::from_file_path(import_map_path).map_err(|_| {
|
||||
anyhow!("Bad file path for import map: {:?}", import_map_str)
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"The path to the import map (\"{}\") is not resolvable.",
|
||||
import_map_str
|
||||
))
|
||||
}?;
|
||||
let import_map_path = import_map_url
|
||||
.to_file_path()
|
||||
.map_err(|_| anyhow!("Bad file path."))?;
|
||||
let import_map_json =
|
||||
fs::read_to_string(import_map_path).await.map_err(|err| {
|
||||
anyhow!(
|
||||
"Failed to load the import map at: {}. [{}]",
|
||||
import_map_url,
|
||||
err
|
||||
)
|
||||
})?;
|
||||
let import_map =
|
||||
ImportMap::from_json(&import_map_url.to_string(), &import_map_json)?;
|
||||
*self.maybe_import_map_uri.write().unwrap() = Some(import_map_url);
|
||||
*self.maybe_import_map.write().unwrap() = Some(import_map);
|
||||
} else {
|
||||
*self.maybe_import_map.write().unwrap() = None;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_tsconfig(&self) -> Result<(), AnyError> {
|
||||
let mut tsconfig = TsConfig::new(json!({
|
||||
"allowJs": true,
|
||||
"experimentalDecorators": true,
|
||||
"isolatedModules": true,
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"module": "esnext",
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"target": "esnext",
|
||||
}));
|
||||
{
|
||||
let config = self.config.read().unwrap();
|
||||
if config.settings.unstable {
|
||||
let unstable_libs = json!({
|
||||
"lib": ["deno.ns", "deno.window", "deno.unstable"]
|
||||
});
|
||||
tsconfig.merge(&unstable_libs);
|
||||
}
|
||||
}
|
||||
self
|
||||
.ts_server
|
||||
.request(self.snapshot(), tsc::RequestMethod::Configure(tsconfig))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,23 +358,9 @@ impl lspower::LanguageServer for LanguageServer {
|
|||
config.update_capabilities(¶ms.capabilities);
|
||||
}
|
||||
|
||||
// TODO(@kitsonk) need to make this configurable, respect unstable
|
||||
let ts_config = TsConfig::new(json!({
|
||||
"allowJs": true,
|
||||
"experimentalDecorators": true,
|
||||
"isolatedModules": true,
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"module": "esnext",
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"target": "esnext",
|
||||
}));
|
||||
// TODO(lucacasonato): handle error correctly
|
||||
self
|
||||
.ts_server
|
||||
.request(self.snapshot(), tsc::RequestMethod::Configure(ts_config))
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(err) = self.update_tsconfig().await {
|
||||
warn!("Updating tsconfig has errored: {}", err);
|
||||
}
|
||||
|
||||
Ok(InitializeResult {
|
||||
capabilities,
|
||||
|
@ -502,6 +515,12 @@ impl lspower::LanguageServer for LanguageServer {
|
|||
.show_message(MessageType::Warning, err.to_string())
|
||||
.await;
|
||||
}
|
||||
if let Err(err) = self.update_tsconfig().await {
|
||||
self
|
||||
.client
|
||||
.show_message(MessageType::Warning, err.to_string())
|
||||
.await;
|
||||
}
|
||||
}
|
||||
_ => error!("received empty extension settings from the client"),
|
||||
}
|
||||
|
@ -1014,4 +1033,83 @@ mod tests {
|
|||
]);
|
||||
harness.run().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_hover_unstable_disabled() {
|
||||
let mut harness = LspTestHarness::new(vec![
|
||||
("initialize_request.json", LspResponse::RequestAny),
|
||||
("initialized_notification.json", LspResponse::None),
|
||||
("did_open_notification_unstable.json", LspResponse::None),
|
||||
(
|
||||
"hover_request.json",
|
||||
LspResponse::Request(
|
||||
2,
|
||||
json!({
|
||||
"contents": [
|
||||
{
|
||||
"language": "typescript",
|
||||
"value": "any"
|
||||
}
|
||||
],
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 17
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 28
|
||||
}
|
||||
}
|
||||
}),
|
||||
),
|
||||
),
|
||||
(
|
||||
"shutdown_request.json",
|
||||
LspResponse::Request(3, json!(null)),
|
||||
),
|
||||
("exit_notification.json", LspResponse::None),
|
||||
]);
|
||||
harness.run().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_hover_unstable_enabled() {
|
||||
let mut harness = LspTestHarness::new(vec![
|
||||
("initialize_request_unstable.json", LspResponse::RequestAny),
|
||||
("initialized_notification.json", LspResponse::None),
|
||||
("did_open_notification_unstable.json", LspResponse::None),
|
||||
(
|
||||
"hover_request.json",
|
||||
LspResponse::Request(
|
||||
2,
|
||||
json!({
|
||||
"contents": [
|
||||
{
|
||||
"language": "typescript",
|
||||
"value": "const Deno.permissions: Deno.Permissions"
|
||||
},
|
||||
"**UNSTABLE**: Under consideration to move to `navigator.permissions` to\nmatch web API. It could look like `navigator.permissions.query({ name: Deno.symbols.read })`."
|
||||
],
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 17
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 28
|
||||
}
|
||||
}
|
||||
}),
|
||||
),
|
||||
),
|
||||
(
|
||||
"shutdown_request.json",
|
||||
LspResponse::Request(3, json!(null)),
|
||||
),
|
||||
("exit_notification.json", LspResponse::None),
|
||||
]);
|
||||
harness.run().await;
|
||||
}
|
||||
}
|
||||
|
|
12
cli/tests/lsp/did_open_notification_unstable.json
Normal file
12
cli/tests/lsp/did_open_notification_unstable.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "textDocument/didOpen",
|
||||
"params": {
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "console.log(Deno.permissions);\n"
|
||||
}
|
||||
}
|
||||
}
|
30
cli/tests/lsp/initialize_request_unstable.json
Normal file
30
cli/tests/lsp/initialize_request_unstable.json
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
"processId": 0,
|
||||
"clientInfo": {
|
||||
"name": "test-harness",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"rootUri": null,
|
||||
"initializationOptions": {
|
||||
"enable": true,
|
||||
"lint": true,
|
||||
"unstable": true,
|
||||
"config": null,
|
||||
"importMap": null
|
||||
},
|
||||
"capabilities": {
|
||||
"textDocument": {
|
||||
"synchronization": {
|
||||
"dynamicRegistration": true,
|
||||
"willSave": true,
|
||||
"willSaveWaitUntil": true,
|
||||
"didSave": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue