mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 23:24:29 +00:00
Merge #4499
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:
commit
c6ed089671
5 changed files with 185 additions and 80 deletions
|
@ -33,6 +33,36 @@ pub struct Config {
|
|||
pub inlay_hints: InlayHintsConfig,
|
||||
pub completion: CompletionConfig,
|
||||
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)]
|
||||
|
@ -107,6 +137,7 @@ impl Default for Config {
|
|||
..CompletionConfig::default()
|
||||
},
|
||||
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, "/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);
|
||||
|
||||
fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
|
||||
|
|
|
@ -812,88 +812,108 @@ pub fn handle_code_lens(
|
|||
params: lsp_types::CodeLensParams,
|
||||
) -> Result<Option<Vec<CodeLens>>> {
|
||||
let _p = profile("handle_code_lens");
|
||||
let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?;
|
||||
let line_index = world.analysis().file_line_index(file_id)?;
|
||||
|
||||
let mut lenses: Vec<CodeLens> = Default::default();
|
||||
|
||||
let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?;
|
||||
// Gather runnables
|
||||
for runnable in world.analysis().runnables(file_id)? {
|
||||
let title = match &runnable.kind {
|
||||
RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶\u{fe0e} Run Test",
|
||||
RunnableKind::DocTest { .. } => "▶\u{fe0e} Run Doctest",
|
||||
RunnableKind::Bench { .. } => "Run Bench",
|
||||
RunnableKind::Bin => {
|
||||
// Do not suggest binary run on other target than binary
|
||||
match &cargo_spec {
|
||||
Some(spec) => match spec.target_kind {
|
||||
TargetKind::Bin => "Run",
|
||||
_ => continue,
|
||||
},
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_string();
|
||||
let mut r = to_lsp_runnable(&world, file_id, runnable)?;
|
||||
let lens = CodeLens {
|
||||
range: r.range,
|
||||
command: Some(Command {
|
||||
title,
|
||||
command: "rust-analyzer.runSingle".into(),
|
||||
arguments: Some(vec![to_value(&r).unwrap()]),
|
||||
}),
|
||||
data: None,
|
||||
};
|
||||
lenses.push(lens);
|
||||
|
||||
if r.args[0] == "run" {
|
||||
r.args[0] = "build".into();
|
||||
} else {
|
||||
r.args.push("--no-run".into());
|
||||
}
|
||||
let debug_lens = CodeLens {
|
||||
range: r.range,
|
||||
command: Some(Command {
|
||||
title: "Debug".into(),
|
||||
command: "rust-analyzer.debugSingle".into(),
|
||||
arguments: Some(vec![to_value(r).unwrap()]),
|
||||
}),
|
||||
data: None,
|
||||
};
|
||||
lenses.push(debug_lens);
|
||||
if world.config.lens.none() {
|
||||
// early return before any db query!
|
||||
return Ok(Some(lenses));
|
||||
}
|
||||
|
||||
// Handle impls
|
||||
lenses.extend(
|
||||
world
|
||||
.analysis()
|
||||
.file_structure(file_id)?
|
||||
.into_iter()
|
||||
.filter(|it| match it.kind {
|
||||
SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true,
|
||||
_ => false,
|
||||
})
|
||||
.map(|it| {
|
||||
let range = to_proto::range(&line_index, it.node_range);
|
||||
let pos = range.start;
|
||||
let lens_params = lsp_types::request::GotoImplementationParams {
|
||||
text_document_position_params: lsp_types::TextDocumentPositionParams::new(
|
||||
params.text_document.clone(),
|
||||
pos,
|
||||
),
|
||||
work_done_progress_params: Default::default(),
|
||||
partial_result_params: Default::default(),
|
||||
};
|
||||
CodeLens {
|
||||
range,
|
||||
command: None,
|
||||
data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()),
|
||||
}
|
||||
}),
|
||||
);
|
||||
let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?;
|
||||
let line_index = world.analysis().file_line_index(file_id)?;
|
||||
let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?;
|
||||
|
||||
if world.config.lens.runnable() {
|
||||
// Gather runnables
|
||||
for runnable in world.analysis().runnables(file_id)? {
|
||||
let (run_title, debugee) = match &runnable.kind {
|
||||
RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => {
|
||||
("▶️\u{fe0e}Run Test", true)
|
||||
}
|
||||
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 => {
|
||||
// Do not suggest binary run on other target than binary
|
||||
match &cargo_spec {
|
||||
Some(spec) => match spec.target_kind {
|
||||
TargetKind::Bin => ("Run", true),
|
||||
_ => continue,
|
||||
},
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut r = to_lsp_runnable(&world, file_id, runnable)?;
|
||||
if world.config.lens.run {
|
||||
let lens = CodeLens {
|
||||
range: r.range,
|
||||
command: Some(Command {
|
||||
title: run_title.to_string(),
|
||||
command: "rust-analyzer.runSingle".into(),
|
||||
arguments: Some(vec![to_value(&r).unwrap()]),
|
||||
}),
|
||||
data: None,
|
||||
};
|
||||
lenses.push(lens);
|
||||
}
|
||||
|
||||
if debugee && world.config.lens.debug {
|
||||
if r.args[0] == "run" {
|
||||
r.args[0] = "build".into();
|
||||
} else {
|
||||
r.args.push("--no-run".into());
|
||||
}
|
||||
let debug_lens = CodeLens {
|
||||
range: r.range,
|
||||
command: Some(Command {
|
||||
title: "Debug".into(),
|
||||
command: "rust-analyzer.debugSingle".into(),
|
||||
arguments: Some(vec![to_value(r).unwrap()]),
|
||||
}),
|
||||
data: None,
|
||||
};
|
||||
lenses.push(debug_lens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if world.config.lens.impementations {
|
||||
// Handle impls
|
||||
lenses.extend(
|
||||
world
|
||||
.analysis()
|
||||
.file_structure(file_id)?
|
||||
.into_iter()
|
||||
.filter(|it| match it.kind {
|
||||
SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true,
|
||||
_ => false,
|
||||
})
|
||||
.map(|it| {
|
||||
let range = to_proto::range(&line_index, it.node_range);
|
||||
let pos = range.start;
|
||||
let lens_params = lsp_types::request::GotoImplementationParams {
|
||||
text_document_position_params: lsp_types::TextDocumentPositionParams::new(
|
||||
params.text_document.clone(),
|
||||
pos,
|
||||
),
|
||||
work_done_progress_params: Default::default(),
|
||||
partial_result_params: Default::default(),
|
||||
};
|
||||
CodeLens {
|
||||
range,
|
||||
command: None,
|
||||
data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()),
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
Ok(Some(lenses))
|
||||
}
|
||||
|
||||
|
|
|
@ -443,6 +443,26 @@
|
|||
"type": "object",
|
||||
"default": {},
|
||||
"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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@ import { startDebugSession, getDebugConfiguration } from '../debug';
|
|||
|
||||
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 client = ctx.client;
|
||||
if (!editor || !client) return;
|
||||
|
@ -33,9 +33,20 @@ async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showBu
|
|||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (debuggeeOnly && (r.label.startsWith('doctest') || r.label.startsWith('cargo'))) {
|
||||
continue;
|
||||
}
|
||||
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) => {
|
||||
const disposables: vscode.Disposable[] = [];
|
||||
const close = (result?: RunnableQuickPick) => {
|
||||
|
@ -107,7 +118,7 @@ export function debug(ctx: Ctx): Cmd {
|
|||
let prevDebuggee: RunnableQuickPick | undefined;
|
||||
|
||||
return async () => {
|
||||
const item = await selectRunnable(ctx, prevDebuggee);
|
||||
const item = await selectRunnable(ctx, prevDebuggee, true);
|
||||
if (!item) return;
|
||||
|
||||
item.detail = 'restart';
|
||||
|
@ -147,7 +158,7 @@ async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise<void>
|
|||
|
||||
export function newDebugConfig(ctx: Ctx): Cmd {
|
||||
return async () => {
|
||||
const item = await selectRunnable(ctx, undefined, false);
|
||||
const item = await selectRunnable(ctx, undefined, true, false);
|
||||
if (!item) return;
|
||||
|
||||
await makeDebugConfig(ctx, item);
|
||||
|
|
|
@ -16,6 +16,10 @@ export class Config {
|
|||
"files",
|
||||
"highlighting",
|
||||
"updates.channel",
|
||||
"lens.enable",
|
||||
"lens.run",
|
||||
"lens.debug",
|
||||
"lens.implementations",
|
||||
]
|
||||
.map(opt => `${this.rootSection}.${opt}`);
|
||||
|
||||
|
@ -119,4 +123,13 @@ export class Config {
|
|||
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"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue