1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 07:14:47 -05:00

feat: support workspace folders configuration (#10488)

Ref #8643
This commit is contained in:
Kitson Kelly 2021-05-10 11:16:04 +10:00 committed by GitHub
parent 33b1a6ed61
commit 84733d90c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 456 additions and 194 deletions

View file

@ -39,6 +39,10 @@
"willSaveWaitUntil": true, "willSaveWaitUntil": true,
"didSave": true "didSave": true
} }
},
"workspace": {
"configuration": true,
"workspaceFolders": true
} }
} }
} }

View file

@ -184,6 +184,22 @@ impl LspClient {
} }
} }
fn read_request<R>(&mut self) -> Result<(u64, String, Option<R>), AnyError>
where
R: de::DeserializeOwned,
{
loop {
if let LspMessage::Request(id, method, maybe_params) = self.read()? {
if let Some(p) = maybe_params {
let params = serde_json::from_value(p)?;
return Ok((id, method, Some(params)));
} else {
return Ok((id, method, None));
}
}
}
}
fn write(&mut self, value: Value) -> Result<(), AnyError> { fn write(&mut self, value: Value) -> Result<(), AnyError> {
let value_str = value.to_string(); let value_str = value.to_string();
let msg = format!( let msg = format!(
@ -222,6 +238,18 @@ impl LspClient {
} }
} }
fn write_response<V>(&mut self, id: u64, result: V) -> Result<(), AnyError>
where
V: Serialize,
{
let value = json!({
"jsonrpc": "2.0",
"id": id,
"result": result
});
self.write(value)
}
fn write_notification<S, V>( fn write_notification<S, V>(
&mut self, &mut self,
method: S, method: S,
@ -266,6 +294,16 @@ fn bench_big_file_edits(deno_exe: &Path) -> Result<Duration, AnyError> {
}), }),
)?; )?;
let (id, method, _): (u64, String, Option<Value>) = client.read_request()?;
assert_eq!(method, "workspace/configuration");
client.write_response(
id,
json!({
"enable": true
}),
)?;
let (method, _): (String, Option<Value>) = client.read_notification()?; let (method, _): (String, Option<Value>) = client.read_notification()?;
assert_eq!(method, "textDocument/publishDiagnostics"); assert_eq!(method, "textDocument/publishDiagnostics");
let (method, _): (String, Option<Value>) = client.read_notification()?; let (method, _): (String, Option<Value>) = client.read_notification()?;
@ -328,6 +366,16 @@ fn bench_startup_shutdown(deno_exe: &Path) -> Result<Duration, AnyError> {
}), }),
)?; )?;
let (id, method, _): (u64, String, Option<Value>) = client.read_request()?;
assert_eq!(method, "workspace/configuration");
client.write_response(
id,
json!({
"enable": true
}),
)?;
let (method, _): (String, Option<Value>) = client.read_notification()?; let (method, _): (String, Option<Value>) = client.read_notification()?;
assert_eq!(method, "textDocument/publishDiagnostics"); assert_eq!(method, "textDocument/publishDiagnostics");
let (method, _): (String, Option<Value>) = client.read_notification()?; let (method, _): (String, Option<Value>) = client.read_notification()?;

View file

@ -61,3 +61,56 @@ with Deno:
textDocument: TextDocumentIdentifier; textDocument: TextDocumentIdentifier;
} }
``` ```
## Settings
There are several settings that the language server supports for a workspace:
- `deno.enable`
- `deno.config`
- `deno.import_map`
- `deno.code_lens.implementations`
- `deno.code_lens.references`
- `deno.code_lens.references_all_functions`
- `deno.suggest.complete_function_calls`
- `deno.suggest.names`
- `deno.suggest.paths`
- `deno.suggest.auto_imports`
- `deno.imports.hosts`
- `deno.lint`
- `deno.unstable`
There are settings that are support on a per resource basis by the language
server:
- `deno.enable`
There are several points in the process where Deno analyzes these settings.
First, when the `initialize` request from the client, the
`initializationOptions` will be assumed to be an object that represents the
`deno` namespace of options. For example, the following value:
```json
{
"enable": true,
"unstable": true
}
```
Would enable Deno with the unstable APIs for this instance of the language
server.
When the language server receives a `workspace/didChangeConfiguration`
notification, it will assess if the client has indicated if it has a
`workspaceConfiguration` capability. If it does, it will send a
`workspace/configuration` request which will include a request for the workspace
configuration as well as the configuration of all URIs that the language server
is currently tracking.
If the client has the `workspaceConfiguration` capability, the language server
will send a configuration request for the URI when it received the
`textDocument/didOpen` notification in order to get the resources specific
settings.
If the client does not have the `workspaceConfiguration` capability, the
language server will assume the workspace setting applies to all resources.

View file

@ -27,6 +27,8 @@ use lspower::lsp::TextDocumentSyncCapability;
use lspower::lsp::TextDocumentSyncKind; use lspower::lsp::TextDocumentSyncKind;
use lspower::lsp::TextDocumentSyncOptions; use lspower::lsp::TextDocumentSyncOptions;
use lspower::lsp::WorkDoneProgressOptions; use lspower::lsp::WorkDoneProgressOptions;
use lspower::lsp::WorkspaceFoldersServerCapabilities;
use lspower::lsp::WorkspaceServerCapabilities;
use super::semantic_tokens::get_legend; use super::semantic_tokens::get_legend;
@ -132,7 +134,13 @@ pub fn server_capabilities(
}, },
), ),
), ),
workspace: None, workspace: Some(WorkspaceServerCapabilities {
workspace_folders: Some(WorkspaceFoldersServerCapabilities {
supported: Some(true),
change_notifications: None,
}),
file_operations: None,
}),
experimental: None, experimental: None,
linked_editing_range_provider: None, linked_editing_range_provider: None,
moniker_provider: None, moniker_provider: None,

View file

@ -4,11 +4,14 @@ use deno_core::serde::Deserialize;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::serde_json::Value; use deno_core::serde_json::Value;
use deno_core::url::Url; use deno_core::url::Url;
use deno_core::ModuleSpecifier;
use lspower::jsonrpc::Error as LSPError; use lspower::jsonrpc::Error as LSPError;
use lspower::jsonrpc::Result as LSPResult; use lspower::jsonrpc::Result as LSPResult;
use lspower::lsp; use lspower::lsp;
use std::collections::HashMap; use std::collections::HashMap;
pub const SETTINGS_SECTION: &str = "deno";
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct ClientCapabilities { pub struct ClientCapabilities {
pub status_notification: bool, pub status_notification: bool,
@ -84,19 +87,43 @@ impl Default for ImportCompletionSettings {
} }
} }
/// Deno language server specific settings that can be applied uniquely to a
/// specifier.
#[derive(Debug, Default, Clone, Deserialize)]
pub struct SpecifierSettings {
/// A flag that indicates if Deno is enabled for this specifier or not.
pub enable: bool,
}
/// Deno language server specific settings that are applied to a workspace.
#[derive(Debug, Default, Clone, Deserialize)] #[derive(Debug, Default, Clone, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct WorkspaceSettings { pub struct WorkspaceSettings {
/// A flag that indicates if Deno is enabled for the workspace.
pub enable: bool, pub enable: bool,
/// An option that points to a path string of the tsconfig file to apply to
/// code within the workspace.
pub config: Option<String>, pub config: Option<String>,
/// An option that points to a path string of the import map to apply to the
/// code within the workspace.
pub import_map: Option<String>, pub import_map: Option<String>,
/// Code lens specific settings for the workspace.
#[serde(default)] #[serde(default)]
pub code_lens: CodeLensSettings, pub code_lens: CodeLensSettings,
#[serde(default)] #[serde(default)]
/// Suggestion (auto-completion) settings for the workspace.
pub suggest: CompletionSettings, pub suggest: CompletionSettings,
/// A flag that indicates if linting is enabled for the workspace.
#[serde(default)] #[serde(default)]
pub lint: bool, pub lint: bool,
/// A flag that indicates if Dene should validate code against the unstable
/// APIs for the workspace.
#[serde(default)] #[serde(default)]
pub unstable: bool, pub unstable: bool,
} }
@ -113,15 +140,21 @@ impl WorkspaceSettings {
pub struct Config { pub struct Config {
pub client_capabilities: ClientCapabilities, pub client_capabilities: ClientCapabilities,
pub root_uri: Option<Url>, pub root_uri: Option<Url>,
pub settings: WorkspaceSettings, pub specifier_settings: HashMap<ModuleSpecifier, SpecifierSettings>,
pub workspace_settings: WorkspaceSettings,
} }
impl Config { impl Config {
pub fn update(&mut self, value: Value) -> LSPResult<()> { pub fn contains(&self, specifier: &ModuleSpecifier) -> bool {
let settings: WorkspaceSettings = serde_json::from_value(value) self.specifier_settings.contains_key(specifier)
.map_err(|err| LSPError::invalid_params(err.to_string()))?; }
self.settings = settings;
Ok(()) pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
if let Some(settings) = self.specifier_settings.get(specifier) {
settings.enable
} else {
self.workspace_settings.enable
}
} }
#[allow(clippy::redundant_closure_call)] #[allow(clippy::redundant_closure_call)]
@ -154,4 +187,68 @@ impl Config {
.unwrap_or(false); .unwrap_or(false);
} }
} }
pub fn update_specifier(
&mut self,
specifier: ModuleSpecifier,
value: Value,
) -> LSPResult<()> {
let settings: SpecifierSettings = serde_json::from_value(value)
.map_err(|err| LSPError::invalid_params(err.to_string()))?;
self.specifier_settings.insert(specifier, settings);
Ok(())
}
pub fn update_workspace(&mut self, value: Value) -> LSPResult<()> {
let settings: WorkspaceSettings = serde_json::from_value(value)
.map_err(|err| LSPError::invalid_params(err.to_string()))?;
self.workspace_settings = settings;
self.specifier_settings = HashMap::new();
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use deno_core::resolve_url;
use deno_core::serde_json::json;
#[test]
fn test_config_contains() {
let mut config = Config::default();
let specifier = resolve_url("https://deno.land/x/a.ts").unwrap();
assert!(!config.contains(&specifier));
config
.update_specifier(
specifier.clone(),
json!({
"enable": true
}),
)
.expect("could not update specifier");
assert!(config.contains(&specifier));
}
#[test]
fn test_config_specifier_enabled() {
let mut config = Config::default();
let specifier = resolve_url("file:///a.ts").unwrap();
assert!(!config.specifier_enabled(&specifier));
config
.update_workspace(json!({
"enable": true
}))
.expect("could not update");
assert!(config.specifier_enabled(&specifier));
config
.update_specifier(
specifier.clone(),
json!({
"enable": false
}),
)
.expect("could not update");
assert!(!config.specifier_enabled(&specifier));
}
} }

View file

@ -65,7 +65,7 @@ async fn publish_diagnostics(
// disabled, otherwise the client will not clear down previous // disabled, otherwise the client will not clear down previous
// diagnostics // diagnostics
let mut diagnostics: Vec<lsp::Diagnostic> = let mut diagnostics: Vec<lsp::Diagnostic> =
if snapshot.config.settings.lint { if snapshot.config.workspace_settings.lint {
collection collection
.diagnostics_for(&specifier, &DiagnosticSource::Lint) .diagnostics_for(&specifier, &DiagnosticSource::Lint)
.cloned() .cloned()
@ -73,7 +73,7 @@ async fn publish_diagnostics(
} else { } else {
vec![] vec![]
}; };
if snapshot.config.settings.enable { if snapshot.config.specifier_enabled(&specifier) {
diagnostics.extend( diagnostics.extend(
collection collection
.diagnostics_for(&specifier, &DiagnosticSource::TypeScript) .diagnostics_for(&specifier, &DiagnosticSource::TypeScript)
@ -98,12 +98,8 @@ async fn update_diagnostics(
snapshot: &language_server::StateSnapshot, snapshot: &language_server::StateSnapshot,
ts_server: &tsc::TsServer, ts_server: &tsc::TsServer,
) { ) {
let (enabled, lint_enabled) = {
let config = &snapshot.config;
(config.settings.enable, config.settings.lint)
};
let mark = snapshot.performance.mark("update_diagnostics"); let mark = snapshot.performance.mark("update_diagnostics");
let lint_enabled = snapshot.config.workspace_settings.lint;
let lint = async { let lint = async {
let collection = collection.clone(); let collection = collection.clone();
@ -128,62 +124,50 @@ async fn update_diagnostics(
}; };
let ts = async { let ts = async {
if enabled { let collection = collection.clone();
let collection = collection.clone(); let mark = snapshot.performance.mark("update_diagnostics_ts");
let mark = snapshot.performance.mark("update_diagnostics_ts"); let diagnostics =
let diagnostics = generate_ts_diagnostics( generate_ts_diagnostics(snapshot.clone(), collection.clone(), ts_server)
snapshot.clone(), .await
collection.clone(), .map_err(|err| {
ts_server, error!("Error generating TypeScript diagnostics: {}", err);
) err
.await })
.map_err(|err| { .unwrap_or_default();
error!("Error generating TypeScript diagnostics: {}", err); {
err let mut collection = collection.lock().unwrap();
}) for (specifier, version, diagnostics) in diagnostics {
.unwrap_or_default(); collection.set(
{ specifier,
let mut collection = collection.lock().unwrap(); DiagnosticSource::TypeScript,
for (specifier, version, diagnostics) in diagnostics { version,
collection.set( diagnostics,
specifier, );
DiagnosticSource::TypeScript,
version,
diagnostics,
);
}
} }
publish_diagnostics(client, collection, snapshot).await; }
snapshot.performance.measure(mark); publish_diagnostics(client, collection, snapshot).await;
}; snapshot.performance.measure(mark);
}; };
let deps = async { let deps = async {
if enabled { let collection = collection.clone();
let collection = collection.clone(); let mark = snapshot.performance.mark("update_diagnostics_deps");
let mark = snapshot.performance.mark("update_diagnostics_deps"); let diagnostics =
let diagnostics = generate_dependency_diagnostics(snapshot.clone(), collection.clone())
generate_dependency_diagnostics(snapshot.clone(), collection.clone()) .await
.await .map_err(|err| {
.map_err(|err| { error!("Error generating dependency diagnostics: {}", err);
error!("Error generating dependency diagnostics: {}", err); err
err })
}) .unwrap_or_default();
.unwrap_or_default(); {
{ let mut collection = collection.lock().unwrap();
let mut collection = collection.lock().unwrap(); for (specifier, version, diagnostics) in diagnostics {
for (specifier, version, diagnostics) in diagnostics { collection.set(specifier, DiagnosticSource::Deno, version, diagnostics);
collection.set(
specifier,
DiagnosticSource::Deno,
version,
diagnostics,
);
}
} }
publish_diagnostics(client, collection, snapshot).await; }
snapshot.performance.measure(mark); publish_diagnostics(client, collection, snapshot).await;
}; snapshot.performance.measure(mark);
}; };
tokio::join!(lint, ts, deps); tokio::join!(lint, ts, deps);
@ -534,11 +518,13 @@ async fn generate_ts_diagnostics(
{ {
let collection = collection.lock().unwrap(); let collection = collection.lock().unwrap();
for specifier in state_snapshot.documents.open_specifiers() { for specifier in state_snapshot.documents.open_specifiers() {
let version = state_snapshot.documents.version(specifier); if state_snapshot.config.specifier_enabled(specifier) {
let current_version = let version = state_snapshot.documents.version(specifier);
collection.get_version(specifier, &DiagnosticSource::TypeScript); let current_version =
if version != current_version { collection.get_version(specifier, &DiagnosticSource::TypeScript);
specifiers.push(specifier.clone()); if version != current_version {
specifiers.push(specifier.clone());
}
} }
} }
} }
@ -568,6 +554,9 @@ async fn generate_dependency_diagnostics(
let sources = &mut state_snapshot.sources; let sources = &mut state_snapshot.sources;
for specifier in state_snapshot.documents.open_specifiers() { for specifier in state_snapshot.documents.open_specifiers() {
if !state_snapshot.config.specifier_enabled(specifier) {
continue;
}
let version = state_snapshot.documents.version(specifier); let version = state_snapshot.documents.version(specifier);
let current_version = collection.lock().unwrap().get_version(specifier, &DiagnosticSource::Deno); let current_version = collection.lock().unwrap().get_version(specifier, &DiagnosticSource::Deno);
if version != current_version { if version != current_version {

View file

@ -44,6 +44,7 @@ use super::analysis::ResolvedDependency;
use super::capabilities; use super::capabilities;
use super::completions; use super::completions;
use super::config::Config; use super::config::Config;
use super::config::SETTINGS_SECTION;
use super::diagnostics; use super::diagnostics;
use super::diagnostics::DiagnosticSource; use super::diagnostics::DiagnosticSource;
use super::documents::DocumentCache; use super::documents::DocumentCache;
@ -78,6 +79,7 @@ pub struct StateSnapshot {
pub module_registries: registries::ModuleRegistry, pub module_registries: registries::ModuleRegistry,
pub performance: Performance, pub performance: Performance,
pub sources: Sources, pub sources: Sources,
pub url_map: urls::LspUrlMap,
} }
#[derive(Debug)] #[derive(Debug)]
@ -188,10 +190,6 @@ impl Inner {
} }
} }
fn enabled(&self) -> bool {
self.config.settings.enable
}
/// Searches assets, open documents and external sources for a line_index, /// Searches assets, open documents and external sources for a line_index,
/// which might be performed asynchronously, hydrating in memory caches for /// which might be performed asynchronously, hydrating in memory caches for
/// subsequent requests. /// subsequent requests.
@ -293,6 +291,7 @@ impl Inner {
module_registries: self.module_registries.clone(), module_registries: self.module_registries.clone(),
performance: self.performance.clone(), performance: self.performance.clone(),
sources: self.sources.clone(), sources: self.sources.clone(),
url_map: self.url_map.clone(),
} }
} }
@ -300,7 +299,10 @@ impl Inner {
let mark = self.performance.mark("update_import_map"); let mark = self.performance.mark("update_import_map");
let (maybe_import_map, maybe_root_uri) = { let (maybe_import_map, maybe_root_uri) = {
let config = &self.config; let config = &self.config;
(config.settings.import_map.clone(), config.root_uri.clone()) (
config.workspace_settings.import_map.clone(),
config.root_uri.clone(),
)
}; };
if let Some(import_map_str) = &maybe_import_map { if let Some(import_map_str) = &maybe_import_map {
info!("Updating import map from: \"{}\"", import_map_str); info!("Updating import map from: \"{}\"", import_map_str);
@ -345,7 +347,8 @@ impl Inner {
async fn update_registries(&mut self) -> Result<(), AnyError> { async fn update_registries(&mut self) -> Result<(), AnyError> {
let mark = self.performance.mark("update_registries"); let mark = self.performance.mark("update_registries");
for (registry, enabled) in self.config.settings.suggest.imports.hosts.iter() for (registry, enabled) in
self.config.workspace_settings.suggest.imports.hosts.iter()
{ {
if *enabled { if *enabled {
info!("Enabling auto complete registry for: {}", registry); info!("Enabling auto complete registry for: {}", registry);
@ -376,13 +379,16 @@ impl Inner {
})); }));
let (maybe_config, maybe_root_uri) = { let (maybe_config, maybe_root_uri) = {
let config = &self.config; let config = &self.config;
if config.settings.unstable { if config.workspace_settings.unstable {
let unstable_libs = json!({ let unstable_libs = json!({
"lib": ["deno.ns", "deno.window", "deno.unstable"] "lib": ["deno.ns", "deno.window", "deno.unstable"]
}); });
tsconfig.merge(&unstable_libs); tsconfig.merge(&unstable_libs);
} }
(config.settings.config.clone(), config.root_uri.clone()) (
config.workspace_settings.config.clone(),
config.root_uri.clone(),
)
}; };
if let Some(config_str) = &maybe_config { if let Some(config_str) = &maybe_config {
info!("Updating TypeScript configuration from: \"{}\"", config_str); info!("Updating TypeScript configuration from: \"{}\"", config_str);
@ -491,7 +497,7 @@ impl Inner {
let config = &mut self.config; let config = &mut self.config;
config.root_uri = params.root_uri; config.root_uri = params.root_uri;
if let Some(value) = params.initialization_options { if let Some(value) = params.initialization_options {
config.update(value)?; config.update_workspace(value)?;
} }
config.update_capabilities(&params.capabilities); config.update_capabilities(&params.capabilities);
} }
@ -573,13 +579,35 @@ impl Inner {
async fn did_open(&mut self, params: DidOpenTextDocumentParams) { async fn did_open(&mut self, params: DidOpenTextDocumentParams) {
let mark = self.performance.mark("did_open"); let mark = self.performance.mark("did_open");
let specifier = self.url_map.normalize_url(&params.text_document.uri);
// we only query the individual resource file if the client supports it
if self.config.client_capabilities.workspace_configuration
&& !self.config.contains(&specifier)
{
if let Ok(value) = self
.client
.configuration(vec![ConfigurationItem {
scope_uri: Some(params.text_document.uri.clone()),
section: Some(SETTINGS_SECTION.to_string()),
}])
.await
{
if let Err(err) = self
.config
.update_specifier(specifier.clone(), value[0].clone())
{
warn!("Error updating specifier configuration: {}", err);
}
}
}
if params.text_document.uri.scheme() == "deno" { if params.text_document.uri.scheme() == "deno" {
// we can ignore virtual text documents opening, as they don't need to // we can ignore virtual text documents opening, as they don't need to
// be tracked in memory, as they are static assets that won't change // be tracked in memory, as they are static assets that won't change
// already managed by the language service // already managed by the language service
return; return;
} }
let specifier = self.url_map.normalize_url(&params.text_document.uri);
self.documents.open( self.documents.open(
specifier.clone(), specifier.clone(),
params.text_document.version, params.text_document.version,
@ -635,56 +663,69 @@ impl Inner {
params: DidChangeConfigurationParams, params: DidChangeConfigurationParams,
) { ) {
let mark = self.performance.mark("did_change_configuration"); let mark = self.performance.mark("did_change_configuration");
let config = if self.config.client_capabilities.workspace_configuration {
self
.client
.configuration(vec![ConfigurationItem {
scope_uri: None,
section: Some("deno".to_string()),
}])
.await
.map(|vec| vec.get(0).cloned())
.unwrap_or_else(|err| {
error!("failed to fetch the extension settings {}", err);
None
})
} else {
params
.settings
.as_object()
.map(|settings| settings.get("deno"))
.flatten()
.cloned()
};
if let Some(config) = config { if self.config.client_capabilities.workspace_configuration {
if let Err(err) = self.config.update(config) { let specifiers: Vec<ModuleSpecifier> =
self.config.specifier_settings.keys().cloned().collect();
let mut snapshot = self.snapshot();
let mut config_items = specifiers
.iter()
.map(|s| ConfigurationItem {
scope_uri: Some(snapshot.url_map.normalize_specifier(s).unwrap()),
section: Some(SETTINGS_SECTION.to_string()),
})
.collect();
let mut items = vec![ConfigurationItem {
scope_uri: None,
section: Some(SETTINGS_SECTION.to_string()),
}];
items.append(&mut config_items);
if let Ok(configs) = self.client.configuration(items).await {
for (i, value) in configs.into_iter().enumerate() {
if let Err(err) = match i {
0 => self.config.update_workspace(value),
_ => self
.config
.update_specifier(specifiers[i - 1].clone(), value),
} {
error!("failed to update settings: {}", err);
}
}
}
} else if let Some(config) = params
.settings
.as_object()
.map(|settings| settings.get(SETTINGS_SECTION))
.flatten()
.cloned()
{
if let Err(err) = self.config.update_workspace(config) {
error!("failed to update settings: {}", err); error!("failed to update settings: {}", err);
} }
if let Err(err) = self.update_import_map().await {
self
.client
.show_message(MessageType::Warning, err.to_string())
.await;
}
if let Err(err) = self.update_registries().await {
self
.client
.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;
}
if let Err(err) = self.diagnostics_server.update() {
error!("{}", err);
}
} else {
error!("received empty extension settings from the client");
} }
if let Err(err) = self.update_import_map().await {
self
.client
.show_message(MessageType::Warning, err.to_string())
.await;
}
if let Err(err) = self.update_registries().await {
self
.client
.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;
}
if let Err(err) = self.diagnostics_server.update() {
error!("{}", err);
}
self.performance.measure(mark); self.performance.measure(mark);
} }
@ -719,14 +760,14 @@ impl Inner {
} }
async fn document_symbol( async fn document_symbol(
&self, &mut self,
params: DocumentSymbolParams, params: DocumentSymbolParams,
) -> LspResult<Option<DocumentSymbolResponse>> { ) -> LspResult<Option<DocumentSymbolResponse>> {
if !self.enabled() { let specifier = self.url_map.normalize_url(&params.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None); return Ok(None);
} }
let mark = self.performance.mark("document_symbol"); let mark = self.performance.mark("document_symbol");
let specifier = self.url_map.normalize_url(&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) {
@ -817,14 +858,15 @@ impl Inner {
} }
} }
async fn hover(&self, params: HoverParams) -> LspResult<Option<Hover>> { async fn hover(&mut self, params: HoverParams) -> LspResult<Option<Hover>> {
if !self.enabled() {
return Ok(None);
}
let mark = self.performance.mark("hover");
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document_position_params.text_document.uri); .normalize_url(&params.text_document_position_params.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None);
}
let mark = self.performance.mark("hover");
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
@ -860,12 +902,12 @@ impl Inner {
&mut self, &mut self,
params: CodeActionParams, params: CodeActionParams,
) -> LspResult<Option<CodeActionResponse>> { ) -> LspResult<Option<CodeActionResponse>> {
if !self.enabled() { let specifier = self.url_map.normalize_url(&params.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None); return Ok(None);
} }
let mark = self.performance.mark("code_action"); let mark = self.performance.mark("code_action");
let specifier = self.url_map.normalize_url(&params.text_document.uri);
let fixable_diagnostics: Vec<&Diagnostic> = params let fixable_diagnostics: Vec<&Diagnostic> = params
.context .context
.diagnostics .diagnostics
@ -1016,12 +1058,14 @@ impl Inner {
&mut self, &mut self,
params: CodeLensParams, params: CodeLensParams,
) -> LspResult<Option<Vec<CodeLens>>> { ) -> LspResult<Option<Vec<CodeLens>>> {
if !self.enabled() || !self.config.settings.enabled_code_lens() { let specifier = self.url_map.normalize_url(&params.text_document.uri);
if !self.config.specifier_enabled(&specifier)
|| !self.config.workspace_settings.enabled_code_lens()
{
return Ok(None); return Ok(None);
} }
let mark = self.performance.mark("code_lens"); let mark = self.performance.mark("code_lens");
let specifier = self.url_map.normalize_url(&params.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| {
@ -1037,7 +1081,7 @@ impl Inner {
let mut code_lenses = cl.borrow_mut(); let mut code_lenses = cl.borrow_mut();
// TSC Implementations Code Lens // TSC Implementations Code Lens
if self.config.settings.code_lens.implementations { if self.config.workspace_settings.code_lens.implementations {
let source = CodeLensSource::Implementations; let source = CodeLensSource::Implementations;
match i.kind { match i.kind {
tsc::ScriptElementKind::InterfaceElement => { tsc::ScriptElementKind::InterfaceElement => {
@ -1061,7 +1105,7 @@ impl Inner {
} }
// TSC References Code Lens // TSC References Code Lens
if self.config.settings.code_lens.references { if self.config.workspace_settings.code_lens.references {
let source = CodeLensSource::References; let source = CodeLensSource::References;
if let Some(parent) = &mp { if let Some(parent) = &mp {
if parent.kind == tsc::ScriptElementKind::EnumElement { if parent.kind == tsc::ScriptElementKind::EnumElement {
@ -1070,7 +1114,12 @@ impl Inner {
} }
match i.kind { match i.kind {
tsc::ScriptElementKind::FunctionElement => { tsc::ScriptElementKind::FunctionElement => {
if self.config.settings.code_lens.references_all_functions { if self
.config
.workspace_settings
.code_lens
.references_all_functions
{
code_lenses.push(i.to_code_lens( code_lenses.push(i.to_code_lens(
&line_index, &line_index,
&specifier, &specifier,
@ -1317,16 +1366,17 @@ impl Inner {
} }
async fn document_highlight( async fn document_highlight(
&self, &mut self,
params: DocumentHighlightParams, params: DocumentHighlightParams,
) -> LspResult<Option<Vec<DocumentHighlight>>> { ) -> LspResult<Option<Vec<DocumentHighlight>>> {
if !self.enabled() {
return Ok(None);
}
let mark = self.performance.mark("document_highlight");
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document_position_params.text_document.uri); .normalize_url(&params.text_document_position_params.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None);
}
let mark = self.performance.mark("document_highlight");
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
@ -1369,13 +1419,13 @@ impl Inner {
&mut self, &mut self,
params: ReferenceParams, params: ReferenceParams,
) -> LspResult<Option<Vec<Location>>> { ) -> LspResult<Option<Vec<Location>>> {
if !self.enabled() {
return Ok(None);
}
let mark = self.performance.mark("references");
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document_position.text_document.uri); .normalize_url(&params.text_document_position.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None);
}
let mark = self.performance.mark("references");
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
@ -1424,13 +1474,13 @@ impl Inner {
&mut self, &mut self,
params: GotoDefinitionParams, params: GotoDefinitionParams,
) -> LspResult<Option<GotoDefinitionResponse>> { ) -> LspResult<Option<GotoDefinitionResponse>> {
if !self.enabled() {
return Ok(None);
}
let mark = self.performance.mark("goto_definition");
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document_position_params.text_document.uri); .normalize_url(&params.text_document_position_params.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None);
}
let mark = self.performance.mark("goto_definition");
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
@ -1464,16 +1514,16 @@ impl Inner {
} }
async fn completion( async fn completion(
&self, &mut self,
params: CompletionParams, params: CompletionParams,
) -> LspResult<Option<CompletionResponse>> { ) -> LspResult<Option<CompletionResponse>> {
if !self.enabled() {
return Ok(None);
}
let mark = self.performance.mark("completion");
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document_position.text_document.uri); .normalize_url(&params.text_document_position.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None);
}
let mark = self.performance.mark("completion");
// Import specifiers are something wholly internal to Deno, so for // Import specifiers are something wholly internal to Deno, so for
// completions, we will use internal logic and if there are completions // completions, we will use internal logic and if there are completions
// for imports, we will return those and not send a message into tsc, where // for imports, we will return those and not send a message into tsc, where
@ -1526,7 +1576,7 @@ impl Inner {
if let Some(completions) = maybe_completion_info { if let Some(completions) = maybe_completion_info {
let results = completions.as_completion_response( let results = completions.as_completion_response(
&line_index, &line_index,
&self.config.settings.suggest, &self.config.workspace_settings.suggest,
&specifier, &specifier,
position, position,
); );
@ -1583,13 +1633,13 @@ impl Inner {
&mut self, &mut self,
params: GotoImplementationParams, params: GotoImplementationParams,
) -> LspResult<Option<GotoImplementationResponse>> { ) -> LspResult<Option<GotoImplementationResponse>> {
if !self.enabled() {
return Ok(None);
}
let mark = self.performance.mark("goto_implementation");
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document_position_params.text_document.uri); .normalize_url(&params.text_document_position_params.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None);
}
let mark = self.performance.mark("goto_implementation");
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
@ -1630,14 +1680,14 @@ impl Inner {
} }
async fn folding_range( async fn folding_range(
&self, &mut self,
params: FoldingRangeParams, params: FoldingRangeParams,
) -> LspResult<Option<Vec<FoldingRange>>> { ) -> LspResult<Option<Vec<FoldingRange>>> {
if !self.enabled() { let specifier = self.url_map.normalize_url(&params.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None); return Ok(None);
} }
let mark = self.performance.mark("folding_range"); let mark = self.performance.mark("folding_range");
let specifier = self.url_map.normalize_url(&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) {
@ -1690,11 +1740,11 @@ impl Inner {
&mut self, &mut self,
params: CallHierarchyIncomingCallsParams, params: CallHierarchyIncomingCallsParams,
) -> LspResult<Option<Vec<CallHierarchyIncomingCall>>> { ) -> LspResult<Option<Vec<CallHierarchyIncomingCall>>> {
if !self.enabled() { let specifier = self.url_map.normalize_url(&params.item.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None); return Ok(None);
} }
let mark = self.performance.mark("incoming_calls"); let mark = self.performance.mark("incoming_calls");
let specifier = self.url_map.normalize_url(&params.item.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) {
@ -1744,11 +1794,11 @@ impl Inner {
&mut self, &mut self,
params: CallHierarchyOutgoingCallsParams, params: CallHierarchyOutgoingCallsParams,
) -> LspResult<Option<Vec<CallHierarchyOutgoingCall>>> { ) -> LspResult<Option<Vec<CallHierarchyOutgoingCall>>> {
if !self.enabled() { let specifier = self.url_map.normalize_url(&params.item.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None); return Ok(None);
} }
let mark = self.performance.mark("outgoing_calls"); let mark = self.performance.mark("outgoing_calls");
let specifier = self.url_map.normalize_url(&params.item.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) {
@ -1799,13 +1849,13 @@ impl Inner {
&mut self, &mut self,
params: CallHierarchyPrepareParams, params: CallHierarchyPrepareParams,
) -> LspResult<Option<Vec<CallHierarchyItem>>> { ) -> LspResult<Option<Vec<CallHierarchyItem>>> {
if !self.enabled() {
return Ok(None);
}
let mark = self.performance.mark("prepare_call_hierarchy");
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document_position_params.text_document.uri); .normalize_url(&params.text_document_position_params.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None);
}
let mark = self.performance.mark("prepare_call_hierarchy");
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) {
@ -1876,13 +1926,13 @@ impl Inner {
&mut self, &mut self,
params: RenameParams, params: RenameParams,
) -> LspResult<Option<WorkspaceEdit>> { ) -> LspResult<Option<WorkspaceEdit>> {
if !self.enabled() {
return Ok(None);
}
let mark = self.performance.mark("rename");
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document_position.text_document.uri); .normalize_url(&params.text_document_position.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None);
}
let mark = self.performance.mark("rename");
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) {
@ -1963,14 +2013,14 @@ impl Inner {
} }
async fn selection_range( async fn selection_range(
&self, &mut self,
params: SelectionRangeParams, params: SelectionRangeParams,
) -> LspResult<Option<Vec<SelectionRange>>> { ) -> LspResult<Option<Vec<SelectionRange>>> {
if !self.enabled() { let specifier = self.url_map.normalize_url(&params.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None); return Ok(None);
} }
let mark = self.performance.mark("selection_range"); let mark = self.performance.mark("selection_range");
let specifier = self.url_map.normalize_url(&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) {
@ -2005,14 +2055,14 @@ impl Inner {
} }
async fn semantic_tokens_full( async fn semantic_tokens_full(
&self, &mut self,
params: SemanticTokensParams, params: SemanticTokensParams,
) -> LspResult<Option<SemanticTokensResult>> { ) -> LspResult<Option<SemanticTokensResult>> {
if !self.enabled() { let specifier = self.url_map.normalize_url(&params.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None); return Ok(None);
} }
let mark = self.performance.mark("semantic_tokens_full"); let mark = self.performance.mark("semantic_tokens_full");
let specifier = self.url_map.normalize_url(&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) {
@ -2052,14 +2102,14 @@ impl Inner {
} }
async fn semantic_tokens_range( async fn semantic_tokens_range(
&self, &mut self,
params: SemanticTokensRangeParams, params: SemanticTokensRangeParams,
) -> LspResult<Option<SemanticTokensRangeResult>> { ) -> LspResult<Option<SemanticTokensRangeResult>> {
if !self.enabled() { let specifier = self.url_map.normalize_url(&params.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None); return Ok(None);
} }
let mark = self.performance.mark("semantic_tokens_range"); let mark = self.performance.mark("semantic_tokens_range");
let specifier = self.url_map.normalize_url(&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) {
@ -2098,16 +2148,16 @@ impl Inner {
} }
async fn signature_help( async fn signature_help(
&self, &mut self,
params: SignatureHelpParams, params: SignatureHelpParams,
) -> LspResult<Option<SignatureHelp>> { ) -> LspResult<Option<SignatureHelp>> {
if !self.enabled() {
return Ok(None);
}
let mark = self.performance.mark("signature_help");
let specifier = self let specifier = self
.url_map .url_map
.normalize_url(&params.text_document_position_params.text_document.uri); .normalize_url(&params.text_document_position_params.text_document.uri);
if !self.config.specifier_enabled(&specifier) {
return Ok(None);
}
let mark = self.performance.mark("signature_help");
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

View file

@ -54,7 +54,7 @@ fn hash_data_specifier(specifier: &ModuleSpecifier) -> String {
/// A bi-directional map of URLs sent to the LSP client and internal module /// 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 /// specifiers. We need to map internal specifiers into `deno:` schema URLs
/// to allow the Deno language server to manage these as virtual documents. /// to allow the Deno language server to manage these as virtual documents.
#[derive(Debug, Default)] #[derive(Debug, Default, Clone)]
pub struct LspUrlMap { pub struct LspUrlMap {
specifier_to_url: HashMap<ModuleSpecifier, Url>, specifier_to_url: HashMap<ModuleSpecifier, Url>,
url_to_specifier: HashMap<Url, ModuleSpecifier>, url_to_specifier: HashMap<Url, ModuleSpecifier>,

View file

@ -114,3 +114,16 @@ compiler. Its main purpose is to ensure that TypeScript and JavaScript can run
under Deno. The secondary ability to do TypeScript and JavaScript emitting via under Deno. The secondary ability to do TypeScript and JavaScript emitting via
the runtime API `Deno.emit()` is intended to be simple and straight forward and the runtime API `Deno.emit()` is intended to be simple and straight forward and
support a certain set of use cases. support a certain set of use cases.
### How do I combine Deno code with non-Deno code in my IDE?
The Deno language server supports the ability to have a "per-resource"
configuration of enabling Deno or not. This also requires a client IDE to
support this ability. For Visual Studio Code the official
[Deno extension](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno)
supports the vscode concept of
[multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces).
This means you just need to add folders to the workspace and set the
`deno.enable` setting as required on each folder.
For other IDEs, the client extensions needs to support the similar IDE concepts.