microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
004f69b490c27fe36f0226d87f3906d07cf54613

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/language_service/src/code_action.rs

97lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use miette::Diagnostic;
5use qsc::{
6 Span,
7 compile::ErrorKind,
8 error::WithSource,
9 line_column::{Encoding, Range},
10};
11
12use crate::{
13 compilation::Compilation,
14 protocol::{CodeAction, CodeActionKind, TextEdit, WorkspaceEdit},
15};
16
17pub(crate) fn get_code_actions(
18 compilation: &Compilation,
19 source_name: &str,
20 range: Range,
21 position_encoding: Encoding,
22) -> Vec<CodeAction> {
23 // Compute quick_fixes and other code_actions, and then merge them together
24 let span = compilation.source_range_to_package_span(source_name, range, position_encoding);
25 quick_fixes(compilation, source_name, span, position_encoding)
26}
27
28fn quick_fixes(
29 compilation: &Compilation,
30 source_name: &str,
31 span: Span,
32 encoding: Encoding,
33) -> Vec<CodeAction> {
34 let mut code_actions = Vec::new();
35
36 // get relevant diagnostics
37 let diagnostics = compilation
38 .compile_errors
39 .iter()
40 .filter(|error| is_error_relevant(error, span));
41
42 // For all diagnostics that are lints, we extract the code action edits from them.
43 for diagnostic in diagnostics {
44 if let ErrorKind::Lint(lint) = diagnostic.error() {
45 if !lint.code_action_edits.is_empty() {
46 let source = compilation
47 .user_unit()
48 .sources
49 .find_by_name(source_name)
50 .expect("source should exist");
51 let text_edits: Vec<TextEdit> = lint
52 .code_action_edits
53 .iter()
54 .map(|(new_text, span)| TextEdit {
55 new_text: new_text.clone(),
56 range: qsc::line_column::Range::from_span(encoding, &source.contents, span),
57 })
58 .collect();
59 code_actions.push(CodeAction {
60 title: diagnostic.to_string(),
61 edit: Some(WorkspaceEdit {
62 changes: vec![(source_name.to_string(), text_edits)],
63 }),
64 kind: Some(CodeActionKind::QuickFix),
65 is_preferred: None,
66 });
67 }
68 }
69 }
70
71 code_actions
72}
73
74/// Returns true if the error has a `Range` and it overlaps
75/// with the code action's range.
76fn is_error_relevant(error: &WithSource<ErrorKind>, span: Span) -> bool {
77 let Some(error_span) = resolve_span(error) else {
78 return false;
79 };
80 span.intersection(&error_span).is_some()
81}
82
83/// Extracts the uri and `Span` from an error.
84fn resolve_span(e: &WithSource<ErrorKind>) -> Option<Span> {
85 e.labels()
86 .into_iter()
87 .flatten()
88 .map(|labeled_span| {
89 let start = u32::try_from(labeled_span.offset()).expect("offset should fit in u32");
90 let len = u32::try_from(labeled_span.len()).expect("length should fit in u32");
91 qsc::Span {
92 lo: start,
93 hi: start + len,
94 }
95 })
96 .next()
97}
98