0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-03 09:31:22 -05:00

feat(lsp): quick fix actions to ignore lint errors (#10627)

Closes #10122
This commit is contained in:
Satya Rohith 2021-06-21 12:13:35 +05:30 committed by GitHub
parent bbc2745350
commit 952caa79b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 266 additions and 1 deletions

View file

@ -5,6 +5,7 @@ use super::tsc;
use crate::ast;
use crate::import_map::ImportMap;
use crate::lsp::documents::DocumentData;
use crate::media_type::MediaType;
use crate::module_graph::parse_deno_types;
use crate::module_graph::parse_ts_reference;
@ -619,6 +620,7 @@ pub struct DenoFixData {
#[derive(Debug, Clone)]
enum CodeActionKind {
Deno(lsp::CodeAction),
DenoLint(lsp::CodeAction),
Tsc(lsp::CodeAction, tsc::CodeFixAction),
}
@ -665,6 +667,106 @@ impl CodeActionCollection {
Ok(())
}
pub(crate) fn add_deno_lint_ignore_action(
&mut self,
specifier: &ModuleSpecifier,
document: Option<&DocumentData>,
diagnostic: &lsp::Diagnostic,
) -> Result<(), AnyError> {
let code = diagnostic
.code
.as_ref()
.map(|v| match v {
lsp::NumberOrString::String(v) => v.to_owned(),
_ => "".to_string(),
})
.unwrap();
let line_content = if let Some(doc) = document {
doc
.content_line(diagnostic.range.start.line as usize)
.ok()
.flatten()
} else {
None
};
let mut changes = HashMap::new();
changes.insert(
specifier.clone(),
vec![lsp::TextEdit {
new_text: prepend_whitespace(
format!("// deno-lint-ignore {}\n", code),
line_content,
),
range: lsp::Range {
start: lsp::Position {
line: diagnostic.range.start.line,
character: 0,
},
end: lsp::Position {
line: diagnostic.range.start.line,
character: 0,
},
},
}],
);
let ignore_error_action = lsp::CodeAction {
title: format!("Disable {} for this line", code),
kind: Some(lsp::CodeActionKind::QUICKFIX),
diagnostics: Some(vec![diagnostic.clone()]),
command: None,
is_preferred: None,
disabled: None,
data: None,
edit: Some(lsp::WorkspaceEdit {
changes: Some(changes),
change_annotations: None,
document_changes: None,
}),
};
self
.actions
.push(CodeActionKind::DenoLint(ignore_error_action));
let mut changes = HashMap::new();
changes.insert(
specifier.clone(),
vec![lsp::TextEdit {
new_text: "// deno-lint-ignore-file\n".to_string(),
range: lsp::Range {
start: lsp::Position {
line: 0,
character: 0,
},
end: lsp::Position {
line: 0,
character: 0,
},
},
}],
);
let ignore_file_action = lsp::CodeAction {
title: "Ignore lint errors for the entire file".to_string(),
kind: Some(lsp::CodeActionKind::QUICKFIX),
diagnostics: Some(vec![diagnostic.clone()]),
command: None,
is_preferred: None,
disabled: None,
data: None,
edit: Some(lsp::WorkspaceEdit {
changes: Some(changes),
change_annotations: None,
document_changes: None,
}),
};
self
.actions
.push(CodeActionKind::DenoLint(ignore_file_action));
Ok(())
}
/// Add a TypeScript code fix action to the code actions collection.
pub(crate) async fn add_ts_fix_action(
&mut self,
@ -779,6 +881,7 @@ impl CodeActionCollection {
.map(|i| match i {
CodeActionKind::Tsc(c, _) => lsp::CodeActionOrCommand::CodeAction(c),
CodeActionKind::Deno(c) => lsp::CodeActionOrCommand::CodeAction(c),
CodeActionKind::DenoLint(c) => lsp::CodeActionOrCommand::CodeAction(c),
})
.collect()
}
@ -833,6 +936,18 @@ impl CodeActionCollection {
}
}
/// Prepend the whitespace characters found at the start of line_content to content.
fn prepend_whitespace(content: String, line_content: Option<String>) -> String {
if let Some(line) = line_content {
let whitespaces =
line.chars().position(|c| !c.is_whitespace()).unwrap_or(0);
let whitespace = &line[0..whitespaces];
format!("{}{}", &whitespace, content)
} else {
content
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -139,12 +139,22 @@ impl DocumentData {
Ok(None)
}
}
pub fn content_line(&self, line: usize) -> Result<Option<String>, AnyError> {
let content = self.content().ok().flatten();
if let Some(content) = content {
let lines = content.lines().into_iter().collect::<Vec<&str>>();
Ok(Some(lines[line].to_string()))
} else {
Ok(None)
}
}
}
#[derive(Debug, Clone, Default)]
pub struct DocumentCache {
dependents_graph: HashMap<ModuleSpecifier, HashSet<ModuleSpecifier>>,
docs: HashMap<ModuleSpecifier, DocumentData>,
pub docs: HashMap<ModuleSpecifier, DocumentData>,
}
impl DocumentCache {

View file

@ -976,6 +976,7 @@ impl Inner {
}
_ => false,
},
"deno-lint" => matches!(&d.code, Some(_)),
"deno" => match &d.code {
Some(NumberOrString::String(code)) => {
code == "no-cache" || code == "no-cache-data"
@ -1049,6 +1050,16 @@ impl Inner {
LspError::internal_error()
})?
}
Some("deno-lint") => code_actions
.add_deno_lint_ignore_action(
&specifier,
self.documents.docs.get(&specifier),
diagnostic,
)
.map_err(|err| {
error!("Unable to fix lint error: {}", err);
LspError::internal_error()
})?,
_ => (),
}
}

View file

@ -2734,3 +2734,31 @@ fn lsp_configuration_did_change() {
);
shutdown(&mut client);
}
#[test]
fn lsp_code_actions_ignore_lint() {
let mut client = init("initialize_params.json");
did_open(
&mut client,
json!({
"textDocument": {
"uri": "file:///a/file.ts",
"languageId": "typescript",
"version": 1,
"text": "let message = 'Hello, Deno!';\nconsole.log(message);\n"
}
}),
);
let (maybe_res, maybe_err) = client
.write_request(
"textDocument/codeAction",
load_fixture("code_action_ignore_lint_params.json"),
)
.unwrap();
assert!(maybe_err.is_none());
assert_eq!(
maybe_res,
Some(load_fixture("code_action_ignore_lint_response.json"))
);
shutdown(&mut client);
}

View file

@ -0,0 +1,39 @@
{
"textDocument": {
"uri": "file:///a/file.ts"
},
"range": {
"start": {
"line": 1,
"character": 5
},
"end": {
"line": 1,
"character": 12
}
},
"context": {
"diagnostics": [
{
"range": {
"start": {
"line": 1,
"character": 5
},
"end": {
"line": 1,
"character": 12
}
},
"severity": 1,
"code": "prefer-const",
"source": "deno-lint",
"message": "'message' is never reassigned\nUse 'const' instead",
"relatedInformation": []
}
],
"only": [
"quickfix"
]
}
}

View file

@ -0,0 +1,62 @@
[
{
"title": "Disable prefer-const for this line",
"kind": "quickfix",
"diagnostics": [
{
"range": {
"start": { "line": 1, "character": 5 },
"end": { "line": 1, "character": 12 }
},
"severity": 1,
"code": "prefer-const",
"source": "deno-lint",
"message": "'message' is never reassigned\nUse 'const' instead",
"relatedInformation": []
}
],
"edit": {
"changes": {
"file:///a/file.ts": [
{
"range": {
"start": { "line": 1, "character": 0 },
"end": { "line": 1, "character": 0 }
},
"newText": "// deno-lint-ignore prefer-const\n"
}
]
}
}
},
{
"title": "Ignore lint errors for the entire file",
"kind": "quickfix",
"diagnostics": [
{
"range": {
"start": { "line": 1, "character": 5 },
"end": { "line": 1, "character": 12 }
},
"severity": 1,
"code": "prefer-const",
"source": "deno-lint",
"message": "'message' is never reassigned\nUse 'const' instead",
"relatedInformation": []
}
],
"edit": {
"changes": {
"file:///a/file.ts": [
{
"range": {
"start": { "line": 0, "character": 0 },
"end": { "line": 0, "character": 0 }
},
"newText": "// deno-lint-ignore-file\n"
}
]
}
}
}
]