mirror of
https://github.com/denoland/deno.git
synced 2025-08-02 18:12:39 +00:00
Fixes #10815
This commit is contained in:
parent
9ae8dbf173
commit
473713c621
8 changed files with 529 additions and 169 deletions
|
@ -178,33 +178,7 @@ pub fn map_content_type(
|
||||||
if let Some(content_type) = maybe_content_type {
|
if let Some(content_type) = maybe_content_type {
|
||||||
let mut content_types = content_type.split(';');
|
let mut content_types = content_type.split(';');
|
||||||
let content_type = content_types.next().unwrap();
|
let content_type = content_types.next().unwrap();
|
||||||
let media_type = match content_type.trim().to_lowercase().as_ref() {
|
let media_type = MediaType::from_content_type(specifier, content_type);
|
||||||
"application/typescript"
|
|
||||||
| "text/typescript"
|
|
||||||
| "video/vnd.dlna.mpeg-tts"
|
|
||||||
| "video/mp2t"
|
|
||||||
| "application/x-typescript" => {
|
|
||||||
map_js_like_extension(specifier, MediaType::TypeScript)
|
|
||||||
}
|
|
||||||
"application/javascript"
|
|
||||||
| "text/javascript"
|
|
||||||
| "application/ecmascript"
|
|
||||||
| "text/ecmascript"
|
|
||||||
| "application/x-javascript"
|
|
||||||
| "application/node" => {
|
|
||||||
map_js_like_extension(specifier, MediaType::JavaScript)
|
|
||||||
}
|
|
||||||
"text/jsx" => MediaType::Jsx,
|
|
||||||
"text/tsx" => MediaType::Tsx,
|
|
||||||
"application/json" | "text/json" => MediaType::Json,
|
|
||||||
"application/wasm" => MediaType::Wasm,
|
|
||||||
// Handle plain and possibly webassembly
|
|
||||||
"text/plain" | "application/octet-stream" => MediaType::from(specifier),
|
|
||||||
_ => {
|
|
||||||
debug!("unknown content type: {}", content_type);
|
|
||||||
MediaType::Unknown
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let charset = content_types
|
let charset = content_types
|
||||||
.map(str::trim)
|
.map(str::trim)
|
||||||
.find_map(|s| s.strip_prefix("charset="))
|
.find_map(|s| s.strip_prefix("charset="))
|
||||||
|
@ -216,55 +190,6 @@ pub fn map_content_type(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to augment media types by using the path part of a module specifier to
|
|
||||||
/// resolve to a more accurate media type.
|
|
||||||
fn map_js_like_extension(
|
|
||||||
specifier: &ModuleSpecifier,
|
|
||||||
default: MediaType,
|
|
||||||
) -> MediaType {
|
|
||||||
let path = if specifier.scheme() == "file" {
|
|
||||||
if let Ok(path) = specifier.to_file_path() {
|
|
||||||
path
|
|
||||||
} else {
|
|
||||||
PathBuf::from(specifier.path())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PathBuf::from(specifier.path())
|
|
||||||
};
|
|
||||||
match path.extension() {
|
|
||||||
None => default,
|
|
||||||
Some(os_str) => match os_str.to_str() {
|
|
||||||
None => default,
|
|
||||||
Some("jsx") => MediaType::Jsx,
|
|
||||||
Some("tsx") => MediaType::Tsx,
|
|
||||||
// Because DTS files do not have a separate media type, or a unique
|
|
||||||
// extension, we have to "guess" at those things that we consider that
|
|
||||||
// look like TypeScript, and end with `.d.ts` are DTS files.
|
|
||||||
Some("ts") => {
|
|
||||||
if default == MediaType::TypeScript {
|
|
||||||
match path.file_stem() {
|
|
||||||
None => default,
|
|
||||||
Some(os_str) => {
|
|
||||||
if let Some(file_stem) = os_str.to_str() {
|
|
||||||
if file_stem.ends_with(".d") {
|
|
||||||
MediaType::Dts
|
|
||||||
} else {
|
|
||||||
default
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(_) => default,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove shebangs from the start of source code strings
|
/// Remove shebangs from the start of source code strings
|
||||||
fn strip_shebang(mut value: String) -> String {
|
fn strip_shebang(mut value: String) -> String {
|
||||||
if value.starts_with("#!") {
|
if value.starts_with("#!") {
|
||||||
|
|
|
@ -559,6 +559,7 @@ mod tests {
|
||||||
use crate::http_cache::HttpCache;
|
use crate::http_cache::HttpCache;
|
||||||
use crate::lsp::analysis;
|
use crate::lsp::analysis;
|
||||||
use crate::lsp::documents::DocumentCache;
|
use crate::lsp::documents::DocumentCache;
|
||||||
|
use crate::lsp::documents::LanguageId;
|
||||||
use crate::lsp::sources::Sources;
|
use crate::lsp::sources::Sources;
|
||||||
use crate::media_type::MediaType;
|
use crate::media_type::MediaType;
|
||||||
use deno_core::resolve_url;
|
use deno_core::resolve_url;
|
||||||
|
@ -567,15 +568,15 @@ mod tests {
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
fn mock_state_snapshot(
|
fn mock_state_snapshot(
|
||||||
fixtures: &[(&str, &str, i32)],
|
fixtures: &[(&str, &str, i32, LanguageId)],
|
||||||
source_fixtures: &[(&str, &str)],
|
source_fixtures: &[(&str, &str)],
|
||||||
location: &Path,
|
location: &Path,
|
||||||
) -> language_server::StateSnapshot {
|
) -> language_server::StateSnapshot {
|
||||||
let mut documents = DocumentCache::default();
|
let mut documents = DocumentCache::default();
|
||||||
for (specifier, source, version) in fixtures {
|
for (specifier, source, version, language_id) in fixtures {
|
||||||
let specifier =
|
let specifier =
|
||||||
resolve_url(specifier).expect("failed to create specifier");
|
resolve_url(specifier).expect("failed to create specifier");
|
||||||
documents.open(specifier.clone(), *version, source);
|
documents.open(specifier.clone(), *version, language_id.clone(), source);
|
||||||
let media_type = MediaType::from(&specifier);
|
let media_type = MediaType::from(&specifier);
|
||||||
let parsed_module =
|
let parsed_module =
|
||||||
analysis::parse_module(&specifier, source, &media_type).unwrap();
|
analysis::parse_module(&specifier, source, &media_type).unwrap();
|
||||||
|
@ -608,7 +609,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
documents: &[(&str, &str, i32)],
|
documents: &[(&str, &str, i32, LanguageId)],
|
||||||
sources: &[(&str, &str)],
|
sources: &[(&str, &str)],
|
||||||
) -> language_server::StateSnapshot {
|
) -> language_server::StateSnapshot {
|
||||||
let temp_dir = TempDir::new().expect("could not create temp dir");
|
let temp_dir = TempDir::new().expect("could not create temp dir");
|
||||||
|
@ -885,8 +886,13 @@ mod tests {
|
||||||
};
|
};
|
||||||
let state_snapshot = setup(
|
let state_snapshot = setup(
|
||||||
&[
|
&[
|
||||||
("file:///a/b/c.ts", "import * as d from \"h\"", 1),
|
(
|
||||||
("file:///a/c.ts", r#""#, 1),
|
"file:///a/b/c.ts",
|
||||||
|
"import * as d from \"h\"",
|
||||||
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
|
),
|
||||||
|
("file:///a/c.ts", r#""#, 1, LanguageId::TypeScript),
|
||||||
],
|
],
|
||||||
&[("https://deno.land/x/a/b/c.ts", "console.log(1);\n")],
|
&[("https://deno.land/x/a/b/c.ts", "console.log(1);\n")],
|
||||||
);
|
);
|
||||||
|
|
|
@ -222,17 +222,6 @@ impl<'a> From<&'a diagnostics::Position> for lsp::Position {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if diagnostics can be generated for the provided media type.
|
|
||||||
pub fn is_diagnosable(media_type: MediaType) -> bool {
|
|
||||||
matches!(
|
|
||||||
media_type,
|
|
||||||
MediaType::TypeScript
|
|
||||||
| MediaType::JavaScript
|
|
||||||
| MediaType::Tsx
|
|
||||||
| MediaType::Jsx
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_diagnostic_message(diagnostic: &diagnostics::Diagnostic) -> String {
|
fn get_diagnostic_message(diagnostic: &diagnostics::Diagnostic) -> String {
|
||||||
if let Some(message) = diagnostic.message_text.clone() {
|
if let Some(message) = diagnostic.message_text.clone() {
|
||||||
message
|
message
|
||||||
|
@ -322,13 +311,16 @@ async fn generate_lint_diagnostics(
|
||||||
let mut diagnostics_vec = Vec::new();
|
let mut diagnostics_vec = Vec::new();
|
||||||
if workspace_settings.lint {
|
if workspace_settings.lint {
|
||||||
for specifier in documents.open_specifiers() {
|
for specifier in documents.open_specifiers() {
|
||||||
|
if !documents.is_diagnosable(specifier) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let version = documents.version(specifier);
|
let version = documents.version(specifier);
|
||||||
let current_version = collection
|
let current_version = collection
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.get_version(specifier, &DiagnosticSource::DenoLint);
|
.get_version(specifier, &DiagnosticSource::DenoLint);
|
||||||
let media_type = MediaType::from(specifier);
|
let media_type = MediaType::from(specifier);
|
||||||
if version != current_version && is_diagnosable(media_type) {
|
if version != current_version {
|
||||||
if let Ok(Some(source_code)) = documents.content(specifier) {
|
if let Ok(Some(source_code)) = documents.content(specifier) {
|
||||||
if let Ok(references) = analysis::get_lint_references(
|
if let Ok(references) = analysis::get_lint_references(
|
||||||
specifier,
|
specifier,
|
||||||
|
@ -366,12 +358,15 @@ async fn generate_ts_diagnostics(
|
||||||
.open_specifiers()
|
.open_specifiers()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|&s| {
|
.filter_map(|&s| {
|
||||||
let version = snapshot.documents.version(s);
|
if snapshot.documents.is_diagnosable(s) {
|
||||||
let current_version =
|
let version = snapshot.documents.version(s);
|
||||||
collection.get_version(s, &DiagnosticSource::TypeScript);
|
let current_version =
|
||||||
let media_type = MediaType::from(s);
|
collection.get_version(s, &DiagnosticSource::TypeScript);
|
||||||
if version != current_version && is_diagnosable(media_type) {
|
if version != current_version {
|
||||||
Some(s.clone())
|
Some(s.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
use super::analysis;
|
use super::analysis;
|
||||||
use super::text::LineIndex;
|
use super::text::LineIndex;
|
||||||
|
|
||||||
|
use crate::media_type::MediaType;
|
||||||
|
|
||||||
|
use deno_core::error::anyhow;
|
||||||
use deno_core::error::custom_error;
|
use deno_core::error::custom_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::error::Context;
|
use deno_core::error::Context;
|
||||||
|
@ -10,6 +13,37 @@ use deno_core::ModuleSpecifier;
|
||||||
use lspower::lsp::TextDocumentContentChangeEvent;
|
use lspower::lsp::TextDocumentContentChangeEvent;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// A representation of the language id sent from the LSP client, which is used
|
||||||
|
/// to determine how the document is handled within the language server.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum LanguageId {
|
||||||
|
JavaScript,
|
||||||
|
Jsx,
|
||||||
|
TypeScript,
|
||||||
|
Tsx,
|
||||||
|
Json,
|
||||||
|
JsonC,
|
||||||
|
Markdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for LanguageId {
|
||||||
|
type Err = AnyError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, AnyError> {
|
||||||
|
match s {
|
||||||
|
"javascript" => Ok(Self::JavaScript),
|
||||||
|
"javascriptreact" => Ok(Self::Jsx),
|
||||||
|
"typescript" => Ok(Self::TypeScript),
|
||||||
|
"typescriptreact" => Ok(Self::Tsx),
|
||||||
|
"json" => Ok(Self::Json),
|
||||||
|
"jsonc" => Ok(Self::JsonC),
|
||||||
|
"markdown" => Ok(Self::Markdown),
|
||||||
|
_ => Err(anyhow!("Unsupported language id: {}", s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
enum IndexValid {
|
enum IndexValid {
|
||||||
|
@ -29,6 +63,7 @@ impl IndexValid {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DocumentData {
|
pub struct DocumentData {
|
||||||
bytes: Option<Vec<u8>>,
|
bytes: Option<Vec<u8>>,
|
||||||
|
language_id: LanguageId,
|
||||||
line_index: Option<LineIndex>,
|
line_index: Option<LineIndex>,
|
||||||
specifier: ModuleSpecifier,
|
specifier: ModuleSpecifier,
|
||||||
dependencies: Option<HashMap<String, analysis::Dependency>>,
|
dependencies: Option<HashMap<String, analysis::Dependency>>,
|
||||||
|
@ -36,9 +71,15 @@ pub struct DocumentData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocumentData {
|
impl DocumentData {
|
||||||
pub fn new(specifier: ModuleSpecifier, version: i32, source: &str) -> Self {
|
pub fn new(
|
||||||
|
specifier: ModuleSpecifier,
|
||||||
|
version: i32,
|
||||||
|
language_id: LanguageId,
|
||||||
|
source: &str,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bytes: Some(source.as_bytes().to_owned()),
|
bytes: Some(source.as_bytes().to_owned()),
|
||||||
|
language_id,
|
||||||
line_index: Some(LineIndex::new(source)),
|
line_index: Some(LineIndex::new(source)),
|
||||||
specifier,
|
specifier,
|
||||||
dependencies: None,
|
dependencies: None,
|
||||||
|
@ -150,6 +191,39 @@ impl DocumentCache {
|
||||||
doc.dependencies.clone()
|
doc.dependencies.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines if the specifier should be processed for diagnostics and other
|
||||||
|
/// related language server features.
|
||||||
|
pub fn is_diagnosable(&self, specifier: &ModuleSpecifier) -> bool {
|
||||||
|
if specifier.scheme() != "file" {
|
||||||
|
// otherwise we look at the media type for the specifier.
|
||||||
|
matches!(
|
||||||
|
MediaType::from(specifier),
|
||||||
|
MediaType::JavaScript
|
||||||
|
| MediaType::Jsx
|
||||||
|
| MediaType::TypeScript
|
||||||
|
| MediaType::Tsx
|
||||||
|
| MediaType::Dts
|
||||||
|
)
|
||||||
|
} else if let Some(doc_data) = self.docs.get(specifier) {
|
||||||
|
// if the document is in the document cache, then use the client provided
|
||||||
|
// language id to determine if the specifier is diagnosable.
|
||||||
|
matches!(
|
||||||
|
doc_data.language_id,
|
||||||
|
LanguageId::JavaScript
|
||||||
|
| LanguageId::Jsx
|
||||||
|
| LanguageId::TypeScript
|
||||||
|
| LanguageId::Tsx
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the specifier can be processed for formatting.
|
||||||
|
pub fn is_formattable(&self, specifier: &ModuleSpecifier) -> bool {
|
||||||
|
self.docs.contains_key(specifier)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.docs.len()
|
self.docs.len()
|
||||||
}
|
}
|
||||||
|
@ -159,10 +233,16 @@ impl DocumentCache {
|
||||||
doc.line_index.clone()
|
doc.line_index.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open(&mut self, specifier: ModuleSpecifier, version: i32, text: &str) {
|
pub fn open(
|
||||||
|
&mut self,
|
||||||
|
specifier: ModuleSpecifier,
|
||||||
|
version: i32,
|
||||||
|
language_id: LanguageId,
|
||||||
|
source: &str,
|
||||||
|
) {
|
||||||
self.docs.insert(
|
self.docs.insert(
|
||||||
specifier.clone(),
|
specifier.clone(),
|
||||||
DocumentData::new(specifier, version, text),
|
DocumentData::new(specifier, version, language_id, source),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +299,12 @@ mod tests {
|
||||||
let mut document_cache = DocumentCache::default();
|
let mut document_cache = DocumentCache::default();
|
||||||
let specifier = resolve_url("file:///a/b.ts").unwrap();
|
let specifier = resolve_url("file:///a/b.ts").unwrap();
|
||||||
let missing_specifier = resolve_url("file:///a/c.ts").unwrap();
|
let missing_specifier = resolve_url("file:///a/c.ts").unwrap();
|
||||||
document_cache.open(specifier.clone(), 1, "console.log(\"Hello Deno\");\n");
|
document_cache.open(
|
||||||
|
specifier.clone(),
|
||||||
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
|
"console.log(\"Hello Deno\");\n",
|
||||||
|
);
|
||||||
assert!(document_cache.contains_key(&specifier));
|
assert!(document_cache.contains_key(&specifier));
|
||||||
assert!(!document_cache.contains_key(&missing_specifier));
|
assert!(!document_cache.contains_key(&missing_specifier));
|
||||||
}
|
}
|
||||||
|
@ -228,7 +313,12 @@ mod tests {
|
||||||
fn test_document_cache_change() {
|
fn test_document_cache_change() {
|
||||||
let mut document_cache = DocumentCache::default();
|
let mut document_cache = DocumentCache::default();
|
||||||
let specifier = resolve_url("file:///a/b.ts").unwrap();
|
let specifier = resolve_url("file:///a/b.ts").unwrap();
|
||||||
document_cache.open(specifier.clone(), 1, "console.log(\"Hello deno\");\n");
|
document_cache.open(
|
||||||
|
specifier.clone(),
|
||||||
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
|
"console.log(\"Hello deno\");\n",
|
||||||
|
);
|
||||||
document_cache
|
document_cache
|
||||||
.change(
|
.change(
|
||||||
&specifier,
|
&specifier,
|
||||||
|
@ -259,7 +349,12 @@ mod tests {
|
||||||
fn test_document_cache_change_utf16() {
|
fn test_document_cache_change_utf16() {
|
||||||
let mut document_cache = DocumentCache::default();
|
let mut document_cache = DocumentCache::default();
|
||||||
let specifier = resolve_url("file:///a/b.ts").unwrap();
|
let specifier = resolve_url("file:///a/b.ts").unwrap();
|
||||||
document_cache.open(specifier.clone(), 1, "console.log(\"Hello 🦕\");\n");
|
document_cache.open(
|
||||||
|
specifier.clone(),
|
||||||
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
|
"console.log(\"Hello 🦕\");\n",
|
||||||
|
);
|
||||||
document_cache
|
document_cache
|
||||||
.change(
|
.change(
|
||||||
&specifier,
|
&specifier,
|
||||||
|
@ -285,4 +380,25 @@ mod tests {
|
||||||
.expect("failed to get content");
|
.expect("failed to get content");
|
||||||
assert_eq!(actual, Some("console.log(\"Hello Deno\");\n".to_string()));
|
assert_eq!(actual, Some("console.log(\"Hello Deno\");\n".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_diagnosable() {
|
||||||
|
let mut document_cache = DocumentCache::default();
|
||||||
|
let specifier = resolve_url("file:///a/file.ts").unwrap();
|
||||||
|
assert!(!document_cache.is_diagnosable(&specifier));
|
||||||
|
document_cache.open(
|
||||||
|
specifier.clone(),
|
||||||
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
|
"console.log(\"hello world\");\n",
|
||||||
|
);
|
||||||
|
assert!(document_cache.is_diagnosable(&specifier));
|
||||||
|
let specifier =
|
||||||
|
resolve_url("asset:///lib.es2015.symbol.wellknown.d.ts").unwrap();
|
||||||
|
assert!(document_cache.is_diagnosable(&specifier));
|
||||||
|
let specifier = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
|
||||||
|
assert!(document_cache.is_diagnosable(&specifier));
|
||||||
|
let specifier = resolve_url("data:application/json;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
|
||||||
|
assert!(!document_cache.is_diagnosable(&specifier));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ 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;
|
||||||
|
use super::documents::LanguageId;
|
||||||
use super::lsp_custom;
|
use super::lsp_custom;
|
||||||
use super::performance::Performance;
|
use super::performance::Performance;
|
||||||
use super::registries;
|
use super::registries;
|
||||||
|
@ -59,7 +60,6 @@ use crate::config_file::TsConfig;
|
||||||
use crate::deno_dir;
|
use crate::deno_dir;
|
||||||
use crate::import_map::ImportMap;
|
use crate::import_map::ImportMap;
|
||||||
use crate::logger;
|
use crate::logger;
|
||||||
use crate::lsp::diagnostics::is_diagnosable;
|
|
||||||
use crate::media_type::MediaType;
|
use crate::media_type::MediaType;
|
||||||
use crate::tools::fmt::format_file;
|
use crate::tools::fmt::format_file;
|
||||||
use crate::tools::fmt::get_typescript_config;
|
use crate::tools::fmt::get_typescript_config;
|
||||||
|
@ -611,17 +611,27 @@ impl Inner {
|
||||||
// already managed by the language service
|
// already managed by the language service
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let language_id = match params.text_document.language_id.parse() {
|
||||||
|
Ok(language_id) => language_id,
|
||||||
|
Err(err) => {
|
||||||
|
error!("{}", err);
|
||||||
|
LanguageId::TypeScript
|
||||||
|
}
|
||||||
|
};
|
||||||
self.documents.open(
|
self.documents.open(
|
||||||
specifier.clone(),
|
specifier.clone(),
|
||||||
params.text_document.version,
|
params.text_document.version,
|
||||||
|
language_id,
|
||||||
¶ms.text_document.text,
|
¶ms.text_document.text,
|
||||||
);
|
);
|
||||||
self.analyze_dependencies(&specifier, ¶ms.text_document.text);
|
|
||||||
self.performance.measure(mark);
|
|
||||||
|
|
||||||
if let Err(err) = self.diagnostics_server.update() {
|
if self.documents.is_diagnosable(&specifier) {
|
||||||
error!("{}", err);
|
self.analyze_dependencies(&specifier, ¶ms.text_document.text);
|
||||||
|
if let Err(err) = self.diagnostics_server.update() {
|
||||||
|
error!("{}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
self.performance.measure(mark);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn did_change(&mut self, params: DidChangeTextDocumentParams) {
|
async fn did_change(&mut self, params: DidChangeTextDocumentParams) {
|
||||||
|
@ -632,15 +642,18 @@ impl Inner {
|
||||||
params.text_document.version,
|
params.text_document.version,
|
||||||
params.content_changes,
|
params.content_changes,
|
||||||
) {
|
) {
|
||||||
Ok(Some(source)) => self.analyze_dependencies(&specifier, &source),
|
Ok(Some(source)) => {
|
||||||
|
if self.documents.is_diagnosable(&specifier) {
|
||||||
|
self.analyze_dependencies(&specifier, &source);
|
||||||
|
if let Err(err) = self.diagnostics_server.update() {
|
||||||
|
error!("{}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(_) => error!("No content returned from change."),
|
Ok(_) => error!("No content returned from change."),
|
||||||
Err(err) => error!("{}", err),
|
Err(err) => error!("{}", err),
|
||||||
}
|
}
|
||||||
self.performance.measure(mark);
|
self.performance.measure(mark);
|
||||||
|
|
||||||
if let Err(err) = self.diagnostics_server.update() {
|
|
||||||
error!("{}", err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn did_close(&mut self, params: DidCloseTextDocumentParams) {
|
async fn did_close(&mut self, params: DidCloseTextDocumentParams) {
|
||||||
|
@ -655,10 +668,12 @@ impl Inner {
|
||||||
self.documents.close(&specifier);
|
self.documents.close(&specifier);
|
||||||
self.navigation_trees.remove(&specifier);
|
self.navigation_trees.remove(&specifier);
|
||||||
|
|
||||||
self.performance.measure(mark);
|
if self.documents.is_diagnosable(&specifier) {
|
||||||
if let Err(err) = self.diagnostics_server.update() {
|
if let Err(err) = self.diagnostics_server.update() {
|
||||||
error!("{}", err);
|
error!("{}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
self.performance.measure(mark);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn did_change_configuration(
|
async fn did_change_configuration(
|
||||||
|
@ -751,11 +766,9 @@ impl Inner {
|
||||||
params: DocumentSymbolParams,
|
params: DocumentSymbolParams,
|
||||||
) -> LspResult<Option<DocumentSymbolResponse>> {
|
) -> LspResult<Option<DocumentSymbolResponse>> {
|
||||||
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
return Ok(None);
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
}
|
{
|
||||||
let media_type = MediaType::from(&specifier);
|
|
||||||
if !is_diagnosable(media_type) {
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -798,8 +811,11 @@ impl Inner {
|
||||||
&self,
|
&self,
|
||||||
params: DocumentFormattingParams,
|
params: DocumentFormattingParams,
|
||||||
) -> LspResult<Option<Vec<TextEdit>>> {
|
) -> LspResult<Option<Vec<TextEdit>>> {
|
||||||
let mark = self.performance.mark("formatting", Some(¶ms));
|
|
||||||
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
|
if !self.documents.is_formattable(&specifier) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let mark = self.performance.mark("formatting", Some(¶ms));
|
||||||
let file_text = self
|
let file_text = self
|
||||||
.documents
|
.documents
|
||||||
.content(&specifier)
|
.content(&specifier)
|
||||||
|
@ -850,7 +866,9 @@ impl Inner {
|
||||||
let specifier = self
|
let specifier = self
|
||||||
.url_map
|
.url_map
|
||||||
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("hover", Some(¶ms));
|
let mark = self.performance.mark("hover", Some(¶ms));
|
||||||
|
@ -891,7 +909,9 @@ impl Inner {
|
||||||
params: CodeActionParams,
|
params: CodeActionParams,
|
||||||
) -> LspResult<Option<CodeActionResponse>> {
|
) -> LspResult<Option<CodeActionResponse>> {
|
||||||
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1054,7 +1074,8 @@ impl Inner {
|
||||||
params: CodeLensParams,
|
params: CodeLensParams,
|
||||||
) -> LspResult<Option<Vec<CodeLens>>> {
|
) -> LspResult<Option<Vec<CodeLens>>> {
|
||||||
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier)
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|| !self.config.get_workspace_settings().enabled_code_lens()
|
|| !self.config.get_workspace_settings().enabled_code_lens()
|
||||||
{
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
@ -1366,7 +1387,9 @@ impl Inner {
|
||||||
let specifier = self
|
let specifier = self
|
||||||
.url_map
|
.url_map
|
||||||
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1416,9 +1439,12 @@ impl Inner {
|
||||||
let specifier = self
|
let specifier = self
|
||||||
.url_map
|
.url_map
|
||||||
.normalize_url(¶ms.text_document_position.text_document.uri);
|
.normalize_url(¶ms.text_document_position.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mark = self.performance.mark("references", Some(¶ms));
|
let mark = self.performance.mark("references", Some(¶ms));
|
||||||
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) {
|
||||||
|
@ -1471,9 +1497,12 @@ impl Inner {
|
||||||
let specifier = self
|
let specifier = self
|
||||||
.url_map
|
.url_map
|
||||||
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mark = self.performance.mark("goto_definition", Some(¶ms));
|
let mark = self.performance.mark("goto_definition", Some(¶ms));
|
||||||
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) {
|
||||||
|
@ -1514,9 +1543,12 @@ impl Inner {
|
||||||
let specifier = self
|
let specifier = self
|
||||||
.url_map
|
.url_map
|
||||||
.normalize_url(¶ms.text_document_position.text_document.uri);
|
.normalize_url(¶ms.text_document_position.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mark = self.performance.mark("completion", Some(¶ms));
|
let mark = self.performance.mark("completion", Some(¶ms));
|
||||||
// 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
|
||||||
|
@ -1632,9 +1664,12 @@ impl Inner {
|
||||||
let specifier = self
|
let specifier = self
|
||||||
.url_map
|
.url_map
|
||||||
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mark = self.performance.mark("goto_implementation", Some(¶ms));
|
let mark = self.performance.mark("goto_implementation", Some(¶ms));
|
||||||
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) {
|
||||||
|
@ -1680,11 +1715,13 @@ impl Inner {
|
||||||
params: FoldingRangeParams,
|
params: FoldingRangeParams,
|
||||||
) -> LspResult<Option<Vec<FoldingRange>>> {
|
) -> LspResult<Option<Vec<FoldingRange>>> {
|
||||||
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("folding_range", Some(¶ms));
|
|
||||||
|
|
||||||
|
let mark = self.performance.mark("folding_range", Some(¶ms));
|
||||||
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
|
||||||
|
@ -1737,11 +1774,13 @@ impl Inner {
|
||||||
params: CallHierarchyIncomingCallsParams,
|
params: CallHierarchyIncomingCallsParams,
|
||||||
) -> LspResult<Option<Vec<CallHierarchyIncomingCall>>> {
|
) -> LspResult<Option<Vec<CallHierarchyIncomingCall>>> {
|
||||||
let specifier = self.url_map.normalize_url(¶ms.item.uri);
|
let specifier = self.url_map.normalize_url(¶ms.item.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("incoming_calls", Some(¶ms));
|
|
||||||
|
|
||||||
|
let mark = self.performance.mark("incoming_calls", Some(¶ms));
|
||||||
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
|
||||||
|
@ -1791,11 +1830,13 @@ impl Inner {
|
||||||
params: CallHierarchyOutgoingCallsParams,
|
params: CallHierarchyOutgoingCallsParams,
|
||||||
) -> LspResult<Option<Vec<CallHierarchyOutgoingCall>>> {
|
) -> LspResult<Option<Vec<CallHierarchyOutgoingCall>>> {
|
||||||
let specifier = self.url_map.normalize_url(¶ms.item.uri);
|
let specifier = self.url_map.normalize_url(¶ms.item.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("outgoing_calls", Some(¶ms));
|
|
||||||
|
|
||||||
|
let mark = self.performance.mark("outgoing_calls", Some(¶ms));
|
||||||
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
|
||||||
|
@ -1848,13 +1889,15 @@ impl Inner {
|
||||||
let specifier = self
|
let specifier = self
|
||||||
.url_map
|
.url_map
|
||||||
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mark = self
|
let mark = self
|
||||||
.performance
|
.performance
|
||||||
.mark("prepare_call_hierarchy", Some(¶ms));
|
.mark("prepare_call_hierarchy", Some(¶ms));
|
||||||
|
|
||||||
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
|
||||||
|
@ -1927,11 +1970,13 @@ impl Inner {
|
||||||
let specifier = self
|
let specifier = self
|
||||||
.url_map
|
.url_map
|
||||||
.normalize_url(¶ms.text_document_position.text_document.uri);
|
.normalize_url(¶ms.text_document_position.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("rename", Some(¶ms));
|
|
||||||
|
|
||||||
|
let mark = self.performance.mark("rename", Some(¶ms));
|
||||||
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
|
||||||
|
@ -2019,11 +2064,13 @@ impl Inner {
|
||||||
params: SelectionRangeParams,
|
params: SelectionRangeParams,
|
||||||
) -> LspResult<Option<Vec<SelectionRange>>> {
|
) -> LspResult<Option<Vec<SelectionRange>>> {
|
||||||
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("selection_range", Some(¶ms));
|
|
||||||
|
|
||||||
|
let mark = self.performance.mark("selection_range", Some(¶ms));
|
||||||
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
|
||||||
|
@ -2061,11 +2108,13 @@ impl Inner {
|
||||||
params: SemanticTokensParams,
|
params: SemanticTokensParams,
|
||||||
) -> LspResult<Option<SemanticTokensResult>> {
|
) -> LspResult<Option<SemanticTokensResult>> {
|
||||||
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let mark = self.performance.mark("semantic_tokens_full", Some(¶ms));
|
|
||||||
|
|
||||||
|
let mark = self.performance.mark("semantic_tokens_full", Some(¶ms));
|
||||||
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
|
||||||
|
@ -2108,13 +2157,15 @@ impl Inner {
|
||||||
params: SemanticTokensRangeParams,
|
params: SemanticTokensRangeParams,
|
||||||
) -> LspResult<Option<SemanticTokensRangeResult>> {
|
) -> LspResult<Option<SemanticTokensRangeResult>> {
|
||||||
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mark = self
|
let mark = self
|
||||||
.performance
|
.performance
|
||||||
.mark("semantic_tokens_range", Some(¶ms));
|
.mark("semantic_tokens_range", Some(¶ms));
|
||||||
|
|
||||||
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
|
||||||
|
@ -2158,9 +2209,12 @@ impl Inner {
|
||||||
let specifier = self
|
let specifier = self
|
||||||
.url_map
|
.url_map
|
||||||
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
.normalize_url(¶ms.text_document_position_params.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier) {
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mark = self.performance.mark("signature_help", Some(¶ms));
|
let mark = self.performance.mark("signature_help", Some(¶ms));
|
||||||
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) {
|
||||||
|
@ -2427,8 +2481,12 @@ impl Inner {
|
||||||
&mut self,
|
&mut self,
|
||||||
params: lsp_custom::CacheParams,
|
params: lsp_custom::CacheParams,
|
||||||
) -> LspResult<Option<Value>> {
|
) -> LspResult<Option<Value>> {
|
||||||
let mark = self.performance.mark("cache", Some(¶ms));
|
|
||||||
let referrer = self.url_map.normalize_url(¶ms.referrer.uri);
|
let referrer = self.url_map.normalize_url(¶ms.referrer.uri);
|
||||||
|
if !self.documents.is_diagnosable(&referrer) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mark = self.performance.mark("cache", Some(¶ms));
|
||||||
if !params.uris.is_empty() {
|
if !params.uris.is_empty() {
|
||||||
for identifier in ¶ms.uris {
|
for identifier in ¶ms.uris {
|
||||||
let specifier = self.url_map.normalize_url(&identifier.uri);
|
let specifier = self.url_map.normalize_url(&identifier.uri);
|
||||||
|
|
|
@ -2566,6 +2566,7 @@ mod tests {
|
||||||
use crate::http_util::HeadersMap;
|
use crate::http_util::HeadersMap;
|
||||||
use crate::lsp::analysis;
|
use crate::lsp::analysis;
|
||||||
use crate::lsp::documents::DocumentCache;
|
use crate::lsp::documents::DocumentCache;
|
||||||
|
use crate::lsp::documents::LanguageId;
|
||||||
use crate::lsp::sources::Sources;
|
use crate::lsp::sources::Sources;
|
||||||
use crate::lsp::text::LineIndex;
|
use crate::lsp::text::LineIndex;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -2573,14 +2574,14 @@ mod tests {
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
fn mock_state_snapshot(
|
fn mock_state_snapshot(
|
||||||
fixtures: &[(&str, &str, i32)],
|
fixtures: &[(&str, &str, i32, LanguageId)],
|
||||||
location: &Path,
|
location: &Path,
|
||||||
) -> StateSnapshot {
|
) -> StateSnapshot {
|
||||||
let mut documents = DocumentCache::default();
|
let mut documents = DocumentCache::default();
|
||||||
for (specifier, source, version) in fixtures {
|
for (specifier, source, version, language_id) in fixtures {
|
||||||
let specifier =
|
let specifier =
|
||||||
resolve_url(specifier).expect("failed to create specifier");
|
resolve_url(specifier).expect("failed to create specifier");
|
||||||
documents.open(specifier.clone(), *version, source);
|
documents.open(specifier.clone(), *version, language_id.clone(), source);
|
||||||
let media_type = MediaType::from(&specifier);
|
let media_type = MediaType::from(&specifier);
|
||||||
if let Ok(parsed_module) =
|
if let Ok(parsed_module) =
|
||||||
analysis::parse_module(&specifier, source, &media_type)
|
analysis::parse_module(&specifier, source, &media_type)
|
||||||
|
@ -2605,7 +2606,7 @@ mod tests {
|
||||||
fn setup(
|
fn setup(
|
||||||
debug: bool,
|
debug: bool,
|
||||||
config: Value,
|
config: Value,
|
||||||
sources: &[(&str, &str, i32)],
|
sources: &[(&str, &str, i32, LanguageId)],
|
||||||
) -> (JsRuntime, StateSnapshot, PathBuf) {
|
) -> (JsRuntime, StateSnapshot, PathBuf) {
|
||||||
let temp_dir = TempDir::new().expect("could not create temp dir");
|
let temp_dir = TempDir::new().expect("could not create temp dir");
|
||||||
let location = temp_dir.path().join("deps");
|
let location = temp_dir.path().join("deps");
|
||||||
|
@ -2688,7 +2689,12 @@ mod tests {
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
}),
|
}),
|
||||||
&[("file:///a.ts", r#"console.log("hello deno");"#, 1)],
|
&[(
|
||||||
|
"file:///a.ts",
|
||||||
|
r#"console.log("hello deno");"#,
|
||||||
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
|
)],
|
||||||
);
|
);
|
||||||
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
||||||
let result = request(
|
let result = request(
|
||||||
|
@ -2733,7 +2739,12 @@ mod tests {
|
||||||
"lib": ["esnext", "dom", "deno.ns"],
|
"lib": ["esnext", "dom", "deno.ns"],
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
}),
|
}),
|
||||||
&[("file:///a.ts", r#"console.log(document.location);"#, 1)],
|
&[(
|
||||||
|
"file:///a.ts",
|
||||||
|
r#"console.log(document.location);"#,
|
||||||
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
|
)],
|
||||||
);
|
);
|
||||||
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
||||||
let result = request(
|
let result = request(
|
||||||
|
@ -2766,6 +2777,7 @@ mod tests {
|
||||||
console.log(b);
|
console.log(b);
|
||||||
"#,
|
"#,
|
||||||
1,
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
||||||
|
@ -2795,6 +2807,7 @@ mod tests {
|
||||||
import { A } from ".";
|
import { A } from ".";
|
||||||
"#,
|
"#,
|
||||||
1,
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
||||||
|
@ -2848,6 +2861,7 @@ mod tests {
|
||||||
console.log(b);
|
console.log(b);
|
||||||
"#,
|
"#,
|
||||||
1,
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
||||||
|
@ -2884,6 +2898,7 @@ mod tests {
|
||||||
import * as test from
|
import * as test from
|
||||||
"#,
|
"#,
|
||||||
1,
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
||||||
|
@ -2941,7 +2956,12 @@ mod tests {
|
||||||
"lib": ["deno.ns", "deno.window"],
|
"lib": ["deno.ns", "deno.window"],
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
}),
|
}),
|
||||||
&[("file:///a.ts", r#"const url = new URL("b.js", import."#, 1)],
|
&[(
|
||||||
|
"file:///a.ts",
|
||||||
|
r#"const url = new URL("b.js", import."#,
|
||||||
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
|
)],
|
||||||
);
|
);
|
||||||
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
||||||
let result = request(
|
let result = request(
|
||||||
|
@ -2998,6 +3018,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
1,
|
1,
|
||||||
|
LanguageId::TypeScript,
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
let cache = HttpCache::new(&location);
|
let cache = HttpCache::new(&location);
|
||||||
|
@ -3099,7 +3120,7 @@ mod tests {
|
||||||
"lib": ["deno.ns", "deno.window"],
|
"lib": ["deno.ns", "deno.window"],
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
}),
|
}),
|
||||||
&[("file:///a.ts", fixture, 1)],
|
&[("file:///a.ts", fixture, 1, LanguageId::TypeScript)],
|
||||||
);
|
);
|
||||||
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
let specifier = resolve_url("file:///a.ts").expect("could not resolve url");
|
||||||
let result = request(
|
let result = request(
|
||||||
|
|
|
@ -1,5 +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 data_url::DataUrl;
|
||||||
use deno_core::serde::Serialize;
|
use deno_core::serde::Serialize;
|
||||||
use deno_core::serde::Serializer;
|
use deno_core::serde::Serializer;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
|
@ -45,34 +46,40 @@ impl fmt::Display for MediaType {
|
||||||
|
|
||||||
impl<'a> From<&'a Path> for MediaType {
|
impl<'a> From<&'a Path> for MediaType {
|
||||||
fn from(path: &'a Path) -> Self {
|
fn from(path: &'a Path) -> Self {
|
||||||
MediaType::from_path(path)
|
Self::from_path(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a PathBuf> for MediaType {
|
impl<'a> From<&'a PathBuf> for MediaType {
|
||||||
fn from(path: &'a PathBuf) -> Self {
|
fn from(path: &'a PathBuf) -> Self {
|
||||||
MediaType::from_path(path)
|
Self::from_path(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a String> for MediaType {
|
impl<'a> From<&'a String> for MediaType {
|
||||||
fn from(specifier: &'a String) -> Self {
|
fn from(specifier: &'a String) -> Self {
|
||||||
MediaType::from_path(&PathBuf::from(specifier))
|
Self::from_path(&PathBuf::from(specifier))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a ModuleSpecifier> for MediaType {
|
impl<'a> From<&'a ModuleSpecifier> for MediaType {
|
||||||
fn from(specifier: &'a ModuleSpecifier) -> Self {
|
fn from(specifier: &'a ModuleSpecifier) -> Self {
|
||||||
let path = if specifier.scheme() == "file" {
|
if specifier.scheme() != "data" {
|
||||||
if let Ok(path) = specifier.to_file_path() {
|
let path = if specifier.scheme() == "file" {
|
||||||
path
|
if let Ok(path) = specifier.to_file_path() {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
PathBuf::from(specifier.path())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
PathBuf::from(specifier.path())
|
PathBuf::from(specifier.path())
|
||||||
}
|
};
|
||||||
|
Self::from_path(&path)
|
||||||
|
} else if let Ok(data_url) = DataUrl::process(specifier.as_str()) {
|
||||||
|
Self::from_content_type(specifier, data_url.mime_type().to_string())
|
||||||
} else {
|
} else {
|
||||||
PathBuf::from(specifier.path())
|
Self::Unknown
|
||||||
};
|
}
|
||||||
MediaType::from_path(&path)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +90,40 @@ impl Default for MediaType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MediaType {
|
impl MediaType {
|
||||||
|
pub fn from_content_type<S: AsRef<str>>(
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
content_type: S,
|
||||||
|
) -> Self {
|
||||||
|
match content_type.as_ref().trim().to_lowercase().as_ref() {
|
||||||
|
"application/typescript"
|
||||||
|
| "text/typescript"
|
||||||
|
| "video/vnd.dlna.mpeg-tts"
|
||||||
|
| "video/mp2t"
|
||||||
|
| "application/x-typescript" => {
|
||||||
|
map_js_like_extension(specifier, Self::TypeScript)
|
||||||
|
}
|
||||||
|
"application/javascript"
|
||||||
|
| "text/javascript"
|
||||||
|
| "application/ecmascript"
|
||||||
|
| "text/ecmascript"
|
||||||
|
| "application/x-javascript"
|
||||||
|
| "application/node" => {
|
||||||
|
map_js_like_extension(specifier, Self::JavaScript)
|
||||||
|
}
|
||||||
|
"text/jsx" => Self::Jsx,
|
||||||
|
"text/tsx" => Self::Tsx,
|
||||||
|
"application/json" | "text/json" => Self::Json,
|
||||||
|
"application/wasm" => Self::Wasm,
|
||||||
|
// Handle plain and possibly webassembly
|
||||||
|
"text/plain" | "application/octet-stream"
|
||||||
|
if specifier.scheme() != "data" =>
|
||||||
|
{
|
||||||
|
Self::from(specifier)
|
||||||
|
}
|
||||||
|
_ => Self::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn from_path(path: &Path) -> Self {
|
fn from_path(path: &Path) -> Self {
|
||||||
match path.extension() {
|
match path.extension() {
|
||||||
None => match path.file_name() {
|
None => match path.file_name() {
|
||||||
|
@ -197,6 +238,55 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to augment media types by using the path part of a module specifier to
|
||||||
|
/// resolve to a more accurate media type.
|
||||||
|
fn map_js_like_extension(
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
default: MediaType,
|
||||||
|
) -> MediaType {
|
||||||
|
let path = if specifier.scheme() == "file" {
|
||||||
|
if let Ok(path) = specifier.to_file_path() {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
PathBuf::from(specifier.path())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PathBuf::from(specifier.path())
|
||||||
|
};
|
||||||
|
match path.extension() {
|
||||||
|
None => default,
|
||||||
|
Some(os_str) => match os_str.to_str() {
|
||||||
|
None => default,
|
||||||
|
Some("jsx") => MediaType::Jsx,
|
||||||
|
Some("tsx") => MediaType::Tsx,
|
||||||
|
// Because DTS files do not have a separate media type, or a unique
|
||||||
|
// extension, we have to "guess" at those things that we consider that
|
||||||
|
// look like TypeScript, and end with `.d.ts` are DTS files.
|
||||||
|
Some("ts") => {
|
||||||
|
if default == MediaType::TypeScript {
|
||||||
|
match path.file_stem() {
|
||||||
|
None => default,
|
||||||
|
Some(os_str) => {
|
||||||
|
if let Some(file_stem) = os_str.to_str() {
|
||||||
|
if file_stem.ends_with(".d") {
|
||||||
|
MediaType::Dts
|
||||||
|
} else {
|
||||||
|
default
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(_) => default,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -245,6 +335,9 @@ mod tests {
|
||||||
("https://deno.land/x/mod.ts", MediaType::TypeScript),
|
("https://deno.land/x/mod.ts", MediaType::TypeScript),
|
||||||
("https://deno.land/x/mod.js", MediaType::JavaScript),
|
("https://deno.land/x/mod.js", MediaType::JavaScript),
|
||||||
("https://deno.land/x/mod.txt", MediaType::Unknown),
|
("https://deno.land/x/mod.txt", MediaType::Unknown),
|
||||||
|
("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=", MediaType::TypeScript),
|
||||||
|
("data:application/javascript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=", MediaType::JavaScript),
|
||||||
|
("data:text/plain;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=", MediaType::Unknown),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (specifier, expected) in fixtures {
|
for (specifier, expected) in fixtures {
|
||||||
|
@ -253,6 +346,52 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_content_type() {
|
||||||
|
let fixtures = vec![
|
||||||
|
(
|
||||||
|
"https://deno.land/x/mod.ts",
|
||||||
|
"application/typescript",
|
||||||
|
MediaType::TypeScript,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/mod.d.ts",
|
||||||
|
"application/typescript",
|
||||||
|
MediaType::Dts,
|
||||||
|
),
|
||||||
|
("https://deno.land/x/mod.tsx", "text/tsx", MediaType::Tsx),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/mod.js",
|
||||||
|
"application/javascript",
|
||||||
|
MediaType::JavaScript,
|
||||||
|
),
|
||||||
|
("https://deno.land/x/mod.jsx", "text/jsx", MediaType::Jsx),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/mod.ts",
|
||||||
|
"text/plain",
|
||||||
|
MediaType::TypeScript,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/mod.js",
|
||||||
|
"text/plain",
|
||||||
|
MediaType::JavaScript,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"https://deno.land/x/mod.wasm",
|
||||||
|
"text/plain",
|
||||||
|
MediaType::Wasm,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (specifier, content_type, expected) in fixtures {
|
||||||
|
let fixture = deno_core::resolve_url_or_path(specifier).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
MediaType::from_content_type(&fixture, content_type),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialization() {
|
fn test_serialization() {
|
||||||
assert_eq!(json!(MediaType::JavaScript), json!(0));
|
assert_eq!(json!(MediaType::JavaScript), json!(0));
|
||||||
|
|
|
@ -2118,6 +2118,56 @@ fn lsp_format_json() {
|
||||||
shutdown(&mut client);
|
shutdown(&mut client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lsp_json_no_diagnostics() {
|
||||||
|
let mut client = init("initialize_params.json");
|
||||||
|
client
|
||||||
|
.write_notification(
|
||||||
|
"textDocument/didOpen",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.json",
|
||||||
|
"languageId": "json",
|
||||||
|
"version": 1,
|
||||||
|
"text": "{\"key\":\"value\"}"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (maybe_res, maybe_err) = client
|
||||||
|
.write_request(
|
||||||
|
"textDocument/semanticTokens/full",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.json"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(maybe_err.is_none());
|
||||||
|
assert_eq!(maybe_res, Some(json!(null)));
|
||||||
|
|
||||||
|
let (maybe_res, maybe_err) = client
|
||||||
|
.write_request::<_, _, Value>(
|
||||||
|
"textDocument/hover",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.json"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"line": 0,
|
||||||
|
"character": 3
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(maybe_err.is_none());
|
||||||
|
assert_eq!(maybe_res, Some(json!(null)));
|
||||||
|
|
||||||
|
shutdown(&mut client);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lsp_format_markdown() {
|
fn lsp_format_markdown() {
|
||||||
let mut client = init("initialize_params.json");
|
let mut client = init("initialize_params.json");
|
||||||
|
@ -2173,6 +2223,56 @@ fn lsp_format_markdown() {
|
||||||
shutdown(&mut client);
|
shutdown(&mut client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lsp_markdown_no_diagnostics() {
|
||||||
|
let mut client = init("initialize_params.json");
|
||||||
|
client
|
||||||
|
.write_notification(
|
||||||
|
"textDocument/didOpen",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.md",
|
||||||
|
"languageId": "markdown",
|
||||||
|
"version": 1,
|
||||||
|
"text": "# Hello World"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (maybe_res, maybe_err) = client
|
||||||
|
.write_request(
|
||||||
|
"textDocument/semanticTokens/full",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.md"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(maybe_err.is_none());
|
||||||
|
assert_eq!(maybe_res, Some(json!(null)));
|
||||||
|
|
||||||
|
let (maybe_res, maybe_err) = client
|
||||||
|
.write_request::<_, _, Value>(
|
||||||
|
"textDocument/hover",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.md"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"line": 0,
|
||||||
|
"character": 3
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(maybe_err.is_none());
|
||||||
|
assert_eq!(maybe_res, Some(json!(null)));
|
||||||
|
|
||||||
|
shutdown(&mut client);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lsp_configuration_did_change() {
|
fn lsp_configuration_did_change() {
|
||||||
let _g = http_server();
|
let _g = http_server();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue