Skip to content

Commit

Permalink
Add OCaml support with ocamlformat
Browse files Browse the repository at this point in the history
  • Loading branch information
adamyi committed Jul 7, 2020
1 parent 9033599 commit 7cf152b
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ helpfiles in the `doc/` directory. The helpfiles are also available via
* Java (google-java-format or clang-format)
* JavaScript (clang-format or [prettier](https://prettier.io))
* JSON (js-beautify)
* [OCaml](https://ocaml.org) ([ocamlformat](https://github.com/ocaml-ppx/ocamlformat))
* Proto (clang-format)
* Python (Autopep8, Black, or YAPF)
* Rust ([rustfmt](https://github.com/rust-lang/rustfmt))
Expand Down
1 change: 1 addition & 0 deletions autoload/codefmt.vim
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
" * fish: fish_indent
" * gn: gn
" * go: gofmt
" * ocaml: ocamlformat
" * python: autopep8, black, yapf


Expand Down
86 changes: 86 additions & 0 deletions autoload/codefmt/ocamlformat.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
" Copyright 2020 Google Inc. All rights reserved.
"
" Licensed under the Apache License, Version 2.0 (the "License");
" you may not use this file except in compliance with the License.
" You may obtain a copy of the License at
"
" http://www.apache.org/licenses/LICENSE-2.0
"
" Unless required by applicable law or agreed to in writing, software
" distributed under the License is distributed on an "AS IS" BASIS,
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
" See the License for the specific language governing permissions and
" limitations under the License.


let s:plugin = maktaba#plugin#Get('codefmt')


""
" @private
" Formatter: ocamlformat
function! codefmt#ocamlformat#GetFormatter() abort
let l:formatter = {
\ 'name': 'ocamlformat',
\ 'setup_instructions': 'Install ocamlformat' .
\ '(https://github.com/ocaml-ppx/ocamlformat)'}

function l:formatter.IsAvailable() abort
return executable(s:plugin.Flag('ocamlformat_executable'))
endfunction

function l:formatter.AppliesToBuffer() abort
return &filetype is# 'ocaml'
endfunction

""
" Reformat the current buffer with ocamlformat or the binary named in
" @flag(ocamlformat_executable), only targeting the range between {startline} and
" {endline}.
function l:formatter.FormatRange(startline, endline) abort
let l:cmd = [ s:plugin.Flag('ocamlformat_executable'), '-' ]
let l:fname = expand('%:p')
if !empty(l:fname)
let l:cmd += ['--name', l:fname]
let l:fname_pattern = '"' . escape(l:fname, '\') . '"'
else
" assume it's an OCaml implementation file (.ml) if no file name is
" provided
let l:cmd += ['--impl']
let l:fname_pattern = '\<standard input\>'
end
try
" NOTE: ocamlformat does not support range formatting.
" See https://github.com/ocaml-ppx/ocamlformat/pull/1188
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
\ a:startline, a:endline, l:cmd)
catch /ERROR(ShellError):/
" Parse all the errors and stick them in the quickfix list.
let l:errors = []
let l:matchidx = 1
while 1
let l:tokens = matchlist(v:exception,
\ '\vFile ' . l:fname_pattern . ', line (\d+), characters (\d+)-\d+:\n(.*)\n', -1, l:matchidx)
if empty(l:tokens)
break
endif
call add(l:errors, {
\ 'filename': @%,
\ 'lnum': l:tokens[1] + a:startline - 1,
\ 'col': l:tokens[2],
\ 'text': l:tokens[3]})
let l:matchidx = l:matchidx + 1
endwhile

if empty(l:errors)
" Couldn't parse ocamlformat error format; display it all.
call maktaba#error#Shout('Error formatting file: %s', v:exception)
else
call setqflist(l:errors, 'r')
cc 1
endif
endtry
endfunction

return l:formatter
endfunction
13 changes: 9 additions & 4 deletions doc/codefmt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ Default: 'dartfmt' `
The path to the js-beautify executable.
Default: 'js-beautify' `

*codefmt:black_executable*
The path to the black executable.
Default: 'black' `

*codefmt:yapf_executable*
The path to the yapf executable.
Default: 'yapf' `

*codefmt:black_executable*
The path to the black executable.
Default: 'black' `

*codefmt:gn_executable*
The path to the gn executable.
Default: 'gn' `
Expand Down Expand Up @@ -122,6 +122,10 @@ Default: 'zprint' `
The path to the fish_indent executable.
Default: 'fish_indent' `

*codefmt:ocamlformat_executable*
The path to the ocamlformat executable.
Default: 'ocamlformat' `

*codefmt:plugin[autocmds]*
Configures whether plugin/autocmds.vim should be loaded.
Default: 1 `
Expand Down Expand Up @@ -168,6 +172,7 @@ The current list of defaults by filetype is:
* fish: fish_indent
* gn: gn
* go: gofmt
* ocaml: ocamlformat
* python: autopep8, black, yapf

==============================================================================
Expand Down
4 changes: 4 additions & 0 deletions instant/flags.vim
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,7 @@ call s:plugin.Flag('zprint_executable', 'zprint')
""
" The path to the fish_indent executable.
call s:plugin.Flag('fish_indent_executable', 'fish_indent')

""
" The path to the ocamlformat executable.
call s:plugin.Flag('ocamlformat_executable', 'ocamlformat')
1 change: 1 addition & 0 deletions plugin/register.vim
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ call s:registry.AddExtension(codefmt#googlejava#GetFormatter())
call s:registry.AddExtension(codefmt#shfmt#GetFormatter())
call s:registry.AddExtension(codefmt#zprint#GetFormatter())
call s:registry.AddExtension(codefmt#fish_indent#GetFormatter())
call s:registry.AddExtension(codefmt#ocamlformat#GetFormatter())
100 changes: 100 additions & 0 deletions vroom/ocamlformat.vroom
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
The built-in ocamlformat formatter knows how to format ocaml code.
If you aren't familiar with basic codefmt usage yet, see main.vroom first.

We'll set up codefmt and configure the vroom environment, then jump into some
examples.

:source $VROOMDIR/setupvroom.vim

:let g:repeat_calls = []
:function FakeRepeat(...)<CR>
| call add(g:repeat_calls, a:000)<CR>
:endfunction
:call maktaba#test#Override('repeat#set', 'FakeRepeat')

:call codefmt#SetWhetherToPerformIsAvailableChecksForTesting(0)


The ocamlformat formatter expects the ocamlformat executable to be installed on your system.

@clear
% f()
:FormatCode ocamlformat
! ocamlformat .*
$ ;;
$ f ()
;;
f ()
@end

The name or path of the ocamlformat executable can be configured via the
ocamlformat_executable flag if the default of "ocamlformat" doesn't work.

@clear
:Glaive codefmt ocamlformat_executable='myocamlformat'
:FormatCode ocamlformat
! myocamlformat .*
$ ;;
$ f ()
:Glaive codefmt ocamlformat_executable='ocamlformat'


You can format any buffer with ocamlformat specifying the formatter explicitly.

@clear
% let x=5 in<CR>
| let inc x=x+1 in<CR>
| print_endline (string_of_int (inc x))

:FormatCode ocamlformat
! ocamlformat .*
$ ;;
$ let x = 5 in
$ let inc x = x + 1 in
$ print_endline (string_of_int (inc x))
;;
let x = 5 in
let inc x = x + 1 in
print_endline (string_of_int (inc x))
@end

The ocaml filetype will use the ocamlformat formatter by default. The path to the
file being edited is passed to ocamlformat so that it can adjust to whether the
file is an implementation or an interface file.

@clear
% f()

:silent file /foo/bar/test.ml
:set filetype=ocaml
:FormatCode
! ocamlformat - --name /foo/bar/test.ml .*
$ ;;
$ f ()
;;
f ()
@end

It can format specific line ranges of code using :FormatLines.

@clear
% print_string "Hello";;<CR>
|let x=1 in let y=2 in<CR>
|y+2

:2,3FormatLines ocamlformat
! ocamlformat .*
$ ;;
$ let x = 1 in
$ let y = 2 in
$ y + 2
print_string "Hello";;
;;
let x = 1 in
let y = 2 in
y + 2
@end

NOTE: the ocamlformat formatter does not natively support range formatting, so there
are certain limitations like not being able to format misaligned braces.

0 comments on commit 7cf152b

Please sign in to comment.