diff options
-rw-r--r-- | bin/Cargo.toml | 2 | ||||
-rw-r--r-- | bin/src/config.rs | 4 | ||||
-rw-r--r-- | bin/src/fix/single.rs | 23 | ||||
-rw-r--r-- | bin/src/main.rs | 25 | ||||
-rw-r--r-- | flake.nix | 11 | ||||
-rw-r--r-- | lib/src/lib.rs | 7 | ||||
-rw-r--r-- | vim-plugin/ftplugin/nix.vim | 8 | ||||
-rw-r--r-- | vim-plugin/plugin/statix.vim | 14 |
8 files changed, 69 insertions, 25 deletions
diff --git a/bin/Cargo.toml b/bin/Cargo.toml index 481217a..971fb47 100644 --- a/bin/Cargo.toml +++ b/bin/Cargo.toml | |||
@@ -1,6 +1,6 @@ | |||
1 | [package] | 1 | [package] |
2 | name = "statix" | 2 | name = "statix" |
3 | version = "0.2.0" | 3 | version = "0.2.2" |
4 | edition = "2018" | 4 | edition = "2018" |
5 | license = "MIT" | 5 | license = "MIT" |
6 | 6 | ||
diff --git a/bin/src/config.rs b/bin/src/config.rs index c856a59..79bed35 100644 --- a/bin/src/config.rs +++ b/bin/src/config.rs | |||
@@ -77,8 +77,8 @@ impl Fix { | |||
77 | #[derive(Clap, Debug)] | 77 | #[derive(Clap, Debug)] |
78 | pub struct Single { | 78 | pub struct Single { |
79 | /// File to run single-fix on | 79 | /// File to run single-fix on |
80 | #[clap(default_value = ".", parse(from_os_str))] | 80 | #[clap(parse(from_os_str))] |
81 | pub target: PathBuf, | 81 | pub target: Option<PathBuf>, |
82 | 82 | ||
83 | /// Position to attempt a fix at | 83 | /// Position to attempt a fix at |
84 | #[clap(short, long, parse(try_from_str = parse_line_col))] | 84 | #[clap(short, long, parse(try_from_str = parse_line_col))] |
diff --git a/bin/src/fix/single.rs b/bin/src/fix/single.rs index c09c710..4d492f8 100644 --- a/bin/src/fix/single.rs +++ b/bin/src/fix/single.rs | |||
@@ -15,7 +15,7 @@ fn pos_to_byte(line: usize, col: usize, src: &str) -> Result<TextSize, SingleFix | |||
15 | for (l, _) in src | 15 | for (l, _) in src |
16 | .split_inclusive('\n') | 16 | .split_inclusive('\n') |
17 | .zip(1..) | 17 | .zip(1..) |
18 | .take_while(|(_, i)| i < &line) | 18 | .take_while(|(_, i)| *i < line) |
19 | { | 19 | { |
20 | byte += TextSize::of(l); | 20 | byte += TextSize::of(l); |
21 | } | 21 | } |
@@ -29,7 +29,6 @@ fn pos_to_byte(line: usize, col: usize, src: &str) -> Result<TextSize, SingleFix | |||
29 | } | 29 | } |
30 | 30 | ||
31 | fn find(offset: TextSize, src: &str) -> Result<Report, SingleFixErr> { | 31 | fn find(offset: TextSize, src: &str) -> Result<Report, SingleFixErr> { |
32 | let offset = offset - TextSize::from(1u32); | ||
33 | // we don't really need the source to form a completely parsed tree | 32 | // we don't really need the source to form a completely parsed tree |
34 | let parsed = rnix::parse(src); | 33 | let parsed = rnix::parse(src); |
35 | 34 | ||
@@ -38,22 +37,18 @@ fn find(offset: TextSize, src: &str) -> Result<Report, SingleFixErr> { | |||
38 | .preorder_with_tokens() | 37 | .preorder_with_tokens() |
39 | .filter_map(|event| match event { | 38 | .filter_map(|event| match event { |
40 | WalkEvent::Enter(child) => { | 39 | WalkEvent::Enter(child) => { |
41 | if child.text_range().start() == offset { | 40 | LINTS.get(&child.kind()).map(|rules| { |
42 | LINTS.get(&child.kind()).map(|rules| { | 41 | rules |
43 | rules | 42 | .iter() |
44 | .iter() | 43 | .filter_map(|rule| rule.validate(&child)) |
45 | .filter_map(|rule| rule.validate(&child)) | 44 | .filter(|report| report.total_suggestion_range().is_some()) |
46 | .filter(|report| report.total_suggestion_range().is_some()) | 45 | .next() |
47 | .next() | 46 | }) |
48 | }) | ||
49 | } else { | ||
50 | None | ||
51 | } | ||
52 | } | 47 | } |
53 | _ => None, | 48 | _ => None, |
54 | }) | 49 | }) |
55 | .flatten() | 50 | .flatten() |
56 | .next() | 51 | .find(|report| report.total_diagnostic_range().unwrap().contains(offset)) |
57 | .ok_or(SingleFixErr::NoOp) | 52 | .ok_or(SingleFixErr::NoOp) |
58 | } | 53 | } |
59 | 54 | ||
diff --git a/bin/src/main.rs b/bin/src/main.rs index 4063621..9e9c8ac 100644 --- a/bin/src/main.rs +++ b/bin/src/main.rs | |||
@@ -4,7 +4,7 @@ mod fix; | |||
4 | mod lint; | 4 | mod lint; |
5 | mod traits; | 5 | mod traits; |
6 | 6 | ||
7 | use std::io; | 7 | use std::io::{self, BufRead}; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | err::{FixErr, SingleFixErr, StatixErr}, | 10 | err::{FixErr, SingleFixErr, StatixErr}, |
@@ -55,15 +55,26 @@ fn _main() -> Result<(), StatixErr> { | |||
55 | } | 55 | } |
56 | } | 56 | } |
57 | } | 57 | } |
58 | // FIXME: this block nasty, configure in/out streams in `impl Single` maybe | ||
58 | SubCommand::Single(single_config) => { | 59 | SubCommand::Single(single_config) => { |
59 | let path = single_config.target; | 60 | let src = if let Some(path) = &single_config.target { |
60 | let src = std::fs::read_to_string(&path).map_err(SingleFixErr::InvalidPath)?; | 61 | std::fs::read_to_string(&path).map_err(SingleFixErr::InvalidPath)? |
62 | } else { | ||
63 | io::stdin().lock().lines().map(|l| l.unwrap()).collect::<Vec<String>>().join("\n") | ||
64 | }; | ||
65 | |||
66 | let path_id = if let Some(path) = &single_config.target { | ||
67 | path.display().to_string() | ||
68 | } else { | ||
69 | "<unknown>".to_owned() | ||
70 | }; | ||
71 | |||
61 | let (line, col) = single_config.position; | 72 | let (line, col) = single_config.position; |
62 | let single_fix_result = fix::single(line, col, &src)?; | 73 | let single_fix_result = fix::single(line, col, &src)?; |
63 | if single_config.diff_only { | 74 | if single_config.diff_only { |
64 | let text_diff = TextDiff::from_lines(src.as_str(), &single_fix_result.src); | 75 | let text_diff = TextDiff::from_lines(src.as_str(), &single_fix_result.src); |
65 | let old_file = format!("{}", path.display()); | 76 | let old_file = format!("{}", path_id); |
66 | let new_file = format!("{} [fixed]", path.display()); | 77 | let new_file = format!("{} [fixed]", path_id); |
67 | println!( | 78 | println!( |
68 | "{}", | 79 | "{}", |
69 | text_diff | 80 | text_diff |
@@ -71,9 +82,11 @@ fn _main() -> Result<(), StatixErr> { | |||
71 | .context_radius(4) | 82 | .context_radius(4) |
72 | .header(&old_file, &new_file) | 83 | .header(&old_file, &new_file) |
73 | ); | 84 | ); |
74 | } else { | 85 | } else if let Some(path) = single_config.target { |
75 | std::fs::write(&path, &*single_fix_result.src) | 86 | std::fs::write(&path, &*single_fix_result.src) |
76 | .map_err(SingleFixErr::InvalidPath)?; | 87 | .map_err(SingleFixErr::InvalidPath)?; |
88 | } else { | ||
89 | print!("{}", &*single_fix_result.src) | ||
77 | } | 90 | } |
78 | } | 91 | } |
79 | } | 92 | } |
@@ -49,7 +49,7 @@ | |||
49 | 49 | ||
50 | statix = with final; pkgs.stdenv.mkDerivation { | 50 | statix = with final; pkgs.stdenv.mkDerivation { |
51 | pname = "statix"; | 51 | pname = "statix"; |
52 | version = "v0.2.0"; | 52 | version = "v0.2.2"; |
53 | src = builtins.path { | 53 | src = builtins.path { |
54 | path = ./.; | 54 | path = ./.; |
55 | name = "statix"; | 55 | name = "statix"; |
@@ -75,10 +75,17 @@ | |||
75 | }; | 75 | }; |
76 | }; | 76 | }; |
77 | 77 | ||
78 | statix-vim = | ||
79 | with final; pkgs.vimUtils.buildVimPlugin { | ||
80 | pname = "statix-vim"; | ||
81 | version = "0.1.0"; | ||
82 | src = ./vim-plugin; | ||
83 | }; | ||
84 | |||
78 | }; | 85 | }; |
79 | 86 | ||
80 | packages = forAllSystems (system: { | 87 | packages = forAllSystems (system: { |
81 | inherit (nixpkgsFor."${system}") statix; | 88 | inherit (nixpkgsFor."${system}") statix statix-vim; |
82 | }); | 89 | }); |
83 | 90 | ||
84 | defaultPackage = | 91 | defaultPackage = |
diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 41e38c8..c2f24c6 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs | |||
@@ -56,6 +56,13 @@ impl Report { | |||
56 | .flat_map(|d| Some(d.suggestion.as_ref()?.at)) | 56 | .flat_map(|d| Some(d.suggestion.as_ref()?.at)) |
57 | .reduce(|acc, next| acc.cover(next)) | 57 | .reduce(|acc, next| acc.cover(next)) |
58 | } | 58 | } |
59 | /// A range that encompasses all the diagnostics provided in this report | ||
60 | pub fn total_diagnostic_range(&self) -> Option<TextRange> { | ||
61 | self.diagnostics | ||
62 | .iter() | ||
63 | .flat_map(|d| Some(d.at)) | ||
64 | .reduce(|acc, next| acc.cover(next)) | ||
65 | } | ||
59 | /// Unsafe but handy replacement for above | 66 | /// Unsafe but handy replacement for above |
60 | pub fn range(&self) -> TextRange { | 67 | pub fn range(&self) -> TextRange { |
61 | self.total_suggestion_range().unwrap() | 68 | self.total_suggestion_range().unwrap() |
diff --git a/vim-plugin/ftplugin/nix.vim b/vim-plugin/ftplugin/nix.vim new file mode 100644 index 0000000..885857f --- /dev/null +++ b/vim-plugin/ftplugin/nix.vim | |||
@@ -0,0 +1,8 @@ | |||
1 | setlocal makeprg=statix\ check\ -o\ errfmt\ % | ||
2 | setlocal errorformat=%f>%l:%c:%t:%n:%m | ||
3 | |||
4 | augroup StatixCheck | ||
5 | autocmd! | ||
6 | autocmd! BufWritePost *.nix | silent make! | silent redraw! | ||
7 | autocmd QuickFixCmdPost [^l]* cwindow | ||
8 | augroup END | ||
diff --git a/vim-plugin/plugin/statix.vim b/vim-plugin/plugin/statix.vim new file mode 100644 index 0000000..736cce2 --- /dev/null +++ b/vim-plugin/plugin/statix.vim | |||
@@ -0,0 +1,14 @@ | |||
1 | function! ApplyStatixSuggestion() | ||
2 | let l:l = line('.') | ||
3 | let l:c = col('.') | ||
4 | let l:filter = "%!statix single -p " . l . "," . c . "" | ||
5 | execute l:filter | ||
6 | |||
7 | silent if v:shell_error == 1 | ||
8 | undo | ||
9 | endif | ||
10 | |||
11 | call cursor(l, c) | ||
12 | endfunction | ||
13 | |||
14 | nnoremap gf :call ApplyStatixSuggestion()<cr> | ||