Skip to content

Commit a57a842

Browse files
authored
cli: add rendering of diffs for fix suggestions (#662)
Also update a change to CI that I don't think is necessary.
1 parent 9ead3c9 commit a57a842

10 files changed

+243
-34
lines changed

.github/workflows/rust.yml

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,6 @@ jobs:
2020
with:
2121
paths_ignore: '["docs/**", "*.md"]'
2222

23-
lint_pre_job:
24-
runs-on: ubuntu-22.04
25-
outputs:
26-
should_skip_lint: ${{ steps.skip_check_lint.outputs.should_skip }}
27-
steps:
28-
- id: skip_check_lint
29-
uses: fkirc/skip-duplicate-actions@c449d86cf33a2a6c7a4193264cc2578e2c3266d4 # pin@v4
30-
with:
31-
paths: '["**/*.rs", "**/Cargo.toml", "Cargo.lock", "s/lint"]'
32-
3323
build:
3424
needs: pre_job
3525
if: needs.pre_job.outputs.should_skip != 'true' || startsWith(github.ref, 'refs/tags/')
@@ -283,8 +273,8 @@ jobs:
283273
NETLIFY_SITE_ID: ${{ secrets.PLAYGROUND_NETLIFY_SITE_ID }}
284274

285275
lint:
286-
needs: lint_pre_job
287-
if: needs.lint_pre_job.outputs.should_skip_lint != 'true'
276+
needs: pre_job
277+
if: needs.pre_job.outputs.should_skip != 'true'
288278

289279
runs-on: ubuntu-22.04
290280

Cargo.lock

Lines changed: 103 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ ungrammar = "1.1.4"
5151
quote = "1.0.40"
5252
xshell = "0.2.7"
5353
proc-macro2 = "1.0.95"
54+
snapbox = { version = "0.6.0", features = ["diff", "term-svg", "cmd"] }
5455

5556
# local
5657
# we have to make the versions explicit otherwise `cargo publish` won't work

README.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,28 +50,42 @@ warning[prefer-bigint-over-int]: Using 32-bit integer fields can result in hitti
5050
6 │ "id" serial NOT NULL PRIMARY KEY,
5151
│ ━━━━━━
5252
53-
╰ help: Use 64-bit integer values instead to prevent hitting this limit.
53+
├ help: Use 64-bit integer values instead to prevent hitting this limit.
54+
╭╴
55+
6 │ "id" bigserial NOT NULL PRIMARY KEY,
56+
╰╴ +++
5457
warning[prefer-identity]: Serial types make schema, dependency, and permission management difficult.
5558
╭▸ example.sql:6:10
5659
5760
6 │ "id" serial NOT NULL PRIMARY KEY,
5861
│ ━━━━━━
5962
60-
╰ help: Use an `IDENTITY` column instead.
63+
├ help: Use an `IDENTITY` column instead.
64+
╭╴
65+
6 - "id" serial NOT NULL PRIMARY KEY,
66+
6 + "id" integer generated by default as identity NOT NULL PRIMARY KEY,
67+
╰╴
6168
warning[prefer-text-field]: Changing the size of a `varchar` field requires an `ACCESS EXCLUSIVE` lock, that will prevent all reads and writes to the table.
6269
╭▸ example.sql:7:13
6370
6471
7 │ "alpha" varchar(100) NOT NULL
6572
│ ━━━━━━━━━━━━
6673
67-
╰ help: Use a `TEXT` field with a `CHECK` constraint.
74+
├ help: Use a `TEXT` field with a `CHECK` constraint.
75+
╭╴
76+
7 - "alpha" varchar(100) NOT NULL
77+
7 + "alpha" text NOT NULL
78+
╰╴
6879
warning[require-concurrent-index-creation]: During normal index creation, table updates are blocked, but reads are still allowed.
6980
╭▸ example.sql:10:1
7081
7182
10 │ CREATE INDEX "field_name_idx" ON "table_name" ("field_name");
7283
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
7384
74-
╰ help: Use `concurrently` to avoid blocking writes.
85+
├ help: Use `concurrently` to avoid blocking writes.
86+
╭╴
87+
10 │ CREATE INDEX concurrently "field_name_idx" ON "table_name" ("field_name");
88+
╰╴ ++++++++++++
7589
warning[constraint-missing-not-valid]: By default new constraints require a table scan and block writes to the table while that scan occurs.
7690
╭▸ example.sql:12:24
7791

crates/squawk/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ lsp-types.workspace = true
4040

4141
[dev-dependencies]
4242
insta.workspace = true
43+
snapbox.workspace = true
4344

4445
[lints]
4546
workspace = true

crates/squawk/src/github.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ mod test_github_comment {
394394
help: Some("Make the field nullable.".to_string()),
395395
column_end: 9,
396396
line_end: 1,
397+
fix: None,
397398
}],
398399
}];
399400

@@ -486,6 +487,7 @@ ALTER TABLE "core_recipe" ADD COLUMN "foo" integer DEFAULT 10;
486487
help: Some("Use bigint instead.".to_string()),
487488
column_end: 0,
488489
line_end: 1,
490+
fix: None,
489491
}],
490492
}];
491493

@@ -520,6 +522,7 @@ ALTER TABLE "core_recipe" ADD COLUMN "foo" integer DEFAULT 10;
520522
help: Some("Use bigint instead.".to_string()),
521523
column_end: 0,
522524
line_end: 1,
525+
fix: None,
523526
}],
524527
}];
525528

crates/squawk/src/reporter.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
1+
use annotate_snippets::{AnnotationKind, Level, Patch, Renderer, Snippet, renderer::DecorStyle};
22
use anyhow::Result;
33
use console::style;
44
use line_index::LineIndex;
55
use line_index::TextRange;
66
use log::info;
77
use serde::Serialize;
8-
use squawk_linter::Linter;
9-
use squawk_linter::Rule;
10-
use squawk_linter::Version;
8+
use squawk_linter::{Fix, Linter, Rule, Version};
119
use squawk_syntax::SourceFile;
1210
use std::hash::DefaultHasher;
1311
use std::hash::Hash;
@@ -56,6 +54,7 @@ fn check_sql(
5654
range: e.range(),
5755
message: e.message().to_string(),
5856
rule_name: "syntax-error".to_string(),
57+
fix: None,
5958
})
6059
}
6160
for e in errors {
@@ -74,6 +73,7 @@ fn check_sql(
7473
level: ViolationLevel::Warning,
7574
message: e.message,
7675
rule_name: e.code.to_string(),
76+
fix: e.fix,
7777
})
7878
}
7979

@@ -100,17 +100,30 @@ fn render_lint_error<W: std::io::Write>(
100100
ViolationLevel::Error => Level::ERROR,
101101
};
102102

103-
let mut group = level.primary_title(title).id(error_name).element(
104-
Snippet::source(sql)
105-
.path(filename)
106-
.fold(true)
107-
.annotation(AnnotationKind::Primary.span(err.range.into())),
108-
);
103+
let snippet = Snippet::source(sql)
104+
.path(filename)
105+
.fold(true)
106+
.annotation(AnnotationKind::Primary.span(err.range.into()));
107+
108+
let mut group = level.primary_title(title).id(error_name).element(snippet);
109109

110110
if let Some(help) = &err.help {
111111
group = group.element(Level::HELP.message(help));
112112
}
113113

114+
if let Some(fix) = &err.fix {
115+
let mut patch_snippet = Snippet::source(sql).path(filename).fold(true);
116+
117+
for edit in &fix.edits {
118+
let start: usize = edit.text_range.start().into();
119+
let end: usize = edit.text_range.end().into();
120+
let replacement = edit.text.as_deref().unwrap_or("");
121+
patch_snippet = patch_snippet.patch(Patch::new(start..end, replacement));
122+
}
123+
124+
group = group.element(patch_snippet);
125+
}
126+
114127
writeln!(f, "{}", renderer.render(&[group]))?;
115128
Ok(())
116129
}
@@ -286,6 +299,8 @@ pub struct ReportViolation {
286299
pub rule_name: String,
287300
pub column_end: usize,
288301
pub line_end: usize,
302+
#[serde(skip_serializing)]
303+
pub fix: Option<Fix>,
289304
}
290305

291306
fn fmt_gcc<W: io::Write>(f: &mut W, reports: &[CheckReport]) -> Result<()> {

0 commit comments

Comments
 (0)