mirror of
https://github.com/denoland/deno.git
synced 2025-01-18 11:53:59 -05:00
parent
d6f6e157bd
commit
3b3be024fa
10 changed files with 749 additions and 19 deletions
|
@ -24,6 +24,7 @@ There are several settings that the language server supports for a workspace:
|
||||||
- `deno.codeLens.implementations`
|
- `deno.codeLens.implementations`
|
||||||
- `deno.codeLens.references`
|
- `deno.codeLens.references`
|
||||||
- `deno.codeLens.referencesAllFunctions`
|
- `deno.codeLens.referencesAllFunctions`
|
||||||
|
- `deno.codeLens.test`
|
||||||
- `deno.suggest.completeFunctionCalls`
|
- `deno.suggest.completeFunctionCalls`
|
||||||
- `deno.suggest.names`
|
- `deno.suggest.names`
|
||||||
- `deno.suggest.paths`
|
- `deno.suggest.paths`
|
||||||
|
@ -33,10 +34,11 @@ There are several settings that the language server supports for a workspace:
|
||||||
- `deno.lint`
|
- `deno.lint`
|
||||||
- `deno.unstable`
|
- `deno.unstable`
|
||||||
|
|
||||||
There are settings that are support on a per resource basis by the language
|
There are settings that are supported on a per resource basis by the language
|
||||||
server:
|
server:
|
||||||
|
|
||||||
- `deno.enable`
|
- `deno.enable`
|
||||||
|
- `deno.codeLens.test`
|
||||||
|
|
||||||
There are several points in the process where Deno analyzes these settings.
|
There are several points in the process where Deno analyzes these settings.
|
||||||
First, when the `initialize` request from the client, the
|
First, when the `initialize` request from the client, the
|
||||||
|
@ -68,7 +70,24 @@ settings.
|
||||||
If the client does not have the `workspaceConfiguration` capability, the
|
If the client does not have the `workspaceConfiguration` capability, the
|
||||||
language server will assume the workspace setting applies to all resources.
|
language server will assume the workspace setting applies to all resources.
|
||||||
|
|
||||||
## Custom requests
|
## Commands
|
||||||
|
|
||||||
|
There are several commands that might be issued by the language server to the
|
||||||
|
client, which the client is expected to implement:
|
||||||
|
|
||||||
|
- `deno.cache` - This is sent as a resolution code action when there is an
|
||||||
|
un-cached module specifier that is being imported into a module. It will be
|
||||||
|
sent with and argument that contains the resolved specifier as a string to be
|
||||||
|
cached.
|
||||||
|
- `deno.showReferences` - This is sent as the command on some code lenses to
|
||||||
|
show locations of references. The arguments contain the specifier that is the
|
||||||
|
subject of the command, the start position of the target and the locations of
|
||||||
|
the references to show.
|
||||||
|
- `deno.test` - This is sent as part of a test code lens to, of which the client
|
||||||
|
is expected to run a test based on the arguments, which are the specifier the
|
||||||
|
test is contained in and the name of the test to filter the tests on.
|
||||||
|
|
||||||
|
## Requests
|
||||||
|
|
||||||
The LSP currently supports the following custom requests. A client should
|
The LSP currently supports the following custom requests. A client should
|
||||||
implement these in order to have a fully functioning client that integrates well
|
implement these in order to have a fully functioning client that integrates well
|
||||||
|
@ -115,9 +134,9 @@ with Deno:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Custom notifications
|
## Notifications
|
||||||
|
|
||||||
There is currently one custom notification that is send from the server to the
|
There is currently one custom notification that is sent from the server to the
|
||||||
client:
|
client:
|
||||||
|
|
||||||
- `deno/registryStatus` - when `deno.suggest.imports.autoDiscover` is `true` and
|
- `deno/registryStatus` - when `deno.suggest.imports.autoDiscover` is `true` and
|
||||||
|
|
|
@ -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 super::analysis;
|
||||||
use super::language_server;
|
use super::language_server;
|
||||||
use super::tsc;
|
use super::tsc;
|
||||||
|
|
||||||
|
@ -14,7 +15,14 @@ use deno_core::ModuleSpecifier;
|
||||||
use lspower::lsp;
|
use lspower::lsp;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use swc_common::SourceMap;
|
||||||
|
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! {
|
lazy_static::lazy_static! {
|
||||||
static ref ABSTRACT_MODIFIER: Regex = Regex::new(r"\babstract\b").unwrap();
|
static ref ABSTRACT_MODIFIER: Regex = Regex::new(r"\babstract\b").unwrap();
|
||||||
|
@ -36,6 +44,174 @@ pub struct CodeLensData {
|
||||||
pub specifier: ModuleSpecifier,
|
pub specifier: ModuleSpecifier,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn span_to_range(span: &Span, source_map: Rc<SourceMap>) -> lsp::Range {
|
||||||
|
let start = source_map.lookup_char_pos(span.lo);
|
||||||
|
let end = source_map.lookup_char_pos(span.hi);
|
||||||
|
lsp::Range {
|
||||||
|
start: lsp::Position {
|
||||||
|
line: (start.line - 1) as u32,
|
||||||
|
character: start.col_display as u32,
|
||||||
|
},
|
||||||
|
end: lsp::Position {
|
||||||
|
line: (end.line - 1) as u32,
|
||||||
|
character: end.col_display as u32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DenoTestCollector {
|
||||||
|
code_lenses: Vec<lsp::CodeLens>,
|
||||||
|
source_map: Rc<SourceMap>,
|
||||||
|
specifier: ModuleSpecifier,
|
||||||
|
test_vars: HashSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DenoTestCollector {
|
||||||
|
pub fn new(specifier: ModuleSpecifier, source_map: Rc<SourceMap>) -> Self {
|
||||||
|
Self {
|
||||||
|
code_lenses: Vec::new(),
|
||||||
|
source_map,
|
||||||
|
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.source_map.clone());
|
||||||
|
self.code_lenses.push(lsp::CodeLens {
|
||||||
|
range,
|
||||||
|
command: Some(lsp::Command {
|
||||||
|
title: "▶\u{fe0e} Run Test".to_string(),
|
||||||
|
command: "deno.test".to_string(),
|
||||||
|
arguments: Some(vec![json!(self.specifier), json!(name.as_ref())]),
|
||||||
|
}),
|
||||||
|
data: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_call_expr(&mut self, node: &ast::CallExpr, span: &Span) {
|
||||||
|
if let Some(expr) = node.args.get(0).map(|es| es.expr.as_ref()) {
|
||||||
|
match expr {
|
||||||
|
ast::Expr::Object(obj_lit) => {
|
||||||
|
for prop in &obj_lit.props {
|
||||||
|
if let ast::PropOrSpread::Prop(prop) = prop {
|
||||||
|
if let ast::Prop::KeyValue(key_value_prop) = prop.as_ref() {
|
||||||
|
if let ast::PropName::Ident(ident) = &key_value_prop.key {
|
||||||
|
if ident.sym.to_string() == "name" {
|
||||||
|
if let ast::Expr::Lit(ast::Lit::Str(lit_str)) =
|
||||||
|
key_value_prop.value.as_ref()
|
||||||
|
{
|
||||||
|
let name = lit_str.value.to_string();
|
||||||
|
self.add_code_lens(name, &span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::Expr::Lit(ast::Lit::Str(lit_str)) => {
|
||||||
|
let name = lit_str.value.to_string();
|
||||||
|
self.add_code_lens(name, &span);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move out the code lenses from the collector.
|
||||||
|
fn take(self) -> Vec<lsp::CodeLens> {
|
||||||
|
self.code_lenses
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visit for DenoTestCollector {
|
||||||
|
fn visit_call_expr(&mut self, node: &ast::CallExpr, _parent: &dyn Node) {
|
||||||
|
if let ast::ExprOrSuper::Expr(callee_expr) = &node.callee {
|
||||||
|
match callee_expr.as_ref() {
|
||||||
|
ast::Expr::Ident(ident) => {
|
||||||
|
if self.test_vars.contains(&ident.sym.to_string()) {
|
||||||
|
self.check_call_expr(node, &ident.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::Expr::Member(member_expr) => {
|
||||||
|
if let ast::Expr::Ident(ns_prop_ident) = member_expr.prop.as_ref() {
|
||||||
|
if ns_prop_ident.sym.to_string() == "test" {
|
||||||
|
if let ast::ExprOrSuper::Expr(obj_expr) = &member_expr.obj {
|
||||||
|
if let ast::Expr::Ident(ident) = obj_expr.as_ref() {
|
||||||
|
if ident.sym.to_string() == "Deno" {
|
||||||
|
self.check_call_expr(node, &ns_prop_ident.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_var_decl(&mut self, node: &ast::VarDecl, _parent: &dyn Node) {
|
||||||
|
for decl in &node.decls {
|
||||||
|
if let Some(init) = &decl.init {
|
||||||
|
match init.as_ref() {
|
||||||
|
// Identify destructured assignments of `test` from `Deno`
|
||||||
|
ast::Expr::Ident(ident) => {
|
||||||
|
if ident.sym.to_string() == "Deno" {
|
||||||
|
if let ast::Pat::Object(object_pat) = &decl.name {
|
||||||
|
for prop in &object_pat.props {
|
||||||
|
match prop {
|
||||||
|
ast::ObjectPatProp::Assign(prop) => {
|
||||||
|
let name = prop.key.sym.to_string();
|
||||||
|
if name == "test" {
|
||||||
|
self.test_vars.insert(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::ObjectPatProp::KeyValue(prop) => {
|
||||||
|
if let ast::PropName::Ident(key_ident) = &prop.key {
|
||||||
|
if key_ident.sym.to_string() == "test" {
|
||||||
|
if let ast::Pat::Ident(value_ident) =
|
||||||
|
&prop.value.as_ref()
|
||||||
|
{
|
||||||
|
self
|
||||||
|
.test_vars
|
||||||
|
.insert(value_ident.id.sym.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Identify variable assignments where the init is `Deno.test`
|
||||||
|
ast::Expr::Member(member_expr) => {
|
||||||
|
if let ast::ExprOrSuper::Expr(expr) = &member_expr.obj {
|
||||||
|
if let ast::Expr::Ident(obj_ident) = expr.as_ref() {
|
||||||
|
if obj_ident.sym.to_string() == "Deno" {
|
||||||
|
if let ast::Expr::Ident(prop_ident) =
|
||||||
|
&member_expr.prop.as_ref()
|
||||||
|
{
|
||||||
|
if prop_ident.sym.to_string() == "test" {
|
||||||
|
if let ast::Pat::Ident(binding_ident) = &decl.name {
|
||||||
|
self.test_vars.insert(binding_ident.id.sym.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn resolve_implementation_code_lens(
|
async fn resolve_implementation_code_lens(
|
||||||
code_lens: lsp::CodeLens,
|
code_lens: lsp::CodeLens,
|
||||||
data: CodeLensData,
|
data: CodeLensData,
|
||||||
|
@ -189,8 +365,51 @@ pub(crate) async fn resolve_code_lens(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn collect(
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
language_server: &mut language_server::Inner,
|
||||||
|
) -> Result<Vec<lsp::CodeLens>, AnyError> {
|
||||||
|
let mut code_lenses = collect_test(specifier, language_server)?;
|
||||||
|
code_lenses.extend(collect_tsc(specifier, language_server).await?);
|
||||||
|
|
||||||
|
Ok(code_lenses)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_test(
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
language_server: &mut language_server::Inner,
|
||||||
|
) -> 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)
|
||||||
|
{
|
||||||
|
let mut collector = DenoTestCollector::new(
|
||||||
|
specifier.clone(),
|
||||||
|
parsed_module.source_map.clone(),
|
||||||
|
);
|
||||||
|
parsed_module.module.visit_with(
|
||||||
|
&ast::Invalid {
|
||||||
|
span: swc_common::DUMMY_SP,
|
||||||
|
},
|
||||||
|
&mut collector,
|
||||||
|
);
|
||||||
|
return Ok(collector.take());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
/// Return tsc navigation tree code lenses.
|
/// Return tsc navigation tree code lenses.
|
||||||
pub(crate) async fn tsc_code_lenses(
|
async fn collect_tsc(
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
language_server: &mut language_server::Inner,
|
language_server: &mut language_server::Inner,
|
||||||
) -> Result<Vec<lsp::CodeLens>, AnyError> {
|
) -> Result<Vec<lsp::CodeLens>, AnyError> {
|
||||||
|
@ -282,3 +501,80 @@ pub(crate) async fn tsc_code_lenses(
|
||||||
});
|
});
|
||||||
Ok(Rc::try_unwrap(code_lenses).unwrap().into_inner())
|
Ok(Rc::try_unwrap(code_lenses).unwrap().into_inner())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::media_type::MediaType;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deno_test_collector() {
|
||||||
|
let specifier = resolve_url("https://deno.land/x/mod.ts").unwrap();
|
||||||
|
let source = r#"
|
||||||
|
Deno.test({
|
||||||
|
name: "test a",
|
||||||
|
fn() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test("test b", function anotherTest() {});
|
||||||
|
"#;
|
||||||
|
let parsed_module =
|
||||||
|
analysis::parse_module(&specifier, source, &MediaType::TypeScript)
|
||||||
|
.unwrap();
|
||||||
|
let mut collector =
|
||||||
|
DenoTestCollector::new(specifier, parsed_module.source_map.clone());
|
||||||
|
parsed_module.module.visit_with(
|
||||||
|
&ast::Invalid {
|
||||||
|
span: swc_common::DUMMY_SP,
|
||||||
|
},
|
||||||
|
&mut collector,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
collector.take(),
|
||||||
|
vec![
|
||||||
|
lsp::CodeLens {
|
||||||
|
range: lsp::Range {
|
||||||
|
start: lsp::Position {
|
||||||
|
line: 1,
|
||||||
|
character: 11
|
||||||
|
},
|
||||||
|
end: lsp::Position {
|
||||||
|
line: 1,
|
||||||
|
character: 15
|
||||||
|
}
|
||||||
|
},
|
||||||
|
command: Some(lsp::Command {
|
||||||
|
title: "▶\u{fe0e} Run Test".to_string(),
|
||||||
|
command: "deno.test".to_string(),
|
||||||
|
arguments: Some(vec![
|
||||||
|
json!("https://deno.land/x/mod.ts"),
|
||||||
|
json!("test a"),
|
||||||
|
])
|
||||||
|
}),
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
lsp::CodeLens {
|
||||||
|
range: lsp::Range {
|
||||||
|
start: lsp::Position {
|
||||||
|
line: 6,
|
||||||
|
character: 11
|
||||||
|
},
|
||||||
|
end: lsp::Position {
|
||||||
|
line: 6,
|
||||||
|
character: 15
|
||||||
|
}
|
||||||
|
},
|
||||||
|
command: Some(lsp::Command {
|
||||||
|
title: "▶\u{fe0e} Run Test".to_string(),
|
||||||
|
command: "deno.test".to_string(),
|
||||||
|
arguments: Some(vec![
|
||||||
|
json!("https://deno.land/x/mod.ts"),
|
||||||
|
json!("test b"),
|
||||||
|
])
|
||||||
|
}),
|
||||||
|
data: None,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ pub struct ClientCapabilities {
|
||||||
pub line_folding_only: bool,
|
pub line_folding_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_true() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CodeLensSettings {
|
pub struct CodeLensSettings {
|
||||||
|
@ -41,6 +45,10 @@ pub struct CodeLensSettings {
|
||||||
/// an impact, the `references` flag needs to be `true`.
|
/// an impact, the `references` flag needs to be `true`.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub references_all_functions: bool,
|
pub references_all_functions: bool,
|
||||||
|
/// Flag for providing test code lens on `Deno.test` statements. There is
|
||||||
|
/// also the `test_args` setting, but this is not used by the server.
|
||||||
|
#[serde(default = "is_true")]
|
||||||
|
pub test: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CodeLensSettings {
|
impl Default for CodeLensSettings {
|
||||||
|
@ -49,12 +57,24 @@ impl Default for CodeLensSettings {
|
||||||
implementations: false,
|
implementations: false,
|
||||||
references: false,
|
references: false,
|
||||||
references_all_functions: false,
|
references_all_functions: false,
|
||||||
|
test: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_true() -> bool {
|
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||||
true
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CodeLensSpecifierSettings {
|
||||||
|
/// Flag for providing test code lens on `Deno.test` statements. There is
|
||||||
|
/// also the `test_args` setting, but this is not used by the server.
|
||||||
|
#[serde(default = "is_true")]
|
||||||
|
pub test: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CodeLensSpecifierSettings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { test: true }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||||
|
@ -109,9 +129,13 @@ impl Default for ImportCompletionSettings {
|
||||||
/// Deno language server specific settings that can be applied uniquely to a
|
/// Deno language server specific settings that can be applied uniquely to a
|
||||||
/// specifier.
|
/// specifier.
|
||||||
#[derive(Debug, Default, Clone, Deserialize)]
|
#[derive(Debug, Default, Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SpecifierSettings {
|
pub struct SpecifierSettings {
|
||||||
/// A flag that indicates if Deno is enabled for this specifier or not.
|
/// A flag that indicates if Deno is enabled for this specifier or not.
|
||||||
pub enable: bool,
|
pub enable: bool,
|
||||||
|
/// Code lens specific settings for the resource.
|
||||||
|
#[serde(default)]
|
||||||
|
pub code_lens: CodeLensSpecifierSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deno language server specific settings that are applied to a workspace.
|
/// Deno language server specific settings that are applied to a workspace.
|
||||||
|
@ -324,11 +348,21 @@ impl Config {
|
||||||
|
|
||||||
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
|
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
|
||||||
let settings = self.settings.read().unwrap();
|
let settings = self.settings.read().unwrap();
|
||||||
if let Some(specifier_settings) = settings.specifiers.get(specifier) {
|
settings
|
||||||
specifier_settings.1.enable
|
.specifiers
|
||||||
} else {
|
.get(specifier)
|
||||||
settings.workspace.enable
|
.map(|(_, s)| s.enable)
|
||||||
}
|
.unwrap_or_else(|| settings.workspace.enable)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn specifier_code_lens_test(&self, specifier: &ModuleSpecifier) -> bool {
|
||||||
|
let settings = self.settings.read().unwrap();
|
||||||
|
let value = settings
|
||||||
|
.specifiers
|
||||||
|
.get(specifier)
|
||||||
|
.map(|(_, s)| s.code_lens.test)
|
||||||
|
.unwrap_or_else(|| settings.workspace.code_lens.test);
|
||||||
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::redundant_closure_call)]
|
#[allow(clippy::redundant_closure_call)]
|
||||||
|
@ -449,6 +483,7 @@ mod tests {
|
||||||
implementations: false,
|
implementations: false,
|
||||||
references: false,
|
references: false,
|
||||||
references_all_functions: false,
|
references_all_functions: false,
|
||||||
|
test: true,
|
||||||
},
|
},
|
||||||
internal_debug: false,
|
internal_debug: false,
|
||||||
lint: false,
|
lint: false,
|
||||||
|
|
|
@ -243,7 +243,10 @@ impl Inner {
|
||||||
// moment
|
// moment
|
||||||
/// Searches already cached assets and documents and returns its text
|
/// Searches already cached assets and documents and returns its text
|
||||||
/// content. If not found, `None` is returned.
|
/// content. If not found, `None` is returned.
|
||||||
fn get_text_content(&self, specifier: &ModuleSpecifier) -> Option<String> {
|
pub(crate) fn get_text_content(
|
||||||
|
&self,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
) -> Option<String> {
|
||||||
if specifier.scheme() == "asset" {
|
if specifier.scheme() == "asset" {
|
||||||
self
|
self
|
||||||
.assets
|
.assets
|
||||||
|
@ -256,6 +259,17 @@ impl Inner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_media_type(
|
||||||
|
&self,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
) -> Option<MediaType> {
|
||||||
|
if specifier.scheme() == "asset" || self.documents.contains_key(specifier) {
|
||||||
|
Some(MediaType::from(specifier))
|
||||||
|
} else {
|
||||||
|
self.sources.get_media_type(specifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_navigation_tree(
|
pub(crate) async fn get_navigation_tree(
|
||||||
&mut self,
|
&mut self,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
|
@ -1099,15 +1113,15 @@ impl Inner {
|
||||||
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_diagnosable(&specifier)
|
if !self.documents.is_diagnosable(&specifier)
|
||||||
|| !self.config.specifier_enabled(&specifier)
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|| !self.config.get_workspace_settings().enabled_code_lens()
|
|| !(self.config.get_workspace_settings().enabled_code_lens()
|
||||||
|
|| self.config.specifier_code_lens_test(&specifier))
|
||||||
{
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mark = self.performance.mark("code_lens", Some(¶ms));
|
let mark = self.performance.mark("code_lens", Some(¶ms));
|
||||||
let code_lenses = code_lens::tsc_code_lenses(&specifier, self)
|
let code_lenses =
|
||||||
.await
|
code_lens::collect(&specifier, self).await.map_err(|err| {
|
||||||
.map_err(|err| {
|
|
||||||
error!("Error getting code lenses for \"{}\": {}", specifier, err);
|
error!("Error getting code lenses for \"{}\": {}", specifier, err);
|
||||||
LspError::internal_error()
|
LspError::internal_error()
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -45,7 +45,15 @@ where
|
||||||
let (id, method, _) = client.read_request::<Value>().unwrap();
|
let (id, method, _) = client.read_request::<Value>().unwrap();
|
||||||
assert_eq!(method, "workspace/configuration");
|
assert_eq!(method, "workspace/configuration");
|
||||||
client
|
client
|
||||||
.write_response(id, json!({ "enable": true }))
|
.write_response(
|
||||||
|
id,
|
||||||
|
json!({
|
||||||
|
"enable": true,
|
||||||
|
"codeLens": {
|
||||||
|
"test": true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut diagnostics = vec![];
|
let mut diagnostics = vec![];
|
||||||
|
@ -1229,6 +1237,76 @@ fn lsp_code_lens_impl() {
|
||||||
shutdown(&mut client);
|
shutdown(&mut client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lsp_code_lens_test() {
|
||||||
|
let mut client = init("initialize_params_code_lens_test.json");
|
||||||
|
did_open(
|
||||||
|
&mut client,
|
||||||
|
load_fixture("did_open_params_test_code_lens.json"),
|
||||||
|
);
|
||||||
|
let (maybe_res, maybe_err) = client
|
||||||
|
.write_request(
|
||||||
|
"textDocument/codeLens",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.ts"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(maybe_err.is_none());
|
||||||
|
assert_eq!(
|
||||||
|
maybe_res,
|
||||||
|
Some(load_fixture("code_lens_response_test.json"))
|
||||||
|
);
|
||||||
|
shutdown(&mut client);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lsp_code_lens_test_disabled() {
|
||||||
|
let mut client = init("initialize_params_code_lens_test_disabled.json");
|
||||||
|
client
|
||||||
|
.write_notification(
|
||||||
|
"textDocument/didOpen",
|
||||||
|
load_fixture("did_open_params_test_code_lens.json"),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (id, method, _) = client.read_request::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "workspace/configuration");
|
||||||
|
client
|
||||||
|
.write_response(
|
||||||
|
id,
|
||||||
|
json!({
|
||||||
|
"enable": true,
|
||||||
|
"codeLens": {
|
||||||
|
"test": false
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
let (maybe_res, maybe_err) = client
|
||||||
|
.write_request(
|
||||||
|
"textDocument/codeLens",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.ts"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(maybe_err.is_none());
|
||||||
|
assert_eq!(maybe_res, Some(json!([])));
|
||||||
|
shutdown(&mut client);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lsp_code_lens_non_doc_nav_tree() {
|
fn lsp_code_lens_non_doc_nav_tree() {
|
||||||
let mut client = init("initialize_params.json");
|
let mut client = init("initialize_params.json");
|
||||||
|
|
162
cli/tests/lsp/code_lens_response_test.json
Normal file
162
cli/tests/lsp/code_lens_response_test.json
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 4,
|
||||||
|
"character": 5
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 4,
|
||||||
|
"character": 9
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"title": "▶︎ Run Test",
|
||||||
|
"command": "deno.test",
|
||||||
|
"arguments": [
|
||||||
|
"file:///a/file.ts",
|
||||||
|
"test a"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 5,
|
||||||
|
"character": 5
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 5,
|
||||||
|
"character": 9
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"title": "▶︎ Run Test",
|
||||||
|
"command": "deno.test",
|
||||||
|
"arguments": [
|
||||||
|
"file:///a/file.ts",
|
||||||
|
"test b"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 9,
|
||||||
|
"character": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 9,
|
||||||
|
"character": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"title": "▶︎ Run Test",
|
||||||
|
"command": "deno.test",
|
||||||
|
"arguments": [
|
||||||
|
"file:///a/file.ts",
|
||||||
|
"test c"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 13,
|
||||||
|
"character": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 13,
|
||||||
|
"character": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"title": "▶︎ Run Test",
|
||||||
|
"command": "deno.test",
|
||||||
|
"arguments": [
|
||||||
|
"file:///a/file.ts",
|
||||||
|
"test d"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 14,
|
||||||
|
"character": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 14,
|
||||||
|
"character": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"title": "▶︎ Run Test",
|
||||||
|
"command": "deno.test",
|
||||||
|
"arguments": [
|
||||||
|
"file:///a/file.ts",
|
||||||
|
"test e"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 18,
|
||||||
|
"character": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 18,
|
||||||
|
"character": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"title": "▶︎ Run Test",
|
||||||
|
"command": "deno.test",
|
||||||
|
"arguments": [
|
||||||
|
"file:///a/file.ts",
|
||||||
|
"test f"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 19,
|
||||||
|
"character": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 19,
|
||||||
|
"character": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"title": "▶︎ Run Test",
|
||||||
|
"command": "deno.test",
|
||||||
|
"arguments": [
|
||||||
|
"file:///a/file.ts",
|
||||||
|
"test g"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"range": {
|
||||||
|
"start": {
|
||||||
|
"line": 23,
|
||||||
|
"character": 0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 23,
|
||||||
|
"character": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"title": "▶︎ Run Test",
|
||||||
|
"command": "deno.test",
|
||||||
|
"arguments": [
|
||||||
|
"file:///a/file.ts",
|
||||||
|
"test h"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
8
cli/tests/lsp/did_open_params_test_code_lens.json
Normal file
8
cli/tests/lsp/did_open_params_test_code_lens.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/file.ts",
|
||||||
|
"languageId": "typescript",
|
||||||
|
"version": 1,
|
||||||
|
"text": "const { test } = Deno;\nconst { test: test2 } = Deno;\nconst test3 = Deno.test;\n\nDeno.test(\"test a\", () => {});\nDeno.test({\n name: \"test b\",\n fn() {},\n});\ntest({\n name: \"test c\",\n fn() {},\n});\ntest(\"test d\", () => {});\ntest2({\n name: \"test e\",\n fn() {},\n});\ntest2(\"test f\", () => {});\ntest3({\n name: \"test g\",\n fn() {},\n});\ntest3(\"test h\", () => {});\n"
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,8 @@
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"codeLens": {
|
"codeLens": {
|
||||||
"implementations": true,
|
"implementations": true,
|
||||||
"references": true
|
"references": true,
|
||||||
|
"test": true
|
||||||
},
|
},
|
||||||
"importMap": null,
|
"importMap": null,
|
||||||
"lint": true,
|
"lint": true,
|
||||||
|
|
56
cli/tests/lsp/initialize_params_code_lens_test.json
Normal file
56
cli/tests/lsp/initialize_params_code_lens_test.json
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
{
|
||||||
|
"processId": 0,
|
||||||
|
"clientInfo": {
|
||||||
|
"name": "test-harness",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"rootUri": null,
|
||||||
|
"initializationOptions": {
|
||||||
|
"enable": true,
|
||||||
|
"importMap": null,
|
||||||
|
"lint": true,
|
||||||
|
"suggest": {
|
||||||
|
"autoImports": true,
|
||||||
|
"completeFunctionCalls": false,
|
||||||
|
"names": true,
|
||||||
|
"paths": true,
|
||||||
|
"imports": {
|
||||||
|
"hosts": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unstable": false
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"textDocument": {
|
||||||
|
"codeAction": {
|
||||||
|
"codeActionLiteralSupport": {
|
||||||
|
"codeActionKind": {
|
||||||
|
"valueSet": [
|
||||||
|
"quickfix"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isPreferredSupport": true,
|
||||||
|
"dataSupport": true,
|
||||||
|
"resolveSupport": {
|
||||||
|
"properties": [
|
||||||
|
"edit"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foldingRange": {
|
||||||
|
"lineFoldingOnly": true
|
||||||
|
},
|
||||||
|
"synchronization": {
|
||||||
|
"dynamicRegistration": true,
|
||||||
|
"willSave": true,
|
||||||
|
"willSaveWaitUntil": true,
|
||||||
|
"didSave": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"configuration": true,
|
||||||
|
"workspaceFolders": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
cli/tests/lsp/initialize_params_code_lens_test_disabled.json
Normal file
61
cli/tests/lsp/initialize_params_code_lens_test_disabled.json
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"processId": 0,
|
||||||
|
"clientInfo": {
|
||||||
|
"name": "test-harness",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"rootUri": null,
|
||||||
|
"initializationOptions": {
|
||||||
|
"enable": true,
|
||||||
|
"codeLens": {
|
||||||
|
"implementations": true,
|
||||||
|
"references": true,
|
||||||
|
"test": false
|
||||||
|
},
|
||||||
|
"importMap": null,
|
||||||
|
"lint": true,
|
||||||
|
"suggest": {
|
||||||
|
"autoImports": true,
|
||||||
|
"completeFunctionCalls": false,
|
||||||
|
"names": true,
|
||||||
|
"paths": true,
|
||||||
|
"imports": {
|
||||||
|
"hosts": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unstable": false
|
||||||
|
},
|
||||||
|
"capabilities": {
|
||||||
|
"textDocument": {
|
||||||
|
"codeAction": {
|
||||||
|
"codeActionLiteralSupport": {
|
||||||
|
"codeActionKind": {
|
||||||
|
"valueSet": [
|
||||||
|
"quickfix"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isPreferredSupport": true,
|
||||||
|
"dataSupport": true,
|
||||||
|
"resolveSupport": {
|
||||||
|
"properties": [
|
||||||
|
"edit"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foldingRange": {
|
||||||
|
"lineFoldingOnly": true
|
||||||
|
},
|
||||||
|
"synchronization": {
|
||||||
|
"dynamicRegistration": true,
|
||||||
|
"willSave": true,
|
||||||
|
"willSaveWaitUntil": true,
|
||||||
|
"didSave": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"configuration": true,
|
||||||
|
"workspaceFolders": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue