aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/ftplugin/elm.vim18
-rw-r--r--runtime/indent/elm.vim114
-rw-r--r--runtime/syntax/elm.vim105
3 files changed, 237 insertions, 0 deletions
diff --git a/runtime/ftplugin/elm.vim b/runtime/ftplugin/elm.vim
new file mode 100644
index 0000000000..1e10346186
--- /dev/null
+++ b/runtime/ftplugin/elm.vim
@@ -0,0 +1,18 @@
+" Elm filetype plugin file
+" Language: Elm
+" Maintainer: Andreas Scharf <as@99n.de>
+" Latest Revision: 2020-05-29
+
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+setlocal comments=s1fl:{-,mb:\ ,ex:-},:--
+setlocal commentstring=--\ %s
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/indent/elm.vim b/runtime/indent/elm.vim
new file mode 100644
index 0000000000..232c347c66
--- /dev/null
+++ b/runtime/indent/elm.vim
@@ -0,0 +1,114 @@
+" Elm indent plugin file
+" Language: Elm
+" Maintainer: Andreas Scharf <as@99n.de>
+" Original Author: Joseph Hager <ajhager@gmail.com>
+" Copyright: Joseph Hager <ajhager@gmail.com>
+" License: BSD3
+" Latest Revision: 2020-05-29
+
+" Only load this indent file when no other was loaded.
+if exists('b:did_indent')
+ finish
+endif
+let b:did_indent = 1
+
+" Local defaults
+setlocal expandtab
+setlocal indentexpr=GetElmIndent()
+setlocal indentkeys+=0=else,0=if,0=of,0=import,0=then,0=type,0\|,0},0\],0),=-},0=in
+setlocal nolisp
+setlocal nosmartindent
+
+" Only define the function once.
+if exists('*GetElmIndent')
+ finish
+endif
+
+" Indent pairs
+function! s:FindPair(pstart, pmid, pend)
+ "call search(a:pend, 'bW')
+ return indent(searchpair(a:pstart, a:pmid, a:pend, 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"'))
+endfunction
+
+function! GetElmIndent()
+ let l:lnum = v:lnum - 1
+
+ " Ident 0 if the first line of the file:
+ if l:lnum == 0
+ return 0
+ endif
+
+ let l:ind = indent(l:lnum)
+ let l:lline = getline(l:lnum)
+ let l:line = getline(v:lnum)
+
+ " Indent if current line begins with '}':
+ if l:line =~? '^\s*}'
+ return s:FindPair('{', '', '}')
+
+ " Indent if current line begins with 'else':
+ elseif l:line =~# '^\s*else\>'
+ if l:lline !~# '^\s*\(if\|then\)\>'
+ return s:FindPair('\<if\>', '', '\<else\>')
+ endif
+
+ " Indent if current line begins with 'then':
+ elseif l:line =~# '^\s*then\>'
+ if l:lline !~# '^\s*\(if\|else\)\>'
+ return s:FindPair('\<if\>', '', '\<then\>')
+ endif
+
+ " HACK: Indent lines in case with nearest case clause:
+ elseif l:line =~# '->' && l:line !~# ':' && l:line !~# '\\'
+ return indent(search('^\s*case', 'bWn')) + &shiftwidth
+
+ " HACK: Don't change the indentation if the last line is a comment.
+ elseif l:lline =~# '^\s*--'
+ return l:ind
+
+ " Align the end of block comments with the start
+ elseif l:line =~# '^\s*-}'
+ return indent(search('{-', 'bWn'))
+
+ " Indent double shift after let with an empty rhs
+ elseif l:lline =~# '\<let\>.*\s=$'
+ return l:ind + 4 + &shiftwidth
+
+ " Align 'in' with the parent let.
+ elseif l:line =~# '^\s*in\>'
+ return indent(search('^\s*let', 'bWn'))
+
+ " Align bindings with the parent let.
+ elseif l:lline =~# '\<let\>'
+ return l:ind + 4
+
+ " Align bindings with the parent in.
+ elseif l:lline =~# '^\s*in\>'
+ return l:ind
+
+ endif
+
+ " Add a 'shiftwidth' after lines ending with:
+ if l:lline =~# '\(|\|=\|->\|<-\|(\|\[\|{\|\<\(of\|else\|if\|then\)\)\s*$'
+ let l:ind = l:ind + &shiftwidth
+
+ " Add a 'shiftwidth' after lines starting with type ending with '=':
+ elseif l:lline =~# '^\s*type' && l:line =~# '^\s*='
+ let l:ind = l:ind + &shiftwidth
+
+ " Back to normal indent after comments:
+ elseif l:lline =~# '-}\s*$'
+ call search('-}', 'bW')
+ let l:ind = indent(searchpair('{-', '', '-}', 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"'))
+
+ " Ident some operators if there aren't any starting the last line.
+ elseif l:line =~# '^\s*\(!\|&\|(\|`\|+\||\|{\|[\|,\)=' && l:lline !~# '^\s*\(!\|&\|(\|`\|+\||\|{\|[\|,\)=' && l:lline !~# '^\s*$'
+ let l:ind = l:ind + &shiftwidth
+
+ elseif l:lline ==# '' && getline(l:lnum - 1) !=# ''
+ let l:ind = indent(search('^\s*\S+', 'bWn'))
+
+ endif
+
+ return l:ind
+endfunc
diff --git a/runtime/syntax/elm.vim b/runtime/syntax/elm.vim
new file mode 100644
index 0000000000..1277827f57
--- /dev/null
+++ b/runtime/syntax/elm.vim
@@ -0,0 +1,105 @@
+" Vim syntax file
+" Language: Elm
+" Maintainer: Andreas Scharf <as@99n.de>
+" Original Author: Joseph Hager <ajhager@gmail.com>
+" Copyright: Joseph Hager <ajhager@gmail.com>
+" License: BSD3
+" Latest Revision: 2020-05-29
+
+if exists('b:current_syntax')
+ finish
+endif
+
+" Keywords
+syn keyword elmConditional else if of then case
+syn keyword elmAlias alias
+syn keyword elmTypedef contained type port
+syn keyword elmImport exposing as import module where
+
+" Operators
+" elm/core
+syn match elmOperator contained "\(<|\||>\|||\|&&\|==\|/=\|<=\|>=\|++\|::\|+\|-\|*\|/\|//\|^\|<>\|>>\|<<\|<\|>\|%\)"
+" elm/parser
+syn match elmOperator contained "\(|.\||=\)"
+" elm/url
+syn match elmOperator contained "\(</>\|<?>\)"
+
+" Types
+syn match elmType "\<[A-Z][0-9A-Za-z_-]*"
+syn keyword elmNumberType number
+
+" Modules
+syn match elmModule "\<\([A-Z][0-9A-Za-z_'-\.]*\)\+\.[A-Za-z]"me=e-2
+syn match elmModule "^\(module\|import\)\s\+[A-Z][0-9A-Za-z_'-\.]*\(\s\+as\s\+[A-Z][0-9A-Za-z_'-\.]*\)\?\(\s\+exposing\)\?" contains=elmImport
+
+" Delimiters
+syn match elmDelimiter "[,;]"
+syn match elmBraces "[()[\]{}]"
+
+" Functions
+syn match elmTupleFunction "\((,\+)\)"
+
+" Comments
+syn keyword elmTodo TODO FIXME XXX contained
+syn match elmLineComment "--.*" contains=elmTodo,@spell
+syn region elmComment matchgroup=elmComment start="{-|\=" end="-}" contains=elmTodo,elmComment,@spell fold
+
+" Strings
+syn match elmStringEscape "\\u[0-9a-fA-F]\{4}" contained
+syn match elmStringEscape "\\[nrfvbt\\\"]" contained
+syn region elmString start="\"" skip="\\\"" end="\"" contains=elmStringEscape,@spell
+syn region elmTripleString start="\"\"\"" skip="\\\"" end="\"\"\"" contains=elmStringEscape,@spell
+syn match elmChar "'[^'\\]'\|'\\.'\|'\\u[0-9a-fA-F]\{4}'"
+
+" Lambda
+syn region elmLambdaFunc start="\\"hs=s+1 end="->"he=e-2
+
+" Debug
+syn match elmDebug "Debug.\(log\|todo\|toString\)"
+
+" Numbers
+syn match elmInt "-\?\<\d\+\>"
+syn match elmFloat "-\?\(\<\d\+\.\d\+\>\)"
+
+" Identifiers
+syn match elmTopLevelDecl "^\s*[a-zA-Z][a-zA-z0-9_]*\('\)*\s\+:\(\r\n\|\r\|\n\|\s\)\+" contains=elmOperator
+syn match elmFuncName /^\l\w*/
+
+" Folding
+syn region elmTopLevelTypedef start="type" end="\n\(\n\n\)\@=" contains=ALL fold
+syn region elmTopLevelFunction start="^[a-zA-Z].\+\n[a-zA-Z].\+=" end="^\(\n\+\)\@=" contains=ALL fold
+syn region elmCaseBlock matchgroup=elmCaseBlockDefinition start="^\z\(\s\+\)\<case\>" end="^\z1\@!\W\@=" end="\(\n\n\z1\@!\)\@=" end="\n\z1\@!\(\n\n\)\@=" contains=ALL fold
+syn region elmCaseItemBlock start="^\z\(\s\+\).\+->$" end="^\z1\@!\W\@=" end="\(\n\n\z1\@!\)\@=" end="\(\n\z1\S\)\@=" contains=ALL fold
+syn region elmLetBlock matchgroup=elmLetBlockDefinition start="\<let\>" end="\<in\>" contains=ALL fold
+
+hi def link elmFuncName Function
+hi def link elmCaseBlockDefinition Conditional
+hi def link elmCaseBlockItemDefinition Conditional
+hi def link elmLetBlockDefinition TypeDef
+hi def link elmTopLevelDecl Function
+hi def link elmTupleFunction Normal
+hi def link elmTodo Todo
+hi def link elmComment Comment
+hi def link elmLineComment Comment
+hi def link elmString String
+hi def link elmTripleString String
+hi def link elmChar String
+hi def link elmStringEscape Special
+hi def link elmInt Number
+hi def link elmFloat Float
+hi def link elmDelimiter Delimiter
+hi def link elmBraces Delimiter
+hi def link elmTypedef TypeDef
+hi def link elmImport Include
+hi def link elmConditional Conditional
+hi def link elmAlias Delimiter
+hi def link elmOperator Operator
+hi def link elmType Type
+hi def link elmNumberType Identifier
+hi def link elmLambdaFunc Function
+hi def link elmDebug Debug
+hi def link elmModule Type
+
+syn sync minlines=500
+
+let b:current_syntax = 'elm'