diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2021-09-07 10:39:32 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-07 10:39:32 -0400 |
commit | 2c2e3ec1ca47803f791ea72ea6247d8eedf87ec8 (patch) | |
tree | 9ba3ddfde58f4a4feaf98fc230ec18861891c9be /cli/lsp/analysis.rs | |
parent | a5bcf7033e66a828dc88a313f4cca11f116c3f83 (diff) |
refactor(lsp): use deno_ast and cache swc ASTs (#11780)
Diffstat (limited to 'cli/lsp/analysis.rs')
-rw-r--r-- | cli/lsp/analysis.rs | 171 |
1 files changed, 97 insertions, 74 deletions
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 4c5f1fea7..12b54a503 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -4,14 +4,22 @@ use super::language_server; use super::tsc; use crate::ast; +use crate::ast::Location; use crate::import_map::ImportMap; use crate::lsp::documents::DocumentData; -use crate::media_type::MediaType; use crate::module_graph::parse_deno_types; use crate::module_graph::parse_ts_reference; use crate::module_graph::TypeScriptReference; use crate::tools::lint::create_linter; +use deno_ast::swc::ast as swc_ast; +use deno_ast::swc::common::DUMMY_SP; +use deno_ast::swc::visit::Node; +use deno_ast::swc::visit::Visit; +use deno_ast::swc::visit::VisitWith; +use deno_ast::Diagnostic; +use deno_ast::MediaType; +use deno_ast::SourceTextInfo; use deno_core::error::anyhow; use deno_core::error::custom_error; use deno_core::error::AnyError; @@ -29,11 +37,6 @@ use regex::Regex; use std::cmp::Ordering; use std::collections::HashMap; use std::fmt; -use swc_common::DUMMY_SP; -use swc_ecmascript::ast as swc_ast; -use swc_ecmascript::visit::Node; -use swc_ecmascript::visit::Visit; -use swc_ecmascript::visit::VisitWith; lazy_static::lazy_static! { /// Diagnostic error codes which actually are the same, and so when grouping @@ -131,17 +134,12 @@ fn as_lsp_range(range: &deno_lint::diagnostic::Range) -> Range { } pub fn get_lint_references( - specifier: &ModuleSpecifier, - media_type: &MediaType, - source_code: &str, + parsed_source: &deno_ast::ParsedSource, ) -> Result<Vec<Reference>, AnyError> { - let syntax = ast::get_syntax(media_type); + let syntax = deno_ast::get_syntax(parsed_source.media_type()); let lint_rules = rules::get_recommended_rules(); let linter = create_linter(syntax, lint_rules); - // TODO(@kitsonk) we should consider caching the swc source file versions for - // reuse by other processes - let (_, lint_diagnostics) = - linter.lint(specifier.to_string(), source_code.to_string())?; + let lint_diagnostics = linter.lint_with_ast(parsed_source); Ok( lint_diagnostics @@ -281,27 +279,34 @@ pub fn resolve_import( pub fn parse_module( specifier: &ModuleSpecifier, - source: &str, - media_type: &MediaType, -) -> Result<ast::ParsedModule, AnyError> { - ast::parse(&specifier.to_string(), source, media_type) + source: SourceTextInfo, + media_type: MediaType, +) -> Result<deno_ast::ParsedSource, Diagnostic> { + deno_ast::parse_module(deno_ast::ParseParams { + specifier: specifier.as_str().to_string(), + source, + media_type, + // capture the tokens for linting and formatting + capture_tokens: true, + maybe_syntax: None, + }) } // TODO(@kitsonk) a lot of this logic is duplicated in module_graph.rs in // Module::parse() and should be refactored out to a common function. pub fn analyze_dependencies( specifier: &ModuleSpecifier, - media_type: &MediaType, - parsed_module: &ast::ParsedModule, + media_type: MediaType, + parsed_source: &deno_ast::ParsedSource, maybe_import_map: &Option<ImportMap>, ) -> (HashMap<String, Dependency>, Option<ResolvedDependency>) { let mut maybe_type = None; let mut dependencies = HashMap::<String, Dependency>::new(); // Parse leading comments for supported triple slash references. - for comment in parsed_module.get_leading_comments().iter() { + for comment in parsed_source.get_leading_comments().iter() { if let Some((ts_reference, span)) = parse_ts_reference(comment) { - let loc = parsed_module.get_location(span.lo); + let loc = parsed_source.source().line_and_column_index(span.lo); match ts_reference { TypeScriptReference::Path(import) => { let dep = dependencies.entry(import.clone()).or_default(); @@ -310,20 +315,19 @@ pub fn analyze_dependencies( dep.maybe_code = Some(resolved_import); dep.maybe_code_specifier_range = Some(Range { start: Position { - line: (loc.line - 1) as u32, - character: loc.col as u32, + line: loc.line_index as u32, + character: loc.column_index as u32, }, end: Position { - line: (loc.line - 1) as u32, - character: (loc.col + import.chars().count() + 2) as u32, + line: loc.line_index as u32, + character: (loc.column_index + import.chars().count() + 2) as u32, }, }); } TypeScriptReference::Types(import) => { let resolved_import = resolve_import(&import, specifier, maybe_import_map); - if media_type == &MediaType::JavaScript - || media_type == &MediaType::Jsx + if media_type == MediaType::JavaScript || media_type == MediaType::Jsx { maybe_type = Some(resolved_import.clone()); } @@ -331,12 +335,12 @@ pub fn analyze_dependencies( dep.maybe_type = Some(resolved_import); dep.maybe_type_specifier_range = Some(Range { start: Position { - line: (loc.line - 1) as u32, - character: loc.col as u32, + line: loc.line_index as u32, + character: loc.column_index as u32, }, end: Position { - line: (loc.line - 1) as u32, - character: (loc.col + import.chars().count() + 2) as u32, + line: loc.line_index as u32, + character: (loc.column_index + import.chars().count() + 2) as u32, }, }); } @@ -345,9 +349,9 @@ pub fn analyze_dependencies( } // Parse ES and type only imports - let descriptors = parsed_module.analyze_dependencies(); + let descriptors = deno_graph::analyze_dependencies(parsed_source); for desc in descriptors.into_iter().filter(|desc| { - desc.kind != swc_ecmascript::dep_graph::DependencyKind::Require + desc.kind != deno_ast::swc::dep_graph::DependencyKind::Require }) { let resolved_import = resolve_import(&desc.specifier, specifier, maybe_import_map); @@ -359,7 +363,7 @@ pub fn analyze_dependencies( ( resolve_import(deno_types, specifier, maybe_import_map), deno_types.clone(), - parsed_module.get_location(span.lo) + parsed_source.source().line_and_column_index(span.lo) ) }) } else { @@ -368,16 +372,20 @@ pub fn analyze_dependencies( let dep = dependencies.entry(desc.specifier.to_string()).or_default(); dep.is_dynamic = desc.is_dynamic; - let start = parsed_module.get_location(desc.specifier_span.lo); - let end = parsed_module.get_location(desc.specifier_span.hi); + let start = parsed_source + .source() + .line_and_column_index(desc.specifier_span.lo); + let end = parsed_source + .source() + .line_and_column_index(desc.specifier_span.hi); let range = Range { start: Position { - line: (start.line - 1) as u32, - character: start.col as u32, + line: start.line_index as u32, + character: start.column_index as u32, }, end: Position { - line: (end.line - 1) as u32, - character: end.col as u32, + line: end.line_index as u32, + character: end.column_index as u32, }, }; dep.maybe_code_specifier_range = Some(range); @@ -388,12 +396,15 @@ pub fn analyze_dependencies( { dep.maybe_type_specifier_range = Some(Range { start: Position { - line: (loc.line - 1) as u32, - character: (loc.col + 1) as u32, + line: loc.line_index as u32, + // +1 to skip quote + character: (loc.column_index + 1) as u32, }, end: Position { - line: (loc.line - 1) as u32, - character: (loc.col + 1 + specifier.chars().count()) as u32, + line: loc.line_index as u32, + // +1 to skip quote + character: (loc.column_index + 1 + specifier.chars().count()) + as u32, }, }); dep.maybe_type = Some(resolved_dependency); @@ -692,14 +703,12 @@ impl CodeActionCollection { }) .unwrap(); - let line_content = if let Some(doc) = document { - doc - .content_line(diagnostic.range.start.line as usize) - .ok() - .flatten() - } else { - None - }; + let line_content = document.map(|d| { + d.source() + .text_info() + .line_text(diagnostic.range.start.line as usize) + .to_string() + }); let mut changes = HashMap::new(); changes.insert( @@ -1021,14 +1030,14 @@ impl DependencyRanges { struct DependencyRangeCollector<'a> { import_ranges: DependencyRanges, - parsed_module: &'a ast::ParsedModule, + parsed_source: &'a deno_ast::ParsedSource, } impl<'a> DependencyRangeCollector<'a> { - pub fn new(parsed_module: &'a ast::ParsedModule) -> Self { + pub fn new(parsed_source: &'a deno_ast::ParsedSource) -> Self { Self { import_ranges: DependencyRanges::default(), - parsed_module, + parsed_source, } } @@ -1043,8 +1052,8 @@ impl<'a> Visit for DependencyRangeCollector<'a> { node: &swc_ast::ImportDecl, _parent: &dyn Node, ) { - let start = self.parsed_module.get_location(node.src.span.lo); - let end = self.parsed_module.get_location(node.src.span.hi); + let start = Location::from_pos(self.parsed_source, node.src.span.lo); + let end = Location::from_pos(self.parsed_source, node.src.span.hi); self.import_ranges.0.push(DependencyRange { range: narrow_range(get_range_from_location(&start, &end)), specifier: node.src.value.to_string(), @@ -1057,8 +1066,8 @@ impl<'a> Visit for DependencyRangeCollector<'a> { _parent: &dyn Node, ) { if let Some(src) = &node.src { - let start = self.parsed_module.get_location(src.span.lo); - let end = self.parsed_module.get_location(src.span.hi); + let start = Location::from_pos(self.parsed_source, src.span.lo); + let end = Location::from_pos(self.parsed_source, src.span.hi); self.import_ranges.0.push(DependencyRange { range: narrow_range(get_range_from_location(&start, &end)), specifier: src.value.to_string(), @@ -1071,8 +1080,8 @@ impl<'a> Visit for DependencyRangeCollector<'a> { node: &swc_ast::ExportAll, _parent: &dyn Node, ) { - let start = self.parsed_module.get_location(node.src.span.lo); - let end = self.parsed_module.get_location(node.src.span.hi); + let start = Location::from_pos(self.parsed_source, node.src.span.lo); + let end = Location::from_pos(self.parsed_source, node.src.span.hi); self.import_ranges.0.push(DependencyRange { range: narrow_range(get_range_from_location(&start, &end)), specifier: node.src.value.to_string(), @@ -1084,8 +1093,8 @@ impl<'a> Visit for DependencyRangeCollector<'a> { node: &swc_ast::TsImportType, _parent: &dyn Node, ) { - let start = self.parsed_module.get_location(node.arg.span.lo); - let end = self.parsed_module.get_location(node.arg.span.hi); + let start = Location::from_pos(self.parsed_source, node.arg.span.lo); + let end = Location::from_pos(self.parsed_source, node.arg.span.hi); self.import_ranges.0.push(DependencyRange { range: narrow_range(get_range_from_location(&start, &end)), specifier: node.arg.value.to_string(), @@ -1096,11 +1105,11 @@ impl<'a> Visit for DependencyRangeCollector<'a> { /// Analyze a document for import ranges, which then can be used to identify if /// a particular position within the document as inside an import range. pub fn analyze_dependency_ranges( - parsed_module: &ast::ParsedModule, + parsed_source: &deno_ast::ParsedSource, ) -> Result<DependencyRanges, AnyError> { - let mut collector = DependencyRangeCollector::new(parsed_module); - parsed_module - .module + let mut collector = DependencyRangeCollector::new(parsed_source); + parsed_source + .module() .visit_with(&swc_ast::Invalid { span: DUMMY_SP }, &mut collector); Ok(collector.take()) } @@ -1202,8 +1211,13 @@ mod tests { fn test_get_lint_references() { let specifier = resolve_url("file:///a.ts").expect("bad specifier"); let source = "const foo = 42;"; - let actual = - get_lint_references(&specifier, &MediaType::TypeScript, source).unwrap(); + let parsed_module = parse_module( + &specifier, + SourceTextInfo::from_string(source.to_string()), + MediaType::TypeScript, + ) + .unwrap(); + let actual = get_lint_references(&parsed_module).unwrap(); assert_eq!( actual, @@ -1246,11 +1260,15 @@ mod tests { // @deno-types="https://deno.land/x/types/react/index.d.ts"; import React from "https://cdn.skypack.dev/react"; "#; - let parsed_module = - parse_module(&specifier, source, &MediaType::TypeScript).unwrap(); + let parsed_module = parse_module( + &specifier, + SourceTextInfo::from_string(source.to_string()), + MediaType::TypeScript, + ) + .unwrap(); let (actual, maybe_type) = analyze_dependencies( &specifier, - &MediaType::TypeScript, + MediaType::TypeScript, &parsed_module, &None, ); @@ -1338,7 +1356,12 @@ mod tests { let source = "import * as a from \"./b.ts\";\nexport * as a from \"./c.ts\";\n"; let media_type = MediaType::TypeScript; - let parsed_module = parse_module(&specifier, source, &media_type).unwrap(); + let parsed_module = parse_module( + &specifier, + SourceTextInfo::from_string(source.to_string()), + media_type, + ) + .unwrap(); let result = analyze_dependency_ranges(&parsed_module); assert!(result.is_ok()); let actual = result.unwrap(); |