Skip to content
This repository was archived by the owner on Sep 28, 2023. It is now read-only.

Clojure formatter#18

Open
pfernandez wants to merge 6 commits intoaxvr:masterfrom
pfernandez:master
Open

Clojure formatter#18
pfernandez wants to merge 6 commits intoaxvr:masterfrom
pfernandez:master

Conversation

@pfernandez
Copy link
Contributor

@pfernandez pfernandez commented May 12, 2022

After our previous discussion I decided to keep this minimal and stick to formatting only, at least for now.

This does a few things to help improve formatting in both the Clojure and Leiningen repls. I've added inline comments describing each.

Comment on lines +7 to +34
function s:stripLeadingEmptyLines(lines) abort
while !empty(a:lines)
if a:lines[0] =~# '\m^\s*$'
call remove(a:lines, 0)
else
break
endif
endwhile
return a:lines
endfunction

function s:stripTrailingEmptyLines(lines) abort
let idx = len(a:lines) - 1
while idx >= 0
if a:lines[idx] =~# '\m^\s*$'
call remove(a:lines, idx)
let idx -= 1
else
break
endif
endwhile
return a:lines
endfunction

function s:normalizeIndents(lines) abort
let depth = len(matchstr(a:lines[0], '\m\C^\s*'))
return map(a:lines, 'v:val[' . depth . ':]')
endfunction
Copy link
Contributor Author

@pfernandez pfernandez May 12, 2022

Choose a reason for hiding this comment

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

The code in these three functions are ripped straight from the other formatters. At some point they could potentially be moved into a shared file like contrib/utils.vim.

Copy link
Owner

Choose a reason for hiding this comment

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

I agree. I've actually thought about this before, but haven't got around to it. I think they should probably live under autoload/zepl/fmt.vim or similar (still not sure on the naming yet).

Then they can be called like this:

return zepl#fmt#NormaliseIndentation(a:lines)

Copy link
Owner

Choose a reason for hiding this comment

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

In fact, the default formatter should probably do some of this anyway. Maybe I'll get round to that at some point in the future.


function s:processLine(...) abort
" Remove trailing newlines.
let line = trim(a:2, "\r\n", 2)
Copy link
Contributor Author

@pfernandez pfernandez May 12, 2022

Choose a reason for hiding this comment

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

This is kind of personal, but it shouldn't hurt anything and may actually help others. I have a function in my Vim config containing the following:

let l:ns = s:getClojureNS()
if strlen(l:ns)
  let l:cmd = "(do (require '" . l:ns . ")\n      (in-ns '" . l:ns . "))"
  execute 'ReplSend' . l:cmd
endif

I include the \n to avoid jamming both commands into a single line, but it comes out with an extra newline.

Before
Screen Shot 2022-05-11 at 4 34 53 PM

After
Screen Shot 2022-05-11 at 4 36 10 PM

Copy link
Owner

@axvr axvr May 16, 2022

Choose a reason for hiding this comment

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

Your example should just work by using \<CR> instead of \n.

let l:ns = s:getClojureNS()
if strlen(l:ns)
  let l:cmd = "(do (require '" . l:ns . ")\<CR>      (in-ns '" . l:ns . "))"
  execute 'ReplSend' . l:cmd
endif

(Vim's handling of newlines is really confusing. I don't understand it fully, but I've found that the rule of thumb is to try using <CR> first. Or \<CR> if inside a " delimited string.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After echoing the line with echomsg a:2, I can see that an ^M has been inserted. Going to do more experimenting to see where that's coming from. ^M is a DOS line break but I'm on a Mac (see my other comment).

Copy link
Owner

Choose a reason for hiding this comment

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

It seems that there is <LF> and <NL> too. I've only ever used <CR> which seems to have worked on Linux, Mac and Windows for me.

let line = trim(a:2, "\r\n", 2)

" Remove two leading spaces added by the Clojure repl.
return substitute(line, '\m^\s\s', '', '')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I noticed this behavior in both the Clojure and Leiningen repls. A two-space indent is always added after the first line break.

Before
Screen Shot 2022-05-11 at 4 35 09 PM

After
Screen Shot 2022-05-11 at 4 36 21 PM

Copy link
Owner

@axvr axvr May 16, 2022

Choose a reason for hiding this comment

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

That's odd. I've never had this problem, so using this formatter actually has the inverse problem. What OS and Vim version are you using?

With formatter Without formatter

Regardless, I'm fine including this, but maybe there should be a config option for it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm using macOS Monterey 12.3.1, and run Vim inside iTerm:

$ vim --version
VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Apr 20 2022 18:55:37)
macOS version - x86_64
Included patches: 1-4800
Compiled by Homebrew
Huge version without GUI.

I'll go back and strip my vimrc to the bare essentials, and also try it with Gvim and on a Ubuntu machine I have lying around. While I'm at it, I'll dig deeper into the mysterious ^M I mentioned above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've been pretty busy at work, but today I removed everything except zepl from my vim config and tried it again. I found that the problem is the same in either case, but that my "eliminate two spaces" code only worked because I was always using a two-space indent inside comment forms. I was just covering up the fact that the indentation code that I borrowed from the F# formatter wasn't actually doing anything.

I'll come back to this when I have more time and give it a fresh look.

Copy link
Owner

Choose a reason for hiding this comment

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

Sure, don't worry, there's no rush. I'm happy to help where needed, but I am quite busy too.

" runtime plugins/zepl.vim/zepl/contrib/clojure.vim
" let g:repl_config = {
" \ 'clojure': {
" \ 'cmd': filereadable('deps.edn') ? 'clj' : 'lein repl',
Copy link
Contributor Author

@pfernandez pfernandez May 12, 2022

Choose a reason for hiding this comment

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

This works to determine whether we're in a leiningen project or not, but assumes that the user is working from the project root. Nevertheless I think it's a useful example.

Copy link
Owner

Choose a reason for hiding this comment

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

Nice. Maybe this example could include the 'rlwrap' flag too? (You don't have to if you'd rather not.)

"   let g:repl_config = {
"   \   'clojure': {
"   \     'cmd': filereadable('deps.edn') ? 'clj' : 'lein repl',
"   \     'rlwrap': filereadable('deps.edn') ? 1 : 0
"   \     'formatter': function('zepl#contrib#clojure#formatter')
"   \   }
"   \ }

(+ If you decide to update this, don't forget to update the example in the doc too.)

Copy link
Owner

@axvr axvr left a comment

Choose a reason for hiding this comment

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

Looking good. Left a few comments.

(Edit: some of the below are actually comments above. For some reason GitHub is showing them below too. 😕)

let line = trim(a:2, "\r\n", 2)

" Remove two leading spaces added by the Clojure repl.
return substitute(line, '\m^\s\s', '', '')
Copy link
Owner

@axvr axvr May 16, 2022

Choose a reason for hiding this comment

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

That's odd. I've never had this problem, so using this formatter actually has the inverse problem. What OS and Vim version are you using?

With formatter Without formatter

Regardless, I'm fine including this, but maybe there should be a config option for it?

Comment on lines +7 to +34
function s:stripLeadingEmptyLines(lines) abort
while !empty(a:lines)
if a:lines[0] =~# '\m^\s*$'
call remove(a:lines, 0)
else
break
endif
endwhile
return a:lines
endfunction

function s:stripTrailingEmptyLines(lines) abort
let idx = len(a:lines) - 1
while idx >= 0
if a:lines[idx] =~# '\m^\s*$'
call remove(a:lines, idx)
let idx -= 1
else
break
endif
endwhile
return a:lines
endfunction

function s:normalizeIndents(lines) abort
let depth = len(matchstr(a:lines[0], '\m\C^\s*'))
return map(a:lines, 'v:val[' . depth . ':]')
endfunction
Copy link
Owner

Choose a reason for hiding this comment

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

I agree. I've actually thought about this before, but haven't got around to it. I think they should probably live under autoload/zepl/fmt.vim or similar (still not sure on the naming yet).

Then they can be called like this:

return zepl#fmt#NormaliseIndentation(a:lines)

" runtime plugins/zepl.vim/zepl/contrib/clojure.vim
" let g:repl_config = {
" \ 'clojure': {
" \ 'cmd': filereadable('deps.edn') ? 'clj' : 'lein repl',
Copy link
Owner

Choose a reason for hiding this comment

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

Nice. Maybe this example could include the 'rlwrap' flag too? (You don't have to if you'd rather not.)

"   let g:repl_config = {
"   \   'clojure': {
"   \     'cmd': filereadable('deps.edn') ? 'clj' : 'lein repl',
"   \     'rlwrap': filereadable('deps.edn') ? 1 : 0
"   \     'formatter': function('zepl#contrib#clojure#formatter')
"   \   }
"   \ }

(+ If you decide to update this, don't forget to update the example in the doc too.)

Comment on lines +59 to +61
" (Replace 'plugins/' with your plugin directory path.)
"
" runtime plugins/zepl.vim/zepl/contrib/clojure.vim
Copy link
Owner

Choose a reason for hiding this comment

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

I'm curious about this, does just runtime zepl/contrib/clojure.vim not work for you?

Which plugin manager are you using?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm using vim-plug, which actually uses ~/.vim/plugged/. I wrote plugins/ because that's Vim's default plugin directory, but mainly was just trying to be more clear that you need to figure out the path for yourself depending on your setup.


function s:processLine(...) abort
" Remove trailing newlines.
let line = trim(a:2, "\r\n", 2)
Copy link
Owner

@axvr axvr May 16, 2022

Choose a reason for hiding this comment

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

Your example should just work by using \<CR> instead of \n.

let l:ns = s:getClojureNS()
if strlen(l:ns)
  let l:cmd = "(do (require '" . l:ns . ")\<CR>      (in-ns '" . l:ns . "))"
  execute 'ReplSend' . l:cmd
endif

(Vim's handling of newlines is really confusing. I don't understand it fully, but I've found that the rule of thumb is to try using <CR> first. Or \<CR> if inside a " delimited string.)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants