From 7ec31e41d02f8db672e70f7d93b2188299d4fff8 Mon Sep 17 00:00:00 2001 From: Aron Griffis Date: Mon, 11 Nov 2019 17:01:46 -0500 Subject: [PATCH] feat: add zprint formatter for clojure zprint unfortunately lacks the ability to format a range, so we implement `:FormatLines` by sending only the lines to be formatted. See https://github.com/kkinnear/zprint/issues/122 --- README.md | 2 +- autoload/codefmt.vim | 5 +- autoload/codefmt/zprint.vim | 91 ++++++++++++++++++++++++++++++++++++ doc/codefmt.txt | 30 +++++++++++- instant/flags.vim | 21 +++++++++ plugin/register.vim | 1 + vroom/zprint.vroom | 93 +++++++++++++++++++++++++++++++++++++ 7 files changed, 238 insertions(+), 5 deletions(-) create mode 100644 autoload/codefmt/zprint.vim create mode 100644 vroom/zprint.vroom diff --git a/README.md b/README.md index b3c2703..cf5bf1f 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ helpfiles in the `doc/` directory. The helpfiles are also available via * [Bazel](https://www.github.com/bazelbuild/bazel) BUILD files (buildifier) * C, C++ (clang-format) +* [Clojure](https://clojure.org/) ([zprint](https://github.com/kkinnear/zprint)) * CSS, Sass, SCSS, Less (js-beautify) -* Chrome GN files (gn) * Dart (dartfmt) * Go (gofmt) * [GN](https://www.chromium.org/developers/gn-build-configuration) (gn) diff --git a/autoload/codefmt.vim b/autoload/codefmt.vim index 49999fb..9624e14 100644 --- a/autoload/codefmt.vim +++ b/autoload/codefmt.vim @@ -28,10 +28,11 @@ " The current list of defaults by filetype is: " * bzl (Bazel): buildifier " * c, cpp, proto, javascript, typescript: clang-format +" * clojure: zprint +" * dart: dartfmt +" * gn: gn " * go: gofmt " * python: autopep8, yapf -" * gn: gn -" * dart: dartfmt let s:plugin = maktaba#plugin#Get('codefmt') diff --git a/autoload/codefmt/zprint.vim b/autoload/codefmt/zprint.vim new file mode 100644 index 0000000..db0af6d --- /dev/null +++ b/autoload/codefmt/zprint.vim @@ -0,0 +1,91 @@ +" Copyright 2019 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. + +"" +" @section Recommended zprint mappings, mappings-zprint +" @parentsection mappings +" +" Since zprint only works on top-level Clojure forms, it doesn't make sense to +" format line ranges that aren't complete forms. If you're using vim-sexp +" (https://github.com/guns/vim-sexp), the following mapping replaces the default +" "format the current line" with "format the current top-level form." > +" autocmd FileType clojure nmap == =iF +" < + + +let s:plugin = maktaba#plugin#Get('codefmt') + + +"" +" @private +" Formatter: zprint +function! codefmt#zprint#GetFormatter() abort + let l:formatter = { + \ 'name': 'zprint', + \ 'setup_instructions': + \ 'Install zprint filter (https://github.com/kkinnear/zprint) ' . + \ 'and configure the zprint_executable flag'} + + function l:formatter.IsAvailable() abort + return executable(s:plugin.Flag('zprint_executable')) + endfunction + + function l:formatter.AppliesToBuffer() abort + return &filetype is# 'clojure' + endfunction + + "" + " Reformat the current buffer with zprint or the binary named in + " @flag(zprint_executable), only targeting the range between {startline} and + " {endline}. + function l:formatter.FormatRange(startline, endline) abort + " Must be upper-cased to call as a function + let l:ZprintOptions = s:plugin.Flag('zprint_options') + if type(l:ZprintOptions) is# type([]) + " Assign upper-case to lower-case + let l:zprint_options = l:ZprintOptions + elseif maktaba#value#IsCallable(l:ZprintOptions) + " Call upper-case to assign lower-case + let l:zprint_options = maktaba#function#Call(l:ZprintOptions) + else + throw maktaba#error#WrongType( + \ 'zprint_options flag must be list or callable. Found %s', + \ string(l:ZprintOptions)) + endif + let l:cmd = [s:plugin.Flag('zprint_executable')] + call extend(l:cmd, l:zprint_options) + + call maktaba#ensure#IsNumber(a:startline) + call maktaba#ensure#IsNumber(a:endline) + let l:lines = getline(1, line('$')) + + " zprint doesn't support formatting a range of lines, so format the range + " individually, ignoring context. This works well for top-level forms, although it's + " not ideal for inner forms because it loses the indentation. + let l:input = join(l:lines[a:startline - 1 : a:endline - 1], "\n") + + " Prepare the syscall, changing to the containing directory in case the user + " has configured {:search-config? true} in ~/.zprintrc + let l:result = maktaba#syscall#Create(l:cmd).WithCwd(expand('%:p:h')).WithStdin(l:input).Call() + let l:formatted = split(l:result.stdout, "\n") + + " Special case empty slice: neither l:lines[:0] nor l:lines[:-1] is right. + let l:before = a:startline > 1 ? l:lines[ : a:startline - 2] : [] + let l:full_formatted = l:before + l:formatted + l:lines[a:endline :] + + call maktaba#buffer#Overwrite(1, line('$'), l:full_formatted) + endfunction + + return l:formatter +endfunction diff --git a/doc/codefmt.txt b/doc/codefmt.txt index 8895b5a..3c1b455 100644 --- a/doc/codefmt.txt +++ b/doc/codefmt.txt @@ -11,6 +11,7 @@ CONTENTS *codefmt-contents* 6. Autocommands...........................................|codefmt-autocmds| 7. Functions.............................................|codefmt-functions| 8. Mappings...............................................|codefmt-mappings| + 1. Recommended zprint mappings.................|codefmt-mappings-zprint| ============================================================================== INTRODUCTION *codefmt-intro* @@ -98,6 +99,18 @@ Default: [] ` The path to the rustfmt executable. Default: 'rustfmt' ` + *codefmt:zprint_options* +Command line arguments to feed zprint. Either a list or callable that takes no +args and returns a list with command line arguments. The default configures +zprint with Vim's textwidth. +Default: function('s:ZprintOptions') ` + + *codefmt:zprint_executable* +The path to the zprint executable. Typically this is one of the native images +(zprintl or zprintm) from https://github.com/kkinnear/zprint/releases +installed as zprint. +Default: 'zprint' ` + *codefmt:plugin[autocmds]* Configures whether plugin/autocmds.vim should be loaded. Default: 1 ` @@ -139,10 +152,11 @@ plugins are enabled or what other software is installed on your system. The current list of defaults by filetype is: * bzl (Bazel): buildifier * c, cpp, proto, javascript, typescript: clang-format + * clojure: zprint + * dart: dartfmt + * gn: gn * go: gofmt * python: autopep8, yapf - * gn: gn - * dart: dartfmt ============================================================================== DICTIONARIES *codefmt-dicts* @@ -230,5 +244,17 @@ formatting ranges that mimic vim's built-in |operator|s: enclosing curly braces. * In visual mode, will format the visual selection. +============================================================================== +RECOMMENDED ZPRINT MAPPINGS *codefmt-mappings-zprint* + + +Since zprint only works on top-level Clojure forms, it doesn't make sense to +format line ranges that aren't complete forms. If you're using vim-sexp +(https://github.com/guns/vim-sexp), the following mapping replaces the default +"format the current line" with "format the current top-level form." +> + autocmd FileType clojure nmap == =iF +< + vim:tw=78:ts=8:ft=help:norl: diff --git a/instant/flags.vim b/instant/flags.vim index 5e6cea9..865082c 100644 --- a/instant/flags.vim +++ b/instant/flags.vim @@ -127,3 +127,24 @@ call s:plugin.Flag('rustfmt_options', []) "" " The path to the rustfmt executable. call s:plugin.Flag('rustfmt_executable', 'rustfmt') + + +"" +" @private +" This is declared above zprint_options to avoid interfering with vimdoc parsing +" the maktaba flag. +function s:ZprintOptions() abort + return &textwidth ? ['{:width ' . &textwidth . '}'] : [] +endfunction + +"" +" Command line arguments to feed zprint. Either a list or callable that takes no +" args and returns a list with command line arguments. The default configures +" zprint with Vim's textwidth. +call s:plugin.Flag('zprint_options', function('s:ZprintOptions')) + +"" +" The path to the zprint executable. Typically this is one of the native +" images (zprintl or zprintm) from https://github.com/kkinnear/zprint/releases +" installed as zprint. +call s:plugin.Flag('zprint_executable', 'zprint') diff --git a/plugin/register.vim b/plugin/register.vim index 69989a2..66486b0 100644 --- a/plugin/register.vim +++ b/plugin/register.vim @@ -35,3 +35,4 @@ call s:registry.AddExtension(codefmt#gn#GetFormatter()) call s:registry.AddExtension(codefmt#buildifier#GetFormatter()) call s:registry.AddExtension(codefmt#googlejava#GetFormatter()) call s:registry.AddExtension(codefmt#shfmt#GetFormatter()) +call s:registry.AddExtension(codefmt#zprint#GetFormatter()) diff --git a/vroom/zprint.vroom b/vroom/zprint.vroom new file mode 100644 index 0000000..f17dad3 --- /dev/null +++ b/vroom/zprint.vroom @@ -0,0 +1,93 @@ +The zprint formatter knows how to format Clojure. +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(...) + | call add(g:repeat_calls, a:000) + :endfunction + :call maktaba#test#Override('repeat#set', 'FakeRepeat') + + :call codefmt#SetWhetherToPerformIsAvailableChecksForTesting(0) + +The zprint formatter expects the zprint executable to be installed on your system. + + :FormatCode zprint + ! cd .* zprint .* + +The name or path of the zprint executable can be configured via the +zprint_executable flag if the default of "zprint" doesn't work. + + :Glaive codefmt zprint_executable='/usr/local/bin/zprint' + :FormatCode zprint + ! cd .* /usr/local/bin/zprint .* + :Glaive codefmt zprint_executable='zprint' + +You can format an entire buffer with :FormatCode. + + @clear + % (defn x [] (cond nil 1 :else 2)) + |(defn y [] (cond nil 3 :else 4)) + + :FormatCode zprint + ! cd .* zprint .* + $ (defn x + $ [] + $ (cond nil 1 + $ :else 2)) + $ (defn y + $ [] + $ (cond nil 3 + $ :else 4)) + (defn x + [] + (cond nil 1 + :else 2)) + (defn y + [] + (cond nil 3 + :else 4)) + @end + +You can format specific line ranges using :FormatLines. (Since zprint works on +top-level forms, the range of lines should be one or more complete forms, +otherwise zprint will generate an error or incorrectly-formatted code.) + + @clear + % (defn x [] (cond nil 1 :else 2)) + |(defn y [] (cond nil 3 :else 4)) + |(defn z [] (cond nil 5 :else 6)) + + :2,2FormatLines zprint + ! cd .* zprint .* + $ (defn y + $ [] + $ (cond nil 3 + $ :else 4)) + (defn x [] (cond nil 1 :else 2)) + (defn y + [] + (cond nil 3 + :else 4)) + (defn z [] (cond nil 5 :else 6)) + @end + +Zprint is the default formatter for the clojure file type, so calling +:FormatCode or :FormatLines will use it automatically. + + :set filetype=clojure + :FormatCode + ! cd .* zprint .* + :set filetype& + +The default setting of zprint_options propagates Vim's textwidth setting to +zprint's command-line. + + :set textwidth=123 + :FormatCode zprint + ! cd .* zprint .*:width 123.* + :set textwidth&