diff --git a/sources_non_forked/vim-elixir/.gitignore b/sources_non_forked/vim-elixir/.gitignore new file mode 100644 index 00000000..1e38fa75 --- /dev/null +++ b/sources_non_forked/vim-elixir/.gitignore @@ -0,0 +1,4 @@ +profile.log +test_indent.result +doc/tags +.gvim_path diff --git a/sources_non_forked/vim-elixir/.travis.yml b/sources_non_forked/vim-elixir/.travis.yml new file mode 100644 index 00000000..a289f07c --- /dev/null +++ b/sources_non_forked/vim-elixir/.travis.yml @@ -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" diff --git a/sources_non_forked/vim-elixir/Dockerfile b/sources_non_forked/vim-elixir/Dockerfile new file mode 100644 index 00000000..dbec90e9 --- /dev/null +++ b/sources_non_forked/vim-elixir/Dockerfile @@ -0,0 +1,5 @@ +FROM ubuntu:latest + +RUN apt-get update && apt-get install -yf vim + +COPY test.vimrc /root/.vimrc diff --git a/sources_non_forked/vim-elixir/Dockerfile.nvim b/sources_non_forked/vim-elixir/Dockerfile.nvim new file mode 100644 index 00000000..4920a48e --- /dev/null +++ b/sources_non_forked/vim-elixir/Dockerfile.nvim @@ -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 diff --git a/sources_non_forked/vim-elixir/Gemfile b/sources_non_forked/vim-elixir/Gemfile new file mode 100644 index 00000000..3c049b84 --- /dev/null +++ b/sources_non_forked/vim-elixir/Gemfile @@ -0,0 +1,7 @@ +source 'https://rubygems.org' + +gem 'rspec' +gem 'vimrunner' +gem 'pry' +gem 'diffy' +gem 'parallel_tests' diff --git a/sources_non_forked/vim-elixir/Gemfile.lock b/sources_non_forked/vim-elixir/Gemfile.lock new file mode 100644 index 00000000..5d4333c1 --- /dev/null +++ b/sources_non_forked/vim-elixir/Gemfile.lock @@ -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 diff --git a/sources_non_forked/vim-elixir/ISSUE_TEMPLATE.md b/sources_non_forked/vim-elixir/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..2727ccd8 --- /dev/null +++ b/sources_non_forked/vim-elixir/ISSUE_TEMPLATE.md @@ -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 +``` diff --git a/sources_non_forked/vim-elixir/LICENSE b/sources_non_forked/vim-elixir/LICENSE new file mode 100644 index 00000000..75cc8980 --- /dev/null +++ b/sources_non_forked/vim-elixir/LICENSE @@ -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. diff --git a/sources_non_forked/vim-elixir/README.md b/sources_non_forked/vim-elixir/README.md new file mode 100644 index 00000000..c964692a --- /dev/null +++ b/sources_non_forked/vim-elixir/README.md @@ -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 diff --git a/sources_non_forked/vim-elixir/autoload/db/adapter/ecto.vim b/sources_non_forked/vim-elixir/autoload/db/adapter/ecto.vim new file mode 100644 index 00000000..9eb36239 --- /dev/null +++ b/sources_non_forked/vim-elixir/autoload/db/adapter/ecto.vim @@ -0,0 +1,20 @@ +let s:path = expand(':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 diff --git a/sources_non_forked/vim-elixir/autoload/db/adapter/get_repos.exs b/sources_non_forked/vim-elixir/autoload/db/adapter/get_repos.exs new file mode 100644 index 00000000..906b0e69 --- /dev/null +++ b/sources_non_forked/vim-elixir/autoload/db/adapter/get_repos.exs @@ -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() diff --git a/sources_non_forked/vim-elixir/autoload/elixir/indent.vim b/sources_non_forked/vim-elixir/autoload/elixir/indent.vim new file mode 100644 index 00000000..66114773 --- /dev/null +++ b/sources_non_forked/vim-elixir/autoload/elixir/indent.vim @@ -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 ':\@:\@!' +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\%(\\|\\|\\|\\|\\|\\|\\|{\|\[\|(\)' + let end_pattern = '\C\%(\\|\]\|}\|)\)' + " 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\%(\\|\\|\\)', '', 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 diff --git a/sources_non_forked/vim-elixir/autoload/elixir/util.vim b/sources_non_forked/vim-elixir/autoload/elixir/util.vim new file mode 100644 index 00000000..927f92bf --- /dev/null +++ b/sources_non_forked/vim-elixir/autoload/elixir/util.vim @@ -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 diff --git a/sources_non_forked/vim-elixir/bin/nvim b/sources_non_forked/vim-elixir/bin/nvim new file mode 100755 index 00000000..c2655449 --- /dev/null +++ b/sources_non_forked/vim-elixir/bin/nvim @@ -0,0 +1,3 @@ +#! /usr/bin/env sh +docker-compose build +docker-compose run nvim nvim $1 diff --git a/sources_non_forked/vim-elixir/bin/profile b/sources_non_forked/vim-elixir/bin/profile new file mode 100755 index 00000000..63b1939d --- /dev/null +++ b/sources_non_forked/vim-elixir/bin/profile @@ -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' +vim.edit! 'large_file.ex' +# remove all indentation +vim.normal 'ggVG999<<' +vim.normal ':profile start profile.log' +vim.normal ':profile func *' +vim.normal ':profile file *' + +t1 = Time.now +# force vim to indent the file +vim.normal 'gg=G' +vim.normal ':profile pause' +vim.normal ':q!' + +Process.wait vim.server.pid +t2 = Time.now + +puts "Took #{t2-t1} seconds to indent large_file.ex. Profile logged to profile.log" diff --git a/sources_non_forked/vim-elixir/bin/projects_to_test.txt b/sources_non_forked/vim-elixir/bin/projects_to_test.txt new file mode 100644 index 00000000..1e43e1cf --- /dev/null +++ b/sources_non_forked/vim-elixir/bin/projects_to_test.txt @@ -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 diff --git a/sources_non_forked/vim-elixir/bin/rspec b/sources_non_forked/vim-elixir/bin/rspec new file mode 100755 index 00000000..d738b23c --- /dev/null +++ b/sources_non_forked/vim-elixir/bin/rspec @@ -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") diff --git a/sources_non_forked/vim-elixir/bin/test_directory b/sources_non_forked/vim-elixir/bin/test_directory new file mode 100755 index 00000000..38c99efb --- /dev/null +++ b/sources_non_forked/vim-elixir/bin/test_directory @@ -0,0 +1,3 @@ +#! /usr/bin/env sh +dirs=`find $1 -iname '*.ex' -o -iname '*.exs' | grep -v '/deps/'` +bin/test_indent $dirs diff --git a/sources_non_forked/vim-elixir/bin/test_for_regressions b/sources_non_forked/vim-elixir/bin/test_for_regressions new file mode 100755 index 00000000..68bdd45c --- /dev/null +++ b/sources_non_forked/vim-elixir/bin/test_for_regressions @@ -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' diff --git a/sources_non_forked/vim-elixir/bin/test_indent b/sources_non_forked/vim-elixir/bin/test_indent new file mode 100755 index 00000000..78513abc --- /dev/null +++ b/sources_non_forked/vim-elixir/bin/test_indent @@ -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' + + vim.normal ':q!' + 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' diff --git a/sources_non_forked/vim-elixir/bin/vim b/sources_non_forked/vim-elixir/bin/vim new file mode 100755 index 00000000..58d2cff5 --- /dev/null +++ b/sources_non_forked/vim-elixir/bin/vim @@ -0,0 +1,3 @@ +#! /usr/bin/env sh +docker-compose build +docker-compose run vim diff --git a/sources_non_forked/vim-elixir/compiler/credo.vim b/sources_non_forked/vim-elixir/compiler/credo.vim new file mode 100644 index 00000000..b2b7b18e --- /dev/null +++ b/sources_non_forked/vim-elixir/compiler/credo.vim @@ -0,0 +1,11 @@ +if exists('current_compiler') + finish +endif +let current_compiler = 'credo' + +if exists(":CompilerSet") != 2 + command -nargs=* CompilerSet setlocal +endif + +CompilerSet errorformat=%f:%l:%c:\ %t:\ %m,%f:%l:\ %t:\ %m +CompilerSet makeprg=mix\ credo\ suggest\ --format=flycheck diff --git a/sources_non_forked/vim-elixir/compiler/exunit.vim b/sources_non_forked/vim-elixir/compiler/exunit.vim new file mode 100644 index 00000000..197fb6be --- /dev/null +++ b/sources_non_forked/vim-elixir/compiler/exunit.vim @@ -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 +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: diff --git a/sources_non_forked/vim-elixir/compiler/mix.vim b/sources_non_forked/vim-elixir/compiler/mix.vim new file mode 100644 index 00000000..14b81db0 --- /dev/null +++ b/sources_non_forked/vim-elixir/compiler/mix.vim @@ -0,0 +1,16 @@ +if exists('current_compiler') + finish +endif +let current_compiler = 'mix' + +if exists(":CompilerSet") != 2 + command -nargs=* CompilerSet setlocal +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 + diff --git a/sources_non_forked/vim-elixir/doc/elixir.txt b/sources_non_forked/vim-elixir/doc/elixir.txt new file mode 100644 index 00000000..273b494f --- /dev/null +++ b/sources_non_forked/vim-elixir/doc/elixir.txt @@ -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 diff --git a/sources_non_forked/vim-elixir/docker-compose.yml b/sources_non_forked/vim-elixir/docker-compose.yml new file mode 100644 index 00000000..17c4b942 --- /dev/null +++ b/sources_non_forked/vim-elixir/docker-compose.yml @@ -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 diff --git a/sources_non_forked/vim-elixir/ftdetect/elixir.vim b/sources_non_forked/vim-elixir/ftdetect/elixir.vim new file mode 100644 index 00000000..7d00e580 --- /dev/null +++ b/sources_non_forked/vim-elixir/ftdetect/elixir.vim @@ -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) =~# '^#!.*\' + set filetype=elixir + endif +endfunction diff --git a/sources_non_forked/vim-elixir/ftplugin/eelixir.vim b/sources_non_forked/vim-elixir/ftplugin/eelixir.vim new file mode 100644 index 00000000..eebfb69a --- /dev/null +++ b/sources_non_forked/vim-elixir/ftplugin/eelixir.vim @@ -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 `` (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 diff --git a/sources_non_forked/vim-elixir/ftplugin/elixir.vim b/sources_non_forked/vim-elixir/ftplugin/elixir.vim new file mode 100644 index 00000000..1b95811c --- /dev/null +++ b/sources_non_forked/vim-elixir/ftplugin/elixir.vim @@ -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 = '\:\@' . + \ ':' . + \ '\<\%(else\|elsif\|catch\|after\|rescue\)\:\@!\>' . + \ ':' . + \ '\:\@' . + \ ',{:},\[:\],(:)' +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 = '\' + +nnoremap ]] ':silent keeppatterns /'.b:block_begin.'' +nnoremap [[ ':silent keeppatterns ?'.b:block_begin.'' +nnoremap ][ ':silent keeppatterns /'.b:block_end .'' +nnoremap [] ':silent keeppatterns ?'.b:block_end .'' + +onoremap ]] ':silent keeppatterns /'.b:block_begin.'' +onoremap [[ ':silent keeppatterns ?'.b:block_begin.'' +onoremap ][ ':silent keeppatterns /'.b:block_end .'' +onoremap [] ':silent keeppatterns ?'.b:block_end .'' + +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' diff --git a/sources_non_forked/vim-elixir/indent/eelixir.vim b/sources_non_forked/vim-elixir/indent/eelixir.vim new file mode 100644 index 00000000..24088efc --- /dev/null +++ b/sources_non_forked/vim-elixir/indent/eelixir.vim @@ -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,*,<>>,{,},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*.*\(\\)\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 diff --git a/sources_non_forked/vim-elixir/indent/elixir.vim b/sources_non_forked/vim-elixir/indent/elixir.vim new file mode 100644 index 00000000..13c97e3c --- /dev/null +++ b/sources_non_forked/vim-elixir/indent/elixir.vim @@ -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+=*,=->,=\|>,=<>,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 diff --git a/sources_non_forked/vim-elixir/large_file.ex b/sources_non_forked/vim-elixir/large_file.ex new file mode 100644 index 00000000..273d6047 --- /dev/null +++ b/sources_non_forked/vim-elixir/large_file.ex @@ -0,0 +1,1000 @@ +defmodule GenStage do + @moduledoc ~S""" + Stages are data-exchange steps that send and/or receive data + from other stages. + + When a stage sends data, it acts as a producer. When it receives + data, it acts as a consumer. Stages may take both producer and + consumer roles at once. + + ## Stage types + + Besides taking both producer and consumer roles, a stage may be + called "source" if it only produces items or called "sink" if it + only consumes items. + + For example, imagine the stages below where A sends data to B + that sends data to C: + + [A] -> [B] -> [C] + + we conclude that: + + * A is only a producer (and therefore a source) + * B is both producer and consumer + * C is only a consumer (and therefore a sink) + + As we will see in the upcoming Examples section, we must + specify the type of the stage when we implement each of them. + + To start the flow of events, we subscribe consumers to + producers. Once the communication channel between them is + established, consumers will ask the producers for events. + We typically say the consumer is sending demand upstream. + Once demand arrives, the producer will emit items, never + emitting more items than the consumer asked for. This provides + a back-pressure mechanism. + + A consumer may have multiple producers and a producer may have + multiple consumers. When a consumer asks for data, each producer + is handled separately, with its own demand. When a producer + receives demand and sends data to multiple consumers, the demand + is tracked and the events are sent by a dispatcher. This allows + producers to send data using different "strategies". See + `GenStage.Dispatcher` for more information. + + Many developers tend to create layers of stages, such as A, B and + C, for achieving concurrency. If all you want is concurrency, using + processes is enough. They are the primitive for achieving concurrency + in Elixir and the VM does all of the work of multiplexing them. + Instead, layers in GenStage must be created when there is a need for + back-pressure or to route the data in different ways. + + For example, if you need the data to go over multiple steps but + without a need for back-pressure or without a need to break the + data apart, do not design it as such: + + [Producer] -> [Step 1] -> [Step 2] -> [Step 3] + + Instead it is better to design it as: + + [Consumer] + / + [Producer]-<-[Consumer] + \ + [Consumer] + + where "Consumer" are multiple processes that subscribe to the same + "Producer" and run exactly the same code, with all of transformation + steps from above. In such scenarios, you may even find the + `Task.async_stream/2` function that ships as part of Elixir to be + enough or achieve the flexibility you need with the `ConsumerSupervisor` + functionality that is included as part of `GenStage`. + + ## Example + + Let's define the simple pipeline below: + + [A] -> [B] -> [C] + + where A is a producer that will emit items starting from 0, + B is a producer-consumer that will receive those items and + multiply them by a given number and C will receive those events + and print them to the terminal. + + Let's start with A. Since A is a producer, its main + responsibility is to receive demand and generate events. + Those events may be in memory or an external queue system. + For simplicity, let's implement a simple counter starting + from a given value of `counter` received on `init/1`: + + defmodule A do + use GenStage + + def start_link(number) do + GenStage.start_link(A, number) + end + + def init(counter) do + {:producer, counter} + end + + def handle_demand(demand, counter) when demand > 0 do + # If the counter is 3 and we ask for 2 items, we will + # emit the items 3 and 4, and set the state to 5. + events = Enum.to_list(counter..counter+demand-1) + {:noreply, events, counter + demand} + end + end + + B is a producer-consumer. This means it does not explicitly + handle the demand because the demand is always forwarded to + its producer. Once A receives the demand from B, it will send + events to B which will be transformed by B as desired. In + our case, B will receive events and multiply them by a number + given on initialization and stored as the state: + + defmodule B do + use GenStage + + def start_link(number) do + GenStage.start_link(B, number) + end + + def init(number) do + {:producer_consumer, number} + end + + def handle_events(events, _from, number) do + events = Enum.map(events, & &1 * number) + {:noreply, events, number} + end + end + + C will finally receive those events and print them every second + to the terminal: + + defmodule C do + use GenStage + + def start_link() do + GenStage.start_link(C, :ok) + end + + def init(:ok) do + {:consumer, :the_state_does_not_matter} + end + + def handle_events(events, _from, state) do + # Wait for a second. + Process.sleep(1000) + + # Inspect the events. + IO.inspect(events) + + # We are a consumer, so we would never emit items. + {:noreply, [], state} + end + end + + Now we can start and connect them: + + {:ok, a} = A.start_link(0) # starting from zero + {:ok, b} = B.start_link(2) # multiply by 2 + {:ok, c} = C.start_link() # state does not matter + + GenStage.sync_subscribe(c, to: b) + GenStage.sync_subscribe(b, to: a) + + Typically, we subscribe from bottom to top. Since A will + start producing items only when B connects to it, we want this + subscription to happen when the whole pipeline is ready. After + you subscribe all of them, demand will start flowing upstream and + events downstream. + + When implementing consumers, we often set the `:max_demand` and + `:min_demand` on subscription. The `:max_demand` specifies the + maximum amount of events that must be in flow while the `:min_demand` + specifies the minimum threshold to trigger for more demand. For + example, if `:max_demand` is 1000 and `:min_demand` is 750, + the consumer will ask for 1000 events initially and ask for more + only after it receives at least 250. + + In the example above, B is a `:producer_consumer` and therefore + acts as a buffer. Getting the proper demand values in B is + important: making the buffer too small may make the whole pipeline + slower, making the buffer too big may unnecessarily consume + memory. + + When such values are applied to the stages above, it is easy + to see the producer works in batches. The producer A ends-up + emitting batches of 50 items which will take approximately + 50 seconds to be consumed by C, which will then request another + batch of 50 items. + + ## `init` and `:subscribe_to` + + In the example above, we have started the processes A, B, and C + independently and subscribed them later on. But most often it is + simpler to subscribe a consumer to its producer on its `c:init/1` + callback. This way, if the consumer crashes, restarting the consumer + will automatically re-invoke its `c:init/1` callback and resubscribe + it to the supervisor. + + This approach works as long as the producer can be referenced when + the consumer starts--such as by name (for a named process) or by pid + for a running unnamed process. For example, assuming the process + `A` and `B` are started as follows: + + # Let's call the stage in module A as A + GenStage.start_link(A, 0, name: A) + # Let's call the stage in module B as B + GenStage.start_link(B, 2, name: B) + # No need to name consumers as they won't be subscribed to + GenStage.start_link(C, :ok) + + We can now change the `c:init/1` callback for C to the following: + + def init(:ok) do + {:consumer, :the_state_does_not_matter, subscribe_to: [B]} + end + + or: + + def init(:ok) do + {:consumer, :the_state_does_not_matter, subscribe_to: [{B, options}]} + end + + And we will no longer need to call `sync_subscribe/2`. + + Another advantage of this approach is that it makes it straight-forward + to leverage concurrency by simply starting multiple consumers that subscribe + to their producer (or producer-consumer). This can be done in the example above + by simply calling start link multiple times: + + # Start 4 consumers + GenStage.start_link(C, :ok) + GenStage.start_link(C, :ok) + GenStage.start_link(C, :ok) + GenStage.start_link(C, :ok) + + In a supervision tree, this is often done by starting multiple workers: + + children = [ + worker(A, [0]), + worker(B, [2]), + worker(C, []), + worker(C, []), + worker(C, []), + worker(C, []) + ] + + Supervisor.start_link(children, strategy: :one_for_one) + + In fact, having multiple consumers is often the easiest and simplest way to + leverage concurrency in a GenStage pipeline, especially if events can + be processed out of order. For example, imagine a scenario where you + have a stream of incoming events and you need to access a number of + external services per event. Instead of building complex stages that + route events through those services, one simple mechanism to leverage + concurrency is to start a producer and N consumers and invoke the external + services directly for each event in each consumer. N is typically the + number of cores (as returned by `System.schedulers_online/0`) but can + likely be increased if the consumers are mostly waiting on IO. + + Another alternative to the scenario above is to use a `ConsumerSupervisor` + for consuming the events instead of N consumers. The `ConsumerSupervisor` + will start a separate supervised process per event where the number of children + is at most `max_demand` and the average amount of children is + `(max_demand - min_demand) / 2`. + + ## Buffering + + In many situations, producers may attempt to emit events while no consumers + have yet subscribed. Similarly, consumers may ask producers for events + that are not yet available. In such cases, it is necessary for producers + to buffer events until a consumer is available or buffer the consumer + demand until events arrive, respectively. As we will see next, buffering + events can be done automatically by `GenStage`, while buffering the demand + is a case that must be explicitly considered by developers implementing + producers. + + ### Buffering events + + Due to the concurrent nature of Elixir software, sometimes a producer + may dispatch events without consumers to send those events to. For example, + imagine a `:consumer` B subscribes to `:producer` A. Next, the consumer B + sends demand to A, which starts producing events to satisfy the demand. + Now, if the consumer B crashes, the producer may attempt to dispatch the + now produced events but it no longer has a consumer to send those events to. + In such cases, the producer will automatically buffer the events until another + consumer subscribes. + + The buffer can also be used in cases where external sources only send + events in batches larger than asked for. For example, if you are + receiving events from an external source that only sends events + in batches of 1000 and the internal demand is smaller than + that, the buffer allows you to always emit batches of 1000 events + even when the consumer has asked for less. + + In all of those cases when an event cannot be sent immediately by + a producer, the event will be automatically stored and sent the next + time consumers ask for events. The size of the buffer is configured + via the `:buffer_size` option returned by `init/1` and the default + value is `10_000`. If the `buffer_size` is exceeded, an error is logged. + See the documentation for `c:init/1` for more detailed infromation about + the `:buffer_size` option. + + ### Buffering demand + + In case consumers send demand and the producer is not yet ready to + fill in the demand, producers must buffer the demand until data arrives. + + As an example, let's implement a producer that broadcasts messages + to consumers. For producers, we need to consider two scenarios: + + 1. what if events arrive and there are no consumers? + 2. what if consumers send demand and there are not enough events? + + One way to implement such a broadcaster is to simply rely on the internal + buffer available in `GenStage`, dispatching events as they arrive, as explained + in the previous section: + + defmodule Broadcaster do + use GenStage + + @doc "Starts the broadcaster." + def start_link() do + GenStage.start_link(__MODULE__, :ok, name: __MODULE__) + end + + @doc "Sends an event and returns only after the event is dispatched." + def sync_notify(event, timeout \\ 5000) do + GenStage.call(__MODULE__, {:notify, event}, timeout) + end + + def init(:ok) do + {:producer, :ok, dispatcher: GenStage.BroadcastDispatcher} + end + + def handle_call({:notify, event}, _from, state) do + {:reply, :ok, [event], state} # Dispatch immediately + end + + def handle_demand(_demand, state) do + {:noreply, [], state} # We don't care about the demand + end + end + + By always sending events as soon as they arrive, if there is any demand, + we will serve the existing demand, otherwise the event will be queued in + `GenStage`'s internal buffer. In case events are being queued and not being + consumed, a log message will be emitted when we exceed the `:buffer_size` + configuration. + + While the implementation above is enough to solve the constraints above, + a more robust implementation would have tighter control over the events + and demand by tracking this data locally, leaving the `GenStage` internal + buffer only for cases where consumers crash without consuming all data. + + To handle such cases, we will use a two-element tuple as the broadcaster state + where the first elemeent is a queue and the second element is the pending + demand. When events arrive and there are no consumers, we will store the + event in the queue alongside information about the process that broadcasted + the event. When consumers send demand and there are not enough events, we will + increase the pending demand. Once we have both data and demand, we + acknowledge the process that has sent the event to the broadcaster and finally + broadcast the event downstream. + + defmodule QueueBroadcaster do + use GenStage + + @doc "Starts the broadcaster." + def start_link() do + GenStage.start_link(__MODULE__, :ok, name: __MODULE__) + end + + @doc "Sends an event and returns only after the event is dispatched." + def sync_notify(event, timeout \\ 5000) do + GenStage.call(__MODULE__, {:notify, event}, timeout) + end + + ## Callbacks + + def init(:ok) do + {:producer, {:queue.new, 0}, dispatcher: GenStage.BroadcastDispatcher} + end + + def handle_call({:notify, event}, from, {queue, pending_demand}) do + queue = :queue.in({from, event}, queue) + dispatch_events(queue, pending_demand, []) + end + + def handle_demand(incoming_demand, {queue, pending_demand}) do + dispatch_events(queue, incoming_demand + pending_demand, []) + end + + defp dispatch_events(queue, 0, events) do + {:noreply, Enum.reverse(events), {queue, 0}} + end + + defp dispatch_events(queue, demand, events) do + case :queue.out(queue) do + {{:value, {from, event}}, queue} -> + GenStage.reply(from, :ok) + dispatch_events(queue, demand - 1, [event | events]) + {:empty, queue} -> + {:noreply, Enum.reverse(events), {queue, demand}} + end + end + end + + Let's also implement a consumer that automatically subscribes to the + broadcaster on `c:init/1`. The advantage of doing so on initialization + is that, if the consumer crashes while it is supervised, the subscription + is automatically re-established when the supervisor restarts it. + + defmodule Printer do + use GenStage + + @doc "Starts the consumer." + def start_link() do + GenStage.start_link(__MODULE__, :ok) + end + + def init(:ok) do + # Starts a permanent subscription to the broadcaster + # which will automatically start requesting items. + {:consumer, :ok, subscribe_to: [QueueBroadcaster]} + end + + def handle_events(events, _from, state) do + for event <- events do + IO.inspect {self(), event} + end + {:noreply, [], state} + end + end + + With the broadcaster in hand, now let's start the producer as well + as multiple consumers: + + # Start the producer + QueueBroadcaster.start_link() + + # Start multiple consumers + Printer.start_link() + Printer.start_link() + Printer.start_link() + Printer.start_link() + + At this point, all consumers must have sent their demand which we were not + able to fulfill. Now by calling `QueueBroadcaster.sync_notify/1`, the event + shall be broadcasted to all consumers at once as we have buffered the demand + in the producer: + + QueueBroadcaster.sync_notify(:hello_world) + + If we had called `QueueBroadcaster.sync_notify(:hello_world)` before any + consumer was available, the event would also have been buffered in our own + queue and served only when demand had been received. + + By having control over the demand and queue, the broadcaster has + full control on how to behave when there are no consumers, when the + queue grows too large, and so forth. + + ## Asynchronous work and `handle_subscribe` + + Both `:producer_consumer` and `:consumer` stages have been designed to do + their work in the `c:handle_events/3` callback. This means that, after + `c:handle_events/3` is invoked, both `:producer_consumer` and `:consumer` + stages will immediately send demand upstream and ask for more items, as the + stage that produced the events assumes events have been fully processed by + `c:handle_events/3`. + + Such default behaviour makes `:producer_consumer` and `:consumer` stages + unfeasible for doing asynchronous work. However, given `GenStage` was designed + to run with multiple consumers, it is not a problem to perform synchronous or + blocking actions inside `handle_events/3` as you can then start multiple + consumers in order to max both CPU and IO usage as necessary. + + On the other hand, if you must perform some work asynchronously, + `GenStage` comes with an option that manually controls how demand + is sent upstream, avoiding the default behaviour where demand is + sent after `c:handle_events/3`. Such can be done by implementing + the `c:handle_subscribe/4` callback and returning `{:manual, state}` + instead of the default `{:automatic, state}`. Once the producer mode + is set to `:manual`, developers must use `GenStage.ask/3` to send + demand upstream when necessary. + + Note that when `:max_demand` and `:min_demand` must be manually respected when + manually asking for demand through `GenStage.ask/3`. + + For example, the `ConsumerSupervisor` module processes events + asynchronously by starting a process for each event and this is achieved by + manually sending demand to producers. `ConsumerSupervisor` + can be used to distribute work to a limited amount of + processes, behaving similar to a pool where a new process is + started for each event. See the `ConsumerSupervisor` docs for more + information. + + Setting the demand to `:manual` in `c:handle_subscribe/4` is not + only useful for asynchronous work but also for setting up other + mechanisms for back-pressure. As an example, let's implement a + consumer that is allowed to process a limited number of events + per time interval. Those are often called rate limiters: + + defmodule RateLimiter do + use GenStage + + def init(_) do + # Our state will keep all producers and their pending demand + {:consumer, %{}} + end + + def handle_subscribe(:producer, opts, from, producers) do + # We will only allow max_demand events every 5000 milliseconds + pending = opts[:max_demand] || 1000 + interval = opts[:interval] || 5000 + + # Register the producer in the state + producers = Map.put(producers, from, {pending, interval}) + # Ask for the pending events and schedule the next time around + producers = ask_and_schedule(producers, from) + + # Returns manual as we want control over the demand + {:manual, producers} + end + + def handle_cancel(_, from, producers) do + # Remove the producers from the map on unsubscribe + {:noreply, [], Map.delete(producers, from)} + end + + def handle_events(events, from, producers) do + # Bump the amount of pending events for the given producer + producers = Map.update!(producers, from, fn {pending, interval} -> + {pending + length(events), interval} + end) + + # Consume the events by printing them. + IO.inspect(events) + + # A producer_consumer would return the processed events here. + {:noreply, [], producers} + end + + def handle_info({:ask, from}, producers) do + # This callback is invoked by the Process.send_after/3 message below. + {:noreply, [], ask_and_schedule(producers, from)} + end + + defp ask_and_schedule(producers, from) do + case producers do + %{^from => {pending, interval}} -> + # Ask for any pending events + GenStage.ask(from, pending) + # And let's check again after interval + Process.send_after(self(), {:ask, from}, interval) + # Finally, reset pending events to 0 + Map.put(producers, from, {0, interval}) + %{} -> + producers + end + end + end + + Let's subscribe the `RateLimiter` above to the + producer we have implemented at the beginning of the module + documentation: + + {:ok, a} = GenStage.start_link(A, 0) + {:ok, b} = GenStage.start_link(RateLimiter, :ok) + + # Ask for 10 items every 2 seconds + GenStage.sync_subscribe(b, to: a, max_demand: 10, interval: 2000) + + Although the rate limiter above is a consumer, it could be made a + producer-consumer by changing `c:init/1` to return a `:producer_consumer` + and then forwarding the events in `c:handle_events/3`. + + ## Notifications + + `GenStage` also supports the ability to send notifications to all + consumers. Those notifications are sent as regular messages outside + of the demand-driven protocol but respecting the event ordering. + See `sync_notify/3` and `async_notify/2`. + + Notifications are useful for out-of-band information, for example, + to notify consumers the producer has sent all events it had to + process or that a new batch of events is starting. + + Note the notification system should not be used for broadcasting + events; for such, consider using `GenStage.BroadcastDispatcher`. + + ## Callbacks + + `GenStage` is implemented on top of a `GenServer` with a few additions. + Besides exposing all of the `GenServer` callbacks, it also provides + `handle_demand/2` to be implemented by producers and `handle_events/3` to be + implemented by consumers, as shown above, as well as subscription-related + callbacks. Furthermore, all the callback responses have been modified to + potentially emit events. See the callbacks documentation for more + information. + + By adding `use GenStage` to your module, Elixir will automatically + define all callbacks for you except for the following ones: + + * `init/1` - must be implemented to choose between `:producer`, `:consumer`, or `:producer_consumer` stages + * `handle_demand/2` - must be implemented by `:producer` stages + * `handle_events/3` - must be implemented by `:producer_consumer` and `:consumer` stages + + Although this module exposes functions similar to the ones found in + the `GenServer` API, like `call/3` and `cast/2`, developers can also + rely directly on GenServer functions such as `GenServer.multi_call/4` + and `GenServer.abcast/3` if they wish to. + + ### Name registration + + `GenStage` is bound to the same name registration rules as a `GenServer`. + Read more about it in the `GenServer` docs. + + ## Message protocol overview + + This section will describe the message protocol implemented + by stages. By documenting these messages, we will allow + developers to provide their own stage implementations. + + ### Back-pressure + + When data is sent between stages, it is done by a message + protocol that provides back-pressure. The first step is + for the consumer to subscribe to the producer. Each + subscription has a unique reference. + + Once subscribed, the consumer may ask the producer for messages + for the given subscription. The consumer may demand more items + whenever it wants to. A consumer must never receive more data + than it has asked for from any given producer stage. + + A consumer may have multiple producers, where each demand is managed + individually (on a per-subscription basis). A producer may have multiple + consumers, where the demand and events are managed and delivered according to + a `GenStage.Dispatcher` implementation. + + ### Producer messages + + The producer is responsible for sending events to consumers + based on demand. These are the messages that consumers can + send to producers: + + * `{:"$gen_producer", from :: {consumer_pid, subscription_tag}, {:subscribe, current, options}}` - + sent by the consumer to the producer to start a new subscription. + + Before sending, the consumer MUST monitor the producer for clean-up + purposes in case of crashes. The `subscription_tag` is unique to + identify the subscription. It is typically the subscriber monitoring + reference although it may be any term. + + Once sent, the consumer MAY immediately send demand to the producer. + + The `current` field, when not `nil`, is a two-item tuple containing a + subscription that must be cancelled with the given reason before the + current one is accepted. + + Once received, the producer MUST monitor the consumer. However, if + the subscription reference is known, it MUST send a `:cancel` message + to the consumer instead of monitoring and accepting the subscription. + + * `{:"$gen_producer", from :: {consumer_pid, subscription_tag}, {:cancel, reason}}` - + sent by the consumer to cancel a given subscription. + + Once received, the producer MUST send a `:cancel` reply to the + registered consumer (which may not necessarily be the one received + in the tuple above). Keep in mind, however, there is no guarantee + such messages can be delivered in case the producer crashes before. + If the pair is unknown, the producer MUST send an appropriate cancel + reply. + + * `{:"$gen_producer", from :: {consumer_pid, subscription_tag}, {:ask, demand}}` - + sent by consumers to ask demand for a given subscription (identified + by `subscription_tag`). + + Once received, the producer MUST send data up to the demand. If the + pair is unknown, the producer MUST send an appropriate cancel reply. + + ### Consumer messages + + The consumer is responsible for starting the subscription + and sending demand to producers. These are the messages that + producers can send to consumers: + + * `{:"$gen_consumer", from :: {producer_pid, subscription_tag}, {:notification, message}}` - + notifications sent by producers. + + * `{:"$gen_consumer", from :: {producer_pid, subscription_tag}, {:cancel, reason}}` - + sent by producers to cancel a given subscription. + + It is used as a confirmation for client cancellations OR + whenever the producer wants to cancel some upstream demand. + + * `{:"$gen_consumer", from :: {producer_pid, subscription_tag}, events :: [event, ...]}` - + events sent by producers to consumers. + + `subscription_tag` identifies the subscription. The third argument + is a non-empty list of events. If the subscription is unknown, the + events must be ignored and a cancel message must be sent to the producer. + + """ + + defstruct [:mod, :state, :type, :dispatcher_mod, :dispatcher_state, :buffer, + :buffer_config, events: :forward, monitors: %{}, producers: %{}, consumers: %{}] + + @typedoc "The supported stage types." + @type type :: :producer | :consumer | :producer_consumer + + @typedoc "The supported init options." + @type options :: keyword() + + @typedoc "The stage." + @type stage :: pid | atom | {:global, term} | {:via, module, term} | {atom, node} + + @typedoc "The term that identifies a subscription." + @opaque subscription_tag :: reference + + @typedoc "The term that identifies a subscription associated with the corresponding producer/consumer." + @type from :: {pid, subscription_tag} + + @doc """ + Invoked when the server is started. + + `start_link/3` (or `start/3`) will block until this callback returns. + `args` is the argument term (second argument) passed to `start_link/3` + (or `start/3`). + + In case of successful start, this callback must return a tuple + where the first element is the stage type, which is one of: + + * `:producer` + * `:consumer` + * `:producer_consumer` (if the stage is acting as both) + + For example: + + def init(args) do + {:producer, some_state} + end + + The returned tuple may also contain 3 or 4 elements. The third + element may be the `:hibernate` atom or a set of options defined + below. + + Returning `:ignore` will cause `start_link/3` to return `:ignore` + and the process will exit normally without entering the loop or + calling `terminate/2`. + + Returning `{:stop, reason}` will cause `start_link/3` to return + `{:error, reason}` and the process to exit with reason `reason` + without entering the loop or calling `terminate/2`. + + ## Options + + This callback may return options. Some options are specific to + the chosen stage type while others are shared across all types. + + ### `:producer` options + + * `:demand` - when `:forward`, the demand is always forwarded to + the `c:handle_demand/2` callback. When `:accumulate`, demand is + accumulated until its mode is set to `:forward` via `demand/2`. + This is useful as a synchronization mechanism, where the demand + is accumulated until all consumers are subscribed. Defaults to + `:forward`. + + ### `:producer` and `:producer_consumer` options + + * `:buffer_size` - the size of the buffer to store events without + demand. Can be `:infinity` to signal no limit on the buffer size. Check + the "Buffer events" section of the module documentation. Defaults to + `10_000` for `:producer`, `:infinity` for `:producer_consumer`. + + * `:buffer_keep` - returns whether the `:first` or `:last` entries + should be kept on the buffer in case the buffer size is exceeded. + Defaults to `:last`. + + * `:dispatcher` - the dispatcher responsible for handling demands. + Defaults to `GenStage.DemandDispatch`. May be either an atom + representing a dispatcher module or a two-element tuple with + the dispatcher module and the dispatcher options. + + ### `:consumer` and `:producer_consumer` options + + * `:subscribe_to` - a list of producers to subscribe to. Each element + represents either the producer module or a tuple with the producer module + and the subscription options (as defined in `sync_subscribe/2`). + + """ + @callback init(args :: term) :: + {type, state} | + {type, state, options} | + :ignore | + {:stop, reason :: any} when state: any + + @doc """ + Invoked on `:producer` stages. + + This callback is invoked on `:producer` stages with the demand from + consumers/dispatcher. The producer that implements this callback must either + store the demand, or return the amount of requested events. + + Must always be explicitly implemented by `:producer` stages. + + ## Examples + + def handle_demand(demand, state) do + # We check if we're able to satisfy the demand and fetch + # events if we aren't. + events = + if length(state.events) >= demand do + events + else + fetch_events() + end + + # We dispatch only the requested number of events. + {to_dispatch, remaining} = Enum.split(events, demand) + + {:noreply, to_dispatch, %{state | events: remaining}} + end + + """ + @callback handle_demand(demand :: pos_integer, state :: term) :: + {:noreply, [event], new_state} | + {:noreply, [event], new_state, :hibernate} | + {:stop, reason, new_state} when new_state: term, reason: term, event: term + + @doc """ + Invoked when a consumer subscribes to a producer. + + This callback is invoked in both producers and consumers. + `producer_or_consumer` will be `:producer` when this callback is + invoked on a consumer that subscribed to a producer, and `:consumer` + if when this callback is invoked on producers a consumer subscribed to. + + For consumers, successful subscriptions must return one of: + + * `{:automatic, new_state}` - means the stage implementation will take care + of automatically sending demand to producers. This is the default. + + * `{:manual, state}` - means that demand must be sent to producers + explicitly via `ask/3`. `:manual` subscriptions must be cancelled when + `c:handle_cancel/3` is called. `:manual` can be used when a special + behaviour is desired (for example, `ConsumerSupervisor` uses `:manual` + demand in its implementation). + + For producers, successful subscriptions must always return + `{:automatic, new_state}`. `:manual` mode is not supported. + + If this callback is not implemented, the default implementation by + `use GenStage` will return `{:automatic, state}`. + + ## Examples + + Let's see an example where we define this callback in a consumer that will use + `:manual` mode. In this case, we'll store the subscription (`from`) in the + state in order to be able to use it later on when asking demand via `ask/3`. + + def handle_subscribe(:producer, _options, from, state) do + new_state = %{state | subscription: from} + {:manual, new_state + end + + """ + @callback handle_subscribe(producer_or_consumer :: :producer | :consumer, options, from, state :: term) :: + {:automatic | :manual, new_state} | + {:stop, reason, new_state} when new_state: term, reason: term + + @doc """ + Invoked when a consumer is no longer subscribed to a producer. + + It receives the cancellation reason, the `from` tuple representing the + cancelled subscription and the state. The `cancel_reason` will be a + `{:cancel, _}` tuple if the reason for cancellation was a `GenStage.cancel/2` + call. Any other value means the cancellation reason was due to an EXIT. + + If this callback is not implemented, the default implementation by + `use GenStage` will return `{:noreply, [], state}`. + + Return values are the same as `c:handle_cast/2`. + """ + @callback handle_cancel(cancellation_reason :: {:cancel | :down, reason :: term}, from, state :: term) :: + {:noreply, [event], new_state} | + {:noreply, [event], new_state, :hibernate} | + {:stop, reason, new_state} when event: term, new_state: term, reason: term + + @doc """ + Invoked on `:producer_consumer` and `:consumer` stages to handle events. + + Must always be explicitly implemented by such types. + + Return values are the same as `c:handle_cast/2`. + """ + @callback handle_events(events :: [event], from, state :: term) :: + {:noreply, [event], new_state} | + {:noreply, [event], new_state, :hibernate} | + {:stop, reason, new_state} when new_state: term, reason: term, event: term + + @doc """ + Invoked to handle synchronous `call/3` messages. + + `call/3` will block until a reply is received (unless the call times out or + nodes are disconnected). + + `request` is the request message sent by a `call/3`, `from` is a two-element tuple + containing the caller's PID and a term that uniquely identifies the call, and + `state` is the current state of the `GenStage`. + + Returning `{:reply, reply, [events], new_state}` sends the response `reply` + to the caller after events are dispatched (or buffered) and continues the + loop with new state `new_state`. In case you want to deliver the reply before + processing events, use `reply/2` and return `{:noreply, [event], + state}`. + + Returning `{:noreply, [event], new_state}` does not send a response to the + caller and processes the given events before continuing the loop with new + state `new_state`. The response must be sent with `reply/2`. + + Hibernating is also supported as an atom to be returned from either + `:reply` and `:noreply` tuples. + + Returning `{:stop, reason, reply, new_state}` stops the loop and `terminate/2` + is called with reason `reason` and state `new_state`. Then the `reply` is sent + as the response to the call and the process exits with reason `reason`. + + Returning `{:stop, reason, new_state}` is similar to + `{:stop, reason, reply, new_state}` except that no reply is sent to the caller. + + If this callback is not implemented, the default implementation by + `use GenStage` will return `{:stop, {:bad_call, request}, state}`. + """ + @callback handle_call(request :: term, from :: GenServer.from, state :: term) :: + {:reply, reply, [event], new_state} | + {:reply, reply, [event], new_state, :hibernate} | + {:noreply, [event], new_state} | + {:noreply, [event], new_state, :hibernate} | + {:stop, reason, reply, new_state} | + {:stop, reason, new_state} when reply: term, new_state: term, reason: term, event: term + + @doc """ + Invoked to handle asynchronous `cast/2` messages. + + `request` is the request message sent by a `cast/2` and `state` is the current + state of the `GenStage`. + + Returning `{:noreply, [event], new_state}` dispatches the events and continues + the loop with new state `new_state`. + + Returning `{:noreply, [event], new_state, :hibernate}` is similar to + `{:noreply, new_state}` except the process is hibernated before continuing the + loop. See the return values for `c:GenServer.handle_call/3` for more information + on hibernation. + + Returning `{:stop, reason, new_state}` stops the loop and `terminate/2` is + called with the reason `reason` and state `new_state`. The process exits with + reason `reason`. + + If this callback is not implemented, the default implementation by + `use GenStage` will return `{:stop, {:bad_cast, request}, state}`. + """ + @callback handle_cast(request :: term, state :: term) :: + {:noreply, [event], new_state} | + {:noreply, [event], new_state, :hibernate} | + {:stop, reason :: term, new_state} when new_state: term, event: term + + @doc """ + Invoked to handle all other messages. + + `message` is the message and `state` is the current state of the `GenStage`. When + a timeout occurs the message is `:timeout`. + + If this callback is not implemented, the default implementation by + `use GenStage` will return `{:noreply, [], state}`. + + Return values are the same as `c:handle_cast/2`. + """ + @callback handle_info(message :: term, state :: term) :: + {:noreply, [event], new_state} | + {:noreply, [event], new_state, :hibernate} | + {:stop, reason :: term, new_state} when new_state: term, event: term + + @doc """ + The same as `c:GenServer.terminate/2`. + """ + @callback terminate(reason, state :: term) :: + term when reason: :normal | :shutdown | {:shutdown, term} | term + + @doc """ + The same as `c:GenServer.code_change/3`. + """ + @callback code_change(old_vsn, state :: term, extra :: term) :: + {:ok, new_state :: term} | diff --git a/sources_non_forked/vim-elixir/manual_install.sh b/sources_non_forked/vim-elixir/manual_install.sh new file mode 100755 index 00000000..49da8ccb --- /dev/null +++ b/sources_non_forked/vim-elixir/manual_install.sh @@ -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 \ No newline at end of file diff --git a/sources_non_forked/vim-elixir/spec/folding/basic_spec.rb b/sources_non_forked/vim-elixir/spec/folding/basic_spec.rb new file mode 100644 index 00000000..8826624b --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/folding/basic_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/anonymous_functions_spec.rb b/sources_non_forked/vim-elixir/spec/indent/anonymous_functions_spec.rb new file mode 100644 index 00000000..0a29890a --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/anonymous_functions_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/basic_spec.rb b/sources_non_forked/vim-elixir/spec/indent/basic_spec.rb new file mode 100644 index 00000000..3ca00c99 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/basic_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/binary_operator_spec.rb b/sources_non_forked/vim-elixir/spec/indent/binary_operator_spec.rb new file mode 100644 index 00000000..e13a4e2e --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/binary_operator_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/blocks_spec.rb b/sources_non_forked/vim-elixir/spec/indent/blocks_spec.rb new file mode 100644 index 00000000..740c96e1 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/blocks_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/case_spec.rb b/sources_non_forked/vim-elixir/spec/indent/case_spec.rb new file mode 100644 index 00000000..1dbfa47c --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/case_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/comment_spec.rb b/sources_non_forked/vim-elixir/spec/indent/comment_spec.rb new file mode 100644 index 00000000..2f49c88b --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/comment_spec.rb @@ -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 + diff --git a/sources_non_forked/vim-elixir/spec/indent/cond_spec.rb b/sources_non_forked/vim-elixir/spec/indent/cond_spec.rb new file mode 100644 index 00000000..a6e5b6e5 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/cond_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/def_spec.rb b/sources_non_forked/vim-elixir/spec/indent/def_spec.rb new file mode 100644 index 00000000..147327c3 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/def_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/documentation_spec.rb b/sources_non_forked/vim-elixir/spec/indent/documentation_spec.rb new file mode 100644 index 00000000..bb0750b0 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/documentation_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/ecto_queries_spec.rb b/sources_non_forked/vim-elixir/spec/indent/ecto_queries_spec.rb new file mode 100644 index 00000000..639beb60 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/ecto_queries_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/ecto_spec.rb b/sources_non_forked/vim-elixir/spec/indent/ecto_spec.rb new file mode 100644 index 00000000..16442096 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/ecto_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/embedded_elixir_spec.rb b/sources_non_forked/vim-elixir/spec/indent/embedded_elixir_spec.rb new file mode 100644 index 00000000..5cc0413b --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/embedded_elixir_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/embedded_views_spec.rb b/sources_non_forked/vim-elixir/spec/indent/embedded_views_spec.rb new file mode 100644 index 00000000..f60f1021 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/embedded_views_spec.rb @@ -0,0 +1,263 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Indenting embedded views' do + i <<~EOF + def render(assigns) do + ~L""" +
+ Some content +
+ """ + end + EOF + + i <<~EOF + def render(assigns) do + ~H""" +
+
+ This is immediately nested +
+ + There's a self-closing tag +
+
+
+ """ + end + EOF + + i <<~EOF + def render(assigns) do + ~L""" +
+ Some content +
+ """ + end + EOF + + i <<~EOF + def render(assigns) do + ~L""" +
+ Some content +
+ """ + end + EOF + + i <<~EOF + def render(assigns) do + ~L""" +
+

Some paragraph

+ """ + end + EOF + + i <<~EOF + def render(assigns) do + ~L""" +
+ it +
+ keeps +
+ nesting +
+
+
+ """ + end + EOF + + i <<~EOF + def render(assgins) do + ~L""" +
+ <%= for i <- iter do %> +
<%= i %>
+ <% end %> +
+ """ + end + EOF + + i <<~EOF + def render(assigns) do + ~L""" + <%= live_component @socket, + Component, + id: "<%= @id %>", + user: @user do + %> + +
+
+

Some Header

+
+
+

Some Section

+

+ I'm some text +

+
+
+ + <% end %> + """ + end + EOF + + i <<~EOF + def render(assigns) do + ~L""" + <%= render_component, + @socket, + Component do %> + +

Multi-line opening eex tag that takes a block

+ <% end %> + """ + end + EOF + + i <<~EOF + def render(assigns) do + ~L""" +
+ <%= render_component, + @socket, + Component %> +
+ + <%= render_component, + @socket, + Component %> +

Multi-line single eex tag

+ """ + end + EOF + + i <<~EOF + def render(assigns) do + ~H""" + "bar" + } + }} + /> + """ + end + EOF + + i <<~EOF + def render(assigns) do + ~L""" + <%= live_component @socket, + Component, + id: "<%= @id %>", + team: @team do + %> + +
+
+
+ A deeply nested tree +
+ with trailing whitespace + +
+
+
+
+ +
+ + <%= for i <- iter do %> +
<%= i %>
+ <% end %> + +
+ +
    +
  • + {{ item }} +
  • +
+ +
+ Hi

hi

+ I'm ok, ok? +
+ hi there! +
+
+
+

hi

+
+
+
+
+ + + + +
content
+
+ +
+ +
hi
+ +
+
+ content +
+
+
+ content in new div after a self-closing div +
+
+ +

+ <%= @solo.eex_tag %> + + content + +

+ + <% end %> + """ + end + EOF +end diff --git a/sources_non_forked/vim-elixir/spec/indent/exunit_spec.rb b/sources_non_forked/vim-elixir/spec/indent/exunit_spec.rb new file mode 100644 index 00000000..93a277d1 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/exunit_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/if_spec.rb b/sources_non_forked/vim-elixir/spec/indent/if_spec.rb new file mode 100644 index 00000000..92ca685c --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/if_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/keyword_spec.rb b/sources_non_forked/vim-elixir/spec/indent/keyword_spec.rb new file mode 100644 index 00000000..6432eeff --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/keyword_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/lists_spec.rb b/sources_non_forked/vim-elixir/spec/indent/lists_spec.rb new file mode 100644 index 00000000..fc66d9f7 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/lists_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/macro_spec.rb b/sources_non_forked/vim-elixir/spec/indent/macro_spec.rb new file mode 100644 index 00000000..2218b683 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/macro_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe 'Macros' do + i <<~EOF + defmodule DeadboltTest do + use ExUnit.Case + doctest Deadbolt + + hello + + end + EOF +end diff --git a/sources_non_forked/vim-elixir/spec/indent/map_spec.rb b/sources_non_forked/vim-elixir/spec/indent/map_spec.rb new file mode 100644 index 00000000..21c15e4f --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/map_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/pipeline_spec.rb b/sources_non_forked/vim-elixir/spec/indent/pipeline_spec.rb new file mode 100644 index 00000000..75f5d343 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/pipeline_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/receive_spec.rb b/sources_non_forked/vim-elixir/spec/indent/receive_spec.rb new file mode 100644 index 00000000..1576f33b --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/receive_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/string_spec.rb b/sources_non_forked/vim-elixir/spec/indent/string_spec.rb new file mode 100644 index 00000000..21951a64 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/string_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/struct_spec.rb b/sources_non_forked/vim-elixir/spec/indent/struct_spec.rb new file mode 100644 index 00000000..563dfdc7 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/struct_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/try_spec.rb b/sources_non_forked/vim-elixir/spec/indent/try_spec.rb new file mode 100644 index 00000000..08095f58 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/try_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/tuples_spec.rb b/sources_non_forked/vim-elixir/spec/indent/tuples_spec.rb new file mode 100644 index 00000000..57e39743 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/tuples_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/indent/with_spec.rb b/sources_non_forked/vim-elixir/spec/indent/with_spec.rb new file mode 100644 index 00000000..23330301 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/indent/with_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/spec_helper.rb b/sources_non_forked/vim-elixir/spec/spec_helper.rb new file mode 100644 index 00000000..d84e8228 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/spec_helper.rb @@ -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 = ''.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("") 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" + cmd += "zO" + cmd += "#{fold_on_line}G" + cmd += "zc" + cmd += "cc#{FOLD_PLACEHOLDER}" + cmd += ":.s/\s*//" + @vim.normal(cmd) + end + end + + private + + def with_file(content = nil) + edit_file(content) + + yield if block_given? + + @vim.normal ":w" + @vim.normal ":redraw" + 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' + cmd += ':filetype plugin indent on' + cmd += ':autocmd FileType * setlocal formatoptions-=c formatoptions-=r formatoptions-=o' # disable automatic comment continuation + cmd += ":set ignorecase" # 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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/alias_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/alias_spec.rb new file mode 100644 index 00000000..872ce360 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/alias_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/anonymous_function_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/anonymous_function_spec.rb new file mode 100644 index 00000000..fc661136 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/anonymous_function_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/atom_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/atom_spec.rb new file mode 100644 index 00000000..a659f9b8 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/atom_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/case_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/case_spec.rb new file mode 100644 index 00000000..fb906a14 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/case_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/comments_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/comments_spec.rb new file mode 100644 index 00000000..22094342 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/comments_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/defmodule_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/defmodule_spec.rb new file mode 100644 index 00000000..d239f1f9 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/defmodule_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/doc_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/doc_spec.rb new file mode 100644 index 00000000..9ff09491 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/doc_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/embedded_elixir_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/embedded_elixir_spec.rb new file mode 100644 index 00000000..0372b858 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/embedded_elixir_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/exunit_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/exunit_spec.rb new file mode 100644 index 00000000..861fc40d --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/exunit_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/function_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/function_spec.rb new file mode 100644 index 00000000..a42584af --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/function_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/guard_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/guard_spec.rb new file mode 100644 index 00000000..4ae4df04 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/guard_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/kernel_function_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/kernel_function_spec.rb new file mode 100644 index 00000000..f9bf8cf5 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/kernel_function_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/keyword_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/keyword_spec.rb new file mode 100644 index 00000000..2a8228ea --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/keyword_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/list_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/list_spec.rb new file mode 100644 index 00000000..b1c51ab5 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/list_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/map_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/map_spec.rb new file mode 100644 index 00000000..fc43d633 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/map_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/module_function_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/module_function_spec.rb new file mode 100644 index 00000000..cf038a3a --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/module_function_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/numbers_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/numbers_spec.rb new file mode 100644 index 00000000..6cedea32 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/numbers_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/operator_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/operator_spec.rb new file mode 100644 index 00000000..d08f3101 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/operator_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/records_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/records_spec.rb new file mode 100644 index 00000000..a857a332 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/records_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/sigil_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/sigil_spec.rb new file mode 100644 index 00000000..4244c0d9 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/sigil_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/strings_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/strings_spec.rb new file mode 100644 index 00000000..96174e25 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/strings_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/struct_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/struct_spec.rb new file mode 100644 index 00000000..c1b09c57 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/struct_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/tuple_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/tuple_spec.rb new file mode 100644 index 00000000..d8478b6c --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/tuple_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/spec/syntax/variable_spec.rb b/sources_non_forked/vim-elixir/spec/syntax/variable_spec.rb new file mode 100644 index 00000000..c767c223 --- /dev/null +++ b/sources_non_forked/vim-elixir/spec/syntax/variable_spec.rb @@ -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 diff --git a/sources_non_forked/vim-elixir/syntax/eelixir.vim b/sources_non_forked/vim-elixir/syntax/eelixir.vim new file mode 100644 index 00000000..edb15c6f --- /dev/null +++ b/sources_non_forked/vim-elixir/syntax/eelixir.vim @@ -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 diff --git a/sources_non_forked/vim-elixir/syntax/elixir.vim b/sources_non_forked/vim-elixir/syntax/elixir.vim new file mode 100644 index 00000000..95a1f116 --- /dev/null +++ b/sources_non_forked/vim-elixir/syntax/elixir.vim @@ -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 '\(\.\)\@:\@!' + +syn keyword elixirInclude import require alias use + +syn keyword elixirSelf self + +syn match elixirUnusedVariable contained '\%(\.\)\@\%((\)\@!' + +syn match elixirOperator '\v\.@' +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]\)\@=]\@!\)\?\|<>\|===\?\|>=\?\|<=\?\)' +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\|#\)\@\|0[0-7]{0,2}[0-7]\@!\>\|[^x0MC]\)\|(\\[MC]-)+\w\|[^\s\\]\)" + +syn region elixirBlock matchgroup=elixirBlockDefinition start="\:\@!" end="\" contains=ALLBUT,@elixirNotTop fold +syn region elixirAnonymousFunction matchgroup=elixirBlockDefinition start="\" 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 '\\(:\)\@!' nextgroup=elixirFunctionDeclaration skipwhite skipnl +syn match elixirPrivateDefine '\\(:\)\@!' nextgroup=elixirPrivateFunctionDeclaration skipwhite skipnl +syn match elixirNumericalDefine '\\(:\)\@!' nextgroup=elixirFunctionDeclaration skipwhite skipnl +syn match elixirGuard '\\(:\)\@!' nextgroup=elixirFunctionDeclaration skipwhite skipnl +syn match elixirPrivateGuard '\\(:\)\@!' nextgroup=elixirPrivateFunctionDeclaration skipwhite skipnl +syn match elixirModuleDefine '\\(:\)\@!' nextgroup=elixirModuleDeclaration skipwhite skipnl +syn match elixirProtocolDefine '\\(:\)\@!' nextgroup=elixirProtocolDeclaration skipwhite skipnl +syn match elixirImplDefine '\\(:\)\@!' nextgroup=elixirImplDeclaration skipwhite skipnl +syn match elixirRecordDefine '\\(:\)\@!' nextgroup=elixirRecordDeclaration skipwhite skipnl +syn match elixirPrivateRecordDefine '\\(:\)\@!' nextgroup=elixirPrivateRecordDeclaration skipwhite skipnl +syn match elixirMacroDefine '\\(:\)\@!' nextgroup=elixirMacroDeclaration skipwhite skipnl +syn match elixirPrivateMacroDefine '\\(:\)\@!' nextgroup=elixirPrivateMacroDeclaration skipwhite skipnl +syn match elixirDelegateDefine '\\(:\)\@!' nextgroup=elixirDelegateDeclaration skipwhite skipnl +syn match elixirOverridableDefine '\\(:\)\@!' nextgroup=elixirOverridableDeclaration skipwhite skipnl +syn match elixirExceptionDefine '\\(:\)\@!' nextgroup=elixirExceptionDeclaration skipwhite skipnl +syn match elixirCallbackDefine '\\(:\)\@!' nextgroup=elixirCallbackDeclaration skipwhite skipnl +syn match elixirStructDefine '\\(:\)\@!' 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 diff --git a/sources_non_forked/vim-elixir/test.init.vim b/sources_non_forked/vim-elixir/test.init.vim new file mode 100644 index 00000000..c11b9402 --- /dev/null +++ b/sources_non_forked/vim-elixir/test.init.vim @@ -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 syn :echo "hi<" . synIDattr(synID(line("."),col("."),1),"name") . '> trans<' + \ . synIDattr(synID(line("."),col("."),0),"name") . "> lo<" + \ . synIDattr(synIDtrans(synID(line("."),col("."),1)),"name") . ">" diff --git a/sources_non_forked/vim-elixir/test.vimrc b/sources_non_forked/vim-elixir/test.vimrc new file mode 100644 index 00000000..c11b9402 --- /dev/null +++ b/sources_non_forked/vim-elixir/test.vimrc @@ -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 syn :echo "hi<" . synIDattr(synID(line("."),col("."),1),"name") . '> trans<' + \ . synIDattr(synID(line("."),col("."),0),"name") . "> lo<" + \ . synIDattr(synIDtrans(synID(line("."),col("."),1)),"name") . ">"