Skip to content

Commit 269ea16

Browse files
committed
seito: Add --vim-config
1 parent f1bce96 commit 269ea16

45 files changed

Lines changed: 509 additions & 25 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
*.cabal linguist-generated=true
2+
/vim/test/assets/*.errors linguist-generated=true

.github/workflows/build.yml

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ jobs:
1717
runs-on: ${{ matrix.os }}
1818

1919
strategy:
20+
fail-fast: true
2021
matrix:
2122
os:
2223
- ubuntu-latest
@@ -29,7 +30,8 @@ jobs:
2930
- os: macos-latest
3031
ghc: '9.10'
3132
steps:
32-
- uses: actions/checkout@v3
33+
- uses: actions/checkout@v4
34+
3335
- uses: hspec/setup-haskell@v1
3436
with:
3537
ghc-version: ${{ matrix.ghc }}
@@ -58,6 +60,23 @@ jobs:
5860
env:
5961
HSPEC_OPTIONS: --color
6062

63+
- run: vim/test/run.vim
64+
if: runner.os == 'Linux'
65+
66+
- run: vim/test/assets/generate.sh
67+
if: ${{ runner.os == 'Linux' && matrix.ghc != '9.4' }}
68+
69+
- run: git diff --exit-code
70+
71+
- shell: bash
72+
run: |
73+
untracked=$(git ls-files --others --exclude-standard)
74+
if [ -n "$untracked" ]; then
75+
echo "Untracked files:"
76+
echo "$untracked"
77+
exit 1
78+
fi
79+
6180
success:
6281
needs: build
6382
runs-on: ubuntu-latest
@@ -67,6 +86,6 @@ jobs:
6786
- run: false
6887
if: needs.build.result != 'success'
6988

70-
- uses: actions/checkout@v3
89+
- uses: actions/checkout@v4
7190
- name: Check for trailing whitespace
72-
run: '! git grep -I "\s\+$"'
91+
run: "! git grep -nI '[[:blank:]]$' -- . ':!vim/test/assets'"

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,14 @@ instead:
5151

5252
### Vim integration
5353

54-
You can use `sensei` to load the result of the last test run into your quickfix
54+
You can use `seito` to load the results of the last test run into your quickfix
5555
list by executing `:make` in Vim.
5656

57-
For this to work, you can either create a `Makefile` or set `makeprg` to a
58-
custom value.
57+
For this to work, you can choose one out of three options:
5958

60-
(In both cases, `sed` is used to strip ANSI color sequences.)
59+
1. Create a `Makefile`
60+
2. Set `makeprg` to a custom value
61+
3. Use [`sensei.vim`](vim/sensei.vim)
6162

6263
#### Option 1: Create a `Makefile`
6364

@@ -78,6 +79,15 @@ Add the following to your Vim configuration (e.g.
7879
:set makeprg=seito
7980
```
8081

82+
#### Option 3: Use `sensei.vim`:
83+
84+
Add the following to your Vim configuration (e.g.
85+
`~/.vim/after/ftplugin/haskell.vim`):
86+
87+
```vim
88+
execute 'source ' . system('seito --vim-config')
89+
```
90+
8191
### Emacs integration
8292

8393
Similarly, you can use `sensei` to load the result of the last test run into an

driver/seito.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import System.Environment
55
import Control.Monad
66
import qualified Data.ByteString.Lazy as L
77

8+
import Paths_sensei (getDataFileName)
9+
810
import Client
911

1012
main :: IO ()
1113
main = do
12-
(success, output) <- getArgs >>= client ""
14+
(success, output) <- getArgs >>= client getDataFileName ""
1315
L.putStr output
1416
unless success exitFailure

package.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ default-extensions:
1717
- RecordWildCards
1818
- ViewPatterns
1919

20-
other-extensions:
21-
- NoFieldSelectors
20+
data-files: vim/sensei.vim
2221

2322
dependencies:
2423
- base >= 4.11 && < 5
@@ -64,6 +63,7 @@ executables:
6463

6564
seito:
6665
source-dirs: driver
66+
generated-other-modules: Paths_sensei
6767
main: seito.hs
6868

6969
tests:

sensei.cabal

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

src/Client.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ import qualified Data.ByteString.Lazy as L
1111

1212
import HTTP (newSocket, socketName)
1313

14-
client :: FilePath -> [String] -> IO (Bool, L.ByteString)
15-
client dir args = case args of
14+
client :: (FilePath -> IO FilePath) -> FilePath -> [String] -> IO (Bool, L.ByteString)
15+
client getDataFileName dir args = case args of
1616
[] -> hIsTerminalDevice stdout >>= run
1717
["--no-color"] -> run False
1818
["--color"] -> run True
19+
["--vim-config"] -> (,) True . fromString <$> getDataFileName "vim/sensei.vim"
1920
_ -> do
2021
hPutStrLn stderr $ "Usage: seito [ --color | --no-color ]"
2122
return (False, "")

test/ClientSpec.hs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,21 @@ spec = do
2424
describe "client" $ do
2525
it "accepts --color" $ do
2626
withSuccess $ \ dir -> do
27-
client dir ["--color"] `shouldReturn` (True, fromString $ withColor Green "success")
27+
client return dir ["--color"] `shouldReturn` (True, fromString $ withColor Green "success")
2828

2929
it "accepts --no-color" $ do
3030
withSuccess $ \ dir -> do
31-
client dir ["--no-color"] `shouldReturn` (True, "success")
31+
client return dir ["--no-color"] `shouldReturn` (True, "success")
3232

3333
it "indicates failure" $ do
3434
withFailure $ \ dir -> do
35-
client dir ["--color"] `shouldReturn` (False, fromString $ withColor Red "failure")
35+
client return dir ["--color"] `shouldReturn` (False, fromString $ withColor Red "failure")
3636

3737
context "when server socket is missing" $ do
3838
it "reports error" $ do
3939
withTempDirectory $ \ dir -> do
40-
client dir [] `shouldReturn` (False, "could not connect to " <> fromString (socketName dir) <> "\n")
40+
client return dir [] `shouldReturn` (False, "could not connect to " <> fromString (socketName dir) <> "\n")
41+
42+
context "with --vim-config" $ do
43+
it "returns a path to a Vim support file" $ do
44+
client return undefined ["--vim-config"] `shouldReturn` (True, "vim/sensei.vim")

vim/sensei.test.vim

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
source vim/sensei.vim
2+
3+
function Require(actual, required)
4+
for i in range(len(a:required))
5+
if a:actual[i] > a:required[i]
6+
return 1
7+
elseif a:actual[i] < a:required[i]
8+
return 0
9+
endif
10+
endfor
11+
return 1
12+
endfunction
13+
14+
function GhcVersion(name)
15+
return matchlist(a:name, '\vghc-(\d+)\.(\d+)\.(\d+)\.errors')[1:3]
16+
endfunction
17+
18+
function PopulateQuickFixList(name)
19+
SUCCESS a:name
20+
execute "cgetfile " . a:name
21+
return filter(getqflist(), 'v:val.valid')
22+
endfunction
23+
24+
function GhcErrorsFor(name)
25+
let errors = glob("vim/test/assets/" . a:name . ".*.errors", v:true, v:true)
26+
call ShouldBe(len(errors), 5)
27+
return errors
28+
endfunction
29+
30+
for name in GhcErrorsFor("lexical-error.hs")
31+
let errors = PopulateQuickFixList(name)
32+
call ShouldBe(len(errors), 1)
33+
34+
let err = errors[0]
35+
call ShouldBe(bufname(err.bufnr), "lexical-error.hs")
36+
call ShouldBe(err.lnum, 1)
37+
call ShouldBe(err.col, 11)
38+
call ShouldBe(err.end_lnum, 0)
39+
call ShouldBe(err.end_col, 0)
40+
call ShouldBe(err.type, 'e')
41+
if Require(GhcVersion(name), [9,6])
42+
call ShouldBe(err.nr, 21231)
43+
else
44+
call ShouldBe(err.nr, -1)
45+
endif
46+
call ShouldBe(err.text, "\n lexical error in string/character literal at character '\\n'")
47+
endfor
48+
49+
for name in GhcErrorsFor("parse-error.hs")
50+
let errors = PopulateQuickFixList(name)
51+
call ShouldBe(len(errors), 1)
52+
53+
let err = errors[0]
54+
call ShouldBe(bufname(err.bufnr), "parse-error.hs")
55+
call ShouldBe(err.lnum, 1)
56+
call ShouldBe(err.col, 1)
57+
call ShouldBe(err.end_lnum, 0)
58+
call ShouldBe(err.end_col, 0)
59+
call ShouldBe(err.type, 'e')
60+
if Require(GhcVersion(name), [9,8])
61+
call ShouldBe(err.nr, 25277)
62+
else
63+
call ShouldBe(err.nr, -1)
64+
endif
65+
call ShouldBe(err.text, "\n Parse error: module header, import declaration\n or top-level declaration expected.")
66+
endfor
67+
68+
for name in GhcErrorsFor("type-error.hs")
69+
let errors = PopulateQuickFixList(name)
70+
call ShouldBe(len(errors), 1)
71+
72+
let err = errors[0]
73+
call ShouldBe(bufname(err.bufnr), "type-error.hs")
74+
call ShouldBe(err.lnum, 2)
75+
call ShouldBe(err.col, 7)
76+
call ShouldBe(err.end_lnum, 0)
77+
call ShouldBe(err.end_col, 0)
78+
call ShouldBe(err.type, 'e')
79+
if Require(GhcVersion(name), [9,6])
80+
call ShouldBe(err.nr, 83865)
81+
call ShouldBe(err.text, "\n Couldn't match type ‘Int’ with ‘[Char]’\n Expected: String\n Actual: Int")
82+
else
83+
call ShouldBe(err.nr, -1)
84+
call ShouldBe(err.text, "\n • Couldn't match type ‘Int’ with ‘[Char]’\n Expected: String\n Actual: Int\n • In the expression: 23 :: Int\n In an equation for ‘foo’: foo = 23 :: Int")
85+
endif
86+
endfor
87+
88+
for name in GhcErrorsFor("suggested-fix.hs")
89+
let errors = PopulateQuickFixList(name)
90+
call ShouldBe(len(errors), 2)
91+
92+
let err = errors[0]
93+
call ShouldBe(bufname(err.bufnr), "suggested-fix.hs")
94+
call ShouldBe(err.lnum, 1)
95+
call ShouldBe(err.col, 1)
96+
call ShouldBe(err.end_lnum, 0)
97+
call ShouldBe(err.end_col, 0)
98+
call ShouldBe(err.type, 'e')
99+
if Require(GhcVersion(name), [9,6])
100+
call ShouldBe(err.nr, 44432)
101+
else
102+
call ShouldBe(err.nr, -1)
103+
endif
104+
call ShouldBe(err.text, "\n The type signature for ‘foo’ lacks an accompanying binding")
105+
106+
let err = errors[1]
107+
call ShouldBe(bufname(err.bufnr), "suggested-fix.hs")
108+
call ShouldBe(err.lnum, 4)
109+
call ShouldBe(err.col, 1)
110+
call ShouldBe(err.end_lnum, 0)
111+
call ShouldBe(err.end_col, 0)
112+
call ShouldBe(err.type, 'e')
113+
if Require(GhcVersion(name), [9,6])
114+
call ShouldBe(err.nr, 44432)
115+
else
116+
call ShouldBe(err.nr, -1)
117+
endif
118+
call ShouldBe(err.text, "\n The type signature for ‘bar’ lacks an accompanying binding")
119+
endfor
120+
121+
for name in GhcErrorsFor("suggested-fix-multiline.hs")
122+
let errors = PopulateQuickFixList(name)
123+
call ShouldBe(len(errors), 2)
124+
125+
let err = errors[0]
126+
call ShouldBe(bufname(err.bufnr), "suggested-fix-multiline.hs")
127+
call ShouldBe(err.lnum, 1)
128+
call ShouldBe(err.col, 1)
129+
call ShouldBe(err.end_lnum, 0)
130+
call ShouldBe(err.end_col, 0)
131+
call ShouldBe(err.type, 'e')
132+
if Require(GhcVersion(name), [9,6])
133+
call ShouldBe(err.nr, 44432)
134+
else
135+
call ShouldBe(err.nr, -1)
136+
endif
137+
call ShouldBe(err.text, "\n The type signature for ‘foo’ lacks an accompanying binding")
138+
139+
let err = errors[1]
140+
call ShouldBe(bufname(err.bufnr), "suggested-fix-multiline.hs")
141+
call ShouldBe(err.lnum, 4)
142+
call ShouldBe(err.col, 1)
143+
call ShouldBe(err.end_lnum, 0)
144+
call ShouldBe(err.end_col, 0)
145+
call ShouldBe(err.type, 'e')
146+
if Require(GhcVersion(name), [9,6])
147+
call ShouldBe(err.nr, 44432)
148+
else
149+
call ShouldBe(err.nr, -1)
150+
endif
151+
call ShouldBe(err.text, "\n The type signature for ‘bar’ lacks an accompanying binding")
152+
endfor
153+
154+
let errors = PopulateQuickFixList("vim/test/assets/hspec.hs.errors")
155+
call ShouldBe(len(errors), 1)
156+
157+
let err = errors[0]
158+
call ShouldBe(bufname(err.bufnr), "vim/test/assets/hspec.hs")
159+
call ShouldBe(err.lnum, 6)
160+
call ShouldBe(err.col, 11)
161+
call ShouldBe(err.end_lnum, 0)
162+
call ShouldBe(err.end_col, 0)
163+
call ShouldBe(err.type, '')
164+
call ShouldBe(err.nr, -1)
165+
call ShouldBe(err.text, "")

0 commit comments

Comments
 (0)