wdl_analysis/handlers/
code_lens.rs1use anyhow::Result;
9use anyhow::anyhow;
10use anyhow::bail;
11use line_index::TextSize;
12use lsp_types::CodeLens;
13use lsp_types::Command;
14use lsp_types::Range;
15use url::Url;
16use wdl_grammar::SyntaxNode;
17
18use crate::graph::DocumentGraph;
19use crate::graph::ParseState;
20use crate::handlers::common::position;
21
22#[derive(Debug)]
24pub struct RunCommand {
25 pub source: String,
27 pub target: String,
29}
30
31impl From<RunCommand> for Command {
32 fn from(command: RunCommand) -> Self {
33 Command {
34 title: format!("Run '{}'", command.target),
35 command: "sprocket.run".to_string(),
36 arguments: Some(vec![command.source.into(), command.target.into()]),
37 }
38 }
39}
40
41pub fn code_lens(graph: &DocumentGraph, document_uri: &Url) -> Result<Option<Vec<CodeLens>>> {
47 let index = graph
48 .get_index(document_uri)
49 .ok_or_else(|| anyhow!("document `{uri}` not found in graph", uri = document_uri))?;
50
51 let node = graph.get(index);
52 let (_, lines) = match node.parse_state() {
53 ParseState::Parsed { lines, root, .. } => {
54 (SyntaxNode::new_root(root.clone()), lines.clone())
55 }
56 _ => bail!("document `{uri}` has not been parsed", uri = document_uri),
57 };
58
59 let Some(analysis_doc) = node.document() else {
60 bail!("document analysis data not available for `{document_uri}`");
61 };
62
63 let mut lenses = Vec::new();
64 for target in analysis_doc.callables() {
65 if target.inputs().values().any(|i| i.required()) {
66 continue;
67 }
68
69 let start_offset = TextSize::from(target.name_span().start() as u32);
70 let end_offset = TextSize::from(target.name_span().end() as u32);
71
72 lenses.push(CodeLens {
73 range: Range {
74 start: position(&lines, start_offset)?,
75 end: position(&lines, end_offset)?,
76 },
77 command: Some(
78 RunCommand {
79 source: document_uri.to_string(),
80 target: target.name().to_string(),
81 }
82 .into(),
83 ),
84 data: None,
85 });
86 }
87
88 if lenses.is_empty() {
89 Ok(None)
90 } else {
91 Ok(Some(lenses))
92 }
93}