Code formatting

Some projects will want to have open curly-brackets on new lines (see Allman indenting style), other will prefer to have the open bracket on the same line as the function/control-statement/… (see K&R indenting style, Java coding style…). We can also choose whether one space shall be inserted before opening braces, and so on.

Of course we could apply reformatting tools (like clang-format) on demand, but then we’d need to identify a set of different tools dedicated to different languages. The day code formatting is handled by Language Server Protocol, we would have access to a simple and extensible solution. In the mean time, here is lh-style.

lh-style doesn’t do any replacement by itself on snippets or abbreviations. It is expected to be used by snippet plugins or from abbreviation definitions. So far, only mu-template and lh-cpp exploit this feature.

Different people will need to do different things:

  • Plugin maintainers will use the dedicated API to reformat on-the-fly the code they generate.
  • End-users will specify the coding style used on their project(s):

Style families

Families already implemented

At this time, the following style families are implemented:

  • EditorConfig styles:
    • curly_bracket_next_line = [yes, no] // true, false, 0, 1.
    • indent_brace_style = [0tbs, 1tbs, allman, bsd_knf, gnu, horstmann, java, K&R, linux_kernel, lisp, none, pico, ratliff, stroustrup, whitesmiths]
    • spaces_around_brackets = [inside, outside, both, none]
  • Clang-Format styles:
    • breakbeforebraces = [allman, attach, gnu, linux, none, stroustrup ]
    • spacesbeforetrailingcomments = [\n, positive number of spaces]
    • spacesbeforeparens = [none, never, always, control-statements]
    • spacesinemptyparentheses = [yes, no] // true, false, 0, 1.
    • spacesinparentheses = [yes, no] // true, false, 0, 1.

Styles can easilly be added in {&rtp}/autoload/lh/style/.

If you want more precise control, without family management, you can use :AddStyle instead.

:UseStyle style-family=value [-buffer] [-ft[=ft]] [-prio=prio]

Given style families, :UseStyle can be used to specify which particular style shall be used when generating code.

For instance,

:UseStyle breakbeforebraces=Allman -ft=c
:UseStyle spacesbeforeparens=control-statements -ft=c
:UseStyle spacesinparentheses=no

will tune snippets/abbreviations to follow Allman indenting style, and to add a space before control-statement parentheses, and to never insert a space inside parentheses.

Note

Some families are incompatible with other families. It happens quickly when we mix overlapping families from different origins.

:UseStyle options

  • style-family=value specifies, given a style family, what choice has been made.

    If you don’t remember them all, don’t worry, :UseStyle supports command-line completion.

    Setting the style to none unsets the whole family for the related buffers/filetype. Giving a new value, overrides the style for the related buffers/filetype.

  • -buffer defines this association only for the current buffer. This option is meant to be used with plugins like local_vimrc.

  • -ft[=ft] defines this association only for the specified filetype. When ft is not specified, it applies only to the current filetype. This option is meant to be used in .vimrc, in the global zone of filetype-plugins or possibly in local_vimrcs (when combined with -buffer).

  • -prio=prio Sets a priority that’ll be used to determine which key is matching the text to enhance. By default all styles have a priority of 1. The typical application is to have template expander ignore single curly brackets.

Note

Local configuration (with -buffer) have the priority over filetype specialized configuration (with -ft).

.editorconfig

lh-style registers a hook to editorconfig-vim in order to extract style choices expressed in any .editorconfig file that applies.

The syntax would be:

[*]
indent_brace_style=Allman

In every buffer where EditorConfig applies its settings, it will be translated into:

:UseStyle -b indent_brace_style=allman

.clang-format

The idea is the same: to detect automatically a .clang-format configuration file in project root directory and apply the styles supported by lh-style.

Warning

At this time, this feature isn’t implemented yet.

Extending the families

New style families can be defined (and even contributed back – as soon as I write the contributing guide…). The following procedure has to be respected:

  1. Create a new autoload plugin named rtp/autoload/lh/style/family-name.vim
  2. Define the following (required) functions:
  • lh#style#family-name#_known_list() which will be used by command-line completion

  • lh#style#family-name#use(styles, value [, options]) which defines the chosen style.

    The typical content of this function is the following:

    function! lh#style#{family-name}#use(styles, value, ...) abort
      let input_options = get(a:, 1, {})
      let [options, local_global, prio, ft] = lh#style#_prepare_options_for_add_style(input_options)
    
      " I usually use a `lh#style#{family-name}#__new()` function for this purpose.
      let s:crt_style = lh#style#define_group('some.unique.family.id', name, local_global, ft)
    
      " Then we dispatch the a:value option to decide how the text should be displayed
      if     a:value =~? value_pattern1
        call s:crt_style.add(regex1, repl1, prio)
      elseif a:value =~? value_pattern2
        call s:crt_style.add(regex2, repl2, prio)
      else
        call s:crt_style.add(regex3, repl3, prio)
      endif
      return 1
    endfunction
    

Note

  • It’ll be best to also define the other functions I have in all my autoload plugins in order to simplify logging and debugging.
  • I highly recommand you take the time to write some unit tests – yeah, I know, I haven’t written them for all possible cases supported by lh-style.

Todo

  • Describe !cursorhere!, !mark! and lh#marker#txt()
  • Describe negative pattern
  • Describe how priorities applies
  • Describe other ways to dispatch
  • Describe none()

Low-level style configuration

Historically, there wasn’t any way to group style configurations as :UseStyle. permits. We add to define everything manually, and switching from one complex configuration to another was tedious.

While using :UseStyle. is now the preferred method, we can still use the low level method.

:AddStyle key [-buffer] [-ft[=ft]] [-prio=prio] Replacement

  • key is a regex that will get replaced automatically (by plugins supporting this API)
  • replacement is what will be inserted in place of key
  • -buffer defines this association only for the current buffer. This option is meant to be used with plugins like local_vimrc.
  • -ft[=ft] defines this association only for the specified filetype. When ft is not specified, it applies only to the current filetype. This option is meant to be used in .vimrc, in the global zone of filetype-plugins or possibly in local_vimrcs (when combined with -buffer).
  • -prio=prio Sets a priority that’ll be used to determine which key is matching the text to enhance. By default all styles have a priority of 1. The typical application is to have template expander ignore single curly brackets.

Note

Local configuration (with -buffer) have the priority over filetype specialized configuration (with -ft).

(Deprecated) :AddStyle Examples:

" # Space before open bracket in C & al {{{2
" A little space before all C constructs in C and child languages
" NB: the spaces isn't put before all open brackets
AddStyle if(     -ft=c   if\ (
AddStyle while(  -ft=c   while\ (
AddStyle for(    -ft=c   for\ (
AddStyle switch( -ft=c   switch\ (
AddStyle catch(  -ft=cpp catch\ (

" # Ignore style in comments after curly brackets {{{2
AddStyle {\ *// -ft=c &
AddStyle }\ *// -ft=c &

" # Multiple C++ namespaces on same line {{{2
AddStyle {\ *namespace -ft=cpp &
AddStyle }\ *} -ft=cpp &

" # Doxygen {{{2
" Doxygen Groups
AddStyle @{  -ft=c @{
AddStyle @}  -ft=c @}

" Doxygen Formulas
AddStyle \\f{ -ft=c \\\\f{
AddStyle \\f} -ft=c \\\\f}

" # Default style in C & al: Stroustrup/K&R {{{2
AddStyle {  -ft=c -prio=10 {\n
AddStyle }; -ft=c -prio=10 \n};\n
AddStyle }  -ft=c -prio=10 \n}

" # Inhibated style in C & al: Allman, Whitesmiths, Pico {{{2
" AddStyle {  -ft=c -prio=10 \n{\n
" AddStyle }; -ft=c -prio=10 \n};\n
" AddStyle }  -ft=c -prio=10 \n}\n

" # Ignore curly-brackets on single lines {{{2
AddStyle ^\ *{\ *$ -ft=c &
AddStyle ^\ *}\ *$ -ft=c &

" # Handle specifically empty pairs of curly-brackets {{{2
" On its own line
" -> Leave it be
AddStyle ^\ *{}\ *$ -ft=c &
" -> Split it
" AddStyle ^\ *{}\ *$ -ft=c {\n}

" Mixed
" -> Split it
" AddStyle {} -ft=c -prio=5 {\n}
" -> On the next line (unsplit)
AddStyle {} -ft=c -prio=5 \n{}
" -> On the next line (split)
" AddStyle {} -ft=c -prio=5 \n{\n}

" # Java style {{{2
" Force Java style in Java
AddStyle { -ft=java -prio=10 {\n
AddStyle } -ft=java -prio=10 \n}

When you wish to adopt Allman coding style, in $project_root/_vimrc_local.vim

AddStyle { -b -ft=c -prio=10 \n{\n
AddStyle } -b -ft=c -prio=10 \n}