refactor(lsp): use deno_ast and cache swc ASTs (#11780)

This commit is contained in:
David Sherret 2021-09-07 10:39:32 -04:00 committed by GitHub
parent a5bcf7033e
commit 2c2e3ec1ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 1037 additions and 1758 deletions

View file

@ -1,11 +1,18 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use super::analysis;
use super::config::Config;
use super::config::WorkspaceSettings;
use super::language_server;
use super::text::LineIndex;
use super::tsc;
use crate::ast::ParsedModule;
use super::tsc::NavigationTree;
use deno_core::error::anyhow;
use deno_ast::swc::ast;
use deno_ast::swc::common::Span;
use deno_ast::swc::visit::Node;
use deno_ast::swc::visit::Visit;
use deno_ast::swc::visit::VisitWith;
use deno_ast::ParsedSource;
use deno_core::error::AnyError;
use deno_core::resolve_url;
use deno_core::serde::Deserialize;
@ -18,11 +25,6 @@ use regex::Regex;
use std::cell::RefCell;
use std::collections::HashSet;
use std::rc::Rc;
use swc_common::Span;
use swc_ecmascript::ast;
use swc_ecmascript::visit::Node;
use swc_ecmascript::visit::Visit;
use swc_ecmascript::visit::VisitWith;
lazy_static::lazy_static! {
static ref ABSTRACT_MODIFIER: Regex = Regex::new(r"\babstract\b").unwrap();
@ -44,24 +46,24 @@ pub struct CodeLensData {
pub specifier: ModuleSpecifier,
}
fn span_to_range(span: &Span, parsed_module: &ParsedModule) -> lsp::Range {
let start = parsed_module.get_location(span.lo);
let end = parsed_module.get_location(span.hi);
fn span_to_range(span: &Span, parsed_source: &ParsedSource) -> lsp::Range {
let start = parsed_source.source().line_and_column_index(span.lo);
let end = parsed_source.source().line_and_column_index(span.hi);
lsp::Range {
start: lsp::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: lsp::Position {
line: (end.line - 1) as u32,
character: end.col as u32,
line: end.line_index as u32,
character: end.column_index as u32,
},
}
}
struct DenoTestCollector<'a> {
code_lenses: Vec<lsp::CodeLens>,
parsed_module: &'a ParsedModule,
parsed_source: &'a ParsedSource,
specifier: ModuleSpecifier,
test_vars: HashSet<String>,
}
@ -69,18 +71,18 @@ struct DenoTestCollector<'a> {
impl<'a> DenoTestCollector<'a> {
pub fn new(
specifier: ModuleSpecifier,
parsed_module: &'a ParsedModule,
parsed_source: &'a ParsedSource,
) -> Self {
Self {
code_lenses: Vec::new(),
parsed_module,
parsed_source,
specifier,
test_vars: HashSet::new(),
}
}
fn add_code_lens<N: AsRef<str>>(&mut self, name: N, span: &Span) {
let range = span_to_range(span, self.parsed_module);
let range = span_to_range(span, self.parsed_source);
self.code_lenses.push(lsp::CodeLens {
range,
command: Some(lsp::Command {
@ -370,36 +372,37 @@ pub(crate) async fn resolve_code_lens(
pub(crate) async fn collect(
specifier: &ModuleSpecifier,
language_server: &mut language_server::Inner,
parsed_source: Option<&ParsedSource>,
config: &Config,
line_index: &LineIndex,
navigation_tree: &NavigationTree,
) -> Result<Vec<lsp::CodeLens>, AnyError> {
let mut code_lenses = collect_test(specifier, language_server)?;
code_lenses.extend(collect_tsc(specifier, language_server).await?);
let mut code_lenses = collect_test(specifier, parsed_source, config)?;
code_lenses.extend(
collect_tsc(
specifier,
&config.get_workspace_settings(),
line_index,
navigation_tree,
)
.await?,
);
Ok(code_lenses)
}
fn collect_test(
specifier: &ModuleSpecifier,
language_server: &mut language_server::Inner,
parsed_source: Option<&ParsedSource>,
config: &Config,
) -> Result<Vec<lsp::CodeLens>, AnyError> {
if language_server.config.specifier_code_lens_test(specifier) {
let source = language_server
.get_text_content(specifier)
.ok_or_else(|| anyhow!("Missing text content: {}", specifier))?;
let media_type = language_server
.get_media_type(specifier)
.ok_or_else(|| anyhow!("Missing media type: {}", specifier))?;
// we swallow parsed errors, as they are meaningless here.
// TODO(@kitsonk) consider caching previous code_lens results to return if
// there is a parse error to avoid issues of lenses popping in and out
if let Ok(parsed_module) =
analysis::parse_module(specifier, &source, &media_type)
{
if config.specifier_code_lens_test(specifier) {
if let Some(parsed_source) = parsed_source {
let mut collector =
DenoTestCollector::new(specifier.clone(), &parsed_module);
parsed_module.module.visit_with(
DenoTestCollector::new(specifier.clone(), parsed_source);
parsed_source.module().visit_with(
&ast::Invalid {
span: swc_common::DUMMY_SP,
span: deno_ast::swc::common::DUMMY_SP,
},
&mut collector,
);
@ -412,13 +415,10 @@ fn collect_test(
/// Return tsc navigation tree code lenses.
async fn collect_tsc(
specifier: &ModuleSpecifier,
language_server: &mut language_server::Inner,
workspace_settings: &WorkspaceSettings,
line_index: &LineIndex,
navigation_tree: &NavigationTree,
) -> Result<Vec<lsp::CodeLens>, AnyError> {
let workspace_settings = language_server.config.get_workspace_settings();
let line_index = language_server
.get_line_index_sync(specifier)
.ok_or_else(|| anyhow!("Missing line index."))?;
let navigation_tree = language_server.get_navigation_tree(specifier).await?;
let code_lenses = Rc::new(RefCell::new(Vec::new()));
navigation_tree.walk(&|i, mp| {
let mut code_lenses = code_lenses.borrow_mut();
@ -428,7 +428,7 @@ async fn collect_tsc(
let source = CodeLensSource::Implementations;
match i.kind {
tsc::ScriptElementKind::InterfaceElement => {
code_lenses.push(i.to_code_lens(&line_index, specifier, &source));
code_lenses.push(i.to_code_lens(line_index, specifier, &source));
}
tsc::ScriptElementKind::ClassElement
| tsc::ScriptElementKind::MemberFunctionElement
@ -436,7 +436,7 @@ async fn collect_tsc(
| tsc::ScriptElementKind::MemberGetAccessorElement
| tsc::ScriptElementKind::MemberSetAccessorElement => {
if ABSTRACT_MODIFIER.is_match(&i.kind_modifiers) {
code_lenses.push(i.to_code_lens(&line_index, specifier, &source));
code_lenses.push(i.to_code_lens(line_index, specifier, &source));
}
}
_ => (),
@ -448,31 +448,31 @@ async fn collect_tsc(
let source = CodeLensSource::References;
if let Some(parent) = &mp {
if parent.kind == tsc::ScriptElementKind::EnumElement {
code_lenses.push(i.to_code_lens(&line_index, specifier, &source));
code_lenses.push(i.to_code_lens(line_index, specifier, &source));
}
}
match i.kind {
tsc::ScriptElementKind::FunctionElement => {
if workspace_settings.code_lens.references_all_functions {
code_lenses.push(i.to_code_lens(&line_index, specifier, &source));
code_lenses.push(i.to_code_lens(line_index, specifier, &source));
}
}
tsc::ScriptElementKind::ConstElement
| tsc::ScriptElementKind::LetElement
| tsc::ScriptElementKind::VariableElement => {
if EXPORT_MODIFIER.is_match(&i.kind_modifiers) {
code_lenses.push(i.to_code_lens(&line_index, specifier, &source));
code_lenses.push(i.to_code_lens(line_index, specifier, &source));
}
}
tsc::ScriptElementKind::ClassElement => {
if i.text != "<class>" {
code_lenses.push(i.to_code_lens(&line_index, specifier, &source));
code_lenses.push(i.to_code_lens(line_index, specifier, &source));
}
}
tsc::ScriptElementKind::InterfaceElement
| tsc::ScriptElementKind::TypeElement
| tsc::ScriptElementKind::EnumElement => {
code_lenses.push(i.to_code_lens(&line_index, specifier, &source));
code_lenses.push(i.to_code_lens(line_index, specifier, &source));
}
tsc::ScriptElementKind::LocalFunctionElement
| tsc::ScriptElementKind::MemberGetAccessorElement
@ -485,11 +485,8 @@ async fn collect_tsc(
tsc::ScriptElementKind::ClassElement
| tsc::ScriptElementKind::InterfaceElement
| tsc::ScriptElementKind::TypeElement => {
code_lenses.push(i.to_code_lens(
&line_index,
specifier,
&source,
));
code_lenses
.push(i.to_code_lens(line_index, specifier, &source));
}
_ => (),
}
@ -505,8 +502,10 @@ async fn collect_tsc(
#[cfg(test)]
mod tests {
use deno_ast::MediaType;
use deno_ast::SourceTextInfo;
use super::*;
use crate::media_type::MediaType;
#[test]
fn test_deno_test_collector() {
@ -519,13 +518,16 @@ mod tests {
Deno.test("test b", function anotherTest() {});
"#;
let parsed_module =
analysis::parse_module(&specifier, source, &MediaType::TypeScript)
.unwrap();
let parsed_module = crate::lsp::analysis::parse_module(
&specifier,
SourceTextInfo::from_string(source.to_string()),
MediaType::TypeScript,
)
.unwrap();
let mut collector = DenoTestCollector::new(specifier, &parsed_module);
parsed_module.module.visit_with(
parsed_module.module().visit_with(
&ast::Invalid {
span: swc_common::DUMMY_SP,
span: deno_ast::swc::common::DUMMY_SP,
},
&mut collector,
);