Skip to content

Commit d0a94bf

Browse files
committed
Add OCaml support with ocamlformat
1 parent 9033599 commit d0a94bf

File tree

7 files changed

+202
-4
lines changed

7 files changed

+202
-4
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ helpfiles in the `doc/` directory. The helpfiles are also available via
2222
* Java (google-java-format or clang-format)
2323
* JavaScript (clang-format or [prettier](https://prettier.io))
2424
* JSON (js-beautify)
25+
* [OCaml](https://ocaml.org) ([ocamlformat](https://github.com/ocaml-ppx/ocamlformat))
2526
* Proto (clang-format)
2627
* Python (Autopep8, Black, or YAPF)
2728
* Rust ([rustfmt](https://github.com/rust-lang/rustfmt))

autoload/codefmt.vim

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
" * fish: fish_indent
3434
" * gn: gn
3535
" * go: gofmt
36+
" * ocaml: ocamlformat
3637
" * python: autopep8, black, yapf
3738

3839

autoload/codefmt/ocamlformat.vim

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
" Copyright 2020 Google Inc. All rights reserved.
2+
"
3+
" Licensed under the Apache License, Version 2.0 (the "License");
4+
" you may not use this file except in compliance with the License.
5+
" You may obtain a copy of the License at
6+
"
7+
" http://www.apache.org/licenses/LICENSE-2.0
8+
"
9+
" Unless required by applicable law or agreed to in writing, software
10+
" distributed under the License is distributed on an "AS IS" BASIS,
11+
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
" See the License for the specific language governing permissions and
13+
" limitations under the License.
14+
15+
16+
let s:plugin = maktaba#plugin#Get('codefmt')
17+
18+
19+
""
20+
" @private
21+
" Formatter: ocamlformat
22+
function! codefmt#ocamlformat#GetFormatter() abort
23+
let l:formatter = {
24+
\ 'name': 'ocamlformat',
25+
\ 'setup_instructions': 'Install ocamlformat' .
26+
\ '(https://github.com/ocaml-ppx/ocamlformat)'}
27+
28+
function l:formatter.IsAvailable() abort
29+
return executable(s:plugin.Flag('ocamlformat_executable'))
30+
endfunction
31+
32+
function l:formatter.AppliesToBuffer() abort
33+
return &filetype is# 'ocaml'
34+
endfunction
35+
36+
""
37+
" Reformat the current buffer with ocamlformat or the binary named in
38+
" @flag(ocamlformat_executable), only targeting the range between {startline} and
39+
" {endline}.
40+
function l:formatter.FormatRange(startline, endline) abort
41+
let l:cmd = [ s:plugin.Flag('ocamlformat_executable'), '-' ]
42+
let l:fname = expand('%:p')
43+
if !empty(l:fname)
44+
let l:cmd += ['--name', l:fname]
45+
let l:fname_pattern = '"' . escape(l:fname, '\') . '"'
46+
else
47+
" assume it's an OCaml implementation file (.ml) if no file name is
48+
" provided
49+
let l:cmd += ['--impl']
50+
let l:fname_pattern = '\<standard input\>'
51+
end
52+
try
53+
" NOTE: ocamlformat does not support range formatting.
54+
" See https://github.com/ocaml-ppx/ocamlformat/pull/1188
55+
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
56+
\ a:startline, a:endline, l:cmd)
57+
catch /ERROR(ShellError):/
58+
" Parse all the errors and stick them in the quickfix list.
59+
let l:errors = []
60+
let l:matchidx = 1
61+
while 1
62+
let l:tokens = matchlist(v:exception,
63+
\ '\vFile ' . l:fname_pattern . ', line (\d+), characters (\d+)-\d+:\n(.*)\n', 0, l:matchidx)
64+
if empty(l:tokens)
65+
break
66+
endif
67+
call add(l:errors, {
68+
\ 'filename': @%,
69+
\ 'lnum': l:tokens[1] + a:startline - 1,
70+
\ 'col': l:tokens[2],
71+
\ 'text': l:tokens[3]})
72+
let l:matchidx = l:matchidx + 1
73+
endwhile
74+
75+
if empty(l:errors)
76+
" Couldn't parse ocamlformat error format; display it all.
77+
call maktaba#error#Shout('Error formatting file: %s', v:exception)
78+
else
79+
call setqflist(l:errors, 'r')
80+
cc 1
81+
endif
82+
endtry
83+
endfunction
84+
85+
return l:formatter
86+
endfunction

doc/codefmt.txt

+9-4
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,14 @@ Default: 'dartfmt' `
5454
The path to the js-beautify executable.
5555
Default: 'js-beautify' `
5656

57-
*codefmt:black_executable*
58-
The path to the black executable.
59-
Default: 'black' `
60-
6157
*codefmt:yapf_executable*
6258
The path to the yapf executable.
6359
Default: 'yapf' `
6460

61+
*codefmt:black_executable*
62+
The path to the black executable.
63+
Default: 'black' `
64+
6565
*codefmt:gn_executable*
6666
The path to the gn executable.
6767
Default: 'gn' `
@@ -122,6 +122,10 @@ Default: 'zprint' `
122122
The path to the fish_indent executable.
123123
Default: 'fish_indent' `
124124

125+
*codefmt:ocamlformat_executable*
126+
The path to the ocamlformat executable.
127+
Default: 'ocamlformat' `
128+
125129
*codefmt:plugin[autocmds]*
126130
Configures whether plugin/autocmds.vim should be loaded.
127131
Default: 1 `
@@ -168,6 +172,7 @@ The current list of defaults by filetype is:
168172
* fish: fish_indent
169173
* gn: gn
170174
* go: gofmt
175+
* ocaml: ocamlformat
171176
* python: autopep8, black, yapf
172177

173178
==============================================================================

instant/flags.vim

+4
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,7 @@ call s:plugin.Flag('zprint_executable', 'zprint')
168168
""
169169
" The path to the fish_indent executable.
170170
call s:plugin.Flag('fish_indent_executable', 'fish_indent')
171+
172+
""
173+
" The path to the ocamlformat executable.
174+
call s:plugin.Flag('ocamlformat_executable', 'ocamlformat')

plugin/register.vim

+1
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ call s:registry.AddExtension(codefmt#googlejava#GetFormatter())
3838
call s:registry.AddExtension(codefmt#shfmt#GetFormatter())
3939
call s:registry.AddExtension(codefmt#zprint#GetFormatter())
4040
call s:registry.AddExtension(codefmt#fish_indent#GetFormatter())
41+
call s:registry.AddExtension(codefmt#ocamlformat#GetFormatter())

vroom/ocamlformat.vroom

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
The built-in ocamlformat formatter knows how to format ocaml code.
2+
If you aren't familiar with basic codefmt usage yet, see main.vroom first.
3+
4+
We'll set up codefmt and configure the vroom environment, then jump into some
5+
examples.
6+
7+
:source $VROOMDIR/setupvroom.vim
8+
9+
:let g:repeat_calls = []
10+
:function FakeRepeat(...)<CR>
11+
| call add(g:repeat_calls, a:000)<CR>
12+
:endfunction
13+
:call maktaba#test#Override('repeat#set', 'FakeRepeat')
14+
15+
:call codefmt#SetWhetherToPerformIsAvailableChecksForTesting(0)
16+
17+
18+
The ocamlformat formatter expects the ocamlformat executable to be installed on your system.
19+
20+
@clear
21+
% f()
22+
:FormatCode ocamlformat
23+
! ocamlformat .*
24+
$ ;;
25+
$ f ()
26+
;;
27+
f ()
28+
@end
29+
30+
The name or path of the ocamlformat executable can be configured via the
31+
ocamlformat_executable flag if the default of "ocamlformat" doesn't work.
32+
33+
@clear
34+
:Glaive codefmt ocamlformat_executable='myocamlformat'
35+
:FormatCode ocamlformat
36+
! myocamlformat .*
37+
$ ;;
38+
$ f ()
39+
:Glaive codefmt ocamlformat_executable='ocamlformat'
40+
41+
42+
You can format any buffer with ocamlformat specifying the formatter explicitly.
43+
44+
@clear
45+
% let x=5 in<CR>
46+
| let inc x=x+1 in<CR>
47+
| print_endline (string_of_int (inc x))
48+
49+
:FormatCode ocamlformat
50+
! ocamlformat .*
51+
$ ;;
52+
$ let x = 5 in
53+
$ let inc x = x + 1 in
54+
$ print_endline (string_of_int (inc x))
55+
;;
56+
let x = 5 in
57+
let inc x = x + 1 in
58+
print_endline (string_of_int (inc x))
59+
@end
60+
61+
The ocaml filetype will use the ocamlformat formatter by default. The path to the
62+
file being edited is passed to ocamlformat so that it can adjust to whether the
63+
file is an implementation or an interface file.
64+
65+
@clear
66+
% f()
67+
68+
:silent file /foo/bar/test.ml
69+
:set filetype=ocaml
70+
:FormatCode
71+
! ocamlformat - --name /foo/bar/test.ml .*
72+
$ ;;
73+
$ f ()
74+
;;
75+
f ()
76+
@end
77+
78+
It can format specific line ranges of code using :FormatLines.
79+
80+
@clear
81+
% print_string "Hello";;<CR>
82+
|let x=1 in let y=2 in<CR>
83+
|y+2
84+
85+
:2,3FormatLines ocamlformat
86+
! ocamlformat .*
87+
$ ;;
88+
$ let x = 1 in
89+
$ let y = 2 in
90+
$ y + 2
91+
print_string "Hello";;
92+
;;
93+
let x = 1 in
94+
let y = 2 in
95+
y + 2
96+
@end
97+
98+
NOTE: the ocamlformat formatter does not natively support range formatting, so there
99+
are certain limitations like not being able to format misaligned braces.
100+

0 commit comments

Comments
 (0)