Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 3494825

Browse files
Merge pull request #201 from flwyd/master
Add mixformat to handle Elixir
2 parents cf9683d + e2a0bbf commit 3494825

File tree

5 files changed

+225
-0
lines changed

5 files changed

+225
-0
lines changed

‎autoload/codefmt/mixformat.vim‎

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
" Copyright 2022 Google Inc. All rights reserved.
2+
"
3+
" Licensed under the Apache License, Version 2.0 (the "License");
4+
" you may not use this file except in compliance with the License.
5+
" You may obtain a copy of the License at
6+
"
7+
" http://www.apache.org/licenses/LICENSE-2.0
8+
"
9+
" Unless required by applicable law or agreed to in writing, software
10+
" distributed under the License is distributed on an "AS IS" BASIS,
11+
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
" See the License for the specific language governing permissions and
13+
" limitations under the License.
14+
15+
let s:plugin = maktaba#plugin#Get('codefmt')
16+
let s:cmdAvailable = {}
17+
18+
""
19+
" @private
20+
" Formatter: mixformat
21+
function! codefmt#mixformat#GetFormatter() abort
22+
let l:formatter = {
23+
\ 'name': 'mixformat',
24+
\ 'setup_instructions': 'mix is usually installed with Elixir ' .
25+
\ '(https://elixir-lang.org/install.html). ' .
26+
\ "If mix is not in your path, configure it in .vimrc:\n" .
27+
\ 'Glaive codefmt mix_executable=/path/to/mix' }
28+
29+
function l:formatter.IsAvailable() abort
30+
let l:cmd = codefmt#formatterhelpers#ResolveFlagToArray('mix_executable')
31+
if codefmt#ShouldPerformIsAvailableChecks() && !executable(l:cmd[0])
32+
return 0
33+
endif
34+
return 1
35+
endfunction
36+
37+
function l:formatter.AppliesToBuffer() abort
38+
return &filetype is# 'elixir' || &filetype is# 'eelixir'
39+
\ || &filetype is# 'heex'
40+
endfunction
41+
42+
""
43+
" Reformat the current buffer using mix format, only targeting {ranges}.
44+
function l:formatter.FormatRange(startline, endline) abort
45+
let l:filename = expand('%:p')
46+
if empty(l:filename)
47+
let l:dir = getcwd()
48+
" Default filename per https://hexdocs.pm/mix/Mix.Tasks.Format.html
49+
let l:filename = 'stdin.exs'
50+
else
51+
let l:dir = s:findMixDir(l:filename)
52+
endif
53+
" mix format docs: https://hexdocs.pm/mix/main/Mix.Tasks.Format.html
54+
let l:cmd = codefmt#formatterhelpers#ResolveFlagToArray('mix_executable')
55+
" Specify stdin as the file
56+
let l:cmd = l:cmd + ['format', '--stdin-filename=' . l:filename, '-']
57+
let l:syscall = maktaba#syscall#Create(l:cmd).WithCwd(l:dir)
58+
try
59+
" mix format doesn't have a line-range option, but does a reasonable job
60+
" (except for leading indent) when given a full valid expression
61+
call codefmt#formatterhelpers#AttemptFakeRangeFormatting(
62+
\ a:startline, a:endline, l:syscall)
63+
catch /ERROR(ShellError):/
64+
" Parse all the errors and stick them in the quickfix list.
65+
let l:errors = []
66+
for l:line in split(v:exception, "\n")
67+
" Example output:
68+
" ** (SyntaxError) foo.exs:57:28: unexpected reserved word: end
69+
" (blank line)
70+
" HINT: it looks like the "end" on line 56 does not have a matching "do" defined before it
71+
" (blank line), (stack trace with 4-space indent)
72+
" TODO gather additional details between error message and stack trace
73+
let l:tokens = matchlist(l:line,
74+
\ printf('\v^\*\* (\(\k+\)) [^:]+:(\d+):(\d+):\s*(.*)'))
75+
if !empty(l:tokens)
76+
call add(l:errors, {
77+
\ 'filename': @%,
78+
\ 'lnum': l:tokens[2] + a:startline - 1,
79+
\ 'col': l:tokens[3],
80+
\ 'text': printf('%s %s', l:tokens[1], l:tokens[4])})
81+
endif
82+
endfor
83+
if empty(l:errors)
84+
" Couldn't parse mix error format; display it all.
85+
call maktaba#error#Shout('Error formatting range: %s', v:exception)
86+
else
87+
call setqflist(l:errors, 'r')
88+
cc 1
89+
endif
90+
endtry
91+
endfunction
92+
93+
return l:formatter
94+
endfunction
95+
96+
" Finds the directory to run mix from. Looks for a mix.exs file first; if that
97+
" is not found looks for a .formatter.exs file, falling back to the parent of
98+
" filepath.
99+
function! s:findMixDir(filepath) abort
100+
let l:path = empty(a:filepath) ? getcwd() : fnamemodify(a:filepath, ':h')
101+
let l:root = findfile('mix.exs', l:path . ';')
102+
if empty(l:root)
103+
let l:root = findfile('.formatter.exs', l:path . ';')
104+
endif
105+
if empty(l:root)
106+
let l:root = l:path
107+
else
108+
let l:root = fnamemodify(l:root, ':h')
109+
endif
110+
return l:root
111+
endfunction

‎doc/codefmt.txt‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ The current list of defaults by filetype is:
3737
* clojure: cljstyle, zprint
3838
* dart: dartfmt
3939
* fish: fish_indent
40+
* elixir: mixformat
4041
* gn: gn
4142
* go: gofmt
4243
* haskell: ormolu
@@ -88,6 +89,10 @@ Default: 'dartfmt' `
8889
The path to the js-beautify executable.
8990
Default: 'js-beautify' `
9091

92+
*codefmt:mix_executable*
93+
The path to the mix executable for Elixir.
94+
Default: 'mix' `
95+
9196
*codefmt:yapf_executable*
9297
The path to the yapf executable.
9398
Default: 'yapf' `

‎instant/flags.vim‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ call s:plugin.Flag('dartfmt_executable', 'dartfmt')
8080
" The path to the js-beautify executable.
8181
call s:plugin.Flag('js_beautify_executable', 'js-beautify')
8282

83+
""
84+
" The path to the mix executable for Elixir.
85+
call s:plugin.Flag('mix_executable', 'mix')
86+
8387
""
8488
" The path to the yapf executable.
8589
call s:plugin.Flag('yapf_executable', 'yapf')

‎plugin/register.vim‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
" * clojure: cljstyle, zprint
3232
" * dart: dartfmt
3333
" * fish: fish_indent
34+
" * elixir: mixformat
3435
" * gn: gn
3536
" * go: gofmt
3637
" * haskell: ormolu
@@ -63,6 +64,7 @@ call s:registry.AddExtension(codefmt#clangformat#GetFormatter())
6364
call s:registry.AddExtension(codefmt#cljstyle#GetFormatter())
6465
call s:registry.AddExtension(codefmt#zprint#GetFormatter())
6566
call s:registry.AddExtension(codefmt#dartfmt#GetFormatter())
67+
call s:registry.AddExtension(codefmt#mixformat#GetFormatter())
6668
call s:registry.AddExtension(codefmt#fish_indent#GetFormatter())
6769
call s:registry.AddExtension(codefmt#gn#GetFormatter())
6870
call s:registry.AddExtension(codefmt#gofmt#GetFormatter())

‎vroom/mixformat.vroom‎

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
2+
The built-in mixformat formatter knows how to format Elixir code.
3+
If you aren't familiar with basic codefmt usage yet, see main.vroom first.
4+
5+
We'll set up codefmt and configure the vroom environment, then jump into some
6+
examples.
7+
8+
:source $VROOMDIR/setupvroom.vim
9+
10+
:let g:repeat_calls = []
11+
:function FakeRepeat(...)<CR>
12+
| call add(g:repeat_calls, a:000)<CR>
13+
:endfunction
14+
:call maktaba#test#Override('repeat#set', 'FakeRepeat')
15+
16+
:call codefmt#SetWhetherToPerformIsAvailableChecksForTesting(0)
17+
18+
19+
The mixformat formatter expects the mix executable to be installed on your
20+
system.
21+
22+
% IO.puts("Hello world")
23+
:FormatCode mixformat
24+
! cd .* && mix format .* - 2>.*
25+
$ IO.puts("Hello world")
26+
27+
The name or path of the mixformat executable can be configured via the
28+
mix_executable flag if the default of "mix" doesn't work.
29+
30+
:Glaive codefmt mix_executable='someothermix'
31+
:FormatCode mixformat
32+
! cd .* && someothermix format .* - 2>.*
33+
$ IO.puts("Hello world")
34+
:Glaive codefmt mix_executable='mix'
35+
36+
37+
You can format any buffer with mixformat specifying the formatter explicitly.
38+
39+
@clear
40+
% def foo() do<CR>
41+
|IO.puts("Hello"); IO.puts("World");<CR>
42+
|end
43+
44+
:FormatCode mixformat
45+
! cd .* && mix format .* - 2>.*
46+
$ def foo() do
47+
$ IO.puts("Hello")
48+
$ IO.puts("World")
49+
$ end
50+
def foo() do
51+
IO.puts("Hello")
52+
IO.puts("World")
53+
end
54+
@end
55+
56+
The elixir, eelixer, and heex filetypes will use the mixformat formatter
57+
by default.
58+
59+
@clear
60+
% IO.puts("Hello world")
61+
62+
:set filetype=elixir
63+
:FormatCode
64+
! cd .* && mix format .* - 2>.*
65+
$ IO.puts("Hello world")
66+
67+
:set filetype=eelixir
68+
:FormatCode
69+
! cd .* && mix format .* - 2>.*
70+
$ IO.puts("Hello world")
71+
72+
:set filetype=heex
73+
:FormatCode
74+
! cd .* && mix format .* - 2>.*
75+
$ IO.puts("Hello world")
76+
77+
:set filetype=
78+
79+
It can format specific line ranges of code using :FormatLines.
80+
81+
@clear
82+
% defmodule Foo do<CR>
83+
|def bar(list) do<CR>
84+
|[head | tail] = list; IO.puts(head)<CR>
85+
|end<CR>
86+
|end
87+
88+
:2,4FormatLines mixformat
89+
! cd .* && mix format .* - 2>.*
90+
$ def bar(list) do
91+
$ [head | tail] = list
92+
$ IO.puts(head)
93+
$ end
94+
defmodule Foo do
95+
def bar(list) do
96+
[head | tail] = list
97+
IO.puts(head)
98+
end
99+
end
100+
@end
101+
102+
NOTE: the mix formatter does not natively support range formatting, so there
103+
are certain limitations like misaligning indentation levels.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /