1
0
Fork 0
mirror of synced 2024-12-24 07:43:20 -05:00

Add support for Elixir.

This commit is contained in:
Kurtis Moxley 2022-05-19 01:31:41 +08:00
parent 9e29fd54b4
commit d26bc75459
89 changed files with 6799 additions and 0 deletions

View file

@ -0,0 +1,4 @@
profile.log
test_indent.result
doc/tags
.gvim_path

View file

@ -0,0 +1,9 @@
language: ruby
rvm:
- 2.3.1
before_install: sudo apt-get install vim-gtk
before_script:
- "vim --version"
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
script: "grep 'focus:.*true' -R spec/indent spec/syntax && exit -1 || CI=true bin/rspec --color -b -f d"

View file

@ -0,0 +1,5 @@
FROM ubuntu:latest
RUN apt-get update && apt-get install -yf vim
COPY test.vimrc /root/.vimrc

View file

@ -0,0 +1,8 @@
# vi: ft=dockerfile
FROM ubuntu:latest
RUN apt-get update && apt-get install -yf neovim
RUN mkdir -p /root/.config/nvim
COPY test.init.vim /root/.config/nvim/init.vim

View file

@ -0,0 +1,7 @@
source 'https://rubygems.org'
gem 'rspec'
gem 'vimrunner'
gem 'pry'
gem 'diffy'
gem 'parallel_tests'

View file

@ -0,0 +1,43 @@
GEM
remote: https://rubygems.org/
specs:
coderay (1.1.1)
diff-lcs (1.2.5)
diffy (3.2.0)
method_source (0.8.2)
parallel (1.12.1)
parallel_tests (2.21.2)
parallel
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
rspec (3.5.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-core (3.5.3)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
slop (3.6.0)
vimrunner (0.3.3)
PLATFORMS
ruby
x86_64-darwin-19
DEPENDENCIES
diffy
parallel_tests
pry
rspec
vimrunner
BUNDLED WITH
2.2.2

View file

@ -0,0 +1,13 @@
Filing a bug? Have you already tried updating `vim-elixir`? For indentation/highlighting bugs, please use the following template:
# Actual
```ex
Example code
```
# Expected
```ex
Example code
```

View file

@ -0,0 +1,13 @@
Copyright 2012 Plataformatec
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.

View file

@ -0,0 +1,143 @@
# vim-elixir
[![Build Status](https://travis-ci.org/elixir-editors/vim-elixir.svg?branch=master)](https://travis-ci.org/elixir-editors/vim-elixir)
[Elixir](http://elixir-lang.org) support for vim
## Description
Features:
* Syntax highlighting for Elixir and EEx files
* Filetype detection for `.ex`, `.exs`, `.eex`, `.heex`, `.leex`, and `.sface` files
* Automatic indentation
* Integration between Ecto projects and [vim-dadbod][] for running SQL queries
on defined Ecto repositories
## Installation
`vim-elixir` can be installed either with a plugin manager or by directly copying the files into your vim folders (location varies between platforms)
### Plugin Managers
If you are using a plugin manager then add `vim-elixir` the way you would any other plugin:
```bash
# Using vim 8 native package loading
# http://vimhelp.appspot.com/repeat.txt.html#packages
git clone https://github.com/elixir-editors/vim-elixir.git ~/.vim/pack/my-packages/start/vim-elixir
# Using pathogen
git clone https://github.com/elixir-editors/vim-elixir.git ~/.vim/bundle/vim-elixir
```
```viml
" Using vim-plug
Plug 'elixir-editors/vim-elixir'
" Using Vundle
Plugin 'elixir-editors/vim-elixir'
" Using NeoBundle
NeoBundle 'elixir-editors/vim-elixir'
```
### Manual Installation
If you are not using a package manager then you can use the provided `manual_install.sh` script to copy the files into their respective homes.
Run [./manual_install.sh](manual_install.sh) to copy the contents of each directory in the respective directories inside `~/.vim`.
## Configuration
You must add the following to your `~/.vimrc`:
```
" Enable syntax highlighting
syntax on
" Enables filetype detection, loads ftplugin, and loads indent
" (Not necessary on nvim and may not be necessary on vim 8.2+)
filetype plugin indent on
```
## Notes/Caveats
### `mix format` Integration
We've decided not to include `mix format` integration into `vim-elixir`.
If you'd like to set it up yourself, you have the following options:
* For asynchronous execution of the formatter, have a look at [vim-mix-format](https://github.com/mhinz/vim-mix-format)
* Add it as a `formatprg` (e.g. `setlocal formatprg=mix\ format\ -`)
Why isn't this supported? We've run into two major issues with calling out to `mix format`.
First `mix format` would not work unless your program compiled.
Second `mix format` added an external process dependency to `vim-elixir`.
If someone really wanted to try and add this then we might be able to model it after `vim-go`'s `go fmt` integration
which I think could be acceptable to merge into master.
## Development
### Maintenance Help
`vim-elixir` is looking for new maintainers.
If you get a lot of value from it, know vimscript well, or eager to learn about it then feel free to get in touch with @jbodah (GH issue, elixir-lang Slack)
### Running the Tests
The tests depend on having Ruby installed.
They also depend on a GUI vim (gvim, mvim) with server support.
If you do not have gvim or mvim in your PATH then you can create a `.gvim_path` file in the vim-elixir root directory which specifies the path to the GUI vim executable.
To run the tests: `bundle exec parallel_rspec spec`
### Developing in Docker
You can spawn a container with vim and your development configs using `bin/vim` or `bin/nvim`
### Debugging Indent
```
# Open vim in a container loading this plugin
bin/vim myfile.ex
# Debug statements should be configured to print automatically
# Write/indent some code
:messages
# You should see output like the following:
# ==> Indenting line 3
# text = ' _ -> :wowo'
# testing handler elixir#indent#handle_top_of_file
# testing handler elixir#indent#handle_starts_with_string_continuation
# testing handler elixir#indent#handle_following_trailing_binary_operator
# testing handler elixir#indent#handle_starts_with_pipe
# testing handler elixir#indent#handle_starts_with_binary_operator
# testing handler elixir#indent#handle_inside_block
# pattern matching relative to lnum 2
# current line contains ->; assuming match definition
# line 3: elixir#indent#handle_inside_block returned 4
# 1 change; before #1 4 seconds ago
#
# This tells you which line is being inspected as well as which handlers are being run
# and which branches are being exercised by those handlers
```
### Feature Wishlist
Here is a list of features that I think would be great additions to `vim-elixir`:
* Regularly merging `vim-elixir` into `vim` and keeping the sync up-to-date
* Fixing our build so it can run regularly on CI
* Live view support
* Testing .exs files and ensuring feature compatibility between .ex and .exs
* Documentation (e.g. `:h vim-elixir`)
* README docs for various .vimrc options/flags
* Identifying and rewriting tests that conflict with `mix format`
* Fixes for indentation rule edge cases (e.g. `with`, see GH issues for examples)
* Simplifying syntax rules
* Performance optimizations for syntax/indent rules (especially for determining if something is a string)
[vim-dadbod]: https://github.com/tpope/vim-dadbod

View file

@ -0,0 +1,20 @@
let s:path = expand('<sfile>:h')
let s:cmd = join(['mix', 'run', '--no-start', '--no-compile', shellescape(s:path.'/get_repos.exs')])
function! s:repo_list() abort
return map(systemlist(s:cmd), 'split(v:val)')
endfunction
function! db#adapter#ecto#canonicalize(url) abort
for l:item in s:repo_list()
let l:name = get(l:item, 0)
let l:url = get(l:item, 1)
if !empty(l:name) && 'ecto:'.l:name ==# a:url
return l:url
endif
endfor
endfunction
function! db#adapter#ecto#complete_opaque(url) abort
return map(s:repo_list(), 'v:val[0]')
endfunction

View file

@ -0,0 +1,66 @@
defmodule LoadRepos do
defp load_apps do
:code.get_path()
|> Enum.flat_map(fn app_dir ->
Path.join(app_dir, "*.app") |> Path.wildcard()
end)
|> Enum.map(fn app_file ->
app_file |> Path.basename() |> Path.rootname(".app") |> String.to_atom()
end)
|> Enum.map(&Application.load/1)
end
defp configs do
for {app, _, _} <- Application.loaded_applications(),
repos = Application.get_env(app, :ecto_repos),
is_list(repos) and repos != [],
repo <- repos,
do: {repo, Map.new(repo.config())}
end
defp config_to_url(_, %{url: url}), do: url
defp config_to_url(repo, config) do
host =
case Map.fetch(config, :socket_dir) do
:error -> Map.fetch!(config, :hostname)
{:ok, socket_dir} -> socket_dir
end
username = Map.get(config, :username)
password = Map.get(config, :password)
database = Map.get(config, :database)
parameters = Map.get(config, :parameters, [])
%URI{
scheme: adapter_to_string(repo.__adapter__),
host: "",
path: Path.join("/", database),
query: encode_options([host: host, user: username, password: password] ++ parameters)
}
|> URI.to_string()
end
defp adapter_to_string(Ecto.Adapters.Postgres), do: "postgres"
defp adapter_to_string(Ecto.Adapters.MySQL), do: "mysql"
defp adapter_to_string(mod), do: raise("Unknown adapter #{inspect(mod)}")
defp encode_options(opts) do
cleaned =
for {k, v} <- opts, not is_nil(v), do: {k, v}
URI.encode_query(cleaned)
end
def main do
load_apps()
configs()
|> Enum.map(fn {repo, config} ->
[inspect(repo), ?\s, config_to_url(repo, config)]
end)
|> Enum.intersperse(?\n)
|> IO.puts()
end
end
LoadRepos.main()

View file

@ -0,0 +1,481 @@
if !exists("g:elixir_indent_max_lookbehind")
let g:elixir_indent_max_lookbehind = 30
endif
" Return the effective value of 'shiftwidth'
function! s:sw()
return &shiftwidth == 0 ? &tabstop : &shiftwidth
endfunction
function! elixir#indent#indent(lnum)
let lnum = a:lnum
let text = getline(lnum)
let prev_nb_lnum = prevnonblank(lnum-1)
let prev_nb_text = getline(prev_nb_lnum)
call s:debug("==> Indenting line " . lnum)
call s:debug("text = '" . text . "'")
let [_, curs_lnum, curs_col, _] = getpos('.')
call cursor(lnum, 0)
let handlers = [
\'inside_embedded_view',
\'top_of_file',
\'starts_with_string_continuation',
\'following_trailing_binary_operator',
\'starts_with_pipe',
\'starts_with_binary_operator',
\'inside_block',
\'starts_with_end',
\'inside_generic_block',
\'follow_prev_nb'
\]
for handler in handlers
call s:debug('testing handler elixir#indent#handle_'.handler)
let context = {'lnum': lnum, 'text': text, 'first_nb_char_idx': match(text, '\w'), 'prev_nb_lnum': prev_nb_lnum, 'prev_nb_text': prev_nb_text}
let indent = function('elixir#indent#handle_'.handler)(context)
if indent == -2
" Keep indent the same
call s:debug('line '.lnum.': elixir#indent#handle_'.handler.' returned -2; returning indent of -1')
call cursor(curs_lnum, curs_col)
return -1
elseif indent != -1
call s:debug('line '.lnum.': elixir#indent#handle_'.handler.' returned '.indent)
call cursor(curs_lnum, curs_col)
return indent
endif
endfor
call s:debug("defaulting")
call cursor(curs_lnum, curs_col)
return 0
endfunction
function! s:debug(str)
if exists("g:elixir_indent_debug") && g:elixir_indent_debug
echom a:str
endif
endfunction
function! s:starts_with(context, expr)
return s:_starts_with(a:context.text, a:expr, a:context.lnum)
endfunction
function! s:prev_starts_with(context, expr)
return s:_starts_with(a:context.prev_nb_text, a:expr, a:context.prev_nb_lnum)
endfunction
function! s:in_embedded_view()
let groups = map(synstack(line('.'), col('.')), "synIDattr(v:val, 'name')")
for group in ['elixirPhoenixESigil', 'elixirLiveViewSigil', 'elixirSurfaceSigil']
if index(groups, group) >= 0
return 1
endif
endfor
return 0
endfunction
" Returns 0 or 1 based on whether or not the text starts with the given
" expression and is not a string or comment
function! s:_starts_with(text, expr, lnum)
let pos = match(a:text, '^\s*'.a:expr)
if pos == -1
return 0
else
" NOTE: @jbodah 2017-02-24: pos is the index of the match which is
" zero-indexed. Add one to make it the column number
if s:is_string_or_comment(a:lnum, pos + 1)
return 0
else
return 1
end
end
endfunction
function! s:prev_ends_with(context, expr)
return s:_ends_with(a:context.prev_nb_text, a:expr, a:context.prev_nb_lnum)
endfunction
" Returns 0 or 1 based on whether or not the text ends with the given
" expression and is not a string or comment
function! s:_ends_with(text, expr, lnum)
let pos = match(a:text, a:expr.'\s*$')
if pos == -1
return 0
else
if s:is_string_or_comment(a:lnum, pos)
return 0
else
return 1
end
end
endfunction
" Returns 0 or 1 based on whether or not the given line number and column
" number pair is a string or comment
function! s:is_string_or_comment(line, col)
return s:syntax_name(a:line, a:col) =~ '\%(String\|Comment\|CharList\)'
endfunction
function! s:syntax_name(line, col)
return synIDattr(synID(a:line, a:col, 1), "name")
endfunction
" Skip expression for searchpair. Returns 0 or 1 based on whether the value
" under the cursor is a string or comment
function! elixir#indent#searchpair_back_skip()
" NOTE: @jbodah 2017-02-27: for some reason this function gets called with
" and index that doesn't exist in the line sometimes. Detect and account for
" that situation
let curr_col = col('.')
if getline('.')[curr_col-1] == ''
let curr_col = curr_col-1
endif
return s:is_string_or_comment(line('.'), curr_col)
endfunction
" DRY up regex for keywords that 1) makes sure we only look at complete words
" and 2) ignores atoms
function! s:keyword(expr)
return ':\@<!\<\C\%('.a:expr.'\)\>:\@!'
endfunction
" Start at the end of text and search backwards looking for a match. Also peek
" ahead if we get a match to make sure we get a complete match. This means
" that the result should be the position of the start of the right-most match
function! s:find_last_pos(lnum, text, match)
let last = len(a:text) - 1
let c = last
while c >= 0
let substr = strpart(a:text, c, last)
let peek = strpart(a:text, c - 1, last)
let ss_match = match(substr, a:match)
if ss_match != -1
let peek_match = match(peek, a:match)
if peek_match == ss_match + 1
let syng = synIDattr(synID(a:lnum, c + ss_match, 1), 'name')
if syng !~ '\%(String\|Comment\|CharList\)'
return c + ss_match
end
end
end
let c -= 1
endwhile
return -1
endfunction
function! elixir#indent#handle_inside_embedded_view(context)
if !s:in_embedded_view()
return -1
endif
" Multi-line Surface data delimiters
let pair_lnum = searchpair('{{', '', '}}', 'bW', "line('.') == ".a:context.lnum." || s:is_string_or_comment(line('.'), col('.'))", max([0, a:context.lnum - g:elixir_indent_max_lookbehind]))
if pair_lnum
if a:context.text =~ '}}$'
return indent(pair_lnum)
elseif a:context.text =~ '}}*>$'
return -1
elseif s:prev_ends_with(a:context, '[\|%{')
return indent(a:context.prev_nb_lnum) + s:sw()
elseif a:context.prev_nb_text =~ ',$'
return indent(a:context.prev_nb_lnum)
else
return indent(pair_lnum) + s:sw()
endif
endif
" Multi-line opening tag -- >, />, or %> are on a different line that their opening <
let pair_lnum = searchpair('^\s\+<.*[^>]$', '', '^[^<]*[/%}]\?>$', 'bW', "line('.') == ".a:context.lnum." || s:is_string_or_comment(line('.'), col('.'))", max([0, a:context.lnum - g:elixir_indent_max_lookbehind]))
if pair_lnum
if a:context.text =~ '^\s\+\%\(>\|\/>\|%>\|}}>\)$'
call s:debug("current line is a lone >, />, or %>")
return indent(pair_lnum)
elseif a:context.text =~ '\%\(>\|\/>\|%>\|}}>\)$'
call s:debug("current line ends in >, />, or %>")
if s:prev_ends_with(a:context, ',')
return indent(a:context.prev_nb_lnum)
else
return -1
endif
else
call s:debug("in the body of a multi-line opening tag")
return indent(pair_lnum) + s:sw()
endif
endif
" Special cases
if s:prev_ends_with(a:context, '^[^<]*do\s%>')
call s:debug("prev line closes a multi-line do block")
return indent(a:context.prev_nb_lnum)
elseif a:context.prev_nb_text =~ 'do\s*%>$'
call s:debug("prev line opens a do block")
return indent(a:context.prev_nb_lnum) + s:sw()
elseif a:context.text =~ '^\s\+<\/[a-zA-Z0-9\.\-_]\+>\|<% end %>'
call s:debug("a single closing tag")
if a:context.prev_nb_text =~ '^\s\+<[^%\/]*[^/]>.*<\/[a-zA-Z0-9\.\-_]\+>$'
call s:debug("opening and closing tags are on the same line")
return indent(a:context.prev_nb_lnum) - s:sw()
elseif a:context.prev_nb_text =~ '^\s\+<[^%\/]*[^/]>\|\s\+>'
call s:debug("prev line is opening html tag or single >")
return indent(a:context.prev_nb_lnum)
elseif s:prev_ends_with(a:context, '^[^<]*\%\(do\s\)\@<!%>')
call s:debug("prev line closes a multi-line eex tag")
return indent(a:context.prev_nb_lnum) - 2 * s:sw()
else
return indent(a:context.prev_nb_lnum) - s:sw()
endif
elseif a:context.text =~ '^\s*<%\s*\%(end\|else\|catch\|rescue\)\>.*%>'
call s:debug("eex middle or closing eex tag")
return indent(a:context.prev_nb_lnum) - s:sw()
elseif a:context.prev_nb_text =~ '\s*<\/\|<% end %>$'
call s:debug("prev is closing tag")
return indent(a:context.prev_nb_lnum)
elseif a:context.prev_nb_text =~ '^\s\+<[^%\/]*[^/]>.*<\/[a-zA-Z0-9\.\-_]\+>$'
call s:debug("opening and closing tags are on the same line")
return indent(a:context.prev_nb_lnum)
elseif s:prev_ends_with(a:context, '\s\+\/>')
call s:debug("prev ends with a single \>")
return indent(a:context.prev_nb_lnum)
elseif s:prev_ends_with(a:context, '^[^<]*\/>')
call s:debug("prev line is closing a multi-line self-closing tag")
return indent(a:context.prev_nb_lnum) - s:sw()
elseif s:prev_ends_with(a:context, '^\s\+<.*\/>')
call s:debug("prev line is closing self-closing tag")
return indent(a:context.prev_nb_lnum)
elseif a:context.prev_nb_text =~ '^\s\+%\?>$'
call s:debug("prev line is a single > or %>")
return indent(a:context.prev_nb_lnum) + s:sw()
endif
" Simple HTML (ie, opening tag is not split across lines)
let pair_lnum = searchpair('^\s\+<[^%\/].*[^\/>]>$', '', '^\s\+<\/\w\+>$', 'bW', "line('.') == ".a:context.lnum." || s:is_string_or_comment(line('.'), col('.'))", max([0, a:context.lnum - g:elixir_indent_max_lookbehind]))
if pair_lnum
call s:debug("simple HTML")
if a:context.text =~ '^\s\+<\/\w\+>$'
return indent(pair_lnum)
else
return indent(pair_lnum) + s:sw()
endif
endif
return -1
endfunction
function! elixir#indent#handle_top_of_file(context)
if a:context.prev_nb_lnum == 0
return 0
else
return -1
end
endfunction
function! elixir#indent#handle_starts_with_string_continuation(context)
if s:syntax_name(a:context.lnum, a:context.first_nb_char_idx) =~ '\(String\|Comment\|CharList\)$'
return -2
else
return -1
end
endfunction
function! elixir#indent#handle_follow_prev_nb(context)
return s:get_base_indent(a:context.prev_nb_lnum, a:context.prev_nb_text)
endfunction
" Given the line at `lnum`, returns the indent of the line that acts as the 'base indent'
" for this line. In particular it traverses backwards up things like pipelines
" to find the beginning of the expression
function! s:get_base_indent(lnum, text)
let prev_nb_lnum = prevnonblank(a:lnum - 1)
let prev_nb_text = getline(prev_nb_lnum)
let binary_operator = '\%(=\|<>\|>>>\|<=\|||\|+\|\~\~\~\|-\|&&\|<<<\|/\|\^\^\^\|\*\)'
let data_structure_close = '\%(\]\|}\|)\)'
let pipe = '|>'
if s:_starts_with(a:text, binary_operator, a:lnum)
return s:get_base_indent(prev_nb_lnum, prev_nb_text)
elseif s:_starts_with(a:text, pipe, a:lnum)
return s:get_base_indent(prev_nb_lnum, prev_nb_text)
elseif s:_ends_with(prev_nb_text, binary_operator, prev_nb_lnum)
return s:get_base_indent(prev_nb_lnum, prev_nb_text)
elseif s:_ends_with(a:text, data_structure_close, a:lnum)
let data_structure_open = '\%(\[\|{\|(\)'
let close_match_idx = match(a:text, data_structure_close . '\s*$')
call cursor(a:lnum, close_match_idx + 1)
let [open_match_lnum, open_match_col] = searchpairpos(data_structure_open, '', data_structure_close, 'bnW')
let open_match_text = getline(open_match_lnum)
return s:get_base_indent(open_match_lnum, open_match_text)
else
return indent(a:lnum)
endif
endfunction
function! elixir#indent#handle_following_trailing_binary_operator(context)
let binary_operator = '\%(=\|<>\|>>>\|<=\|||\|+\|\~\~\~\|-\|&&\|<<<\|/\|\^\^\^\|\*\)'
if s:prev_ends_with(a:context, binary_operator)
return indent(a:context.prev_nb_lnum) + s:sw()
else
return -1
endif
endfunction
function! elixir#indent#handle_starts_with_pipe(context)
if s:starts_with(a:context, '|>')
let match_operator = '\%(!\|=\|<\|>\)\@<!=\%(=\|>\|\~\)\@!'
let pos = s:find_last_pos(a:context.prev_nb_lnum, a:context.prev_nb_text, match_operator)
if pos == -1
return indent(a:context.prev_nb_lnum)
else
let next_word_pos = match(strpart(a:context.prev_nb_text, pos+1, len(a:context.prev_nb_text)-1), '\S')
if next_word_pos == -1
return indent(a:context.prev_nb_lnum) + s:sw()
else
return pos + 1 + next_word_pos
end
end
else
return -1
endif
endfunction
function! elixir#indent#handle_starts_with_end(context)
if s:starts_with(a:context, s:keyword('end'))
let pair_lnum = searchpair(s:keyword('do\|fn'), '', s:keyword('end').'\zs', 'bnW', "line('.') == " . line('.') . " || elixir#indent#searchpair_back_skip()")
return indent(pair_lnum)
else
return -1
endif
endfunction
function! elixir#indent#handle_starts_with_binary_operator(context)
let binary_operator = '\%(=\|<>\|>>>\|<=\|||\|+\|\~\~\~\|-\|&&\|<<<\|/\|\^\^\^\|\*\)'
if s:starts_with(a:context, binary_operator)
let match_operator = '\%(!\|=\|<\|>\)\@<!=\%(=\|>\|\~\)\@!'
let pos = s:find_last_pos(a:context.prev_nb_lnum, a:context.prev_nb_text, match_operator)
if pos == -1
return indent(a:context.prev_nb_lnum)
else
let next_word_pos = match(strpart(a:context.prev_nb_text, pos+1, len(a:context.prev_nb_text)-1), '\S')
if next_word_pos == -1
return indent(a:context.prev_nb_lnum) + s:sw()
else
return pos + 1 + next_word_pos
end
end
else
return -1
endif
endfunction
" To handle nested structures properly we need to find the innermost
" nested structure. For example, we might be in a function in a map in a
" function, etc... so we need to first figure out what the innermost structure
" is then forward execution to the proper handler
function! elixir#indent#handle_inside_block(context)
let start_pattern = '\C\%(\<with\>\|\<if\>\|\<case\>\|\<cond\>\|\<try\>\|\<receive\>\|\<fn\>\|{\|\[\|(\)'
let end_pattern = '\C\%(\<end\>\|\]\|}\|)\)'
" hack - handle do: better
let block_info = searchpairpos(start_pattern, '', end_pattern, 'bnW', "line('.') == " . line('.') . " || elixir#indent#searchpair_back_skip() || getline(line('.')) =~ 'do:'", max([0, a:context.lnum - g:elixir_indent_max_lookbehind]))
let block_start_lnum = block_info[0]
call s:debug("block_start_lnum=" . block_start_lnum)
let block_start_col = block_info[1]
if block_start_lnum != 0 || block_start_col != 0
let block_text = getline(block_start_lnum)
let block_start_char = block_text[block_start_col - 1]
call s:debug("block_start_char=" . block_start_char)
let never_match = ''
let config = {
\'f': {'aligned_clauses': s:keyword('end'), 'pattern_match_clauses': never_match},
\'c': {'aligned_clauses': s:keyword('end'), 'pattern_match_clauses': never_match},
\'t': {'aligned_clauses': s:keyword('end\|catch\|rescue\|after\|else'), 'pattern_match_clauses': s:keyword('catch\|rescue\|else')},
\'r': {'aligned_clauses': s:keyword('end\|after'), 'pattern_match_clauses': s:keyword('after')},
\'i': {'aligned_clauses': s:keyword('end\|else'), 'pattern_match_clauses': never_match},
\'[': {'aligned_clauses': ']', 'pattern_match_clauses': never_match},
\'{': {'aligned_clauses': '}', 'pattern_match_clauses': never_match},
\'(': {'aligned_clauses': ')', 'pattern_match_clauses': never_match}
\}
" if `with` clause...
if block_start_char == 'w'
call s:debug("testing s:handle_with")
return s:handle_with(block_start_lnum, block_start_col, a:context)
else
let block_config = config[block_start_char]
" if aligned clause (closing tag/`else` clause/etc...) then indent this
" at the same level as the block open tag (e.g. `if`/`case`/etc...)
if s:starts_with(a:context, block_config.aligned_clauses)
call s:debug("clause")
return indent(block_start_lnum)
else
if block_config.pattern_match_clauses == never_match
let relative_lnum = block_start_lnum
else
let clause_lnum = searchpair(block_config.pattern_match_clauses, '', '*', 'bnW', "line('.') == " . line('.') . " || elixir#indent#searchpair_back_skip()", block_start_lnum)
call s:debug("clause_lum=" . clause_lnum)
let relative_lnum = max([clause_lnum, block_start_lnum])
end
call s:debug("pattern matching relative to lnum " . relative_lnum)
return s:do_handle_pattern_match_block(relative_lnum, a:context)
endif
end
else
return -1
end
endfunction
function! s:handle_with(start_lnum, start_col, context)
let block_info = searchpairpos('\C\%(\<with\>\|\<do\>\|\<else\>\)', '', s:keyword('end'), 'bnW', "line('.') == " . line('.') . " || elixir#indent#searchpair_back_skip()")
let block_start_lnum = block_info[0]
let block_start_col = block_info[1]
let block_start_text = getline(block_start_lnum)
let block_start_char = block_start_text[block_start_col - 1]
if s:starts_with(a:context, s:keyword('do\|else\|end'))
return indent(a:start_lnum)
elseif block_start_char == 'w' || s:starts_with(a:context, '\C\(do\|else\):')
return indent(a:start_lnum) + 5
elseif s:_starts_with(block_start_text, '\C\(do\|else\):', a:start_lnum)
return indent(block_start_lnum) + s:sw()
else
return s:do_handle_pattern_match_block(a:start_lnum, a:context)
end
endfunction
function! s:do_handle_pattern_match_block(relative_line, context)
let relative_indent = indent(a:relative_line)
" hack!
if a:context.text =~ '\(fn.*\)\@<!->'
call s:debug("current line contains ->; assuming match definition")
return relative_indent + s:sw()
elseif search('\(fn.*\)\@<!->', 'bnW', a:relative_line) != 0
call s:debug("a previous line contains ->; assuming match handler")
return relative_indent + 2 * s:sw()
else
call s:debug("couldn't find any previous ->; assuming body text")
return relative_indent + s:sw()
end
endfunction
function! elixir#indent#handle_inside_generic_block(context)
let pair_lnum = searchpair(s:keyword('do\|fn'), '', s:keyword('end'), 'bW', "line('.') == ".a:context.lnum." || s:is_string_or_comment(line('.'), col('.'))", max([0, a:context.lnum - g:elixir_indent_max_lookbehind]))
if pair_lnum
" TODO: @jbodah 2017-03-29: this should probably be the case in *all*
" blocks
if s:prev_ends_with(a:context, ',')
return indent(pair_lnum) + 2 * s:sw()
else
return indent(pair_lnum) + s:sw()
endif
else
return -1
endif
endfunction

View file

@ -0,0 +1,24 @@
function! elixir#util#get_filename(word) abort
let word = a:word
" get first thing that starts uppercase, until the first space or end of line
let word = substitute(word,'^\s*\(\u[^ ]\+\).*$','\1','g')
" remove any trailing characters that don't look like a nested module
let word = substitute(word,'\.\U.*$','','g')
" replace module dots with slash
let word = substitute(word,'\.','/','g')
" remove any special chars
let word = substitute(word,'[^A-z0-9-_/]','','g')
" convert to snake_case
let word = substitute(word,'\(\u\+\)\(\u\l\)','\1_\2','g')
let word = substitute(word,'\(\u\+\)\(\u\l\)','\1_\2','g')
let word = substitute(word,'\(\l\|\d\)\(\u\)','\1_\2','g')
let word = substitute(word,'-','_','g')
let word = tolower(word)
return word
endfunction

View file

@ -0,0 +1,3 @@
#! /usr/bin/env sh
docker-compose build
docker-compose run nvim nvim $1

View file

@ -0,0 +1,25 @@
#! /usr/bin/env ruby
require 'bundler/setup'
require 'vimrunner'
dir = File.expand_path('..', __dir__)
plugin = 'ftdetect/elixir.vim'
vim = Vimrunner.start_gvim
vim.add_plugin(dir, plugin)
vim.normal ':let g:elixir_indent_debug=0<CR>'
vim.edit! 'large_file.ex'
# remove all indentation
vim.normal 'ggVG999<<'
vim.normal ':profile start profile.log<CR>'
vim.normal ':profile func *<CR>'
vim.normal ':profile file *<CR>'
t1 = Time.now
# force vim to indent the file
vim.normal 'gg=G'
vim.normal ':profile pause<CR>'
vim.normal ':q!<CR>'
Process.wait vim.server.pid
t2 = Time.now
puts "Took #{t2-t1} seconds to indent large_file.ex. Profile logged to profile.log"

View file

@ -0,0 +1,8 @@
wistia/ttl_cache
wistia/generational_cache
wistia/gen_poller
wistia/assignment_ex
wistia/http_monitor
wistia/impersonate_ex
wistia/m3u8_parser
wistia/simple_http_server

View file

@ -0,0 +1,17 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'rspec' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require "rubygems"
require "bundler/setup"
load Gem.bin_path("rspec-core", "rspec")

View file

@ -0,0 +1,3 @@
#! /usr/bin/env sh
dirs=`find $1 -iname '*.ex' -o -iname '*.exs' | grep -v '/deps/'`
bin/test_indent $dirs

View file

@ -0,0 +1,3 @@
#! /usr/bin/env sh
rm -rf tmp
cat bin/projects_to_test.txt | xargs -n 1 -I{} sh -c 'git clone -q git@github.com:{}.git tmp && echo "Testing directory: {}..." && bin/test_directory tmp && rm -rf tmp'

View file

@ -0,0 +1,57 @@
#! /usr/bin/env ruby
require 'bundler/setup'
require 'vimrunner'
require 'diffy'
require 'fileutils'
dir = File.expand_path('..', __dir__)
plugin = 'ftdetect/elixir.vim'
def bm
t1 = Time.now
yield
Time.now - t1
end
def detect_change(f)
pre = File.read(f)
pre = strip_doc_blocks(pre)
yield
post = File.read('test_indent.result')
post = strip_doc_blocks(post)
pre == post ? nil : Diffy::Diff.new(pre, post)
end
def strip_doc_blocks(body)
body.gsub(/@\w+ """.*"""/m, '')
end
ARGV.each do |f|
vim = Vimrunner.start_gvim
vim.add_plugin(dir, plugin)
vim.edit! f
print "## Testing #{File.expand_path(f)} ... "
time = nil
diff = detect_change(f) do
time = bm do
vim.normal 'ggVG999<<'
vim.normal 'gg=G'
vim.normal ':w! test_indent.result<CR>'
vim.normal ':q!<CR>'
Process.wait vim.server.pid
end
end
if diff
puts "error [#{time}s]"
puts diff
else
puts "ok [#{time}s]"
end
end
FileUtils.rm 'test_indent.result'

View file

@ -0,0 +1,3 @@
#! /usr/bin/env sh
docker-compose build
docker-compose run vim

View file

@ -0,0 +1,11 @@
if exists('current_compiler')
finish
endif
let current_compiler = 'credo'
if exists(":CompilerSet") != 2
command -nargs=* CompilerSet setlocal <args>
endif
CompilerSet errorformat=%f:%l:%c:\ %t:\ %m,%f:%l:\ %t:\ %m
CompilerSet makeprg=mix\ credo\ suggest\ --format=flycheck

View file

@ -0,0 +1,25 @@
if exists("current_compiler")
finish
endif
let current_compiler = "exunit"
if exists(":CompilerSet") != 2 " older Vim always used :setlocal
command -nargs=* CompilerSet setlocal <args>
endif
let s:cpo_save = &cpo
set cpo-=C
CompilerSet makeprg=mix\ test
CompilerSet errorformat=
\%E\ \ %n)\ %m,
\%+G\ \ \ \ \ **\ %m,
\%+G\ \ \ \ \ stacktrace:,
\%C\ \ \ \ \ %f:%l,
\%+G\ \ \ \ \ \ \ (%\\w%\\+)\ %f:%l:\ %m,
\%+G\ \ \ \ \ \ \ %f:%l:\ %.%#,
\**\ (%\\w%\\+)\ %f:%l:\ %m
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: nowrap sw=2 sts=2 ts=8:

View file

@ -0,0 +1,16 @@
if exists('current_compiler')
finish
endif
let current_compiler = 'mix'
if exists(":CompilerSet") != 2
command -nargs=* CompilerSet setlocal <args>
endif
CompilerSet makeprg=mix\ compile
CompilerSet errorformat=
\%Wwarning:\ %m,
\%C%f:%l,%Z,
\%E==\ Compilation\ error\ in\ file\ %f\ ==,
\%C**\ (%\\w%\\+)\ %f:%l:\ %m,%Z

View file

@ -0,0 +1,112 @@
*elixir.txt* Vim configuration files for Elixir http://elixir-lang.org/
Author: Plataformatec
License: Apache License Version 2.0
==============================================================================
CONTENTS *elixir-contents*
INTRODUCTION |elixir-introduction|
INTERFACE |elixir-interface|
FUNCTIONS |elixir-functions|
KEY MAPPINGS |elixir-key-mappings|
OPTIONS |elixir-options|
SETTINGS |elixir-settings|
==============================================================================
INTRODUCTION *elixir-introduction*
*elixir* provides Vim configuration files for Elixir http://elixir-lang.org/
* Syntax highlighting for Elixir and EEx files
* Filetype detection for `.ex`, `.exs`, `.eex`, `.heex`, `.leex`, and `.sface` files
* Automatic indentation
* Integration between Ecto projects and |vim-dadbod| for running SQL queries
on defined Ecto repositories
Latest Version:
https://github.com/elixir-editors/vim-elixir
==============================================================================
INTERFACE *elixir-interface*
------------------------------------------------------------------------------
FUNCTIONS *elixir-functions*
db#adapter#ecto#canonicalize({url}) *db#adapter#ecto#canonicalize()*
TODO
db#adapter#ecto#complete_opaque({url}) *db#adapter#ecto#complete_opaque()*
TODO
elixir#indent#indent({lnum}) *elixir#indent#indent()*
TODO
elixir#indent#searchpair_back_skip() *elixir#indent#searchpair_back_skip()*
TODO
*elixir#indent#handle_top_of_file()*
elixir#indent#handle_top_of_file({context})
TODO
*elixir#indent#handle_follow_prev_nb()*
elixir#indent#handle_follow_prev_nb({context})
TODO
*elixir#indent#handle_following_trailing_binary_operator()*
elixir#indent#handle_following_trailing_binary_operator({context})
TODO
*elixir#indent#handle_starts_with_pipe()*
elixir#indent#handle_starts_with_pipe({context})
TODO
*elixir#indent#handle_starts_with_end()*
elixir#indent#handle_starts_with_end({context})
TODO
*elixir#indent#handle_starts_with_binary_operator()*
elixir#indent#handle_starts_with_binary_operator({context})
TODO
*elixir#indent#handle_inside_block()*
elixir#indent#handle_inside_block({context})
TODO
*elixir#indent#handle_inside_generic_block()*
elixir#indent#handle_inside_generic_block({context})
TODO
elixir#util#get_filename({word}) *elixir#util#get_filename({word})*
TODO
------------------------------------------------------------------------------
KEY MAPPINGS *elixir-key-mappings*
TODO
==============================================================================
SETTINGS *elixir-settings*
*g:eelixir_default_subtype*
TODO
*g:elixir_indent_debug*
TODO
*g:elixir_indent_max_lookbehind*
TODO
*g:elixir_use_markdown_for_docs*
TODO
*g:path*
TODO
==============================================================================
vim:tw=78:fo=tcq2mM:ts=8:ft=help:norl

View file

@ -0,0 +1,16 @@
version: "2"
services:
vim:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/root/vim-elixir
working_dir: /root/vim-elixir
nvim:
build:
context: .
dockerfile: Dockerfile.nvim
volumes:
- .:/root/vim-elixir
working_dir: /root/vim-elixir

View file

@ -0,0 +1,8 @@
au BufRead,BufNewFile *.lexs set filetype=elixir "File extension used by https://github.com/mhanberg/temple
au BufRead,BufNewFile * call s:DetectElixir()
function! s:DetectElixir()
if (!did_filetype() || &filetype !=# 'elixir') && getline(1) =~# '^#!.*\<elixir\>'
set filetype=elixir
endif
endfunction

View file

@ -0,0 +1,117 @@
if exists("b:did_ftplugin")
finish
endif
let s:save_cpo = &cpo
set cpo-=C
let s:undo_ftplugin = ""
let s:browsefilter = "All Files (*.*)\t*.*\n"
let s:match_words = ""
if !exists("g:eelixir_default_subtype")
let g:eelixir_default_subtype = "html"
endif
if !exists("b:eelixir_subtype")
let s:lines = join(getline(1, 5) + [getline('$')], "\n")
let b:eelixir_subtype = matchstr(s:lines,'eelixir_subtype=\zs\w\+')
if b:eelixir_subtype == ''
let b:eelixir_subtype = matchstr(&filetype,'^eex\.\zs\w\+')
endif
if b:eelixir_subtype == ''
let b:eelixir_subtype = matchstr(&filetype,'^heex\.\zs\w\+')
endif
if b:eelixir_subtype == ''
let b:eelixir_subtype = matchstr(&filetype,'^leex\.\zs\w\+')
endif
if b:eelixir_subtype == ''
let b:eelixir_subtype = matchstr(&filetype,'^sface\.\zs\w\+')
endif
if b:eelixir_subtype == ''
let b:eelixir_subtype = matchstr(substitute(expand("%:t"),'\c\%(\.eex\|\.heex\|\.leex\|\.sface\|\.eelixir\)\+$','',''),'\.\zs\w\+$')
endif
if b:eelixir_subtype == 'ex'
let b:eelixir_subtype = 'elixir'
elseif b:eelixir_subtype == 'exs'
let b:eelixir_subtype = 'elixir'
elseif b:eelixir_subtype == 'yml'
let b:eelixir_subtype = 'yaml'
elseif b:eelixir_subtype == 'js'
let b:eelixir_subtype = 'javascript'
elseif b:eelixir_subtype == 'txt'
" Conventional; not a real file type
let b:eelixir_subtype = 'text'
elseif b:eelixir_subtype == ''
let b:eelixir_subtype = g:eelixir_default_subtype
endif
endif
if exists("b:eelixir_subtype") && b:eelixir_subtype != ''
exe "runtime! ftplugin/".b:eelixir_subtype.".vim ftplugin/".b:eelixir_subtype."_*.vim ftplugin/".b:eelixir_subtype."/*.vim"
else
runtime! ftplugin/html.vim ftplugin/html_*.vim ftplugin/html/*.vim
endif
unlet! b:did_ftplugin
" Override our defaults if these were set by an included ftplugin.
if exists("b:undo_ftplugin")
let s:undo_ftplugin = b:undo_ftplugin
unlet b:undo_ftplugin
endif
if exists("b:browsefilter")
let s:browsefilter = b:browsefilter
unlet b:browsefilter
endif
if exists("b:match_words")
let s:match_words = b:match_words
unlet b:match_words
endif
runtime! ftplugin/elixir.vim ftplugin/elixir_*.vim ftplugin/elixir/*.vim
let b:did_ftplugin = 1
" Combine the new set of values with those previously included.
if exists("b:undo_ftplugin")
let s:undo_ftplugin = b:undo_ftplugin . " | " . s:undo_ftplugin
endif
if exists ("b:browsefilter")
let s:browsefilter = substitute(b:browsefilter,'\cAll Files (\*\.\*)\t\*\.\*\n','','') . s:browsefilter
endif
if exists("b:match_words")
let s:match_words = b:match_words . ',' . s:match_words
endif
" Load the combined list of match_words for matchit.vim
if exists("loaded_matchit")
let b:match_words = s:match_words
endif
if !exists('b:surround_45')
" When using surround `-` (ASCII 45) would provide `<% selection %>`
let b:surround_45 = "<% \r %>"
endif
if !exists('b:surround_61')
" When using surround `=` (ASCII 61) would provide `<%= selection %>`
let b:surround_61 = "<%= \r %>"
endif
if !exists('b:surround_35')
" When using surround `#` (ASCII 35) would provide `<%# selection %>`
let b:surround_35 = "<%# \r %>"
endif
if !exists('b:surround_123')
" When using surround `{` (ASCII 123) would provide `{{ selection }}`
let b:surround_123 = "{{ \r }}"
endif
if !exists('b:surround_5')
" When using surround `<C-e>` (ASCII 5 `ENQ`) would provide `<% selection %>\n<% end %>`
let b:surround_5 = "<% \r %>\n<% end %>"
endif
setlocal comments=:<%#
setlocal commentstring=<%#\ %s\ %>
let b:undo_ftplugin = "setl cms< " .
\ " | unlet! b:browsefilter b:match_words | " . s:undo_ftplugin
let &cpo = s:save_cpo

View file

@ -0,0 +1,52 @@
if exists('b:did_ftplugin')
finish
endif
let b:did_ftplugin = 1
" Matchit support
if exists('loaded_matchit') && !exists('b:match_words')
let b:match_ignorecase = 0
let b:match_words = '\:\@<!\<\%(do\|fn\)\:\@!\>' .
\ ':' .
\ '\<\%(else\|elsif\|catch\|after\|rescue\)\:\@!\>' .
\ ':' .
\ '\:\@<!\<end\>' .
\ ',{:},\[:\],(:)'
endif
setlocal shiftwidth=2 softtabstop=2 expandtab iskeyword+=!,?
setlocal comments=:#
setlocal commentstring=#\ %s
let &l:path =
\ join([
\ 'lib/**',
\ 'src/**',
\ 'test/**',
\ 'deps/**/lib/**',
\ 'deps/**/src/**',
\ &g:path
\ ], ',')
setlocal includeexpr=elixir#util#get_filename(v:fname)
setlocal suffixesadd=.ex,.exs,.eex,.heex,.leex,.sface,.erl,.xrl,.yrl,.hrl
let &l:define = 'def\(macro\|guard\|delegate\)\=p\='
silent! setlocal formatoptions-=t formatoptions+=croqlj
let b:block_begin = '\<\(do$\|fn\>\)'
let b:block_end = '\<end\>'
nnoremap <buffer> <silent> <expr> ]] ':silent keeppatterns /'.b:block_begin.'<CR>'
nnoremap <buffer> <silent> <expr> [[ ':silent keeppatterns ?'.b:block_begin.'<CR>'
nnoremap <buffer> <silent> <expr> ][ ':silent keeppatterns /'.b:block_end .'<CR>'
nnoremap <buffer> <silent> <expr> [] ':silent keeppatterns ?'.b:block_end .'<CR>'
onoremap <buffer> <silent> <expr> ]] ':silent keeppatterns /'.b:block_begin.'<CR>'
onoremap <buffer> <silent> <expr> [[ ':silent keeppatterns ?'.b:block_begin.'<CR>'
onoremap <buffer> <silent> <expr> ][ ':silent keeppatterns /'.b:block_end .'<CR>'
onoremap <buffer> <silent> <expr> [] ':silent keeppatterns ?'.b:block_end .'<CR>'
let b:undo_ftplugin = 'setlocal sw< sts< et< isk< com< cms< path< inex< sua< def< fo<'.
\ '| unlet! b:match_ignorecase b:match_words b:block_begin b:block_end'

View file

@ -0,0 +1,72 @@
if exists("b:did_indent")
finish
endif
runtime! indent/elixir.vim
unlet! b:did_indent
setlocal indentexpr=
let s:cpo_save = &cpo
set cpo&vim
if exists("b:eelixir_subtype")
exe "runtime! indent/".b:eelixir_subtype.".vim"
else
runtime! indent/html.vim
endif
unlet! b:did_indent
if &l:indentexpr == ''
if &l:cindent
let &l:indentexpr = 'cindent(v:lnum)'
else
let &l:indentexpr = 'indent(prevnonblank(v:lnum-1))'
endif
endif
let b:eelixir_subtype_indentexpr = &l:indentexpr
let b:did_indent = 1
setlocal indentexpr=GetEelixirIndent()
setlocal indentkeys=o,O,*<Return>,<>>,{,},0),0],o,O,!^F,=end,=else,=elsif,=catch,=after,=rescue
" Only define the function once.
if exists("*GetEelixirIndent")
finish
endif
function! GetEelixirIndent(...)
if a:0 && a:1 == '.'
let v:lnum = line('.')
elseif a:0 && a:1 =~ '^\d'
let v:lnum = a:1
endif
let vcol = col('.')
call cursor(v:lnum,1)
let inelixir = searchpair('<%','','%>','W')
call cursor(v:lnum,vcol)
if inelixir && getline(v:lnum) !~ '^<%\|^\s*%>'
let ind = GetElixirIndent()
else
exe "let ind = ".b:eelixir_subtype_indentexpr
endif
let lnum = prevnonblank(v:lnum-1)
let line = getline(lnum)
let cline = getline(v:lnum)
if cline =~# '^\s*<%\s*\%(end\|else\|elsif\|catch\|after\|rescue\)\>.*%>'
let ind -= &sw
elseif line =~# '\S\s*<%\s*end\s*%>'
let ind -= &sw
endif
if line =~# '<%[=%]\=\s*.*\(\<do\|->\)\s*%>' ||
\ line =~# '<%\s*\%(else\|elsif\|catch\|after\|rescue\)\>.*%>'
let ind += &sw
endif
if cline =~# '^\s*%>\s*$'
let ind -= &sw
endif
return ind
endfunction
let &cpo = s:cpo_save
unlet s:cpo_save

View file

@ -0,0 +1,15 @@
if exists("b:did_indent")
finish
end
let b:did_indent = 1
setlocal indentexpr=elixir#indent(v:lnum)
setlocal indentkeys+==after,=catch,=do,=else,=end,=rescue,
setlocal indentkeys+=*<Return>,=->,=\|>,=<>,0},0],0),>
" TODO: @jbodah 2017-02-27: all operators should cause reindent when typed
function! elixir#indent(lnum)
return elixir#indent#indent(a:lnum)
endfunction

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,64 @@
#!/usr/bin/env bash
exit_whit_error_message() {
printf '%s\n' "$1" >&2
exit 1
}
show_help() {
echo "The script to install the vim-elixir plugin"
echo
echo "Usage: ./manual_install.sh [OPTIONS]"
echo
echo "Options:"
echo "-o, --output-dir string The name of the directory where plugin will be installed. By default the output directory name is 'vim-elixir'"
echo " Example: ./manual_install.sh -o vim-elixir # The plugin will be installed in ~/.vim/pack/vim-elixir/start/vim-elixir directory"
echo " ./manual_install.sh -o elixir-opts # The plugin will be installed in ~/.vim/pack/elixir-opts/start/elixir-opts directory"
echo
}
# Initialize all the option variables.
# This ensures we are not contaminated by variables from the environment.
VIM_PLUGIN_NAME=vim-elixir
while :; do
case $1 in
-h|-\?|--help)
show_help
exit
;;
-o|--output-dir)
if [ "$2" ]; then
VIM_PLUGIN_NAME=$2
shift
else
exit_whit_error_message 'ERROR: "--name" requires a non-empty option argument.'
fi
;;
--output-dir=?*)
# Delete everything up to "=" and assign the remainder.
VIM_PLUGIN_NAME=${1#*=}
;;
--output-dir=) # Handle the case of an empty --name=
exit_whit_error_message 'ERROR: "--name" requires a non-empty option argument.'
;;
-?*)
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
;;
*)
# Default case: No more options, so break out of the loop.
break
esac
shift
done
VIM_INSTALL_DIR=~/.vim/pack/$VIM_PLUGIN_NAME/start/$VIM_PLUGIN_NAME
mkdir -p $VIM_INSTALL_DIR
echo "Installing plugin in the ${VIM_INSTALL_DIR} directory"
for DIR in autoload compiler ftdetect ftplugin indent syntax
do
cp -R $DIR $VIM_INSTALL_DIR
done

View file

@ -0,0 +1,96 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Basic folding' do
def self.fold(content)
it("properly folds \n#{content}") do
expect(content).to fold_lines
end
end
fold <<~EOF
defmodule M do # fold
end # fold
"not in fold"
EOF
fold <<~EOF
defmodule M do # fold
def some_func do # fold
end # fold
end # fold
"not in fold"
EOF
fold <<~EOF
defmodule M do
def some_func do # fold
end # fold
end
"not in fold"
EOF
fold <<~EOF
if true do # fold
end # fold
"not in fold"
EOF
fold <<~EOF
if true do # fold
nil # fold
else # fold
nil # fold
end # fold
"not in fold"
EOF
fold <<~EOF
defmodule M do
def some_func do
[ # fold
:hello, # fold
:world # fold
] # fold
:hello_world
end
end
EOF
fold <<~EOF
defmodule M do
def some_func do
{ # fold
:hello, # fold
:world # fold
} # fold
:hello_world
end
end
EOF
fold <<~EOF
defmodule M do
def some_func do
%{ # fold
hello: "a", # fold
world: "b" # fold
} # fold
:hello_world
end
end
EOF
fold <<~EOF
defmodule M do
def some_func do
%User{ # fold
hello: "a", # fold
world: "b" # fold
} # fold
:hello_world
end
end
EOF
end

View file

@ -0,0 +1,89 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting anonymous functions' do
i <<~EOF
def do
some_func = fn x -> x end
end
EOF
i <<~EOF
def do
some_func = function do x -> x end
end
EOF
i <<~EOF
def test do
assert_raise Queue.Empty, fn ->
Q.new |> Q.deq!
end
end
EOF
i <<~EOF
defmodule Test do
def lol do
Enum.map([1,2,3], fn x ->
x * 3
end)
end
end
EOF
i <<~EOF
fizzbuzz = fn
0, 0, _ -> "FizzBuzz"
0, _, _ -> "Fizz"
_, 0, _ -> "Buzz"
_, _, x -> x
end
EOF
i <<~EOF
fizzbuzz = function do
0, 0, _ -> "FizzBuzz"
0, _, _ -> "Fizz"
_, 0, _ -> "Buzz"
_, _, x -> x
end
EOF
i <<~EOF
{:ok, 0} = Mod.exec!(cmd, fn progress ->
if event_handler do
event_handler.({:progress_updated, progress})
end
end
)
EOF
i <<~EOF
defp handle_chunk(:err, line, state) do
update_in(state[:stderr], fn
true -> true
false -> false
end)
Map.update(state, :stderr, [line], &(&1 ++ [line]))
end
EOF
i <<~EOF
defp handle_chunk(:err, line, state) do
update_in(state[:stderr], fn
hello -> :ok
world -> :ok
end)
Map.update(state, :stderr, [line], &(&1 ++ [line]))
end
EOF
i <<~EOF
fn ->
end
EOF
end

View file

@ -0,0 +1,569 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Basic indenting' do
i <<~EOF
defmodule Hello do
EOF
i <<~EOF
defmodule Hello do
def some_func do
EOF
i <<~EOF
defmodule Hello do
def some_func do
end
EOF
i <<~EOF
defmodule Hello do
def some_func do
end
end
EOF
i <<~EOF
defmodule Hello.World do
def some_func do
IO.puts "hello world"
end
end
EOF
i <<~EOF
defmodule Hello.World do
def some_func do
IO.puts "hello world"
end
def some_other_func do
IO.puts "hello world"
end
end
EOF
i <<~EOF
defmodule Hello.World do
def some_func do
IO.puts "hello world"
end
def some_other_func do
IO.puts "hello world"
end
end
EOF
i <<~EOF
defmodule Hello.World do
def some_func do
IO.puts "hello world"
end
def some_other_func do
IO.puts "hello world"
IO.puts "hello world"
IO.puts "hello world"
IO.puts "hello world"
end
end
EOF
i <<~EOF
defmodule Hello.World do
def some_func do
IO.puts "hello world"
end
def some_other_func do
if blah? do
blah
else
not_blah
end
end
end
EOF
i <<~EOF
defmodule Hello.World do
def some_func do
IO.puts "hello world"
end
def some_other_func do
if blah? do
blah
else
not_blah
end
if blah? do
blah
else
not_blah
end
end
end
EOF
i <<~EOF
defmodule Hello.World do
def some_func do
IO.puts "hello world"
end
def some_other_func do
if blah? do
blah
if blah? do
blah
else
not_blah
end
else
not_blah
end
end
end
EOF
i <<~EOF
defmodule Hello.World do
def some_func do
cond do
{:abc} -> false
_ -> true
end
end
end
EOF
i <<~EOF
defmodule Hello.World do
def some_func do
cond do
{:abc} -> false
_ -> true
end
end
end
EOF
i <<~EOF
defmodule Hello.World do
def some_func do
cond do
{:abc} ->
say_hello
say_goodbye
_ ->
say_hello
say_goodbye
end
end
end
EOF
i <<~EOF
defmodule Hello.World do
def some_func do
cond do
{:abc} ->
cond do
{:abc} ->
say_hello
say_goodbye
_ ->
say_hello
say_goodbye
end
say_hello
say_goodbye
_ ->
say_hello
say_goodbye
end
end
end
EOF
i <<~EOF
defmodule Hello do
def hello do
case word do
:one -> :two
:high -> :low
end
end
end
EOF
i <<~EOF
defmodule Hello do
def hello do
case word do
:one -> :two
:high -> :low
end
end
end
EOF
i <<~EOF
defmodule Hello do
def hello do
case word do
:one ->
:two
:high ->
:low
end
end
end
EOF
i <<~EOF
defmodule Hello do
def hello do
case word do
:one ->
case word do
:one ->
:two
:high ->
:low
end
:two
:high ->
:low
end
end
end
EOF
i <<~EOF
defmodule Hello do
defmacro hello do
quote do
blah
end
end
end
EOF
i <<~EOF
defmodule Hello do
def hello do
unless blah do
blah
end
end
end
EOF
i <<~EOF
defmodule Hello do
def hello do
if stinky?, do: clean
if smelly?, do: clean
end
end
EOF
i <<~EOF
defmodule Hello do
def hello do
name =
"one"
street =
"two"
end
end
EOF
%w(= == === != !== <= >= <> && || + - * / ~~~ ^^^ <<< >>> ||| &&&).each do |bin_op|
i <<~EOF
defmodule Hello do
def hello do
name #{bin_op}
"one"
street #{bin_op}
"two"
end
end
EOF
i <<~EOF
defmodule Hello do
def hello do
name #{bin_op} "one"
street #{bin_op} "two"
end
end
EOF
end
i <<~EOF
defmodule Hi do
def hi do
fn hello ->
:world
end
end
end
EOF
i <<~EOF
defmodule Hello do
def hello do
name = "one"
street = "two"
end
end
EOF
i <<~EOF
defmodule Hi do
def hi do
fn hello -> :world end
fn hello -> :world end
end
end
EOF
i <<~EOF
defmodule Hi do
def hi do
fn hello ->
case hello do
:one ->
case word do
:one ->
:two
:high ->
:low
end
:two
:high ->
:low
end
end
end
end
EOF
i <<~EOF
hello =
"str"
|> Pipe.do_stuff
|> Pipe.do_stuff
|> Pipe.do_stuff
|> Pipe.do_stuff(fn ->
more stuff
end)
|> Pipe.do_stuff
EOF
i <<~EOF
defmodule Hi do
defp hi do
:hello
end
defp hi do
:hello
end
end
EOF
i <<~EOF
defmodule Hi do
defp hi do
[
:one,
:two,
fn ->
:three
end,
:four
]
end
end
EOF
i <<~EOF
defmodule Hi do
defp hi do
{
:one,
:two,
fn ->
:three
end,
:four
}
end
end
EOF
i <<~EOF
defmodule Hi do
defp hi do
%Struct{
:one,
:two,
fn ->
:three
end,
:four
}
end
end
EOF
i <<~EOF
defmodule Hi do
defp hi do
%{
:one,
:two,
fn ->
:three
end,
:four
}
end
end
EOF
i <<~EOF
defmodule Hi do
defp hi do
try do
raise "boom"
rescue
e in errs ->
IO.puts "one"
_ ->
IO.puts "one"
end
end
end
EOF
i <<~EOF
defmodule Hi do
defp hi do
try do
raise "wtf"
catch
e ->
IO.puts "one"
_ ->
IO.puts "one"
end
end
end
EOF
i <<~EOF
defmodule Hi do
defp hi do
receive do
{:hello, world} ->
:ok
after
1000 ->
IO.puts "one"
2000 ->
IO.puts "one"
end
end
end
EOF
i <<~EOF
defmodule Hi do
defp hi do
receive do
{:hello, world} ->
:ok
_ ->
:err
end
end
end
EOF
i <<~EOF
defmodule Hi do
defp hi do
fn
:ok ->
IO.puts :ok
_ ->
IO.puts :err
end
end
end
EOF
i <<~EOF
defmodule Hi do
defp hi do
fn
:ok -> IO.puts :ok
_ -> IO.puts :err
end
end
end
EOF
i <<~EOF
fun2 = fn :foo ->
:bar
'end'
end
EOF
i <<~EOF
fun2 = fn :foo ->
:bar
'end'
end
EOF
i <<~EOF
fun3 = fn :foo ->
:bar
:send
end
EOF
i <<~EOF
defmodule Hi do
def hello_world do
"end"
'end'
end
EOF
end

View file

@ -0,0 +1,60 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Binary operators' do
i <<~EOF
word =
"h"
<> "e"
<> "l"
<> "l"
<> "o"
IO.puts word
EOF
i <<~EOF
def hello do
expected = "hello"
<> "world"
IO.puts expected
end
EOF
i <<~EOF
def hello do
expected =
"hello"
<> "world"
IO.puts expected
end
EOF
i <<~EOF
alias Rumbl.Repo
alias Rumbl.Category
for category <- ~w(Action Drama Romance Comedy Sci-fi) do
Repo.get_by(Category, name: category) ||
Repo.insert!(%Category{name: category})
end
EOF
i <<~EOF
data = [
"blah",
"blah2", # *
"blah3"
]
EOF
i <<~EOF
data = [
"blah",
# +
"blah2",
"blah3"
]
EOF
end

View file

@ -0,0 +1,109 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting blocks' do
i <<~EOF
do
something
end
EOF
i <<~EOF
defmodule Test do
def lol do
IO.inspect :end
end
end
EOF
i <<~EOF
defmodule Hello do
def name, do: IO.puts "bobmarley"
# expect next line starting here
def name(param) do
param
end
end
EOF
i <<~EOF
defmodule Hello do
def name, do: IO.puts "bobmarley"
def name(param) do
param
end
end
EOF
i <<~EOF
def f do
if true, do: 42
end
EOF
i <<~EOF
def f do
x = :do
end
EOF
i <<~EOF
defmodule Test do
def test do
one =
user
|> build_assoc(:videos)
|> Video.changeset()
other =
user2
|> build_assoc(:videos)
|> Video.changeset()
end
end
EOF
i <<~EOF
defmodule MyMod do
def how_are_you do
IO.puts "I'm filling bad :("
IO.puts "really bad"
end
end
EOF
i <<~EOF
defmodule MyMod do
def how_are_you do
"function return"
end
end
EOF
i <<~EOF
scope "/", API do
pipe_through :api # Use the default browser stack
get "/url", Controller, :index
post "/url", Controller, :create
end
EOF
i <<~EOF
def hello do
{:ok, _} = TaskRunner.TaskStore.start_link(name: @task_store)
{:ok, _} = Workspace.start_link
{:ok, pending_task_sup} = TaskRunner.PendingTaskSupervisor.start_link
end
EOF
i <<~EOF
def handle_info(:tick, state = %{policy_iteration: []}) do
state = put_in(state[:policy_iteration], state.policy)
{:noreply, state}
end
EOF
end

View file

@ -0,0 +1,111 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting case statements' do
i <<~EOF
case some_function do
:ok ->
:ok
{ :error, :message } ->
{ :error, :message }
end
EOF
i <<~EOF
case Connection.open(rabbitmq) do
{:ok, conn} ->
Woody.info "CONNECTION_SUCCESSFUL"
{:ok, chan} = Channel.open(conn)
{:error, error} ->
Woody.info "CONNECTION_FAILED"
:timer.sleep(10000)
end
EOF
i <<~EOF
defmodule M do
defp _fetch(result, key, deep_key) do
case _fetch(result, key) do
{:ok, val} ->
case _fetch(val, deep_key) do
:error -> {:error, :deep}
res -> res
end
:error -> {:error, :shallow}
end
end
EOF
i <<~EOF
case Connection.open(rabbitmq) do
{:ok, conn} ->
Woody.info "CONNECTION_SUCCESSFUL"
{:ok, chan} = Channel.open(conn)
{:error, error} ->
Woody.info "CONNECTION_FAILED"
:timer.sleep(10000)
end
EOF
i <<~'EOF'
decoded_msg = case JSON.decode(msg) do
{:error, _} ->
a = "a"
b = "dasdas"
">#{a}<>#{b}<"
{:ok, decoded} -> decoded
end
EOF
i <<~EOF
case Repo.insert(changeset) do
{:ok, user} ->
conn
|> put_flash(:info, "%{user.name} created!")
|> redirect(to: user_path(conn, :index))
{:error, changeset} ->
render(conn, "new.html", changeset: changeset)
end
EOF
i <<~EOF
case st do
sym ->
code = if true do
:ok
else
:error
end
Logger.info(code)
st
end
EOF
i <<~EOF
case world do
"apple" ->
IO.puts "its an apple"
IO.puts "no really, its an apple"
"orange" ->
IO.puts "its not an apple"
IO.puts "believe it or not"
end
EOF
i <<~EOF
case o do
a ->
e(fn -> f end)
end
EOF
i <<~EOF
case pattern do
:* -> :ok
_ -> :error
end
EOF
end

View file

@ -0,0 +1,56 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting *after* comments' do
i <<~EOF
# do
IO.puts :test
EOF
i <<~EOF
defmodule Foo do
def run do
list =
File.read!("/path/to/file")
|> String.split()
# now start a new line
# used to start here
# but now starts here
end
end
EOF
i <<~EOF
defmodule Foo do
def run(task) when task in [:t1, :t2] do
end
# now starts a new line
# use to start here
# but now starts here
end
EOF
i <<~EOF
receive do
{{:lock_ready, ^key}, ^pid} ->
after
# NOTE: @jbodah 2017-03-28: we should do some math to adjust the timeout
timeout ->
{:error, :timed_out_waiting_for_lock}
end
EOF
it "bulk indenting comments" do
expect(<<~EOF).to be_elixir_indentation
defmodule Test do
# SELECT *
# FROM table
# WHERE column = 123
# AND another_column = 456
end
EOF
end
end

View file

@ -0,0 +1,12 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting cond statements' do
i <<~EOF
cond do
foo -> 1
bar -> 2
end
EOF
end

View file

@ -0,0 +1,28 @@
require 'spec_helper'
describe 'def indentation' do
i <<~EOF
def handle_call({:release_lock, key}, _from, state) do
case get_lock(state, key) do
nil ->
{:reply, {:error, :already_unlocked}, state}
_ ->
new_state = delete_lock(state, key)
{:reply, :ok, new_state}
end
end
def
EOF
i <<~EOF
defmodule Hello do
def hello do
end
#{"\n" * 40}
def world do
end
end
EOF
end

View file

@ -0,0 +1,34 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting documentation' do
i <<~EOF
defmodule Test do
@doc """
end
"""
end
EOF
it "bulk indenting doc blocks" do
expect(<<~EOF).to be_elixir_indentation
defmodule Test do
@doc """
do not reindent
any indent that i do
please
"""
end
EOF
end
i <<~EOF
defmodule Test do
@doc """
it should
have reasonable
default start indent when typed
"""
EOF
end

View file

@ -0,0 +1,45 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting Ecto queries' do
i <<~EOF
defmodule New do
def do_query do
from user in Users,
select: user.name,
join: signup in Signups, where: user.id == signup.user_id
end
end
EOF
i <<~EOF
def smth do
from = 1
to = 7
end
EOF
i <<~EOF
fromin,
EOF
i <<~EOF
query = from u in query, select: u.city
EOF
i <<~EOF
def do_query do
where = [category: "fresh and new"]
order_by = [desc: :published_at]
select = [:id, :title, :body]
from Post, where: ^where, order_by: ^order_by, select: ^select
end
EOF
i <<~EOF
def alphabetical(query) do
from c in query, order_by: c.name
end
EOF
end

View file

@ -0,0 +1,26 @@
require 'spec_helper'
describe 'EctoEnum' do
i <<~EOF
defmodule Onemedical.Types do
import EctoEnum
defenum(Work.Occupation, :work_occupation, [
:actor, :architect, :athlete, :baker, :bank_clerk, :banker, :barber, :blogger,
:bricklayer, :broadcaster, :builder, :captain, :carpenter, :choreographer,
:computer_engineer, :computer_programmer, :custom_officer, :dancer, :designer,
:director, :doctor, :driver, :editor, :entertainer, :engineer, :facility_manager,
:farmer, :fashion_designer, :geologist, :goldsmith, :graphic_designer, :hairdresser,
:host_hostess, :house_girl, :interior_designer, :judge, :land_surveyor, :lecturer,
:make_up_artist, :manager, :mechanic, :midwife, :model, :music_director, :musician,
:nanny, :nurse, :pastor, :paediatrician, :photographer, :physicist, :pilot, :plumber,
:police_officer, :printer, :producer, :publisher, :quality_inspector, :radiographer,
:real_estate_agent, :referee, :refuse_collector, :registrar, :safety_engineer, :sales_manager,
:script_writer, :secretary, :security_guard, :shoemaker, :songwriter, :sound_engineer,
:stock_broker, :surveyor, :tailor, :teacher, :telecommunications_engineer, :usher,
:waiter, :writer, :zookeeper, :other])
defenum(Work.Type, :work_type, [
:full_time, :part_time, :volunteer, :temporary
])
end
EOF
end

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting eelixir' do
it 'anonymous function' do
expect(<<~EOF).to be_eelixir_indentation
<%= form_for @changeset, user_path(@conn, :create), fn f -> %>
It is obviously true
<% end %>
EOF
end
it 'if..do..end' do
expect(<<~EOF).to be_eelixir_indentation
<%= if true do %>
It is obviously true
<% end %>
EOF
end
it 'if..do..else..end' do
expect(<<~EOF).to be_eelixir_indentation
<%= if true do %>
It is obviously true
<% else %>
This will never appear
<% end %>
EOF
end
end

View file

@ -0,0 +1,263 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting embedded views' do
i <<~EOF
def render(assigns) do
~L"""
<div>
Some content
</div>
"""
end
EOF
i <<~EOF
def render(assigns) do
~H"""
<div class="theres a-/ in the class names from tailwind">
<div class="some more classes">
This is immediately nested
<div>
<input type="number" value="2" />
There's a self-closing tag
</div>
</div>
</div>
"""
end
EOF
i <<~EOF
def render(assigns) do
~L"""
<div id="123456">
Some content
</div>
"""
end
EOF
i <<~EOF
def render(assigns) do
~L"""
<div
id="123456"
>
Some content
</div>
"""
end
EOF
i <<~EOF
def render(assigns) do
~L"""
<div />
<p>Some paragraph</p>
"""
end
EOF
i <<~EOF
def render(assigns) do
~L"""
<div>
it
<div>
keeps
<div>
nesting
</div>
</div>
</div>
"""
end
EOF
i <<~EOF
def render(assgins) do
~L"""
<div>
<%= for i <- iter do %>
<div><%= i %></div>
<% end %>
</div>
"""
end
EOF
i <<~EOF
def render(assigns) do
~L"""
<%= live_component @socket,
Component,
id: "<%= @id %>",
user: @user do
%>
<main>
<header>
<h1>Some Header</h1>
</header>
<section>
<h1>Some Section</h1>
<p>
I'm some text
</p>
</section>
</main>
<% end %>
"""
end
EOF
i <<~EOF
def render(assigns) do
~L"""
<%= render_component,
@socket,
Component do %>
<p>Multi-line opening eex tag that takes a block</p>
<% end %>
"""
end
EOF
i <<~EOF
def render(assigns) do
~L"""
<div>
<%= render_component,
@socket,
Component %>
</div>
<%= render_component,
@socket,
Component %>
<p>Multi-line single eex tag</p>
"""
end
EOF
i <<~EOF
def render(assigns) do
~H"""
<Component
foo={{
foo: [
'one',
'two',
'three'
],
bar: %{
"foo" => "bar"
}
}}
/>
"""
end
EOF
i <<~EOF
def render(assigns) do
~L"""
<%= live_component @socket,
Component,
id: "<%= @id %>",
team: @team do
%>
<div>
<div>
<div>
A deeply nested tree
<div>
with trailing whitespace
</div>
</div>
</div>
</div>
<div id="id-ends-with-greater-than->"
propWithEexTag="<%= @id %>"
anotherProp="foo"
/>
<%= for i <- iter do %>
<div><%= i %></div>
<% end %>
<div
opts={{
opt1: "optA",
opt2: "optB"
}}
id="hi"
bye="hi" />
<ul>
<li :for={{ item <- @items }}>
{{ item }}
</li>
</ul>
<div id="hi">
Hi <p>hi</p>
I'm ok, ok?
<div>
hi there!
</div>
<div>
<div>
<p>hi</p>
<hr />
</div>
</div>
</div>
<Some.Surface.Component />
<Another
prop="prop"
prop2="prop2"
>
<div>content</div>
</Another>
<div foo />
<div>hi</div>
<div>
<div>
content
</div>
<div />
<div>
content in new div after a self-closing div
</div>
</div>
<p
id="<%= @id %>"
class="multi-line opening single letter p tag"
>
<%= @solo.eex_tag %>
<Nested
prop="nested"
>
content
</Nested>
</p>
<% end %>
"""
end
EOF
end

View file

@ -0,0 +1,32 @@
require 'spec_helper'
describe 'exunit' do
i <<~EOF
test "test" do
Mod.fun(fn ->
map = %Mod.Map{
id: "abc123",
state: "processing",
submod: %Mod.Submod{
options: %{}
}
}
EOF
i <<~EOF
test "test" do
Mod.fun(fn ->
map = %Mod.Map{
id: "abc123",
fun: fn ->
IO.inspect :hello
IO.inspect %{
this_is: :a_map
}
end,
submod: %Mod.Submod{
options: %{}
}
}
EOF
end

View file

@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting if clauses' do
i <<~EOF
if foo do
bar
end
EOF
i <<~EOF
if foo do
bar
else
baz
end
EOF
i <<~EOF
def test do
"else"
end
EOF
i <<~EOF
if true do
else
end
EOF
i <<~EOF
def exec(command, progress_func \\ fn(_, state) -> state end, key \\ nil, output \\ nil) do
if key do
with_cache(key, output, fn -> do_exec(command, progress_func) end)
else
do_exec(command, progress_func)
end
end
EOF
end

View file

@ -0,0 +1,29 @@
require 'spec_helper'
describe 'Keywords' do
i <<~EOF
def handle_call({:get_in_line_for_lock, key}, from, state) do
queue = state[:queues][key] || :queue.new
queue = queue.in(from, queue)
hello
end
EOF
# Has cond in milliseconds
i <<~EOF
if arg[:arg] do
finish_time = Timex.Duration.now
start_time = Mod.Mod.arg(@attr, fun(state))
duration = Timex.Duration.diff(finish_time, start_time, :milliseconds)
Mod.fun(:arg, arg, arg: arg, arg: arg, arg)
e
EOF
i <<~EOF
Logger.metadata(
task_id: state.recipe.task_id,
hashed_id: state.recipe.config.some_id,
task
)
EOF
end

View file

@ -0,0 +1,205 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting lists' do
i <<~EOF
def example do
[ :foo,
:bar,
:baz ]
end
EOF
i <<~EOF
[
[
:foo
]
]
EOF
i <<~EOF
def project do
[ name: "mix",
version: "0.1.0",
deps: deps ]
end
EOF
i <<~EOF
def config do
[ name:
"John" ]
end
EOF
i <<~EOF
def test do
[ { :cowboy, github: "extend/cowboy" },
{ :dynamo, "0.1.0-dev", github: "elixir-lang/dynamo" },
{ :ecto, github: "elixir-lang/ecto" },
{ :pgsql, github: "semiocast/pgsql" } ]
end
EOF
i <<~EOF
def test do
[ [:a, :b, :c],
[:d, :e, :f] ]
end
EOF
i <<~EOF
def test do
[ app: :first,
version: "0.0.1",
dynamos: [First.Dynamo],
compilers: [:elixir, :dynamo, :ecto, :app],
env: [prod: [compile_path: "ebin"]],
compile_path: "tmp/first/ebin",
deps: deps ]
end
EOF
i <<~EOF
def project do
[
{ :bar, path: "deps/umbrella/apps/bar" },
{ :umbrella, path: "deps/umbrella" }
]
end
EOF
i <<~EOF
def test do
a = [
%{
foo: 1,
bar: 2
}
]
b = %{
[
:foo,
:bar
]
}
[
a,
b
]
end
EOF
i <<~EOF
def create(conn, %{
"grant_type" => "password",
"username" => username,
"password" => password
}) do
1
end
EOF
i <<~EOF
def double(x) do
add(
x,
y
)
end
EOF
i <<~EOF
def double(x) do
add(
x,
y,
w,
z
)
end
EOF
i <<~EOF
def double(x) do
result = add(
x,
z
)
div(result, 2)
end
EOF
i <<~EOF
defmodule Module do
@person1 { name: "name",
age: 18,
enabled?: true }
@person2 { name: "other name",
age: 21,
enabled?: false }
end
EOF
i <<~EOF
def test_another_feature do
assert json_response(conn, 200) == %{
"results" => [
%{
"id" => result.id,
}
]
}
end
EOF
i <<~EOF
defmodule Mod do
def test do
foo == %{
}
assert json_response == %{
"id" => "identifier"
}
end
end
EOF
i <<~EOF
defmodule Mod do
def fun do
json_logger = Keyword.merge(Application.get_env(:logger, :json_logger, []), options)
Application.put_env(:logger, :json_logger, json_logger)
level = Keyword.get(json_logger, :level)
%{level: level, output: :console}
end
end
EOF
i <<~EOF
defmodule Mod do
def fun do
Enum.each(s.routing_keys, fn k -> Queue.bind(chan, s.queue, s.exchange, routing_key: k) end)
Basic.consume(chan, s.queue, nil, no_ack: true)
end
end
EOF
i <<~EOF
def init(_) do
children = [
worker(QueueSet, [[name: @queue_set]]),
worker(Producer, [[name: @producer]]),
worker(ConsumerSupervisor, [[{@producer, max_demand: @max_executors}]])
]
supervise(children, strategy: :one_for_one)
end
EOF
end

View file

@ -0,0 +1,13 @@
require 'spec_helper'
describe 'Macros' do
i <<~EOF
defmodule DeadboltTest do
use ExUnit.Case
doctest Deadbolt
hello
end
EOF
end

View file

@ -0,0 +1,47 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Map indent' do
i <<~'EOF'
DrMock.mock(fn ->
params = %{
}
end)
EOF
i <<~EOF
x = %{
foo: :bar
}
y = :foo
EOF
i <<~EOF
x =
%{ foo: :bar }
y = :foo
EOF
i <<~EOF
x = %{
foo: :bar }
y = :foo
EOF
i <<~EOF
test "test" do
Mod.fun(fn ->
map = %Mod.Map{
id: "abc123",
state: "processing",
submod: %Mod.Submod{
options: %{}
}
}
EOF
end

View file

@ -0,0 +1,145 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting pipeline' do
i <<~EOF
"a,b,c,d"
|> String.split(",")
|> Enum.reverse
EOF
i <<~EOF
[ h | t ] = "a,b,c,d"
|> String.split(",")
|> Enum.reverse
EOF
i <<~EOF
def test do
[ h | t ] = "a,b,c,d"
|> String.split(",")
|> Enum.reverse
{ :ok, h }
end
EOF
i <<~EOF
def test do
my_post = Post
|> where([p], p.id == 10)
|> where([p], u.user_id == 1)
|> select([p], p)
end
EOF
i <<~EOF
def test do
"a,b,c,d"
|> String.split(",")
|> Enum.first
|> case do
"a" -> "A"
_ -> "Z"
end
end
EOF
i <<~EOF
defrecord RECORD, field_a: nil, field_b: nil
rec = RECORD.new
|> IO.inspect
EOF
i <<~EOF
defmodule MyMod do
def export_info(users) do
{:ok, infos} = users
|> Enum.map(fn (u) -> do_something(u) end)
|> Enum.map(fn (u) ->
do_even_more(u)
end)
|> finall_thing
infos
end
end
EOF
i <<~EOF
def build_command(input, output) do
"embedded=here"
|>
end
EOF
i <<~EOF
def build_command(input, output) do
'embedded=here'
|>
EOF
i <<~EOF
def build_command(input, output) do
%{:hello => :world}
|>
end
EOF
%w(<= >= == != === !== =~).each do |op|
i <<~EOF
def build_command(input, output) do
true #{op} false
|> IO.inspect
end
EOF
end
i <<~EOF
upcased_names = names
|> Enum.map(fn name ->
String.upcase(name)
end)
IO.inspect names
EOF
i <<~EOF
upcased_names = names
|> Enum.map(fn name ->
String.upcase(name) end)
IO.inspect names
EOF
i <<~EOF
upcased_names = names
|> Enum.map(fn name ->
String.upcase(name)
end)
|> do_stuff
EOF
i <<~EOF
def hello do
do_something
|> Pipe.to_me
{:ok}
end
EOF
i <<~EOF
defmodule MyModule do
def do_stuff do
name =
"Dr. Zaius"
|> determine_name
hello
end
end
EOF
end

View file

@ -0,0 +1,22 @@
require 'spec_helper'
describe 'receive indent' do
i <<~EOF
receive do
after
end
EOF
i <<~EOF
def obtain_lock(pid, key, timeout \\ 60_000) do
case GenServer.call(pid, {:obtain_lock, key}) do
:will_notify ->
receive do
after
timeout ->
end
res -> res
end
end
EOF
end

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting strings' do
it "bulk indenting strings" do
expect(<<~EOF).to be_elixir_indentation
defp sql do
"""
SELECT *
FROM table
WHERE column = 123
AND another_column = 456
"""
end
EOF
end
end

View file

@ -0,0 +1,23 @@
require 'spec_helper'
describe 'defstruct' do
i <<~EOF
defmodule A do
defmodule State do
defstruct field: nil, field: nil, field: nil,
field: [], field: nil, field: 0,
field: false, field: %{}
end
defmodule State do
defstruct field: nil, field: nil, field: nil
end
defmodule State do
defstruct field: nil,
field: [],
field: false
end
end
EOF
end

View file

@ -0,0 +1,41 @@
require 'spec_helper'
describe 'try indent' do
i <<~EOF
try do
rescue
end
EOF
i <<~EOF
try do
catch
end
EOF
i <<~EOF
try do
after
end
EOF
i <<~EOF
test "it proceses the command" do
out = "testfile"
try do
cmd = "thing \#{@test_file} \#{out}"
{:ok, 0, _} = Thing.exec(cmd)
after
File.rm!(out)
end
end
EOF
i <<~EOF
try do
foo()
else
value -> value
end
EOF
end

View file

@ -0,0 +1,29 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Indenting tuples' do
i <<~EOF
def xpto do
{ :a,
:b,
:c }
end
EOF
i <<~EOF
def method do
{
:bar,
path: "deps/umbrella/apps/bar"
}
end
EOF
i <<~EOF
x = [
{:text, "asd {"},
{:text, "qwe"},
]
EOF
end

View file

@ -0,0 +1,121 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'with' do
i <<~EOF
with {:ok, msg} <- Msgpax.unpack(payload) do
{:ok, rebuild(msg)}
else
error -> error
end
EOF
i <<~EOF
with {:ok, width} <- Map.fetch(opts, :width),
double_width = width * 2,
{:ok, height} <- Map.fetch(opts, :height)
do
{:ok, double_width * height}
end
EOF
i <<~EOF
with {:ok, width} <- Map.fetch(opts, :width),
double_width = width * 2,
{:ok, height} <- Map.fetch(opts, :height),
do: {:ok, double_width * height}
EOF
i <<~EOF
with {:ok, width} <- Map.fetch(opts, :width),
{:ok, height} <- Map.fetch(opts, :height)
do
{:ok, width * height}
else
:error ->
{:error, :wrong_data}
end
EOF
i <<~EOF
with {:ok, width} <- Map.fetch(opts, :width),
{:ok, height} <- Map.fetch(opts, :height),
do:
{:ok,
width * height * height * height * height * height * height * height * height * height *
height * height * height * height * height * height * height},
else: (:error -> {:error, :wrong_data})
EOF
i <<~'EOF'
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config
import_config "#{Mix.env}.exs"
EOF
i <<~'EOF'
with {:ok, %File.Stat{size: size}} when size > 0 <- File.stat(first_frame_path) do
File.rename(first_frame_path, output_path)
{:ok, %Result{path: output_path}}
else
error ->
{:error, error}
end
EOF
i <<~'EOF'
def resend_confirmation(username) when is_binary(username) do
with user = %User{confirmed_at: nil} <- get_by(username: username) do
{:ok, user} =
user
|> DB.add_confirm_token
|> update_user()
Log.info(%Log{user: user.id, message: "send new confirmation"})
send_welcome(user)
{:ok, user}
else
nil ->
{:error, "not found"}
%User{email: email} ->
Email.already_confirmed(email)
{:error, "already confirmed"}
end
end
EOF
i <<~'EOF'
def create_user(params) do
profile = UserProfile.registration_changeset(%UserProfile{}, params)
user_cs =
%User{}
|> User.registration_changeset(params)
|> put_assoc(:user_profile, profile)
with {:ok, user} <- Repo.insert(user_cs, returning: false) do
Log.info(%Log{user: user.id, message: "user created"})
send_welcome(user)
{:ok, user}
end
end
EOF
i <<~'EOF'
def my_function do
with :ok <- some_call,
:ok <- another_call do
end
end
EOF
i <<~'EOF'
with {:ok, foo} <- thing(1),
{:ok, bar} <- thing(2) do
foo + bar
end
EOF
end

View file

@ -0,0 +1,322 @@
require 'rspec/expectations'
require 'tmpdir'
require 'vimrunner'
require 'vimrunner/rspec'
GVIM_PATH_FILE = File.expand_path('../../.gvim_path', __FILE__)
class Buffer
FOLD_PLACEHOLDER = '<!-- FOLD -->'.freeze
def initialize(vim, type)
@file = ".fixture.#{type}"
@vim = vim
end
def reindent(content)
with_file content do
min_indent = content.each_line.map { |line| line[/\s*/].size }.min
cmd = "ggVG:s/\\s\\{0,#{min_indent}}//" # remove all indentation
cmd += 'gg=G' # force vim to indent the file
@vim.normal cmd
end
end
def type(content)
with_file do
@vim.normal 'gg'
lines = content.each_line
count = lines.count
@vim.type("i")
lines.each_with_index do |line, index|
@vim.type("#{line.strip}")
@vim.type("<CR>") if index < count - 1
end
end
end
def syntax(content, pattern)
with_file content
# Using this function with a `pattern` that is not in `content` is pointless.
#
# @vim.search() silently fails if a pattern is not found and the cursor
# won't move. So, if the current cursor position happens to sport the
# expected syntax group already, this can lead to false positive tests.
#
# We work around this by using Vim's search() function, which returns 0 if
# there is no match.
if @vim.echo("search(#{pattern.inspect})") == '0'
return []
end
syngroups = @vim.echo <<~EOF
map(synstack(line('.'), col('.')), 'synIDattr(v:val, "name")')
EOF
# From: "['elixirRecordDeclaration', 'elixirAtom']"
# To: ["elixirRecordDeclaration", "elixirAtom"]
syngroups.gsub!(/["'\[\]]/, '').split(', ')
end
def fold_and_replace(content, fold_on_line)
with_file content do
cmd = ":set foldmethod=syntax<CR>"
cmd += "zO"
cmd += "#{fold_on_line}G"
cmd += "zc"
cmd += "cc#{FOLD_PLACEHOLDER}<Esc>"
cmd += ":.s/\s*//<CR>"
@vim.normal(cmd)
end
end
private
def with_file(content = nil)
edit_file(content)
yield if block_given?
@vim.normal ":w<CR>"
@vim.normal ":redraw<CR>"
IO.read(@file)
end
def edit_file(content)
File.write(@file, content) if content
@vim.edit @file
end
end
class Differ
def self.diff(result, expected)
instance.diff(result, expected)
end
def self.instance
@instance ||= new
end
def initialize
@differ = RSpec::Support::Differ.new(
object_preparer: -> (object) do
RSpec::Matchers::Composable.surface_descriptions_in(object)
end,
color: RSpec::Matchers.configuration.color?
)
end
def diff(result, expected)
@differ.diff_as_string(result, expected)
end
end
module ExBuffer
def self.new
Buffer.new(VIM, :ex)
end
end
module EexBuffer
def self.new
Buffer.new(VIM, :eex)
end
end
module HeexBuffer
def self.new
Buffer.new(VIM, :heex)
end
end
module LeexBuffer
def self.new
Buffer.new(VIM, :leex)
end
end
module SurfaceBuffer
def self.new
Buffer.new(VIM, :sface)
end
end
RSpec::Matchers.define :be_typed_with_right_indent do |syntax|
buffer = Buffer.new(VIM, syntax || :ex)
match do |code|
@typed = buffer.type(code)
@typed == code
end
failure_message do |code|
<<~EOM
Expected
#{@typed}
to be indented as
#{code}
when typed
EOM
end
end
{
be_elixir_indentation: :ex,
be_eelixir_indentation: :eex,
be_heelixir_indentation: :heex,
be_leelixir_indentation: :leex,
be_surface_indentation: :sface
}.each do |matcher, type|
RSpec::Matchers.define matcher do
buffer = Buffer.new(VIM, type)
match do |code|
reindented = buffer.reindent(code)
reindented == code
end
failure_message do |code|
<<~EOM
Expected
#{buffer.reindent(code)}
to be indented as
#{code}
when bulk indented
EOM
end
end
end
{
include_elixir_syntax: :ex,
include_eelixir_syntax: :eex,
include_heelixir_syntax: :heex,
include_leelixir_syntax: :leex,
include_surface_syntax: :sface
}.each do |matcher, type|
RSpec::Matchers.define matcher do |syntax, pattern|
buffer = Buffer.new(VIM, type)
match do |code|
buffer.syntax(code, pattern).include? syntax.to_s
end
failure_message do |code|
<<~EOF
expected #{buffer.syntax(code, pattern)}
to include syntax '#{syntax}'
for pattern: /#{pattern}/
in:
#{code}
EOF
end
failure_message_when_negated do |code|
<<~EOF
expected #{buffer.syntax(code, pattern)}
*NOT* to include syntax '#{syntax}'
for pattern: /#{pattern}/
in:
#{code}
EOF
end
end
end
RSpec::Matchers.define :fold_lines do
buffer = Buffer.new(VIM, :ex)
match do |code|
@code = code
pattern = /# fold\s*$/
placeholder_set = false
@expected = code.each_line.reduce([]) do |acc, line|
if line =~ pattern
if !placeholder_set
placeholder_set = true
acc << (Buffer::FOLD_PLACEHOLDER + "\n")
end
else
acc << line
end
acc
end.join
fold_on_line = code.each_line.find_index { |l| l =~ pattern } + 1
@actual = buffer.fold_and_replace(code, fold_on_line)
@expected == @actual
end
failure_message do |code|
<<~EOF
Folded
#{@code}
and unexpectedly got
#{@actual}
EOF
end
end
Vimrunner::RSpec.configure do |config|
config.reuse_server = true
config.start_vim do
VIM =
if File.exists?(GVIM_PATH_FILE)
Vimrunner::Server.new(executable: File.read(GVIM_PATH_FILE).rstrip).start
else
Vimrunner.start_gvim
end
VIM.add_plugin(File.expand_path('..', __dir__))
cmd = ':filetype off<CR>'
cmd += ':filetype plugin indent on<CR>'
cmd += ':autocmd FileType * setlocal formatoptions-=c formatoptions-=r formatoptions-=o<CR>' # disable automatic comment continuation
cmd += ":set ignorecase<CR>" # make sure we test ignorecase
VIM.normal(cmd)
VIM
end
end
RSpec.configure do |config|
config.order = :random
# Run a single spec by adding the `focus: true` option
config.filter_run_including focus: true
config.run_all_when_everything_filtered = true
end
RSpec::Core::ExampleGroup.instance_eval do
def i(str)
gen_tests(:it, str)
end
def ip(str)
gen_tests(:pending, str)
end
private
def gen_tests(method, str)
send method, "\n#{str}" do
expect(str).to be_elixir_indentation
end
send method, "typed: \n#{str}" do
expect(str).to be_typed_with_right_indent
end
end
end

View file

@ -0,0 +1,48 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Alias syntax' do
it 'colorize only module alias' do
str = "Enum.empty?(...)"
expect(str).to include_elixir_syntax('elixirAlias', 'Enum')
expect(str).to include_elixir_syntax('elixirOperator', '\.')
expect(str).to include_elixir_syntax('elixirId', 'empty?')
end
it 'colorize the module alias even if it starts with `!`' do
expect(<<~EOF).to include_elixir_syntax('elixirAlias', 'Enum')
!Enum.empty?(...)
EOF
end
it 'does not colorize the preceding ! in an alias' do
expect(<<~EOF).not_to include_elixir_syntax('elixirAlias', '!')
!Enum.empty?(...)
EOF
end
it 'does not colorize words starting with lowercase letters' do
expect(<<~EOF).not_to include_elixir_syntax('elixirAlias', 'aEnum')
aEnum.empty?(...)
EOF
end
it 'colorizes numbers in aliases' do
str = "S3Manager"
expect(str).to include_elixir_syntax('elixirAlias', 'S')
expect(str).to include_elixir_syntax('elixirAlias', '3')
expect(str).to include_elixir_syntax('elixirAlias', 'Manager')
end
it 'colorize dots in module alias' do
str = "Foo.Bar.Baz.fun(...)"
expect(str).to include_elixir_syntax('elixirAlias', 'Foo')
expect(str).to include_elixir_syntax('elixirAlias', '\.\(Bar\)\@=')
expect(str).to include_elixir_syntax('elixirAlias', 'Bar')
expect(str).to include_elixir_syntax('elixirAlias', '\.\(Baz\)\@=')
expect(str).to include_elixir_syntax('elixirAlias', 'Baz')
expect(str).to include_elixir_syntax('elixirOperator', '\.\(fun\)\@=')
expect(str).to include_elixir_syntax('elixirId', 'fun')
end
end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Anonymous function syntax' do
it 'anonymous function' do
expect(<<~'EOF').to include_elixir_syntax('elixirAnonymousFunction', 'fn')
fn(_, state) -> state end
EOF
end
it 'as a default argument' do
expect(<<~'EOF').to include_elixir_syntax('elixirAnonymousFunction', 'fn')
def exec(func \\ fn(_, state) -> state end) do
end
EOF
end
it 'as a default argument in a module' do
str = <<~'EOF'
defmodule HelloWorld do
def exec(func \\ fn(_, state) -> state end) do
end
end
EOF
expect(str).to include_elixir_syntax('elixirAnonymousFunction', 'fn')
# Test that the syntax properly closed
expect(str).to include_elixir_syntax('elixirBlockDefinition', '^end')
end
end

View file

@ -0,0 +1,91 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Atom syntax' do
KEYWORDS = %w(
def
defp
defmodule
defprotocol
defimpl
defrecord
defrecordp
defmacro
defmacrop
defdelegate
defoverridable
defexception
defcallback
defstruct
)
it '`atom:` style keyword used as an atom' do
KEYWORDS.each do |kw|
expect(<<~EOF).to include_elixir_syntax('elixirAtom', kw), "expected #{kw} to be an elixirAtom"
defmodule XmlElement do
require Record
import Record, only: [#{kw}: 2, extract: 2]
end
EOF
end
end
it '`:atom =>` style keyword used as an atom' do
KEYWORDS.each do |kw|
expect(<<~EOF).to include_elixir_syntax('elixirAtom', kw), "expected #{kw} to be an elixirAtom"
defmodule XmlElement do
require Record
import Record, only: [:#{kw} => 2, :extract => 2]
end
EOF
end
end
it 'atoms as part of a comprehension' do
s = 'for kvp <- map, do: &atomize_key/1, into: %{}'
expect(s).to include_elixir_syntax('elixirAtom', 'do')
expect(s).to include_elixir_syntax('elixirAtom', 'into')
end
it 'defoverridable' do
expect(<<~EOF).to include_elixir_syntax('elixirAtom', 'init:')
defmodule Test do
defmacro __using__(_options) do
quote do
def init(args) do
{:ok, args}
end
defoverridable init: 1
end
end
end
EOF
expect(<<~EOF).to include_elixir_syntax('elixirAtom', 'init:')
defmodule Test do
defmacro __using__(_options) do
quote do
def init(args) do
{:ok, args}
end
defoverridable [init: 1]
end
end
end
EOF
end
it '`Atom:` style atoms used in keyword list' do
expect(<<~EOF).to include_elixir_syntax('elixirAtom', 'Protocols:')
def project do
[
docs: [
groups_for_modules: [
Protocols: [Enumerable],
]
]
]
end
EOF
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Syntax case statements' do
it ':* is recognized as an atom' do
expect(<<~EOF).to include_elixir_syntax('elixirAtom', '\*')
case pattern do
:* -> :ok
_ -> :error
end
EOF
end
end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Comments syntax' do
it 'full line comment' do
expect(<<~EOF).to include_elixir_syntax('elixirComment', '#\ this\ is\ a\ comment')
# this is a comment
EOF
end
it 'end line comment' do
expect(<<~EOF).to include_elixir_syntax('elixirComment', '#\ this\ is\ a\ comment')
IO.puts "some text" # this is a comment
EOF
end
it 'after arguments' do
t = <<~EOF
def foo(<<
0 :: 1, # Foo
1 :: size(1), # Bar
# Blah
baz :: 8, # Baz
>>), do: baz
EOF
expect(t).to include_elixir_syntax('elixirComment', '#\ Foo')
expect(t).to include_elixir_syntax('elixirComment', '#\ Bar')
expect(t).to include_elixir_syntax('elixirComment', '#\ Blah')
expect(t).to include_elixir_syntax('elixirComment', '#\ Baz')
end
end

View file

@ -0,0 +1,30 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Defmodule syntax' do
it 'defines `defmodule` keyword as elixirModuleDefine' do
expect(<<~EOF).to include_elixir_syntax('elixirModuleDefine', 'defmodule')
defmodule HelloPhoenix.HelloController do
EOF
end
it 'defines module name as elixirModuleDeclaration' do
str = "defmodule HelloPhoenix.HelloController do"
expect(str).to include_elixir_syntax('elixirModuleDeclaration', 'HelloPhoenix')
expect(str).to include_elixir_syntax('elixirModuleDeclaration', '\.')
expect(str).to include_elixir_syntax('elixirModuleDeclaration', 'HelloController')
end
it 'does not define module name as elixirAlias' do
expect(<<~EOF).not_to include_elixir_syntax('elixirAlias', 'HelloPhoenix.HelloController')
defmodule HelloPhoenix.HelloController do
EOF
end
it 'defines `do` keyword as elixirBlock' do
expect(<<~EOF).to include_elixir_syntax('elixirBlock', 'do')
defmodule HelloPhoenix.HelloController do
EOF
end
end

View file

@ -0,0 +1,218 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'documentation syntax' do
describe 'string' do
it 'doc in double quotes' do
ex = '@doc "foo"'
expect(ex).to include_elixir_syntax('elixirDocString', 'foo')
expect(ex).to include_elixir_syntax('elixirDocStringDelimiter', '"')
end
it 'doc in sigil_S' do
ex = '@doc ~S(foo)'
expect(ex).to include_elixir_syntax('elixirDocString', 'foo')
expect(ex).to include_elixir_syntax('elixirDocSigilDelimiter', 'S')
end
end
describe 'heredoc' do
it 'doc with multiline content' do
ex = <<~'EOF'
@callbackdoc """
foo
"""
EOF
expect(ex).to include_elixir_syntax('elixirVariable', 'doc')
expect(ex).to include_elixir_syntax('elixirDocString', 'foo')
expect(ex).to include_elixir_syntax('elixirDocStringDelimiter', '"""')
end
it 'doc with sigil_S triple double-quoted multiline content' do
ex = <<~'EOF'
@doc ~S"""
foo
"""
EOF
expect(ex).to include_elixir_syntax('elixirVariable', 'doc')
expect(ex).to include_elixir_syntax('elixirDocSigilDelimiter', 'S"""')
expect(ex).to include_elixir_syntax('elixirDocString', 'foo')
end
it 'doc with sigil_S triple double-quoted multiline content with parentheses' do
ex = <<~'EOF'
@doc(~S"""
foo
""")
EOF
expect(ex).to include_elixir_syntax('elixirVariable', 'doc')
expect(ex).to include_elixir_syntax('elixirDocSigilDelimiter', 'S"""')
expect(ex).to include_elixir_syntax('elixirDocString', 'foo')
end
it 'doc with sigil_S triple single-quoted multiline content' do
ex = <<~'EOF'
@doc ~S'''
foo
'''
EOF
expect(ex).to include_elixir_syntax('elixirVariable', 'doc')
expect(ex).to include_elixir_syntax('elixirDocSigilDelimiter', "S'''")
expect(ex).to include_elixir_syntax('elixirDocString', 'foo')
end
it 'doc with sigil_S triple single-quoted multiline content with parentheses' do
ex = <<~'EOF'
@doc(~S'''
foo
''')
EOF
expect(ex).to include_elixir_syntax('elixirVariable', 'doc')
expect(ex).to include_elixir_syntax('elixirDocSigilDelimiter', "S'''")
expect(ex).to include_elixir_syntax('elixirDocString', 'foo')
end
it 'doc with triple single-quoted multiline content is not a doc string' do
ex = <<~'EOF'
@doc '''
foo
'''
EOF
expect(ex).not_to include_elixir_syntax('elixirDocString', 'foo')
end
it 'doc with multiline escaped' do
ex = <<~'EOF'
@doc """
foo
```
@xxx \"""
bar
\"""
```
baz
"""
EOF
expect(ex).to include_elixir_syntax('elixirDocString', 'foo')
expect(ex).to include_elixir_syntax('elixirDocString', 'bar')
expect(ex).to include_elixir_syntax('elixirDocString', 'baz')
end
it 'doc skip interpolation' do
ex = <<~'EOF'
@doc """
foo #{bar}
"""
EOF
expect(ex).to include_elixir_syntax('elixirDocString', 'foo')
expect(ex).to include_elixir_syntax('elixirDocStringDelimiter', '"""')
expect(ex).to include_elixir_syntax('elixirInterpolation', 'bar')
end
it 'doc with doctest' do
ex = <<~'EOF'
@doc """
doctest
iex> Enum.map [1, 2, 3], fn(x) ->
...> x * 2
...> end
[2, 4, 6]
"""
EOF
expect(ex).to include_elixir_syntax('elixirDocString', 'doctest')
expect(ex).to include_elixir_syntax('elixirDocTest', 'map')
expect(ex).to include_elixir_syntax('elixirDocTest', 'x \* 2')
expect(ex).to include_elixir_syntax('elixirDocTest', '2, 4, 6')
end
describe 'doctest without newline after' do
it 'with heredoc' do
ex = <<~'EOF'
@doc """
doctest
iex> 1 + 2
3
"""
def some_fun(x), do: x
EOF
expect(ex).to include_elixir_syntax('elixirDocString', 'doctest')
expect(ex).to include_elixir_syntax('elixirDocTest', '1 + 2')
expect(ex).to include_elixir_syntax('elixirDefine', 'def')
end
it 'with double quote' do
ex = <<~'EOF'
@doc "
doctest
iex> \"bob\"
\"bob\"
"
def some_fun(x), do: x
EOF
expect(ex).to include_elixir_syntax('elixirDocString', 'doctest')
expect(ex).to include_elixir_syntax('elixirDocTest', 'bob')
expect(ex).to include_elixir_syntax('elixirDefine', 'def')
end
it 'with sigil_S' do
ex = <<~'EOF'
@doc ~S(
doctest
iex> to_string("bob"\)
"bob"
)
def some_fun(x), do: x
EOF
expect(ex).to include_elixir_syntax('elixirDocString', 'doctest')
expect(ex).to include_elixir_syntax('elixirDocTest', 'bob')
expect(ex).to include_elixir_syntax('elixirDefine', 'def')
end
it 'with sigil_s' do
ex = <<~'EOF'
@doc ~s(
doctest
iex> to_string("bob"\)
"bob"
)
def some_fun(x), do: x
EOF
expect(ex).to include_elixir_syntax('elixirDocString', 'doctest')
expect(ex).to include_elixir_syntax('elixirDocTest', 'bob')
expect(ex).to include_elixir_syntax('elixirDefine', 'def')
end
end
it 'doc with inline code' do
ex = <<~'EOF'
@doc """
doctest with inline code `List.wrap([])`
"""
EOF
expect(ex).to include_elixir_syntax('elixirDocString', 'doctest')
expect(ex).to include_elixir_syntax('elixirDocString', 'wrap')
end
describe "use markdown for docs" do
before(:each) { VIM.command("let g:elixir_use_markdown_for_docs = 1") }
after(:each) { VIM.command("let g:elixir_use_markdown_for_docs = 0") }
it 'doc with inline code' do
ex = <<~'EOF'
@doc """
doc with inline code `List.wrap([])`
"""
EOF
expect(ex).to include_elixir_syntax('elixirDocString', 'inline')
expect(ex).to include_elixir_syntax('markdownCode', 'wrap')
end
end
end
end

View file

@ -0,0 +1,61 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Embedded Elixir syntax' do
it 'elixir' do
expect('<%= if true do %>').to include_eelixir_syntax('elixirKeyword', 'if')
expect('<%= if true do %>').to include_eelixir_syntax('elixirBoolean', 'true')
end
it 'expression' do
expect('<%= if true do %>').to include_eelixir_syntax('eelixirExpression', 'if')
expect('<% end %>').to include_eelixir_syntax('eelixirExpression', 'end')
end
it 'quote' do
expect('<%% def f %>').to include_eelixir_syntax('eelixirQuote', 'def')
end
it 'comment' do
expect('<%# foo bar baz %>').to include_eelixir_syntax('eelixirComment', 'foo')
end
it 'delimiters' do
expect('<% end %>').to include_eelixir_syntax('eelixirDelimiter', '<%')
expect('<% end %>').to include_eelixir_syntax('eelixirDelimiter', '%>')
end
end
describe 'Embedded Live Elixir syntax' do
it 'elixir' do
expect('<%= if true do %>').to include_leelixir_syntax('elixirKeyword', 'if')
expect('<%= if true do %>').to include_leelixir_syntax('elixirBoolean', 'true')
end
it 'expression' do
expect('<%= if true do %>').to include_leelixir_syntax('eelixirExpression', 'if')
expect('<% end %>').to include_leelixir_syntax('eelixirExpression', 'end')
end
it 'quote' do
expect('<%% def f %>').to include_leelixir_syntax('eelixirQuote', 'def')
end
it 'comment' do
expect('<%# foo bar baz %>').to include_leelixir_syntax('eelixirComment', 'foo')
end
it 'delimiters' do
expect('<% end %>').to include_leelixir_syntax('eelixirDelimiter', '<%')
expect('<% end %>').to include_leelixir_syntax('eelixirDelimiter', '%>')
end
end
describe 'Embedded Surface syntax' do
it 'elixir' do
expect('{{ @foo }}').to include_surface_syntax('elixirVariable', 'foo')
expect('{{ @foo }}').to include_surface_syntax('surfaceDelimiter', '{{')
expect('{{ @foo }}').to include_surface_syntax('surfaceDelimiter', '}}')
end
end

View file

@ -0,0 +1,174 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'ExUnit syntax' do
it 'test macro' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitMacro', 'test')
test 'that stuff works' do
assert true
end
EOF
end
it 'describe macro' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitMacro', 'describe')
describe 'some_function/1' do
test 'that stuff works' do
assert true
end
end
EOF
end
it 'setup macro' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitMacro', 'setup')
setup do
IO.puts "hi mom"
end
test 'that stuff works' do
assert true
end
EOF
end
it 'setup_all macro' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitMacro', 'setup_all')
setup_all do
IO.puts "hi mom"
end
test 'that stuff works' do
assert true
end
EOF
end
it 'on_exit macro' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitMacro', 'on_exit')
setup_all do
IO.puts "hi mom"
on_exit fn() ->
do_something
end
end
test 'that stuff works' do
assert true
end
EOF
end
it 'assert' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'assert')
test 'that stuff works' do
assert true
end
EOF
end
it 'assert_in_delta' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'assert_in_delta')
test 'that stuff works' do
assert_in_delta true
end
EOF
end
it 'assert_raise' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'assert_raise')
test 'that stuff works' do
assert_raise true
end
EOF
end
it 'assert_receive' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'assert_receive')
test 'that stuff works' do
assert_receive true
end
EOF
end
it 'assert_received' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'assert_received')
test 'that stuff works' do
assert_received true
end
EOF
end
it 'catch_error' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'catch_error')
test 'that stuff works' do
catch_error true
end
EOF
end
it 'catch_exit' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'catch_exit')
test 'that stuff works' do
catch_exit true
end
EOF
end
it 'catch_throw' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'catch_throw')
test 'that stuff works' do
catch_throw true
end
EOF
end
it 'flunk' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'flunk')
test 'that stuff works' do
flunk true
end
EOF
end
it 'refute' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'refute')
test 'that stuff works' do
refute true
end
EOF
end
it 'refute_in_delta' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'refute_in_delta')
test 'that stuff works' do
refute_in_delta true
end
EOF
end
it 'refute_receive' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'refute_receive')
test 'that stuff works' do
refute_receive true
end
EOF
end
it 'refute_received' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitAssert', 'refute_received')
test 'that stuff works' do
refute_received true
end
EOF
end
it 'doctest' do
expect(<<~EOF).to include_elixir_syntax('elixirExUnitMacro', 'doctest')
module MyTest do
doctest MyModule
end
EOF
end
end

View file

@ -0,0 +1,20 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'function syntax' do
it 'doesnt treat underscored functions like unsued variables' do
expect(<<~EOF).to include_elixir_syntax('elixirId', '__ensure_defimpl__')
defp derive(protocol, for, struct, opts, env) do
# ... code ...
__ensure_defimpl__(protocol, for, env)
EOF
expect(<<~EOF).not_to include_elixir_syntax('elixirUnusedVariable', '__ensure_defimpl__')
defp derive(protocol, for, struct, opts, env) do
# ... code ...
__ensure_defimpl__(protocol, for, env)
EOF
end
end

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'defguard syntax' do
it 'defines `defguard` keyword as elixirGuard' do
expect(<<~EOF).to include_elixir_syntax('elixirGuard', 'defguard')
defguard some_guard(x) when is_integer(x)
EOF
end
it 'defines `defguardp` keyword as elixirPrivateGuard' do
expect(<<~EOF).to include_elixir_syntax('elixirPrivateGuard', 'defguardp')
defguardp some_private_guard(x) when is_integer(x)
EOF
end
end

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Kernel function syntax' do
it 'kernel function used as an atom key in a keyword list outside of a block' do
expect(<<~EOF).not_to include_elixir_syntax('elixirKeyword', 'length')
do
plug Plug.Parsers, length: 400_000_000
end
EOF
end
it 'kernel function used as an atom key in a keyword list contained in a block' do
expect(<<~EOF).not_to include_elixir_syntax('elixirKeyword', 'length')
plug Plug.Parsers, length: 400_000_000
EOF
end
end

View file

@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Keyword syntax' do
it 'for used as keyword' do
expect(<<~EOF).to include_elixir_syntax('elixirKeyword', 'for')
for v <- [1, 3, 3]
EOF
end
it 'case used as keyword' do
expect(<<~EOF).to include_elixir_syntax('elixirKeyword', 'case')
case true do
EOF
end
it 'raise used as keyword' do
expect(<<~EOF).to include_elixir_syntax('elixirKeyword', 'raise')
raise "oops"
EOF
expect(<<~EOF).to include_elixir_syntax('elixirKeyword', 'raise')
raise ArgumentError, message: "invalid argument foo"
EOF
end
end

View file

@ -0,0 +1,40 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'List syntax' do
it 'should properly handle "\\\\" inside' do
syntax = <<~EOF
'"\\\\'
var = 1
EOF
expect(syntax).to include_elixir_syntax('elixirId', 'var')
expect(syntax).not_to include_elixir_syntax('elixirString', 'var')
end
it 'recognizes lists' do
syntax = <<~EOF
[
:hello,
:world
]
EOF
expect(syntax).to include_elixir_syntax('elixirListDelimiter', '[')
expect(syntax).to include_elixir_syntax('elixirList', ':hello')
expect(syntax).to include_elixir_syntax('elixirListDelimiter', ']')
end
it 'recognizes lists inside functions' do
syntax = <<~EOF
def hello_world do
[
:hello,
:world
]
end
EOF
expect(syntax).to include_elixir_syntax('elixirListDelimiter', '[')
expect(syntax).to include_elixir_syntax('elixirList', ':hello')
expect(syntax).to include_elixir_syntax('elixirListDelimiter', ']')
end
end

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Map syntax' do
it 'maps' do
str = %q(%{name: "one"})
expect(str).to include_elixir_syntax('elixirMapDelimiter', '%')
expect(str).to include_elixir_syntax('elixirMapDelimiter', '{')
expect(str).to include_elixir_syntax('elixirAtom', 'name:')
expect(str).to include_elixir_syntax('elixirMap', 'name:')
expect(str).to include_elixir_syntax('elixirMapDelimiter', '}')
end
end

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Module function syntax' do
it 'for used as module function' do
expect(<<~EOF).to include_elixir_syntax('elixirId', 'for')
OverridesDefault.for
EOF
end
it 'case used as module function' do
expect(<<~EOF).to include_elixir_syntax('elixirId', 'case')
OverridesDefault.case
EOF
end
end

View file

@ -0,0 +1,45 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Numbers syntax' do
describe 'decimal' do
it 'positive is colorized' do
expect('123').to include_elixir_syntax('elixirNumber', '123')
end
it 'negative is colorized' do
expect('-123').to include_elixir_syntax('elixirNumber', '123')
end
end
describe 'hexadecimal' do
it 'positive is colorized' do
expect('0xdeadbeaf').to include_elixir_syntax('elixirNumber', '0xdeadbeaf')
end
it 'negative is colorized' do
expect('-0xdeadbeaf').to include_elixir_syntax('elixirNumber', '0xdeadbeaf')
end
end
describe 'octal' do
it 'positive is colorized' do
expect('0o777').to include_elixir_syntax('elixirNumber', '0o777')
end
it 'negative is colorized' do
expect('-0o777').to include_elixir_syntax('elixirNumber', '0o777')
end
end
describe 'binary' do
it 'positive is colorized' do
expect('0b1011').to include_elixir_syntax('elixirNumber', '0b1011')
end
it 'negative is colorized' do
expect('-0b1011').to include_elixir_syntax('elixirNumber', '0b1011')
end
end
end

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Operators' do
it 'default argument' do
expect(<<~'EOF').to include_elixir_syntax('elixirOperator', '\\')
def foo(bar \\ :baz)
EOF
expect(<<~EOF).to include_elixir_syntax('elixirOperator', '\/')
def foo(bar // :baz)
EOF
end
it 'in' do
expect(<<~EOF).to include_elixir_syntax('elixirOperator', 'in')
'x' in ['x']
EOF
expect(<<~EOF).not_to include_elixir_syntax('elixirOperator', 'in')
:queue.in x, 5
EOF
end
it 'does not highlight operators inside of elixirIds' do
expect(<<~EOF).not_to include_elixir_syntax('elixirOperator', 'in')
incoming
EOF
end
end

View file

@ -0,0 +1,12 @@
# frozen_string_literal: true
# encoding: utf-8
require 'spec_helper'
describe 'Record syntax' do
it 'private record symbol' do
expect(<<~EOF).to include_elixir_syntax('elixirAtom', ':user')
defrecordp :user, name: "José", age: 25
EOF
end
end

View file

@ -0,0 +1,146 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Sigil syntax' do
it 'as function argument' do
expect('def f(~s(")), do: true').to include_elixir_syntax('elixirSigilDelimiter', '\~s(')
expect('def f(~s(")), do: true').to include_elixir_syntax('elixirSigil', '"')
expect("def f(~s(')), do: true").to include_elixir_syntax('elixirSigil', "'")
expect('def f(~s(")), do: true').not_to include_elixir_syntax('elixirSigilDelimiter', '"')
end
it 'as function argument multiline content' do
ex = <<~'EOF'
f(
~S"""
foo
""",
bar
)
EOF
expect(ex).to include_elixir_syntax('elixirSigilDelimiter', 'S"""')
expect(ex).to include_elixir_syntax('elixirSigil', 'foo')
end
describe 'upper case' do
it 'string' do
expect('~S(string)').to include_elixir_syntax('elixirSigilDelimiter', 'S')
expect('~S(string)').to include_elixir_syntax('elixirSigil', 'string')
end
it 'character list' do
expect('~C(charlist)').to include_elixir_syntax('elixirSigilDelimiter', 'C')
expect('~C(charlist)').to include_elixir_syntax('elixirSigil', 'charlist')
end
it 'regular expression' do
expect('~R(regex)').to include_elixir_syntax('elixirSigilDelimiter', 'R')
expect('~R(regex)').to include_elixir_syntax('elixirSigil', 'regex')
end
it 'list of words' do
expect('~W(list of words)').to include_elixir_syntax('elixirSigilDelimiter', 'W')
expect('~W(list of words)').to include_elixir_syntax('elixirSigil', 'list')
end
it 'delimited with parenthesis' do
expect('~S(foo bar)').to include_elixir_syntax('elixirSigilDelimiter', '(')
expect('~S(foo bar)').to include_elixir_syntax('elixirSigilDelimiter', ')')
end
it 'delimited with braces' do
expect('~S{foo bar}').to include_elixir_syntax('elixirSigilDelimiter', '{')
expect('~S{foo bar}').to include_elixir_syntax('elixirSigilDelimiter', '}')
end
it 'delimited with brackets' do
expect('~S[foo bar]').to include_elixir_syntax('elixirSigilDelimiter', '[')
expect('~S[foo bar]').to include_elixir_syntax('elixirSigilDelimiter', ']')
end
it 'escapes double quotes unless only preceded by whitespace' do
expect(<<~EOF).to include_elixir_syntax('elixirSigilDelimiter', %q(^\s*\zs"""))
~r"""
foo """
"""
EOF
end
it 'escapes single quotes unless only preceded by whitespace' do
expect(<<~EOF).to include_elixir_syntax('elixirSigilDelimiter', %q(^\s*\zs'''))
~r'''
foo '''
'''
EOF
end
it 'without escapes' do
expect('~S(foo \n bar)').not_to include_elixir_syntax('elixirRegexEscape', '\\')
end
it 'without interpolation' do
expect('~S(foo #{bar})').not_to include_elixir_syntax('elixirInterpolation', 'bar')
end
it 'without escaped parenthesis' do
expect('~S(\( )').not_to include_elixir_syntax('elixirRegexEscapePunctuation', '( ')
end
it 'Live EEx' do
expect('~L"""liveview template"""').to include_elixir_syntax('elixirSigilDelimiter', '"""')
end
it 'Surface EEx' do
expect('~H"""surface template"""').to include_elixir_syntax('elixirSigilDelimiter', '"""')
end
it 'EEx' do
expect('~E"""Phoenix.HTML template"""').to include_elixir_syntax('elixirSigilDelimiter', '"""')
expect('~e"""Phoenix.HTML template"""').to include_elixir_syntax('elixirSigilDelimiter', '"""')
end
end
describe 'lower case' do
it 'string' do
expect('~s(string)').to include_elixir_syntax('elixirSigilDelimiter', 's')
expect('~s(string)').to include_elixir_syntax('elixirSigil', 'string')
end
it 'character list' do
expect('~c(charlist)').to include_elixir_syntax('elixirSigilDelimiter', 'c')
expect('~c(charlist)').to include_elixir_syntax('elixirSigil', 'charlist')
end
it 'regular expression' do
expect('~r(regex)').to include_elixir_syntax('elixirSigilDelimiter', 'r')
expect('~r(regex)').to include_elixir_syntax('elixirSigil', 'regex')
end
it 'list of words' do
expect('~w(list of words)').to include_elixir_syntax('elixirSigilDelimiter', 'w')
expect('~w(list of words)').to include_elixir_syntax('elixirSigil', 'list')
end
it 'with escapes' do
expect('~s(foo \n bar)').to include_elixir_syntax('elixirRegexEscapePunctuation', '\\')
end
it 'with interpolation' do
expect('~s(foo #{bar})').to include_elixir_syntax('elixirInterpolation', 'bar')
end
it 'with escaped parenthesis' do
expect('~s(\( )').to include_elixir_syntax('elixirRegexEscapePunctuation', '( ')
end
it 'interpolation with slashes' do
expect('~s/foo #{bar}/').to include_elixir_syntax('elixirInterpolation', 'bar')
end
it 'escapes with slashes' do
expect('~s/foo \n bar/').to include_elixir_syntax('elixirRegexEscapePunctuation', '\\')
end
end
end

View file

@ -0,0 +1,109 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'String syntax' do
describe 'binary' do
it 'double quoted string' do
expect('foo "test"').to include_elixir_syntax('elixirString', 'test')
end
it 'double quoted string with escaped quote' do
expect('"this \"test is all one string"').to include_elixir_syntax('elixirString', 'test')
end
it 'charlist with escaped quote' do
expect(<<-'EOF').to include_elixir_syntax('elixirCharList', 'test')
'this \'test is all one charlist'
EOF
end
it 'interpolation in string' do
expect('do_something "foo #{test}"').to include_elixir_syntax('elixirInterpolation', 'test')
end
end
describe 'heredoc' do
it 'heredoc must be string' do
ex = <<~EOF
def function do
"""
foo "test"
"""
end
EOF
expect(ex).to include_elixir_syntax('elixirString', 'foo')
expect(ex).to include_elixir_syntax('elixirString', 'test')
end
it 'interpolation in string in heredoc' do
expect(<<~'EOF').to include_elixir_syntax('elixirInterpolation', '#{')
def function do
"""
foo "#{test}"
"""
end
EOF
end
it 'interpolation in heredoc' do
expect(<<~'EOF').to include_elixir_syntax('elixirInterpolation', '#{')
def function do
"""
foo #{test}
"""
end
EOF
end
it 'correctly terminates heredocs with no spaces at the start of the line' do
expect(<<~'EOF'.gsub(/^\s+/, '')).to include_elixir_syntax('elixirAtom', ':bar')
"""
foo
"""
:bar
EOF
expect(<<~'EOF'.gsub(/^\s+/, '')).to include_elixir_syntax('elixirAtom', ':bar')
'''
foo
'''
:bar
EOF
end
it 'interpolation with a tuple' do
str = <<~'EOF'
"Failed sending tasks #{inspect {:unexpected_status_code, s}}"
EOF
expect(str).not_to include_elixir_syntax('elixirInterpolationDelimiter', '}}"$')
expect(str).to include_elixir_syntax('elixirInterpolationDelimiter', '}"$')
end
it 'interpolation with a tuple' do
str = <<~'EOF'
"Failed sending tasks #{inspect %{unexpected_status_code: s}}"
EOF
expect(str).not_to include_elixir_syntax('elixirInterpolationDelimiter', '}}"$')
expect(str).to include_elixir_syntax('elixirInterpolationDelimiter', '}"$')
end
it 'interpolation with a struct' do
str = <<~'EOF'
"Failed sending tasks #{inspect %ResponseStruct{unexpected_status_code: s}}"
EOF
expect(str).not_to include_elixir_syntax('elixirInterpolationDelimiter', '}}"$')
expect(str).to include_elixir_syntax('elixirInterpolationDelimiter', '}"$')
end
it 'strings with embedded braces' do
str = <<~EOF
x = [
{:text, "asd {"},
{:text, "qwe"},
]
EOF
expect(str).to include_elixir_syntax('elixirString', '{"}')
end
end
end

View file

@ -0,0 +1,64 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Struct syntax' do
it 'without defaults' do
expect(<<~EOF).to include_elixir_syntax('elixirAtom', ':name')
defstruct [:name, :age]
EOF
end
it 'with defaults' do
expect(<<~EOF).to include_elixir_syntax('elixirAtom', 'name:')
defstruct name: "john", age: 27
EOF
end
it 'structs' do
str = %q(%MyStruct{name: "one"})
expect(str).to include_elixir_syntax('elixirStructDelimiter', '%')
expect(str).to include_elixir_syntax('elixirStruct', '%')
expect(str).to include_elixir_syntax('elixirAlias', 'MyStruct')
expect(str).to include_elixir_syntax('elixirStruct', 'MyStruct')
expect(str).to include_elixir_syntax('elixirStructDelimiter', '{')
expect(str).to include_elixir_syntax('elixirStruct', '{')
expect(str).to include_elixir_syntax('elixirAtom', 'name:')
expect(str).to include_elixir_syntax('elixirStruct', 'name:')
expect(str).to include_elixir_syntax('elixirStructDelimiter', '}')
end
it 'properly closes strings in structs' do
str = <<~'EOF'
%MyStruct{url: "http://127.0.0.1:#{port}"} # anchor
# this should not be a string still
EOF
expect(str).to include_elixir_syntax('elixirStruct', '{url')
expect(str).to include_elixir_syntax('elixirStringDelimiter', '"http')
expect(str).to include_elixir_syntax('elixirStruct', '"http')
expect(str).to include_elixir_syntax('elixirInterpolationDelimiter', '#{')
expect(str).to include_elixir_syntax('elixirStruct', '#{')
expect(str).to include_elixir_syntax('elixirInterpolationDelimiter', '}"}')
expect(str).to include_elixir_syntax('elixirStruct', '}"}')
expect(str).not_to include_elixir_syntax('elixirStructDelimiter', '}"}')
expect(str).to include_elixir_syntax('elixirStringDelimiter', '"}')
expect(str).to include_elixir_syntax('elixirStruct', '"}')
expect(str).to include_elixir_syntax('elixirStringDelimiter', '"}')
expect(str).to include_elixir_syntax('elixirStruct', '"}')
expect(str).to include_elixir_syntax('elixirStructDelimiter', '} #')
expect(str).to include_elixir_syntax('elixirComment', '# this should not be a string still')
end
end

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Tuple syntax' do
it 'tuples' do
str = %q({:name, "one"})
expect(str).to include_elixir_syntax('elixirTupleDelimiter', '{')
expect(str).to include_elixir_syntax('elixirTuple', '{')
expect(str).to include_elixir_syntax('elixirAtom', ':name')
expect(str).to include_elixir_syntax('elixirTuple', ':name')
expect(str).to include_elixir_syntax('elixirTupleDelimiter', '}')
end
end

View file

@ -0,0 +1,75 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'Variable syntax' do
it 'unused' do
expect(<<~EOF).to include_elixir_syntax('elixirUnusedVariable', '_from')
def handle_call(:pop, _from, [h|stack]) do
{ :reply, h, stack }
end
EOF
end
it 'unused in function body' do
expect(<<~EOF).not_to include_elixir_syntax('elixirUnusedVariable', '_from')
def handle_call(:pop)
Hello._from
end
EOF
end
it 'unused, multiple lines' do
expect(<<~EOF).to include_elixir_syntax('elixirUnusedVariable', '_from')
def handle_call(:pop,
_from,
[h|stack]) do
{ :reply, h, stack }
end
EOF
end
it 'unused, single char' do
expect(<<~EOF).to include_elixir_syntax('elixirUnusedVariable', '_')
def call(:pop, _, [h|stack]) do
{ :reply, h, stack }
end
EOF
end
it 'unused in pattern_match' do
str = <<~EOF
def sign_in(conn, %{
"data" => %{
"type" => "doctor",
"attributes" => %{
"institution_code" => institution_code,
"password" => password,
"email_or_phone" => email_or_phone}}}, _user, _claims) do
:ok
end
EOF
expect(str).to include_elixir_syntax('elixirUnusedVariable', '_user')
expect(str).to include_elixir_syntax('elixirUnusedVariable', '_claims')
end
it 'unused, in anonymous function, inline' do
expect(<<~EOF).to include_elixir_syntax('elixirUnusedVariable', '_unused')
fun = fn _unused -> false end
EOF
end
it 'unused, in anonymous function, multiple lines' do
expect(<<~EOF).to include_elixir_syntax('elixirUnusedVariable', '_unused')
fun = fn
([], _unused) -> true
end
EOF
end
it 'unused, in pattern matching' do
expect(<<~EOF).to include_elixir_syntax('elixirUnusedVariable', '_unused')
_unused = false
EOF
end
end

View file

@ -0,0 +1,80 @@
if exists("b:current_syntax")
finish
endif
let s:cpo_save = &cpo
set cpo&vim
if !exists("main_syntax")
let main_syntax = 'eelixir'
endif
if !exists("g:eelixir_default_subtype")
let g:eelixir_default_subtype = "html"
endif
if !exists("b:eelixir_subtype")
let s:lines = getline(1)."\n".getline(2)."\n".getline(3)."\n".getline(4)."\n".getline(5)."\n".getline("$")
let b:eelixir_subtype = matchstr(s:lines,'eelixir_subtype=\zs\w\+')
if b:eelixir_subtype == ''
let b:eelixir_subtype = matchstr(&filetype,'^eex\.\zs\w\+')
endif
if b:eelixir_subtype == ''
let b:eelixir_subtype = matchstr(&filetype,'^heex\.\zs\w\+')
endif
if b:eelixir_subtype == ''
let b:eelixir_subtype = matchstr(&filetype,'^leex\.\zs\w\+')
endif
if b:eelixir_subtype == ''
let b:eelixir_subtype = matchstr(&filetype,'^sface\.\zs\w\+')
endif
if b:eelixir_subtype == ''
let b:eelixir_subtype = matchstr(substitute(expand("%:t"),'\c\%(\.eex\|\.heex\|\.leex\|\.sface\|\.eelixir\)\+$','',''),'\.\zs\w\+$')
endif
if b:eelixir_subtype == 'ex'
let b:eelixir_subtype = 'elixir'
elseif b:eelixir_subtype == 'exs'
let b:eelixir_subtype = 'elixir'
elseif b:eelixir_subtype == 'yml'
let b:eelixir_subtype = 'yaml'
elseif b:eelixir_subtype == 'js'
let b:eelixir_subtype = 'javascript'
elseif b:eelixir_subtype == 'txt'
" Conventional; not a real file type
let b:eelixir_subtype = 'text'
elseif b:eelixir_subtype == ''
let b:eelixir_subtype = g:eelixir_default_subtype
endif
endif
if exists("b:eelixir_subtype") && b:eelixir_subtype != ''
exe "runtime! syntax/".b:eelixir_subtype.".vim"
unlet! b:current_syntax
endif
syn include @elixirTop syntax/elixir.vim
syn cluster eelixirRegions contains=eelixirBlock,surfaceExpression,eelixirExpression,eelixirComment
exe 'syn region eelixirExpression matchgroup=eelixirDelimiter start="<%" end="%\@<!%>" contains=@elixirTop containedin=ALLBUT,@eelixirRegions keepend'
exe 'syn region eelixirExpression matchgroup=eelixirDelimiter start="<%=" end="%\@<!%>" contains=@elixirTop containedin=ALLBUT,@eelixirRegions keepend'
exe 'syn region surfaceExpression matchgroup=surfaceDelimiter start="{{" end="}}" contains=@elixirTop containedin=ALLBUT,@eelixirRegions keepend'
exe 'syn region surfaceExpression matchgroup=surfaceDelimiter start="{" end="}" contains=@elixirTop containedin=ALLBUT,@eelixirRegions keepend'
exe 'syn region surfaceExpression matchgroup=surfaceDelimiter start="{" end="}" skip="#{[^}]*}" contains=@elixirTop containedin=htmlValue keepend'
exe 'syn region eelixirQuote matchgroup=eelixirDelimiter start="<%%" end="%\@<!%>" contains=@elixirTop containedin=ALLBUT,@eelixirRegions keepend'
exe 'syn region eelixirComment matchgroup=eelixirDelimiter start="<%#" end="%\@<!%>" contains=elixirTodo,@Spell containedin=ALLBUT,@eelixirRegions keepend'
" Define the default highlighting.
hi def link eelixirDelimiter PreProc
hi def link surfaceDelimiter PreProc
hi def link eelixirComment Comment
let b:current_syntax = 'eelixir'
if main_syntax == 'eelixir'
unlet main_syntax
endif
let &cpo = s:cpo_save
unlet s:cpo_save

View file

@ -0,0 +1,254 @@
if !exists("main_syntax")
if exists("b:current_syntax")
finish
endif
let main_syntax = "elixir"
endif
let s:cpo_save = &cpo
set cpo&vim
syn cluster elixirNotTop contains=@elixirRegexSpecial,@elixirStringContained,@elixirDeclaration,elixirTodo,elixirArguments,elixirBlockDefinition,elixirUnusedVariable,elixirStructDelimiter,elixirListDelimiter
syn cluster elixirRegexSpecial contains=elixirRegexEscape,elixirRegexCharClass,elixirRegexQuantifier,elixirRegexEscapePunctuation
syn cluster elixirStringContained contains=elixirInterpolation,elixirRegexEscape,elixirRegexCharClass
syn cluster elixirDeclaration contains=elixirFunctionDeclaration,elixirPrivateFunctionDeclaration,elixirModuleDeclaration,elixirProtocolDeclaration,elixirImplDeclaration,elixirRecordDeclaration,elixirPrivateRecordDeclaration,elixirMacroDeclaration,elixirPrivateMacroDeclaration,elixirDelegateDeclaration,elixirOverridableDeclaration,elixirExceptionDeclaration,elixirCallbackDeclaration,elixirStructDeclaration
syn match elixirComment '#.*' contains=elixirTodo,@Spell
syn keyword elixirTodo FIXME NOTE TODO OPTIMIZE XXX HACK contained
syn match elixirId '\<[_a-zA-Z]\w*[!?]\?\>' contains=elixirUnusedVariable
syn match elixirKeyword '\(\.\)\@<!\<\(for\|case\|when\|with\|cond\|if\|unless\|try\|receive\|after\|raise\|rescue\|catch\|else\|quote\|unquote\|super\|unquote_splicing\)\>:\@!'
syn keyword elixirInclude import require alias use
syn keyword elixirSelf self
syn match elixirUnusedVariable contained '\%(\.\)\@<!\<_\w*\>\%((\)\@!'
syn match elixirOperator '\v\.@<!<%(and|or|in|not)>'
syn match elixirOperator '!==\|!=\|!'
syn match elixirOperator '=\~\|===\|==\|='
syn match elixirOperator '<<<\|<<\|<=\|<-\|<'
syn match elixirOperator '>>>\|>>\|>=\|>'
syn match elixirOperator '->\|--\|-'
syn match elixirOperator '++\|+'
syn match elixirOperator '&&&\|&&\|&'
syn match elixirOperator '|||\|||\||>\||'
syn match elixirOperator '\.\.\|\.'
syn match elixirOperator "\^\^\^\|\^"
syn match elixirOperator '\\\\\|::\|\*\|/\|\~\~\~\|@'
syn match elixirOperator '\~>\|\~>>\|<\~\|<<\~\|<\~>'
syn match elixirAlias '\([a-z]\)\@<![A-Z]\w*\%(\.[A-Z]\w*\)*'
syn match elixirAtom '\(:\)\@<!:\%([a-zA-Z_*]\w*\%([?!]\|=[>=]\@!\)\?\|<>\|===\?\|>=\?\|<=\?\)'
syn match elixirAtom '\(:\)\@<!:\%(<=>\|&&\?\|%\(()\|\[\]\|{}\)\|++\?\|--\?\|||\?\|!\|//\|[%&`/|]\)'
syn match elixirAtom "\%([a-zA-Z_]\w*[?!]\?\):\(:\)\@!"
syn keyword elixirBoolean true false nil
syn match elixirVariable '@[a-z]\w*'
syn match elixirVariable '&\d\+'
syn keyword elixirPseudoVariable __FILE__ __DIR__ __MODULE__ __ENV__ __CALLER__ __STACKTRACE__
syn match elixirNumber '\<-\?\d\(_\?\d\)*\(\.[^[:space:][:digit:]]\@!\(_\?\d\)*\)\?\([eE][-+]\?\d\(_\?\d\)*\)\?\>'
syn match elixirNumber '\<-\?0[xX][0-9A-Fa-f]\+\>'
syn match elixirNumber '\<-\?0[oO][0-7]\+\>'
syn match elixirNumber '\<-\?0[bB][01]\+\>'
syn match elixirRegexEscape "\\\\\|\\[aAbBcdDefGhHnrsStvVwW]\|\\\d\{3}\|\\x[0-9a-fA-F]\{2}" contained
syn match elixirRegexEscapePunctuation "?\|\\.\|*\|\\\[\|\\\]\|+\|\\^\|\\\$\|\\|\|\\(\|\\)\|\\{\|\\}" contained
syn match elixirRegexQuantifier "[*?+][?+]\=" contained display
syn match elixirRegexQuantifier "{\d\+\%(,\d*\)\=}?\=" contained display
syn match elixirRegexCharClass "\[:\(alnum\|alpha\|ascii\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|word\|xdigit\):\]" contained display
syn region elixirRegex matchgroup=elixirRegexDelimiter start="%r/" end="/[uiomxfr]*" skip="\\\\" contains=@elixirRegexSpecial
syn region elixirTuple matchgroup=elixirTupleDelimiter start="\(\w\|#\)\@<!{" end="}" contains=ALLBUT,@elixirNotTop fold
syn match elixirListDelimiter '\[' contained containedin=elixirList
syn region elixirList matchgroup=elixirListDelimiter start='\[' end='\]' contains=ALLBUT,@elixirNotTop fold
syn match elixirStructDelimiter '{' contained containedin=elixirStruct
syn region elixirStruct matchgroup=elixirStructDelimiter start="%\(\w\+{\)\@=" end="}" contains=ALLBUT,@elixirNotTop fold
syn region elixirMap matchgroup=elixirMapDelimiter start="%{" end="}" contains=ALLBUT,@elixirNotTop fold
syn region elixirCharList matchgroup=elixirCharListDelimiter start=+\z('\)+ end=+\z1+ skip=+\\\\\|\\\z1+ contains=@Spell,@elixirStringContained
syn region elixirString matchgroup=elixirStringDelimiter start=+\z("\)+ end=+\z1+ skip=+\\\\\|\\\z1+ contains=@Spell,@elixirStringContained
syn region elixirString matchgroup=elixirStringDelimiter start=+\z('''\)+ end=+^\s*\z1+ contains=@Spell,@elixirStringContained
syn region elixirString matchgroup=elixirStringDelimiter start=+\z("""\)+ end=+^\s*\z1+ contains=@Spell,@elixirStringContained
syn region elixirInterpolation matchgroup=elixirInterpolationDelimiter start="#{" end="}" contained contains=ALLBUT,elixirComment,@elixirNotTop
syn match elixirAtomInterpolated ':\("\)\@=' contains=elixirString
syn match elixirString "\(\w\)\@<!?\%(\\\(x\d{1,2}\|\h{1,2}\h\@!\>\|0[0-7]{0,2}[0-7]\@!\>\|[^x0MC]\)\|(\\[MC]-)+\w\|[^\s\\]\)"
syn region elixirBlock matchgroup=elixirBlockDefinition start="\<do\>:\@!" end="\<end\>" contains=ALLBUT,@elixirNotTop fold
syn region elixirAnonymousFunction matchgroup=elixirBlockDefinition start="\<fn\>" end="\<end\>" contains=ALLBUT,@elixirNotTop fold
syn region elixirArguments start="(" end=")" contained contains=elixirOperator,elixirAtom,elixirPseudoVariable,elixirAlias,elixirBoolean,elixirVariable,elixirUnusedVariable,elixirNumber,elixirDocString,elixirAtomInterpolated,elixirRegex,elixirString,elixirStringDelimiter,elixirRegexDelimiter,elixirInterpolationDelimiter,elixirSigil,elixirAnonymousFunction,elixirComment,elixirCharList,elixirCharListDelimiter
syn match elixirDelimEscape "\\[(<{\[)>}\]/\"'|]" transparent display contained contains=NONE
syn region elixirSigil matchgroup=elixirSigilDelimiter start="\~\u\z(/\|\"\|'\||\)" end="\z1" skip="\\\\\|\\\z1" contains=elixirDelimEscape fold
syn region elixirSigil matchgroup=elixirSigilDelimiter start="\~\u{" end="}" skip="\\\\\|\\}" contains=elixirDelimEscape fold
syn region elixirSigil matchgroup=elixirSigilDelimiter start="\~\u<" end=">" skip="\\\\\|\\>" contains=elixirDelimEscape fold
syn region elixirSigil matchgroup=elixirSigilDelimiter start="\~\u\[" end="\]" skip="\\\\\|\\\]" contains=elixirDelimEscape fold
syn region elixirSigil matchgroup=elixirSigilDelimiter start="\~\u(" end=")" skip="\\\\\|\\)" contains=elixirDelimEscape fold
syn region elixirSigil matchgroup=elixirSigilDelimiter start="\~\l\z(/\|\"\|'\||\)" end="\z1" skip="\\\\\|\\\z1" fold
syn region elixirSigil matchgroup=elixirSigilDelimiter start="\~\l{" end="}" skip="\\\\\|\\}" contains=@elixirStringContained,elixirRegexEscapePunctuation fold
syn region elixirSigil matchgroup=elixirSigilDelimiter start="\~\l<" end=">" skip="\\\\\|\\>" contains=@elixirStringContained,elixirRegexEscapePunctuation fold
syn region elixirSigil matchgroup=elixirSigilDelimiter start="\~\l\[" end="\]" skip="\\\\\|\\\]" contains=@elixirStringContained,elixirRegexEscapePunctuation fold
syn region elixirSigil matchgroup=elixirSigilDelimiter start="\~\l(" end=")" skip="\\\\\|\\)" contains=@elixirStringContained,elixirRegexEscapePunctuation fold
syn region elixirSigil matchgroup=elixirSigilDelimiter start="\~\l\/" end="\/" skip="\\\\\|\\\/" contains=@elixirStringContained,elixirRegexEscapePunctuation fold
" Sigils surrounded with heredoc
syn region elixirSigil matchgroup=elixirSigilDelimiter start=+\~\a\z("""\)+ end=+^\s*\z1+ skip=+\\"+ fold
syn region elixirSigil matchgroup=elixirSigilDelimiter start=+\~\a\z('''\)+ end=+^\s*\z1+ skip=+\\'+ fold
" LiveView Sigils surrounded with ~L"""
syntax include @HTML syntax/html.vim
unlet b:current_syntax
syntax region elixirLiveViewSigil matchgroup=elixirSigilDelimiter keepend start=+\~L\z("""\)+ end=+^\s*\z1+ skip=+\\"+ contains=@HTML fold
syntax region elixirSurfaceSigil matchgroup=elixirSigilDelimiter keepend start=+\~H\z("""\)+ end=+^\s*\z1+ skip=+\\"+ contains=@HTML fold
syntax region elixirSurfaceSigil matchgroup=elixirSigilDelimiter keepend start=+\~F\z("""\)+ end=+^\s*\z1+ skip=+\\"+ contains=@HTML fold
syntax region elixirPhoenixESigil matchgroup=elixirSigilDelimiter keepend start=+\~E\z("""\)+ end=+^\s*\z1+ skip=+\\"+ contains=@HTML fold
syntax region elixirPhoenixeSigil matchgroup=elixirSigilDelimiter keepend start=+\~e\z("""\)+ end=+^\s*\z1+ skip=+\\"+ contains=@HTML fold
" Documentation
if exists('g:elixir_use_markdown_for_docs') && g:elixir_use_markdown_for_docs
syn include @markdown syntax/markdown.vim
unlet b:current_syntax
syn cluster elixirDocStringContained contains=@markdown,@Spell,elixirInterpolation
else
let g:elixir_use_markdown_for_docs = 0
syn cluster elixirDocStringContained contains=elixirDocTest,elixirTodo,@Spell,elixirInterpolation
" doctests
syn region elixirDocTest start="^\s*\%(iex\|\.\.\.\)\%((\d*)\)\?>\s" end="^\s*$" contained
endif
syn region elixirDocString matchgroup=elixirDocSigilDelimiter start="\%(@\w*doc\(\s\|(\)\+\)\@<=\~[Ss]\z(/\|\"\|'\||\)" end="\z1" skip="\\\\\|\\\z1" contains=@elixirDocStringContained fold keepend
syn region elixirDocString matchgroup=elixirDocSigilDelimiter start="\%(@\w*doc\(\s\|(\)\+\)\@<=\~[Ss]{" end="}" skip="\\\\\|\\}" contains=@elixirDocStringContained fold keepend
syn region elixirDocString matchgroup=elixirDocSigilDelimiter start="\%(@\w*doc\(\s\|(\)\+\)\@<=\~[Ss]<" end=">" skip="\\\\\|\\>" contains=@elixirDocStringContained fold keepend
syn region elixirDocString matchgroup=elixirDocSigilDelimiter start="\%(@\w*doc\(\s\|(\)\+\)\@<=\~[Ss]\[" end="\]" skip="\\\\\|\\\]" contains=@elixirDocStringContained fold keepend
syn region elixirDocString matchgroup=elixirDocSigilDelimiter start="\%(@\w*doc\(\s\|(\)\+\)\@<=\~[Ss](" end=")" skip="\\\\\|\\)" contains=@elixirDocStringContained fold keepend
syn region elixirDocString matchgroup=elixirDocStringDelimiter start=+\%(@\w*doc\(\s\|(\)\+\)\@<=\z("\)+ end=+\z1+ skip=+\\\\\|\\\z1+ contains=@elixirDocStringContained keepend
syn region elixirDocString matchgroup=elixirDocStringDelimiter start=+\%(@\w*doc\(\s\|(\)\+\)\@<=\z("""\)+ end=+^\s*\z1+ contains=@elixirDocStringContained fold keepend
syn region elixirDocString matchgroup=elixirDocSigilDelimiter start=+\%(@\w*doc\(\s\|(\)\+\)\@<=\~[Ss]\z('''\)+ end=+^\s*\z1+ contains=@elixirDocStringContained fold keepend
syn region elixirDocString matchgroup=elixirDocSigilDelimiter start=+\%(@\w*doc\(\s\|(\)\+\)\@<=\~[Ss]\z("""\)+ end=+^\s*\z1+ contains=@elixirDocStringContained fold keepend
" Defines
syn match elixirDefine '\<def\>\(:\)\@!' nextgroup=elixirFunctionDeclaration skipwhite skipnl
syn match elixirPrivateDefine '\<defp\>\(:\)\@!' nextgroup=elixirPrivateFunctionDeclaration skipwhite skipnl
syn match elixirNumericalDefine '\<defn\>\(:\)\@!' nextgroup=elixirFunctionDeclaration skipwhite skipnl
syn match elixirGuard '\<defguard\>\(:\)\@!' nextgroup=elixirFunctionDeclaration skipwhite skipnl
syn match elixirPrivateGuard '\<defguardp\>\(:\)\@!' nextgroup=elixirPrivateFunctionDeclaration skipwhite skipnl
syn match elixirModuleDefine '\<defmodule\>\(:\)\@!' nextgroup=elixirModuleDeclaration skipwhite skipnl
syn match elixirProtocolDefine '\<defprotocol\>\(:\)\@!' nextgroup=elixirProtocolDeclaration skipwhite skipnl
syn match elixirImplDefine '\<defimpl\>\(:\)\@!' nextgroup=elixirImplDeclaration skipwhite skipnl
syn match elixirRecordDefine '\<defrecord\>\(:\)\@!' nextgroup=elixirRecordDeclaration skipwhite skipnl
syn match elixirPrivateRecordDefine '\<defrecordp\>\(:\)\@!' nextgroup=elixirPrivateRecordDeclaration skipwhite skipnl
syn match elixirMacroDefine '\<defmacro\>\(:\)\@!' nextgroup=elixirMacroDeclaration skipwhite skipnl
syn match elixirPrivateMacroDefine '\<defmacrop\>\(:\)\@!' nextgroup=elixirPrivateMacroDeclaration skipwhite skipnl
syn match elixirDelegateDefine '\<defdelegate\>\(:\)\@!' nextgroup=elixirDelegateDeclaration skipwhite skipnl
syn match elixirOverridableDefine '\<defoverridable\>\(:\)\@!' nextgroup=elixirOverridableDeclaration skipwhite skipnl
syn match elixirExceptionDefine '\<defexception\>\(:\)\@!' nextgroup=elixirExceptionDeclaration skipwhite skipnl
syn match elixirCallbackDefine '\<defcallback\>\(:\)\@!' nextgroup=elixirCallbackDeclaration skipwhite skipnl
syn match elixirStructDefine '\<defstruct\>\(:\)\@!' skipwhite skipnl
" Declarations
syn match elixirModuleDeclaration "[^[:space:];#<,()\[\]]\+" contained nextgroup=elixirBlock skipwhite skipnl
syn match elixirFunctionDeclaration "[^[:space:];#<,()\[\]]\+" contained nextgroup=elixirArguments skipwhite skipnl
syn match elixirPrivateFunctionDeclaration "[^[:space:];#<,()\[\]]\+" contained nextgroup=elixirArguments skipwhite skipnl
syn match elixirProtocolDeclaration "[^[:space:];#<]\+" contained contains=elixirAlias skipwhite skipnl
syn match elixirImplDeclaration "[^[:space:];#<]\+" contained contains=elixirAlias skipwhite skipnl
syn match elixirRecordDeclaration "[^[:space:];#<]\+" contained contains=elixirAlias,elixirAtom skipwhite skipnl
syn match elixirPrivateRecordDeclaration "[^[:space:];#<]\+" contained contains=elixirAlias,elixirAtom skipwhite skipnl
syn match elixirMacroDeclaration "[^[:space:];#<,()\[\]]\+" contained nextgroup=elixirArguments skipwhite skipnl
syn match elixirPrivateMacroDeclaration "[^[:space:];#<,()\[\]]\+" contained nextgroup=elixirArguments skipwhite skipnl
syn match elixirDelegateDeclaration "[^[:space:];#<,()\[\]]\+" contained contains=elixirFunctionDeclaration skipwhite skipnl
syn region elixirDelegateDeclaration start='\[' end='\]' contained contains=elixirFunctionDeclaration skipwhite skipnl
syn match elixirOverridableDeclaration "[^[:space:];#<]\+" contained contains=elixirAlias,elixirAtom skipwhite skipnl
syn match elixirExceptionDeclaration "[^[:space:];#<]\+" contained contains=elixirAlias,elixirAtom skipwhite skipnl
syn match elixirCallbackDeclaration "[^[:space:];#<,()\[\]]\+" contained contains=elixirFunctionDeclaration skipwhite skipnl
" ExUnit
syn match elixirExUnitMacro "\C\(^\s*\)\@<=\<\(test\|describe\|setup\|setup_all\|on_exit\|doctest\)\>"
syn match elixirExUnitAssert "\C\(^\s*\)\@<=\<\(assert\|assert_in_delta\|assert_raise\|assert_receive\|assert_received\|catch_error\)\>"
syn match elixirExUnitAssert "\C\(^\s*\)\@<=\<\(catch_exit\|catch_throw\|flunk\|refute\|refute_in_delta\|refute_receive\|refute_received\)\>"
" syncing starts 2000 lines before top line so docstrings don't screw things up
syn sync minlines=2000
hi def link elixirBlockDefinition Define
hi def link elixirDefine Define
hi def link elixirPrivateDefine Define
hi def link elixirNumericalDefine Define
hi def link elixirGuard Define
hi def link elixirPrivateGuard Define
hi def link elixirModuleDefine Define
hi def link elixirProtocolDefine Define
hi def link elixirImplDefine Define
hi def link elixirRecordDefine Define
hi def link elixirPrivateRecordDefine Define
hi def link elixirMacroDefine Define
hi def link elixirPrivateMacroDefine Define
hi def link elixirDelegateDefine Define
hi def link elixirOverridableDefine Define
hi def link elixirExceptionDefine Define
hi def link elixirCallbackDefine Define
hi def link elixirStructDefine Define
hi def link elixirExUnitMacro Define
hi def link elixirModuleDeclaration Type
hi def link elixirPrivateFunctionDeclaration elixirFunctionDeclaration
hi def link elixirFunctionDeclaration Function
hi def link elixirPrivateMacroDeclaration elixirMacroDeclaration
hi def link elixirMacroDeclaration Macro
hi def link elixirInclude Include
hi def link elixirComment Comment
hi def link elixirTodo Todo
hi def link elixirKeyword Define
hi def link elixirExUnitAssert Keyword
hi def link elixirOperator Operator
hi def link elixirAtom Constant
hi def link elixirPseudoVariable Constant
hi def link elixirAlias Type
hi def link elixirBoolean Boolean
hi def link elixirVariable Identifier
hi def link elixirSelf Identifier
hi def link elixirUnusedVariable Comment
hi def link elixirNumber Number
hi def link elixirDocString Comment
hi def link elixirDocTest elixirKeyword
hi def link elixirAtomInterpolated elixirAtom
hi def link elixirRegex elixirString
hi def link elixirRegexEscape elixirSpecial
hi def link elixirRegexEscapePunctuation elixirSpecial
hi def link elixirRegexCharClass elixirSpecial
hi def link elixirRegexQuantifier elixirSpecial
hi def link elixirSpecial Special
hi def link elixirString String
hi def link elixirCharList String
hi def link elixirSigil String
hi def link elixirDocStringDelimiter elixirStringDelimiter
hi def link elixirDocSigilDelimiter elixirSigilDelimiter
hi def link elixirStringDelimiter Delimiter
hi def link elixirCharListDelimiter Delimiter
hi def link elixirRegexDelimiter Delimiter
hi def link elixirInterpolationDelimiter Delimiter
hi def link elixirSigilDelimiter Delimiter
hi def link elixirPrivateRecordDeclaration elixirRecordDeclaration
let b:current_syntax = "elixir"
if main_syntax == "elixir"
unlet main_syntax
endif
let &cpo = s:cpo_save
unlet s:cpo_save

View file

@ -0,0 +1,16 @@
set runtimepath+=/root/vim-elixir
runtime ftdetect/elixir.vim
filetype plugin indent on
set ruler
set hidden
let g:elixir_indent_debug=1
let mapleader=","
map <leader>syn :echo "hi<" . synIDattr(synID(line("."),col("."),1),"name") . '> trans<'
\ . synIDattr(synID(line("."),col("."),0),"name") . "> lo<"
\ . synIDattr(synIDtrans(synID(line("."),col("."),1)),"name") . ">"<CR>

View file

@ -0,0 +1,16 @@
set runtimepath+=/root/vim-elixir
runtime ftdetect/elixir.vim
filetype plugin indent on
set ruler
set hidden
let g:elixir_indent_debug=1
let mapleader=","
map <leader>syn :echo "hi<" . synIDattr(synID(line("."),col("."),1),"name") . '> trans<'
\ . synIDattr(synID(line("."),col("."),0),"name") . "> lo<"
\ . synIDattr(synIDtrans(synID(line("."),col("."),1)),"name") . ">"<CR>