Skip to content

Commit 48c0348

Browse files
committed
seito: Add --vim-config
1 parent aa2c6aa commit 48c0348

38 files changed

+420
-27
lines changed

.github/workflows/build.yml

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ jobs:
3030
- os: macos-12
3131
ghc: system
3232
steps:
33-
- uses: actions/checkout@v3
33+
- uses: actions/checkout@v4
34+
3435
- uses: hspec/setup-haskell@v1
3536
with:
3637
ghc-version: ${{ matrix.ghc }}
@@ -47,7 +48,7 @@ jobs:
4748
# https://github.com/actions/runner-images/blob/macOS-12/20230416.1/images/macos/macos-12-Readme.md#tools
4849
#
4950
# (on Linux this is a noop)
50-
run: ghcup install cabal 3.10 --set
51+
run: ghcup install cabal latest --set
5152

5253
- shell: bash
5354
run: git config --global init.defaultBranch main
@@ -66,6 +67,20 @@ jobs:
6667
env:
6768
HSPEC_OPTIONS: --color
6869

70+
- run: vim/run-tests.vim
71+
if: runner.os == 'Linux'
72+
73+
- shell: bash
74+
run: |
75+
vim/test/assets/generate.sh
76+
untracked=$(git ls-files --others --exclude-standard)
77+
if [ -n "$untracked" ]; then
78+
echo "Untracked files:"
79+
echo "$untracked"
80+
exit 1
81+
fi
82+
git diff --exit-code
83+
6984
success:
7085
needs: build
7186
runs-on: ubuntu-latest
@@ -75,6 +90,6 @@ jobs:
7590
- run: false
7691
if: needs.build.result != 'success'
7792

78-
- uses: actions/checkout@v3
93+
- uses: actions/checkout@v4
7994
- name: Check for trailing whitespace
80-
run: '! git grep -I "\s\+$"'
95+
run: "! git grep -nI '[[:blank:]]$' -- . ':!vim/test/assets'"

README.md

Lines changed: 15 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,16 @@ 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+
let vim_config = system('seito --vim-config')
89+
execute 'source ' . vim_config
90+
```
91+
8192
### Emacs integration
8293

8394
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
@@ -62,6 +61,7 @@ executables:
6261

6362
seito:
6463
source-dirs: driver
64+
generated-other-modules: Paths_sensei
6565
main: seito.hs
6666

6767
tests:

sensei.cabal

Lines changed: 7 additions & 10 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 [] `shouldReturn` (False, "failure")
35+
client return dir [] `shouldReturn` (False, "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 Vim support files" $ do
44+
client return undefined ["--vim-config"] `shouldReturn` (True, "vim/sensei.vim")

vim/run-tests.vim

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/bin/env -S vim -u NONE -S
2+
3+
set t_ti=
4+
set t_te=
5+
6+
highlight red ctermfg=red
7+
highlight green ctermfg=green
8+
9+
command -nargs=* FAILURE echohl red | echo <args> | echohl none
10+
command -nargs=* SUCCESS echohl green | echo <args> | echohl none
11+
12+
function CheckResults()
13+
if !empty(v:errors)
14+
FAILURE "FAILURES:\n"
15+
for error in v:errors
16+
echo "\n"
17+
FAILURE substitute(substitute(substitute(error, "^command line..script ", "", ""), " Expected ", "\n\nexpected: ", ""), " but got", "\n but got:", "")
18+
endfor
19+
cquit
20+
endif
21+
endfunction
22+
23+
try
24+
for name in glob("**/*.test.vim", v:true, v:true)
25+
SUCCESS "running " . name
26+
execute "source " . name
27+
endfor
28+
catch
29+
FAILURE substitute(v:throwpoint, "^command line..script ", "", "")
30+
echo "\n"
31+
FAILURE v:exception
32+
cquit
33+
endtry
34+
35+
echo "\n"
36+
37+
call CheckResults()
38+
SUCCESS "SUCCESS"
39+
quit

vim/sensei.test.vim

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
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+
for name in glob("vim/test/assets/lexical-error.hs.*.errors", v:true, v:true)
25+
let errors = PopulateQuickFixList(name)
26+
call assert_equal(1, len(errors))
27+
28+
let err = errors[0]
29+
call assert_equal("vim/test/assets/lexical-error.hs", bufname(err.bufnr))
30+
call assert_equal(1, err.lnum)
31+
call assert_equal(11, err.col)
32+
call assert_equal(0, err.end_lnum)
33+
call assert_equal(0, err.end_col)
34+
call assert_equal('e', err.type)
35+
if Require(GhcVersion(name), [9,6])
36+
call assert_equal(21231, err.nr)
37+
else
38+
call assert_equal(-1, err.nr)
39+
endif
40+
call assert_equal("\n lexical error in string/character literal at character '\\n'", err.text)
41+
endfor
42+
43+
for name in glob("vim/test/assets/parse-error.hs.*.errors", v:true, v:true)
44+
let errors = PopulateQuickFixList(name)
45+
call assert_equal(1, len(errors))
46+
47+
let err = errors[0]
48+
call assert_equal("vim/test/assets/parse-error.hs", bufname(err.bufnr))
49+
call assert_equal(1, err.lnum)
50+
call assert_equal(1, err.col)
51+
call assert_equal(0, err.end_lnum)
52+
call assert_equal(0, err.end_col)
53+
call assert_equal('e', err.type)
54+
if Require(GhcVersion(name), [9,8])
55+
call assert_equal(25277, err.nr)
56+
else
57+
call assert_equal(-1, err.nr)
58+
endif
59+
call assert_equal("\n Parse error: module header, import declaration\n or top-level declaration expected.", err.text)
60+
endfor
61+
62+
for name in glob("vim/test/assets/type-error.hs.*.errors", v:true, v:true)
63+
let errors = PopulateQuickFixList(name)
64+
call assert_equal(1, len(errors))
65+
66+
let err = errors[0]
67+
call assert_equal("vim/test/assets/type-error.hs", bufname(err.bufnr))
68+
call assert_equal(2, err.lnum)
69+
call assert_equal(7, err.col)
70+
call assert_equal(0, err.end_lnum)
71+
call assert_equal(0, err.end_col)
72+
call assert_equal('e', err.type)
73+
if Require(GhcVersion(name), [9,6])
74+
call assert_equal(83865, err.nr)
75+
call assert_equal("\n Couldn't match type ‘Int’ with ‘[Char]’\n Expected: String\n Actual: Int", err.text)
76+
else
77+
call assert_equal(-1, err.nr)
78+
call assert_equal("\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", err.text)
79+
endif
80+
endfor
81+
82+
for name in glob("vim/test/assets/type-signature-lacks-binding.hs.*.errors", v:true, v:true)
83+
let errors = PopulateQuickFixList(name)
84+
call assert_equal(2, len(errors))
85+
86+
let err = errors[0]
87+
call assert_equal("vim/test/assets/type-signature-lacks-binding.hs", bufname(err.bufnr))
88+
call assert_equal(1, err.lnum)
89+
call assert_equal(1, err.col)
90+
call assert_equal(0, err.end_lnum)
91+
call assert_equal(0, err.end_col)
92+
call assert_equal('e', err.type)
93+
if Require(GhcVersion(name), [9,6])
94+
call assert_equal(44432, err.nr)
95+
else
96+
call assert_equal(-1, err.nr)
97+
endif
98+
call assert_equal("\n The type signature for ‘foo’ lacks an accompanying binding", err.text)
99+
100+
let err = errors[1]
101+
call assert_equal("vim/test/assets/type-signature-lacks-binding.hs", bufname(err.bufnr))
102+
call assert_equal(4, err.lnum)
103+
call assert_equal(1, err.col)
104+
call assert_equal(0, err.end_lnum)
105+
call assert_equal(0, err.end_col)
106+
call assert_equal('e', err.type)
107+
if Require(GhcVersion(name), [9,6])
108+
call assert_equal(44432, err.nr)
109+
else
110+
call assert_equal(-1, err.nr)
111+
endif
112+
call assert_equal("\n The type signature for ‘bar’ lacks an accompanying binding", err.text)
113+
endfor
114+
115+
let errors = PopulateQuickFixList("vim/test/assets/hspec.hs.errors")
116+
call assert_equal(1, len(errors))
117+
118+
let err = errors[0]
119+
call assert_equal("vim/test/assets/hspec.hs", bufname(err.bufnr))
120+
call assert_equal(6, err.lnum)
121+
call assert_equal(11, err.col)
122+
call assert_equal(0, err.end_lnum)
123+
call assert_equal(0, err.end_col)
124+
call assert_equal('', err.type)
125+
call assert_equal(-1, err.nr)
126+
call assert_equal("", err.text)

0 commit comments

Comments
 (0)