4499: CodeLens configuration options r=vsrs a=vsrs

This PR
- adds an option to granularly enable\disable all CodeLens, just like the TypeScript extension.
- fixes a minor bug for doctests. It makes no sense to show `Debug` lens for them as cargo `Can't skip running doc tests with --no-run`.

Co-authored-by: vsrs <vit@conrlab.com>
This commit is contained in:
bors[bot]
2020-05-18 07:37:04 +00:00
committed by GitHub
5 changed files with 185 additions and 80 deletions

View File

@@ -33,6 +33,36 @@ pub struct Config {
pub inlay_hints: InlayHintsConfig, pub inlay_hints: InlayHintsConfig,
pub completion: CompletionConfig, pub completion: CompletionConfig,
pub call_info_full: bool, pub call_info_full: bool,
pub lens: LensConfig,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LensConfig {
pub run: bool,
pub debug: bool,
pub impementations: bool,
}
impl Default for LensConfig {
fn default() -> Self {
Self { run: true, debug: true, impementations: true }
}
}
impl LensConfig {
pub const NO_LENS: LensConfig = Self { run: false, debug: false, impementations: false };
pub fn any(&self) -> bool {
self.impementations || self.runnable()
}
pub fn none(&self) -> bool {
!self.any()
}
pub fn runnable(&self) -> bool {
self.run || self.debug
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -107,6 +137,7 @@ impl Default for Config {
..CompletionConfig::default() ..CompletionConfig::default()
}, },
call_info_full: true, call_info_full: true,
lens: LensConfig::default(),
} }
} }
} }
@@ -196,6 +227,16 @@ impl Config {
set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets); set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets);
set(value, "/callInfo/full", &mut self.call_info_full); set(value, "/callInfo/full", &mut self.call_info_full);
let mut lens_enabled = true;
set(value, "/lens/enable", &mut lens_enabled);
if lens_enabled {
set(value, "/lens/run", &mut self.lens.run);
set(value, "/lens/debug", &mut self.lens.debug);
set(value, "/lens/implementations", &mut self.lens.impementations);
} else {
self.lens = LensConfig::NO_LENS;
}
log::info!("Config::update() = {:#?}", self); log::info!("Config::update() = {:#?}", self);
fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {

View File

@@ -812,42 +812,59 @@ pub fn handle_code_lens(
params: lsp_types::CodeLensParams, params: lsp_types::CodeLensParams,
) -> Result<Option<Vec<CodeLens>>> { ) -> Result<Option<Vec<CodeLens>>> {
let _p = profile("handle_code_lens"); let _p = profile("handle_code_lens");
let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
let line_index = world.analysis().file_line_index(file_id)?;
let mut lenses: Vec<CodeLens> = Default::default(); let mut lenses: Vec<CodeLens> = Default::default();
if world.config.lens.none() {
// early return before any db query!
return Ok(Some(lenses));
}
let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
let line_index = world.analysis().file_line_index(file_id)?;
let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?;
if world.config.lens.runnable() {
// Gather runnables // Gather runnables
for runnable in world.analysis().runnables(file_id)? { for runnable in world.analysis().runnables(file_id)? {
let title = match &runnable.kind { let (run_title, debugee) = match &runnable.kind {
RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "\u{fe0e} Run Test", RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => {
RunnableKind::DocTest { .. } => "\u{fe0e} Run Doctest", ("\u{fe0e}Run Test", true)
RunnableKind::Bench { .. } => "Run Bench", }
RunnableKind::DocTest { .. } => {
// cargo does not support -no-run for doctests
("▶️\u{fe0e}Run Doctest", false)
}
RunnableKind::Bench { .. } => {
// Nothing wrong with bench debugging
("Run Bench", true)
}
RunnableKind::Bin => { RunnableKind::Bin => {
// Do not suggest binary run on other target than binary // Do not suggest binary run on other target than binary
match &cargo_spec { match &cargo_spec {
Some(spec) => match spec.target_kind { Some(spec) => match spec.target_kind {
TargetKind::Bin => "Run", TargetKind::Bin => ("Run", true),
_ => continue, _ => continue,
}, },
None => continue, None => continue,
} }
} }
} };
.to_string();
let mut r = to_lsp_runnable(&world, file_id, runnable)?; let mut r = to_lsp_runnable(&world, file_id, runnable)?;
if world.config.lens.run {
let lens = CodeLens { let lens = CodeLens {
range: r.range, range: r.range,
command: Some(Command { command: Some(Command {
title, title: run_title.to_string(),
command: "rust-analyzer.runSingle".into(), command: "rust-analyzer.runSingle".into(),
arguments: Some(vec![to_value(&r).unwrap()]), arguments: Some(vec![to_value(&r).unwrap()]),
}), }),
data: None, data: None,
}; };
lenses.push(lens); lenses.push(lens);
}
if debugee && world.config.lens.debug {
if r.args[0] == "run" { if r.args[0] == "run" {
r.args[0] = "build".into(); r.args[0] = "build".into();
} else { } else {
@@ -864,7 +881,10 @@ pub fn handle_code_lens(
}; };
lenses.push(debug_lens); lenses.push(debug_lens);
} }
}
}
if world.config.lens.impementations {
// Handle impls // Handle impls
lenses.extend( lenses.extend(
world world
@@ -893,7 +913,7 @@ pub fn handle_code_lens(
} }
}), }),
); );
}
Ok(Some(lenses)) Ok(Some(lenses))
} }

View File

@@ -443,6 +443,26 @@
"type": "object", "type": "object",
"default": {}, "default": {},
"description": "Optional settings passed to the debug engine. Example:\n{ \"lldb\": { \"terminal\":\"external\"} }" "description": "Optional settings passed to the debug engine. Example:\n{ \"lldb\": { \"terminal\":\"external\"} }"
},
"rust-analyzer.lens.enable": {
"description": "Whether to show CodeLens in Rust files.",
"type": "boolean",
"default": true
},
"rust-analyzer.lens.run": {
"markdownDescription": "Whether to show Run lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
"type": "boolean",
"default": true
},
"rust-analyzer.lens.debug": {
"markdownDescription": "Whether to show Debug lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
"type": "boolean",
"default": true
},
"rust-analyzer.lens.implementations": {
"markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.",
"type": "boolean",
"default": true
} }
} }
}, },

View File

@@ -7,7 +7,7 @@ import { startDebugSession, getDebugConfiguration } from '../debug';
const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }];
async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> { async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debuggeeOnly = false, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> {
const editor = ctx.activeRustEditor; const editor = ctx.activeRustEditor;
const client = ctx.client; const client = ctx.client;
if (!editor || !client) return; if (!editor || !client) return;
@@ -33,9 +33,20 @@ async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showBu
) { ) {
continue; continue;
} }
if (debuggeeOnly && (r.label.startsWith('doctest') || r.label.startsWith('cargo'))) {
continue;
}
items.push(new RunnableQuickPick(r)); items.push(new RunnableQuickPick(r));
} }
if (items.length === 0) {
// it is the debug case, run always has at least 'cargo check ...'
// see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables
vscode.window.showErrorMessage("There's no debug target!");
return;
}
return await new Promise((resolve) => { return await new Promise((resolve) => {
const disposables: vscode.Disposable[] = []; const disposables: vscode.Disposable[] = [];
const close = (result?: RunnableQuickPick) => { const close = (result?: RunnableQuickPick) => {
@@ -107,7 +118,7 @@ export function debug(ctx: Ctx): Cmd {
let prevDebuggee: RunnableQuickPick | undefined; let prevDebuggee: RunnableQuickPick | undefined;
return async () => { return async () => {
const item = await selectRunnable(ctx, prevDebuggee); const item = await selectRunnable(ctx, prevDebuggee, true);
if (!item) return; if (!item) return;
item.detail = 'restart'; item.detail = 'restart';
@@ -147,7 +158,7 @@ async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise<void>
export function newDebugConfig(ctx: Ctx): Cmd { export function newDebugConfig(ctx: Ctx): Cmd {
return async () => { return async () => {
const item = await selectRunnable(ctx, undefined, false); const item = await selectRunnable(ctx, undefined, true, false);
if (!item) return; if (!item) return;
await makeDebugConfig(ctx, item); await makeDebugConfig(ctx, item);

View File

@@ -16,6 +16,10 @@ export class Config {
"files", "files",
"highlighting", "highlighting",
"updates.channel", "updates.channel",
"lens.enable",
"lens.run",
"lens.debug",
"lens.implementations",
] ]
.map(opt => `${this.rootSection}.${opt}`); .map(opt => `${this.rootSection}.${opt}`);
@@ -119,4 +123,13 @@ export class Config {
sourceFileMap: sourceFileMap sourceFileMap: sourceFileMap
}; };
} }
get lens() {
return {
enable: this.get<boolean>("lens.enable"),
run: this.get<boolean>("lens.run"),
debug: this.get<boolean>("lens.debug"),
implementations: this.get<boolean>("lens.implementations"),
};
}
} }