Closed brandonbloom closed 10 years ago
Hi Brandon,
I definitely understand your desire here. Lisp indenting for literal data sometimes feels too right-indented after writing some JavaScript literals and the like.
OTOH, the standard convention of "subforms are never indented behind the opening bracket" is the one rule that keeps Lisp readable without having to move to full K&R brace style:
(defn example []
(foo {
:alpha (bar {
(baz {:alpha (quux '[
one {:beta abc
:gamma (zab [
rab
oof])} three])})})}))
;; At a glance: who is the grandparent of (zab)?
This contrived example is a terrible mixture of Lisp and C conventions that totally obscures the tree structure of the code. Rewriting this in either Lisp or C brace style would be better, but mixing them is sinful.
The indenting script offers two options that can help with deeply nested forms: lispwords
and g:clojure_fuzzy_indent_patterns
. For instance, if we add foo
to lispwords
, your examples could look like¹:
(foo bar
[some
long
list
of
stuff
█
])
(foo bar
[{:some "kinda" :thing "here"}
█
])
This looks acceptable to me, so I think what we might lack here is a quick way to add symbols to lispwords
on the fly:¹
function! ToggleLispwords(word)
" Strip leading namespace qualifiers and macro characters from symbol
let word = substitute(a:word, "\\v%(.*/|[#'`~@^,]*)(.*)", '\1', '')
if &lispwords =~# '\V\<' . word . '\>'
execute 'setlocal lispwords-=' . word
echo "Removed " . word . " from lispwords."
else
execute 'setlocal lispwords+=' . word
echo "Added " . word . " to lispwords."
endif
endfunction
nnoremap <Leader>l :<C-U>call ToggleLispwords(expand('<cword>'))<CR>
Now I can place my cursor on any symbol that I'd like to always two-space indent and use <Leader>l
to add it to lispwords
. Of course, if you find that you always want this symbol indented like this, you would add this to your vimrc to make it persist.
My personal philosophy on developer tools is that they should be maximally flexible (unlike, say, client software), but I also think they should teach users good habits. This is especially true when the habit to be learned is Lisp indenting.
Hope I've been persuasive, but if not, I am always open to discussion. :)
¹ Indenting compound forms in this way is really intended for "special" functions and macros, but eh, it's our code and we can do what we like.
EDIT: Fixed the vim snippet
Thanks for the detailed response. Please allow me to clarify my position in light of your perspective.
Ideally, pressing return after naked braces would indent 2 spaces.
I've already got good habits, so I wouldn't press return there unless I wanted a long flat list like I described above. I understand your position regarding teaching good habits, so I rescind this first half of my request. However...
Alternatively, the indenting should prefer to align with the previous line, not the most recent open-bracket.
What I mean by this is that if I forcibly outdent, the indenter should respect that on the next line break. My last example is clearly wrong indenting. When faced with such a case, which would be an abomination anywhere outside of the long data-like forms I'm interested in here, it would be great of the indented didn't fight me.
Does that make sense?
When faced with such a case, which would be an abomination anywhere outside of the long data-like forms I'm interested in here, it would be great of the indented didn't fight me.
While I happen to like the current behavior very much, I have definitely been on the other side of this very same fence in another language (it was the infuriatingly inflexible behavior of the Ruby indenter WRT indenting var = if…else…end
, fixed in the past year).
So okay, I agree that this is a good idea. This would add flexibility without impacting my own workflow or encouraging bad habits. The builtin lisp
indenter also behaves like this, so that's another argument in your favor.
Please note that using ={motion}
will still re-indent forms to the conventional format, since there is little value to an indent system if it cannot tidy existing lines. The builtin lisp
indenter also behaves this way.
For now, you can accomplish this same effect by temporarily disabling the indentexpr
and switching on autoindent
:
" Toggle between simple indenting and Clojure indenting
if &autoindent
setlocal noautoindent indentexpr=GetClojureIndent()
else
setlocal autoindent indentexpr=
end
I understand your position regarding teaching good habits, so I rescind this first half of my request.
I can just imagine a Vim -> Emacs convert opening this ticket on clojure-mode a year from now:
"Issue #1000: Indent lines after a lone brace like the Clojure filetype in Vim"
Then surely:
Hyperbole?
Okay, I've made the change! This was a good opportunity to start an indent test suite, so I made a test case for this behavior. I hope to expand these tests, but I would really appreciate your feedback and testing.
Works wonderfully. Thanks!
On Fri, Apr 4, 2014 at 4:13 PM, Sung Pae notifications@github.com wrote:
Okay, I've made the change! This was a good opportunity to start an indent test suite, so I made a test case for this behavior. I hope to expand these tests, but I would really appreciate your feedback and testing.
Reply to this email directly or view it on GitHubhttps://github.com/guns/vim-clojure-static/issues/47#issuecomment-39606557 .
Thank you for not giving up too easily. This is a great addition.
Sometimes, I want to write code that looks like this:
It's bad style for small cases, but much nicer for long lists, particularly with extra whitespace and whatnot.
Right now, if you press
<cr>
after a naked(
,[
, or{
, you get something like this:Where the
|
stands for the cursor. Worse, if you forcibly outdent, type some, then press<cr>
again, you get this:Ideally, pressing return after naked braces would indent 2 spaces. Alternatively, the indenting should prefer to align with the previous line, not the most recent open-bracket.