From 5d23d8bc299b718e7f026a7e6c1363dde3342817 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 27 Feb 2021 17:59:52 +0300 Subject: Add runnables::related_tests --- crates/cfg/src/cfg_expr.rs | 2 +- crates/ide/src/lib.rs | 9 ++ crates/ide/src/runnables.rs | 338 +++++++++++++++++++++++++++++++++++++++++++- crates/ide_db/src/search.rs | 4 + 4 files changed, 346 insertions(+), 7 deletions(-) diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs index 42327f1e1..069fc01d0 100644 --- a/crates/cfg/src/cfg_expr.rs +++ b/crates/cfg/src/cfg_expr.rs @@ -49,7 +49,7 @@ impl fmt::Display for CfgAtom { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CfgExpr { Invalid, Atom(CfgAtom), diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index b600178ee..baa80cf43 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -445,6 +445,15 @@ impl Analysis { self.with_db(|db| runnables::runnables(db, file_id)) } + /// Returns the set of tests for the given file position. + pub fn related_tests( + &self, + position: FilePosition, + search_scope: Option, + ) -> Cancelable> { + self.with_db(|db| runnables::related_tests(db, position, search_scope)) + } + /// Computes syntax highlighting for the given file pub fn highlight(&self, file_id: FileId) -> Cancelable> { self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 1e7baed20..ce3a2e7ba 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -1,10 +1,14 @@ use std::fmt; +use ast::NameOwner; use cfg::CfgExpr; use hir::{AsAssocItem, HasAttrs, HasSource, Semantics}; use ide_assists::utils::test_related_attribute; -use ide_db::{defs::Definition, RootDatabase, SymbolKind}; +use ide_db::{ + base_db::FilePosition, defs::Definition, search::SearchScope, RootDatabase, SymbolKind, +}; use itertools::Itertools; +use rustc_hash::FxHashSet; use syntax::{ ast::{self, AstNode, AttrsOwner}, match_ast, SyntaxNode, @@ -13,17 +17,17 @@ use test_utils::mark; use crate::{ display::{ToNav, TryToNav}, - FileId, NavigationTarget, + references, FileId, NavigationTarget, }; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Runnable { pub nav: NavigationTarget, pub kind: RunnableKind, pub cfg: Option, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum TestId { Name(String), Path(String), @@ -38,7 +42,7 @@ impl fmt::Display for TestId { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum RunnableKind { Test { test_id: TestId, attr: TestAttr }, TestMod { path: String }, @@ -106,6 +110,102 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { res } +// Feature: Run Test +// +// Shows a popup suggesting to run a test in which the item **at the current cursor +// location** is used (if any). +// +// |=== +// | Editor | Action Name +// +// | VS Code | **Rust Analyzer: Run Test** +// |=== +pub(crate) fn related_tests( + db: &RootDatabase, + position: FilePosition, + search_scope: Option, +) -> Vec { + let sema = Semantics::new(db); + let mut res: FxHashSet = FxHashSet::default(); + + find_related_tests(&sema, position, search_scope, &mut res); + + res.into_iter().collect_vec() +} + +fn find_related_tests( + sema: &Semantics, + position: FilePosition, + search_scope: Option, + tests: &mut FxHashSet, +) { + if let Some(refs) = references::find_all_refs(&sema, position, search_scope) { + for (file_id, refs) in refs.references { + let file = sema.parse(file_id); + let file = file.syntax(); + let functions = refs.iter().filter_map(|(range, _)| { + let token = file.token_at_offset(range.start()).next()?; + let token = sema.descend_into_macros(token); + let syntax = token.parent(); + syntax.ancestors().find_map(ast::Fn::cast) + }); + + for fn_def in functions { + if let Some(runnable) = as_test_runnable(&sema, &fn_def) { + // direct test + tests.insert(runnable); + } else if let Some(module) = parent_test_module(&sema, &fn_def) { + // indirect test + find_related_tests_in_module(sema, &fn_def, &module, tests); + } + } + } + } +} + +fn find_related_tests_in_module( + sema: &Semantics, + fn_def: &ast::Fn, + parent_module: &hir::Module, + tests: &mut FxHashSet, +) { + if let Some(fn_name) = fn_def.name() { + let mod_source = parent_module.definition_source(sema.db); + let range = match mod_source.value { + hir::ModuleSource::Module(m) => m.syntax().text_range(), + hir::ModuleSource::BlockExpr(b) => b.syntax().text_range(), + hir::ModuleSource::SourceFile(f) => f.syntax().text_range(), + }; + + let file_id = mod_source.file_id.original_file(sema.db); + let mod_scope = SearchScope::file_part(file_id, range); + let fn_pos = FilePosition { file_id, offset: fn_name.syntax().text_range().start() }; + find_related_tests(sema, fn_pos, Some(mod_scope), tests) + } +} + +fn as_test_runnable(sema: &Semantics, fn_def: &ast::Fn) -> Option { + if test_related_attribute(&fn_def).is_some() { + let function = sema.to_def(fn_def)?; + runnable_fn(sema, function) + } else { + None + } +} + +fn parent_test_module(sema: &Semantics, fn_def: &ast::Fn) -> Option { + fn_def.syntax().ancestors().find_map(|node| { + let module = ast::Module::cast(node)?; + let module = sema.to_def(&module)?; + + if has_test_function_or_multiple_test_submodules(sema, &module) { + Some(module) + } else { + None + } + }) +} + fn runnables_mod(sema: &Semantics, acc: &mut Vec, module: hir::Module) { acc.extend(module.declarations(sema.db).into_iter().filter_map(|def| { let runnable = match def { @@ -255,7 +355,7 @@ fn module_def_doctest(sema: &Semantics, def: hir::ModuleDef) -> Op Some(res) } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct TestAttr { pub ignore: bool, } @@ -349,6 +449,12 @@ mod tests { ); } + fn check_tests(ra_fixture: &str, expect: Expect) { + let (analysis, position) = fixture::position(ra_fixture); + let tests = analysis.related_tests(position, None).unwrap(); + expect.assert_debug_eq(&tests); + } + #[test] fn test_runnables() { check( @@ -1074,4 +1180,224 @@ mod tests { "#]], ); } + + #[test] + fn find_no_tests() { + check_tests( + r#" +//- /lib.rs +fn foo$0() { }; +"#, + expect![[r#" + [] + "#]], + ); + } + + #[test] + fn find_direct_fn_test() { + check_tests( + r#" +//- /lib.rs +fn foo$0() { }; + +mod tests { + #[test] + fn foo_test() { + super::foo() + } +} +"#, + expect![[r#" + [ + Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 31..85, + focus_range: 46..54, + name: "foo_test", + kind: Function, + }, + kind: Test { + test_id: Path( + "tests::foo_test", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + ] + "#]], + ); + } + + #[test] + fn find_direct_struct_test() { + check_tests( + r#" +//- /lib.rs +struct Fo$0o; +fn foo(arg: &Foo) { }; + +mod tests { + use super::*; + + #[test] + fn foo_test() { + foo(Foo); + } +} +"#, + expect![[r#" + [ + Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 71..122, + focus_range: 86..94, + name: "foo_test", + kind: Function, + }, + kind: Test { + test_id: Path( + "tests::foo_test", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + ] + "#]], + ); + } + + #[test] + fn find_indirect_fn_test() { + check_tests( + r#" +//- /lib.rs +fn foo$0() { }; + +mod tests { + use super::foo; + + fn check1() { + check2() + } + + fn check2() { + foo() + } + + #[test] + fn foo_test() { + check1() + } +} +"#, + expect![[r#" + [ + Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 133..183, + focus_range: 148..156, + name: "foo_test", + kind: Function, + }, + kind: Test { + test_id: Path( + "tests::foo_test", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + ] + "#]], + ); + } + + #[test] + fn tests_are_unique() { + check_tests( + r#" +//- /lib.rs +fn foo$0() { }; + +mod tests { + use super::foo; + + #[test] + fn foo_test() { + foo(); + foo(); + } + + #[test] + fn foo2_test() { + foo(); + foo(); + } + +} +"#, + expect![[r#" + [ + Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 52..115, + focus_range: 67..75, + name: "foo_test", + kind: Function, + }, + kind: Test { + test_id: Path( + "tests::foo_test", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + Runnable { + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 121..185, + focus_range: 136..145, + name: "foo2_test", + kind: Function, + }, + kind: Test { + test_id: Path( + "tests::foo2_test", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + ] + "#]], + ); + } } diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index ddcfbd3f3..8b211256e 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs @@ -86,6 +86,10 @@ impl SearchScope { SearchScope::new(std::iter::once((file, None)).collect()) } + pub fn file_part(file: FileId, range: TextRange) -> SearchScope { + SearchScope::new(std::iter::once((file, Some(range))).collect()) + } + pub fn files(files: &[FileId]) -> SearchScope { SearchScope::new(files.iter().map(|f| (*f, None)).collect()) } -- cgit v1.2.3 From 31f5f816e3747c1a0972d2f0aca25ded9980cd36 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 27 Feb 2021 19:07:14 +0300 Subject: Remove unnecessary file_id argument --- crates/rust-analyzer/src/handlers.rs | 12 +++++------- crates/rust-analyzer/src/to_proto.rs | 5 ++--- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 4f6f250d6..f198b1f25 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -555,7 +555,7 @@ pub(crate) fn handle_runnables( if should_skip_target(&runnable, cargo_spec.as_ref()) { continue; } - let mut runnable = to_proto::runnable(&snap, file_id, runnable)?; + let mut runnable = to_proto::runnable(&snap, runnable)?; if expect_test { runnable.label = format!("{} + expect", runnable.label); runnable.args.expect_test = Some(true); @@ -773,7 +773,7 @@ pub(crate) fn handle_hover( contents: HoverContents::Markup(to_proto::markup_content(info.info.markup)), range: Some(range), }, - actions: prepare_hover_actions(&snap, position.file_id, &info.info.actions), + actions: prepare_hover_actions(&snap, &info.info.actions), }; Ok(Some(hover)) @@ -1438,17 +1438,16 @@ fn show_impl_command_link( fn runnable_action_links( snap: &GlobalStateSnapshot, - file_id: FileId, runnable: Runnable, ) -> Option { - let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?; + let cargo_spec = CargoTargetSpec::for_file(&snap, runnable.nav.file_id).ok()?; let hover_config = snap.config.hover(); if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) { return None; } let action: &'static _ = runnable.action(); - to_proto::runnable(snap, file_id, runnable).ok().map(|r| { + to_proto::runnable(snap, runnable).ok().map(|r| { let mut group = lsp_ext::CommandLinkGroup::default(); if hover_config.run { @@ -1487,7 +1486,6 @@ fn goto_type_action_links( fn prepare_hover_actions( snap: &GlobalStateSnapshot, - file_id: FileId, actions: &[HoverAction], ) -> Vec { if snap.config.hover().none() || !snap.config.hover_actions() { @@ -1498,7 +1496,7 @@ fn prepare_hover_actions( .iter() .filter_map(|it| match it { HoverAction::Implementation(position) => show_impl_command_link(snap, position), - HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()), + HoverAction::Runnable(r) => runnable_action_links(snap, r.clone()), HoverAction::GoToType(targets) => goto_type_action_links(snap, targets), }) .collect() diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 70cb7fbab..e8fd9bb28 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -828,11 +828,10 @@ pub(crate) fn resolved_code_action( pub(crate) fn runnable( snap: &GlobalStateSnapshot, - file_id: FileId, runnable: Runnable, ) -> Result { let config = snap.config.runnables(); - let spec = CargoTargetSpec::for_file(snap, file_id)?; + let spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id)?; let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone()); let target = spec.as_ref().map(|s| s.target.clone()); let (cargo_args, executable_args) = @@ -865,7 +864,7 @@ pub(crate) fn code_lens( let annotation_range = range(&line_index, annotation.range); let action = run.action(); - let r = runnable(&snap, run.nav.file_id, run)?; + let r = runnable(&snap, run)?; let command = if debug { command::debug_single(&r) -- cgit v1.2.3 From 669e11764430be3a098d6c8fe875d8efbb3547a3 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 27 Feb 2021 20:04:43 +0300 Subject: Add LSP request and VSCode command --- crates/rust-analyzer/src/handlers.rs | 18 +++++++++++++ crates/rust-analyzer/src/lsp_ext.rs | 20 ++++++++++++++ crates/rust-analyzer/src/main_loop.rs | 1 + editors/code/package.json | 5 ++++ editors/code/src/commands.ts | 51 ++++++++++++++++++++++++++++------- editors/code/src/lsp_ext.ts | 9 +++++++ editors/code/src/main.ts | 1 + 7 files changed, 96 insertions(+), 9 deletions(-) diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index f198b1f25..53b0d3e41 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -607,6 +607,24 @@ pub(crate) fn handle_runnables( Ok(res) } +pub(crate) fn handle_related_tests( + snap: GlobalStateSnapshot, + params: lsp_ext::RelatedTestsParams, +) -> Result> { + let _p = profile::span("handle_related_tests"); + let position = from_proto::file_position(&snap, params.text_document_position)?; + + let tests = snap.analysis.related_tests(position, None)?; + let mut res = Vec::new(); + for it in tests { + if let Ok(runnable) = to_proto::runnable(&snap, it) { + res.push(lsp_ext::TestInfo { runnable }) + } + } + + Ok(res) +} + pub(crate) fn handle_completion( snap: GlobalStateSnapshot, params: lsp_types::CompletionParams, diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 0d2c8f7ff..fe11903f9 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -177,6 +177,26 @@ pub struct CargoRunnable { pub expect_test: Option, } +pub enum RelatedTests {} + +impl Request for RelatedTests { + type Params = RelatedTestsParams; + type Result = Vec; + const METHOD: &'static str = "rust-analyzer/relatedTests"; +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RelatedTestsParams { + #[serde(flatten)] + pub text_document_position: lsp_types::TextDocumentPositionParams, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct TestInfo { + pub runnable: Runnable, +} + pub enum InlayHints {} impl Request for InlayHints { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 2829d5970..9f86b8c0d 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -500,6 +500,7 @@ impl GlobalState { .on::(handlers::handle_expand_macro) .on::(handlers::handle_parent_module) .on::(handlers::handle_runnables) + .on::(handlers::handle_related_tests) .on::(handlers::handle_inlay_hints) .on::(handlers::handle_code_action) .on::(handlers::handle_code_action_resolve) diff --git a/editors/code/package.json b/editors/code/package.json index e3e0ebff0..43ea1225a 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -202,6 +202,11 @@ "command": "rust-analyzer.openCargoToml", "title": "Open Cargo.toml", "category": "Rust Analyzer" + }, + { + "command": "rust-analyzer.peekTests", + "title": "Peek related tests", + "category": "Rust Analyzer" } ], "keybindings": [ diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 283b9a160..3512fefdf 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -9,6 +9,7 @@ import { RunnableQuickPick, selectRunnable, createTask, createArgs } from './run import { AstInspector } from './ast_inspector'; import { isRustDocument, sleep, isRustEditor } from './util'; import { startDebugSession, makeDebugConfig } from './debug'; +import { LanguageClient } from 'vscode-languageclient/node'; export * from './ast_inspector'; export * from './run'; @@ -456,17 +457,20 @@ export function reloadWorkspace(ctx: Ctx): Cmd { return async () => ctx.client.sendRequest(ra.reloadWorkspace); } +async function showReferencesImpl(client: LanguageClient, uri: string, position: lc.Position, locations: lc.Location[]) { + if (client) { + await vscode.commands.executeCommand( + 'editor.action.showReferences', + vscode.Uri.parse(uri), + client.protocol2CodeConverter.asPosition(position), + locations.map(client.protocol2CodeConverter.asLocation), + ); + } +} + export function showReferences(ctx: Ctx): Cmd { return async (uri: string, position: lc.Position, locations: lc.Location[]) => { - const client = ctx.client; - if (client) { - await vscode.commands.executeCommand( - 'editor.action.showReferences', - vscode.Uri.parse(uri), - client.protocol2CodeConverter.asPosition(position), - locations.map(client.protocol2CodeConverter.asLocation), - ); - } + await showReferencesImpl(ctx.client, uri, position, locations); }; } @@ -555,6 +559,35 @@ export function run(ctx: Ctx): Cmd { }; } +export function peekTests(ctx: Ctx): Cmd { + const client = ctx.client; + + return async () => { + const editor = ctx.activeRustEditor; + if (!editor || !client) return; + + const uri = editor.document.uri.toString(); + const position = client.code2ProtocolConverter.asPosition( + editor.selection.active, + ); + + const tests = await client.sendRequest(ra.relatedTests, { + textDocument: { uri: uri }, + position: position, + }); + + const locations: lc.Location[] = tests.map( it => { + return { + uri: it.runnable.location!.targetUri, + range: it.runnable.location!.targetSelectionRange + }; + }); + + await showReferencesImpl(client, uri, position, locations); + }; +} + + export function runSingle(ctx: Ctx): Cmd { return async (runnable: ra.Runnable) => { const editor = ctx.activeRustEditor; diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 2de1e427d..11d4d5f00 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -72,6 +72,15 @@ export interface Runnable { } export const runnables = new lc.RequestType("experimental/runnables"); +export interface RelatedTestsParams extends lc.TextDocumentPositionParams { +} + +export interface TestInfo { + runnable: Runnable; +} + +export const relatedTests = new lc.RequestType("rust-analyzer/relatedTests"); + export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint; export namespace InlayHint { diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 00393d6e8..f1a2020aa 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -113,6 +113,7 @@ async function tryActivate(context: vscode.ExtensionContext) { ctx.registerCommand('newDebugConfig', commands.newDebugConfig); ctx.registerCommand('openDocs', commands.openDocs); ctx.registerCommand('openCargoToml', commands.openCargoToml); + ctx.registerCommand('peekTests', commands.peekTests); defaultOnEnter.dispose(); ctx.registerCommand('onEnter', commands.onEnter); -- cgit v1.2.3 From 45d4e6b639b627ef9926ecfbc150cdfe8c292ae1 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 27 Feb 2021 21:07:23 +0300 Subject: Add progress reporting --- editors/code/package.json | 9 ++++++++- editors/code/src/commands.ts | 33 +++++++++++++++++---------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index 43ea1225a..7ee5d82ad 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1164,7 +1164,14 @@ "command": "rust-analyzer.openCargoToml", "when": "inRustProject" } + ], + "editor/context": [ + { + "command": "rust-analyzer.peekTests", + "when": "inRustProject", + "group": "navigation@1000" + } ] } } -} +} \ No newline at end of file diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 3512fefdf..d43db7307 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -566,24 +566,25 @@ export function peekTests(ctx: Ctx): Cmd { const editor = ctx.activeRustEditor; if (!editor || !client) return; - const uri = editor.document.uri.toString(); - const position = client.code2ProtocolConverter.asPosition( - editor.selection.active, - ); - - const tests = await client.sendRequest(ra.relatedTests, { - textDocument: { uri: uri }, - position: position, - }); + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: "Looking for tests...", + cancellable: false, + }, async (_progress, _token) => { + const uri = editor.document.uri.toString(); + const position = client.code2ProtocolConverter.asPosition( + editor.selection.active, + ); - const locations: lc.Location[] = tests.map( it => { - return { - uri: it.runnable.location!.targetUri, - range: it.runnable.location!.targetSelectionRange - }; - }); + const tests = await client.sendRequest(ra.relatedTests, { + textDocument: { uri: uri }, + position: position, + }); + const locations: lc.Location[] = tests.map(it => + lc.Location.create(it.runnable.location!.targetUri, it.runnable.location!.targetSelectionRange)); - await showReferencesImpl(client, uri, position, locations); + await showReferencesImpl(client, uri, position, locations); + }); }; } -- cgit v1.2.3 From 5e88436517637dfe34ccdb98b71147ce78f744a5 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 27 Feb 2021 21:07:58 +0300 Subject: Update lsp-extensions.md --- docs/dev/lsp-extensions.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 164c8482e..c5f439083 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@