Skip to content

Add ktfmt support for Kotlin #183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 16, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,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)
* Kotlin ([ktfmt](https://github.com/facebookincubator/ktfmt))
* Proto (clang-format)
* Python (Autopep8, Black, or YAPF)
* Rust ([rustfmt](https://github.com/rust-lang/rustfmt))
Expand Down
87 changes: 87 additions & 0 deletions autoload/codefmt/ktfmt.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
" Copyright 2017 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: ktfmt
function! codefmt#ktfmt#GetFormatter() abort
let l:formatter = {
\ 'name': 'ktfmt',
\ 'setup_instructions': 'Install ktfmt ' .
\ "(https://github.com/facebookincubator/ktfmt).\n" .
\ 'Enable with "Glaive codefmt ktfmt_executable=' .
\ '"java -jar /path/to/ktfmt-<VERSION>-jar-with-dependencies.jar" ' .
\ 'in your .vimrc' }

function l:formatter.IsAvailable() abort
let l:exec = split(s:plugin.Flag('ktfmt_executable'), '\\\@<! ')
if empty(l:exec)
return 0
endif
if executable(l:exec[0])
return 1
elseif !empty(l:exec[0]) && l:exec[0] isnot# 'ktfmt'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This branch is intended to allow non-default formatters to work, but in this case exec[0] would never be ktfmt, since that's not the default.

This particular pattern is only present for google-java-format, and I think there only because we aren't doing the splitting that you have here on L31, so we just give up trying to check for an executable.

I think we probably want one or the other: either split to find an executable ("java", here), and check whether it's runnable, or alternatively skip that check if the flag value's been overridden. Since there's no single ktfmt binary to run, I think probably the former?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did something fancy, see my response to @dbarnett above.

" The user has specified a custom formatter command. Hope it works.
return 1
else
return 0
endif
endfunction

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

""
" Reformat the current buffer using ktfmt, only targeting {ranges}.
function l:formatter.FormatRange(startline, endline) abort
" Split the command on spaces, except when there's a proceeding \
let l:cmd = split(s:plugin.Flag('ktfmt_executable'), '\\\@<! ')
" ktfmt requires '-' as a filename arg to read stdin
let l:cmd = add(l:cmd, '-')
try
" TODO(tstone) Switch to using --lines once that arg is added, see
" https://github.com/facebookincubator/ktfmt/issues/218
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 = []
for l:line in split(v:exception, "\n")
let l:tokens = matchlist(l:line, '\C\v^<stdin>:(\d+):(\d+):\s*(.*)')
if !empty(l:tokens)
call add(l:errors, {
\ 'filename': @%,
\ 'lnum': l:tokens[1] + a:startline - 1,
\ 'col': l:tokens[2],
\ 'text': l:tokens[3]})
endif
endfor
if empty(l:errors)
" Couldn't parse ktfmt error format; display it all.
call maktaba#error#Shout('Error formatting range: %s', v:exception)
else
call setqflist(l:errors, 'r')
cc 1
endif
endtry
endfunction

return l:formatter
endfunction


5 changes: 5 additions & 0 deletions doc/codefmt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ The path to the google-java executable. Generally, this should have the form:
`java -jar /path/to/google-java`
Default: 'google-java-format' `

*codefmt:ktfmt_executable*
The path to the ktfmt executable. Generally, this should have the form: `java
-jar /path/to/ktfmt-VERSION-jar-with-dependencies.jar`
Default: 'ktfmt' `

*codefmt:shfmt_options*
Command line arguments to feed shfmt. Either a list or callable that takes no
args and returns a list with command line arguments. By default, uses the
Expand Down
5 changes: 5 additions & 0 deletions instant/flags.vim
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ call s:plugin.Flag('buildifier_executable', 'buildifier')
" `java -jar /path/to/google-java`
call s:plugin.Flag('google_java_executable', 'google-java-format')

""
" The path to the ktfmt executable. Generally, this should have the form:
" `java -jar /path/to/ktfmt-VERSION-jar-with-dependencies.jar`
call s:plugin.Flag('ktfmt_executable', 'ktfmt')

""
" Command line arguments to feed shfmt. Either a list or callable that
" takes no args and returns a list with command line arguments. By default, uses
Expand Down
1 change: 1 addition & 0 deletions plugin/register.vim
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ call s:registry.AddExtension(codefmt#gofmt#GetFormatter())
call s:registry.AddExtension(codefmt#googlejava#GetFormatter())
call s:registry.AddExtension(codefmt#jsbeautify#GetFormatter())
call s:registry.AddExtension(codefmt#prettier#GetFormatter())
call s:registry.AddExtension(codefmt#ktfmt#GetFormatter())
call s:registry.AddExtension(codefmt#luaformatterfiveone#GetFormatter())
call s:registry.AddExtension(codefmt#nixpkgs_fmt#GetFormatter())
call s:registry.AddExtension(codefmt#autopep8#GetFormatter())
Expand Down
40 changes: 40 additions & 0 deletions vroom/ktfmt.vroom
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
The built-in ktfmt formatter knows how to format Java BUILD files. 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 ktfmt formatter expects a ktfmt executable to be installed on your system.

% class Foo { public bar() : String { return "bar"; } }
:FormatCode ktfmt
! ktfmt .*
$ class Foo {
$ public bar(): String {
$ return "bar"
$ }
$ }

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

:Glaive codefmt ktfmt_executable='java -jar /path/to/ktfmt.jar'
:FormatCode ktfmt
! java -jar /path/to/ktfmt.jar .*
$ class Foo {
$ public bar(): String {
$ return "bar"
$ }
$ }
:Glaive codefmt ktfmt_executable='ktfmt'