From 2bde381723b9604dccb5b9dbe4588a0251ad8544 Mon Sep 17 00:00:00 2001 From: Adam Stankiewicz Date: Fri, 22 Jan 2016 09:08:30 +0100 Subject: [PATCH] Add nim support --- README.md | 1 + autoload/nim.vim | 232 ++++++++++++++++++++++++++++++++++++++++++ build | 1 + compiler/nim.vim | 27 +++++ ftdetect/polyglot.vim | 4 + ftplugin/nim.vim | 27 +++++ indent/nim.vim | 146 ++++++++++++++++++++++++++ syntax/nim.vim | 192 ++++++++++++++++++++++++++++++++++ 8 files changed, 630 insertions(+) create mode 100644 autoload/nim.vim create mode 100644 compiler/nim.vim create mode 100644 ftplugin/nim.vim create mode 100644 indent/nim.vim create mode 100644 syntax/nim.vim diff --git a/README.md b/README.md index 6ebdd65..3f0e3b4 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ Optionally download one of the [releases](https://github.com/sheerun/vim-polyglo - [liquid](https://github.com/tpope/vim-liquid) (syntax, indent, ftplugin, ftdetect) - [markdown](https://github.com/tpope/vim-markdown) (syntax, ftplugin, ftdetect) - [nginx](https://github.com/nginx/nginx) (syntax, indent, ftdetect) +- [nim](https://github.com/zah/nim.vim) (syntax, indent, compiler, autoload, ftplugin, ftdetect) - [nix](https://github.com/spwhitt/vim-nix) (syntax, ftplugin, ftdetect) - [objc](https://github.com/b4winckler/vim-objc) (ftplugin, syntax, indent) - [ocaml](https://github.com/jrk/vim-ocaml) (syntax, indent, ftplugin) diff --git a/autoload/nim.vim b/autoload/nim.vim new file mode 100644 index 0000000..3e99aa4 --- /dev/null +++ b/autoload/nim.vim @@ -0,0 +1,232 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'nim') == -1 + +let g:nim_log = [] +let s:plugin_path = escape(expand(':p:h'), ' \') + +if !exists("g:nim_caas_enabled") + let g:nim_caas_enabled = 0 +endif + +if !executable('nim') + echoerr "the Nim compiler must be in your system's PATH" +endif + +exe 'pyfile ' . fnameescape(s:plugin_path) . '/nim_vim.py' + +fun! nim#init() + let cmd = printf("nim --dump.format:json --verbosity:0 dump %s", s:CurrentNimFile()) + let raw_dumpdata = system(cmd) + if !v:shell_error + let dumpdata = eval(substitute(raw_dumpdata, "\n", "", "g")) + + let b:nim_project_root = dumpdata['project_path'] + let b:nim_defined_symbols = dumpdata['defined_symbols'] + let b:nim_caas_enabled = g:nim_caas_enabled || index(dumpdata['defined_symbols'], 'forcecaas') != -1 + + for path in dumpdata['lib_paths'] + if finddir(path) == path + let &l:path = path . "," . &l:path + endif + endfor + else + let b:nim_caas_enabled = 0 + endif +endf + +fun! s:UpdateNimLog() + setlocal buftype=nofile + setlocal bufhidden=hide + setlocal noswapfile + + for entry in g:nim_log + call append(line('$'), split(entry, "\n")) + endfor + + let g:nim_log = [] + + match Search /^nim\ .*/ +endf + +augroup NimVim + au! + au BufEnter log://nim call s:UpdateNimLog() + " au QuitPre * :py nimTerminateAll() + au VimLeavePre * :py nimTerminateAll() +augroup END + +command! NimLog :e log://nim + +command! NimTerminateService + \ :exe printf("py nimTerminateService('%s')", b:nim_project_root) + +command! NimRestartService + \ :exe printf("py nimRestartService('%s')", b:nim_project_root) + +fun! s:CurrentNimFile() + let save_cur = getpos('.') + call cursor(0, 0, 0) + + let PATTERN = "\\v^\\#\\s*included from \\zs.*\\ze" + let l = search(PATTERN, "n") + + if l != 0 + let f = matchstr(getline(l), PATTERN) + let l:to_check = expand('%:h') . "/" . f + else + let l:to_check = expand("%") + endif + + call setpos('.', save_cur) + return l:to_check +endf + +let g:nim_symbol_types = { + \ 'skParam': 'v', + \ 'skVar': 'v', + \ 'skLet': 'v', + \ 'skTemp': 'v', + \ 'skForVar': 'v', + \ 'skConst': 'v', + \ 'skResult': 'v', + \ 'skGenericParam': 't', + \ 'skType': 't', + \ 'skField': 'm', + \ 'skProc': 'f', + \ 'skMethod': 'f', + \ 'skIterator': 'f', + \ 'skConverter': 'f', + \ 'skMacro': 'f', + \ 'skTemplate': 'f', + \ 'skEnumField': 'v', + \ } + +fun! NimExec(op) + let isDirty = getbufvar(bufnr('%'), "&modified") + if isDirty + let tmp = tempname() . bufname("%") . "_dirty.nim" + silent! exe ":w " . tmp + + let cmd = printf("idetools %s --trackDirty:\"%s,%s,%d,%d\" \"%s\"", + \ a:op, tmp, expand('%:p'), line('.'), col('.')-1, s:CurrentNimFile()) + else + let cmd = printf("idetools %s --track:\"%s,%d,%d\" \"%s\"", + \ a:op, expand('%:p'), line('.'), col('.')-1, s:CurrentNimFile()) + endif + + if b:nim_caas_enabled + exe printf("py nimExecCmd('%s', '%s', False)", b:nim_project_root, cmd) + let output = l:py_res + else + let output = system("nim " . cmd) + endif + + call add(g:nim_log, "nim " . cmd . "\n" . output) + return output +endf + +fun! NimExecAsync(op, Handler) + let result = NimExec(a:op) + call a:Handler(result) +endf + +fun! NimComplete(findstart, base) + if b:nim_caas_enabled == 0 + return -1 + endif + + if a:findstart + if synIDattr(synIDtrans(synID(line("."),col("."),1)), "name") == 'Comment' + return -1 + endif + return col('.') + else + let result = [] + let sugOut = NimExec("--suggest") + for line in split(sugOut, '\n') + let lineData = split(line, '\t') + if len(lineData) > 0 && lineData[0] == "sug" + let kind = get(g:nim_symbol_types, lineData[1], '') + let c = { 'word': lineData[2], 'kind': kind, 'menu': lineData[3], 'dup': 1 } + call add(result, c) + endif + endfor + return result + endif +endf + +if !exists("g:neocomplcache_omni_patterns") + let g:neocomplcache_omni_patterns = {} +endif + +let g:neocomplcache_omni_patterns['nim'] = '[^. *\t]\.\w*' +let g:nim_completion_callbacks = {} + +fun! NimAsyncCmdComplete(cmd, output) + call add(g:nim_log, a:output) + echom g:nim_completion_callbacks + if has_key(g:nim_completion_callbacks, a:cmd) + let Callback = get(g:nim_completion_callbacks, a:cmd) + call Callback(a:output) + " remove(g:nim_completion_callbacks, a:cmd) + else + echom "ERROR, Unknown Command: " . a:cmd + endif + return 1 +endf + +fun! GotoDefinition_nim_ready(def_output) + if v:shell_error + echo "nim was unable to locate the definition. exit code: " . v:shell_error + " echoerr a:def_output + return 0 + endif + + let rawDef = matchstr(a:def_output, 'def\t\([^\n]*\)') + if rawDef == "" + echo "the current cursor position does not match any definitions" + return 0 + endif + + let defBits = split(rawDef, '\t') + let file = defBits[4] + let line = defBits[5] + exe printf("e +%d %s", line, file) + return 1 +endf + +fun! GotoDefinition_nim() + call NimExecAsync("--def", function("GotoDefinition_nim_ready")) +endf + +fun! FindReferences_nim() + setloclist() +endf + +" Syntastic syntax checking +fun! SyntaxCheckers_nim_nim_GetLocList() + let makeprg = 'nim check --hints:off --listfullpaths ' . s:CurrentNimFile() + let errorformat = &errorformat + + return SyntasticMake({ 'makeprg': makeprg, 'errorformat': errorformat }) +endf + +function! SyntaxCheckers_nim_nim_IsAvailable() + return executable("nim") +endfunction + +if exists("g:SyntasticRegistry") + call g:SyntasticRegistry.CreateAndRegisterChecker({ + \ 'filetype': 'nim', + \ 'name': 'nim'}) +endif + +if !exists("g:quickrun_config") + let g:quickrun_config = {} +endif + +if !exists("g:quickrun_config.nim") + let g:quickrun_config.nim = { "exec": "nim c --run --verbosity:0 %S" } +endif + + +endif diff --git a/build b/build index 558691e..77c3cee 100755 --- a/build +++ b/build @@ -137,6 +137,7 @@ PACKS=" liquid:tpope/vim-liquid markdown:tpope/vim-markdown nginx:nginx/nginx::/contrib/vim/ + nim:zah/nim.vim nix:spwhitt/vim-nix objc:b4winckler/vim-objc ocaml:jrk/vim-ocaml diff --git a/compiler/nim.vim b/compiler/nim.vim new file mode 100644 index 0000000..47886ed --- /dev/null +++ b/compiler/nim.vim @@ -0,0 +1,27 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'nim') == -1 + +if exists("current_compiler") + finish +endif + +let current_compiler = "nim" + +if exists(":CompilerSet") != 2 " older Vim always used :setlocal + command -nargs=* CompilerSet setlocal +endif + +let s:cpo_save = &cpo +set cpo-=C + +CompilerSet makeprg=nim\ c\ $* + +CompilerSet errorformat= + \%-GHint:\ %m, + \%E%f(%l\\,\ %c)\ Error:\ %m, + \%W%f(%l\\,\ %c)\ Hint:\ %m + +let &cpo = s:cpo_save +unlet s:cpo_save + + +endif diff --git a/ftdetect/polyglot.vim b/ftdetect/polyglot.vim index 521eced..52b1768 100644 --- a/ftdetect/polyglot.vim +++ b/ftdetect/polyglot.vim @@ -277,6 +277,10 @@ au BufRead,BufNewFile */etc/nginx/* set ft=nginx au BufRead,BufNewFile */usr/local/nginx/conf/* set ft=nginx au BufRead,BufNewFile nginx.conf set ft=nginx endif +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'nim') == -1 + +au BufNewFile,BufRead *.nim set filetype=nim +endif if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'nix') == -1 autocmd BufNewFile,BufRead *.nix setfiletype nix diff --git a/ftplugin/nim.vim b/ftplugin/nim.vim new file mode 100644 index 0000000..5f3e93d --- /dev/null +++ b/ftplugin/nim.vim @@ -0,0 +1,27 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'nim') == -1 + +if exists("b:nim_loaded") + finish +endif + +let b:nim_loaded = 1 + +let s:cpo_save = &cpo +set cpo&vim + +call nim#init() + +setlocal formatoptions-=t formatoptions+=croql +setlocal comments=:##,:# +setlocal commentstring=#\ %s +setlocal omnifunc=NimComplete +setlocal suffixesadd=.nim +setlocal expandtab "Make sure that only spaces are used + +compiler nim + +let &cpo = s:cpo_save +unlet s:cpo_save + + +endif diff --git a/indent/nim.vim b/indent/nim.vim new file mode 100644 index 0000000..a0eae56 --- /dev/null +++ b/indent/nim.vim @@ -0,0 +1,146 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'nim') == -1 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" Some preliminary settings +setlocal nolisp " Make sure lisp indenting doesn't supersede us +setlocal autoindent " indentexpr isn't much help otherwise + +setlocal indentexpr=GetNimIndent(v:lnum) +setlocal indentkeys=!^F,o,O,<:>,0),0],0},=elif + +" Only define the function once. +if exists("*GetNimIndent") + finish +endif + +function! s:FindStartLine(fromln, pattern) + let lnum = a:fromln + let safechoice = indent(lnum) + while getline(lnum) !~ a:pattern + if indent(lnum) == 0 || lnum == 1 + return safechoice + endif + let lnum = lnum - 1 + endwhile + return indent(lnum) +endfunction + +function! GetNimIndent(lnum) + " Search backwards for the previous non-empty line. + let plnum = prevnonblank(a:lnum - 1) + + if plnum == 0 + " This is the first non-empty line, use zero indent. + return 0 + endif + + " If the start of the line is in a string don't change the indent. + if has('syntax_items') && synIDattr(synID(a:lnum, 1, 1), "name") =~ "String$" + return -1 + endif + + let pline = getline(plnum) + let cline = getline(a:lnum) + let pline_len = strlen(pline) + let plindent = indent(plnum) + let clindent = indent(a:lnum) + + " Remove any trailing comment from previous line. + " Use syntax highlighting attributes when possible. + if has('syntax_items') + " If the last character in the line is a comment, do a binary search for + " the start of the comment. synID() is slow, a linear search would take + " too long on a long line. + if synIDattr(synID(plnum, pline_len, 1), "name") =~ "Comment$" + let min = 1 + let max = pline_len + while min < max + let col = (min + max) / 2 + if synIDattr(synID(plnum, col, 1), "name") =~ "Comment$" + let max = col + else + let min = col + 1 + endif + endwhile + let pline = strpart(pline, 0, min - 1) + endif + else + let col = 0 + while col < pline_len + if pline[col] == '#' + let pline = strpart(pline, 0, col) + break + endif + let col = col + 1 + endwhile + endif + + if cline =~ '^\s*\(if\|when\|for\|while\|case\|of\|try\)\>' + " This is a benign line, do nothing + return -1 + endif + + " If the current line begins with a keyword that lines up with "try" + if cline =~ '^\s*\(except\|finally\)\>' + let lnum = a:lnum - 1 + while lnum >= 1 + if getline(lnum) =~ '^\s*\(try\|except\)\>' + let ind = indent(lnum) + if ind >= clindent + return -1 " indent is already less than this + endif + return ind " line up with previous try or except + endif + let lnum = lnum - 1 + endwhile + return -1 " no matching "try"! + endif + + " If the current line begins with a header keyword, dedent + if cline =~ '^\s*\(elif\|else\)\>' + return s:FindStartLine(a:lnum, '^\s*\(if\|when\|elif\|of\)') + endif + + if pline =~ ':\s*$' + "return s:FindStartLine(plnum, '(^\s*\(if\|when\|else\|elif\|case\|of\|try\|except\|finally\)\>)\|\') + &sw + return s:FindStartLine(plnum, '^\s*\(if\|when\|else\|elif\|for\|while\|case\|of\|try\|except\|finally\)\>') + &sw + endif + + if pline =~ '=\s*$' + return s:FindStartLine(plnum, '^\s*\(proc\|template\|macro\|iterator\)\>') + &sw + endif + + " if we got here, this should be the begging of a multi-line if expression for example + if pline =~ '^\s*\(if\|when\|proc\|iterator\|macro\|template\|for\|while\)[^:]*$' + return plindent + &sw + endif + + if pline =~ '\(type\|import\|const\|var\)\s*$' + \ || pline =~ '=\s*\(object\|enum\|tuple\|generic\)' + return plindent + &sw + endif + + " If the previous line was a stop-execution statement... + if pline =~ '^\s*\(break\|continue\|raise\|return\)\>' + " See if the user has already dedented + if indent(a:lnum) > plindent - &sw + " If not, recommend one dedent + return plindent - &sw + endif + " Otherwise, trust the user + return -1 + endif + + return -1 + +endfunction + +" vim:sw=2 + + +endif diff --git a/syntax/nim.vim b/syntax/nim.vim new file mode 100644 index 0000000..868da69 --- /dev/null +++ b/syntax/nim.vim @@ -0,0 +1,192 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'nim') == -1 + +" For version 5.x: Clear all syntax items +" For version 6.x: Quit when a syntax file was already loaded +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +" Keep user-supplied options +if !exists("nim_highlight_numbers") + let nim_highlight_numbers = 1 +endif +if !exists("nim_highlight_builtins") + let nim_highlight_builtins = 1 +endif +if !exists("nim_highlight_exceptions") + let nim_highlight_exceptions = 1 +endif +if !exists("nim_highlight_space_errors") + let nim_highlight_space_errors = 1 +endif + +if exists("nim_highlight_all") + let nim_highlight_numbers = 1 + let nim_highlight_builtins = 1 + let nim_highlight_exceptions = 1 + let nim_highlight_space_errors = 1 +endif + +syn region nimBrackets contained extend keepend matchgroup=Bold start=+\(\\\)\@" + syn match nimNumber "\v<[0-9_]+(\'(i|I|f|F|u|U)(8|16|32|64))?>" + syn match nimNumber "\v[0-9]\.[0-9_]+([eE][+-]=[0-9_]+)=>" + syn match nimNumber "\v<[0-9_]+(\.[0-9_]+)?([eE][+-]?[0-9_]+)?(\'(f|F)(32|64))?>" +endif + +if nim_highlight_builtins == 1 + " builtin functions, types and objects, not really part of the syntax + syn keyword nimBuiltin int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float float32 float64 bool + syn keyword nimBuiltin char string cstring pointer range array openarray seq + syn keyword nimBuiltin set Byte Natural Positive TObject PObject Conversion TResult TAddress + syn keyword nimBuiltin BiggestInt BiggestFloat cchar cschar cshort cint csize cuchar cushort + syn keyword nimBuiltin clong clonglong cfloat cdouble clongdouble cuint culong culonglong cchar + syn keyword nimBuiltin cstringArray TEndian PFloat32 PFloat64 PInt64 PInt32 + syn keyword nimBuiltin TGC_Strategy TFile TFileMode TFileHandle isMainModule + syn keyword nimBuiltin CompileDate CompileTime nimVersion nimMajor + syn keyword nimBuiltin nimMinor nimPatch cpuEndian hostOS hostCPU inf + syn keyword nimBuiltin neginf nan QuitSuccess QuitFailure dbgLineHook stdin + syn keyword nimBuiltin stdout stderr defined new high low sizeof succ pred + syn keyword nimBuiltin inc dec newSeq len incl excl card ord chr ze ze64 + syn keyword nimBuiltin toU8 toU16 toU32 abs min max add repr + syn match nimBuiltin "\" + syn keyword nimBuiltin toFloat toBiggestFloat toInt toBiggestInt addQuitProc + syn keyword nimBuiltin copy setLen newString zeroMem copyMem moveMem + syn keyword nimBuiltin equalMem alloc alloc0 realloc dealloc setLen assert + syn keyword nimBuiltin swap getRefcount getCurrentException Msg + syn keyword nimBuiltin getOccupiedMem getFreeMem getTotalMem isNil seqToPtr + syn keyword nimBuiltin find pop GC_disable GC_enable GC_fullCollect + syn keyword nimBuiltin GC_setStrategy GC_enableMarkAnd Sweep + syn keyword nimBuiltin GC_disableMarkAnd Sweep GC_getStatistics GC_ref + syn keyword nimBuiltin GC_ref GC_ref GC_unref GC_unref GC_unref quit + syn keyword nimBuiltin OpenFile OpenFile CloseFile EndOfFile readChar + syn keyword nimBuiltin FlushFile readFile write readLine writeln writeln + syn keyword nimBuiltin getFileSize ReadBytes ReadChars readBuffer writeBytes + syn keyword nimBuiltin writeChars writeBuffer setFilePos getFilePos + syn keyword nimBuiltin fileHandle countdown countup items lines +endif + +if nim_highlight_exceptions == 1 + " builtin exceptions and warnings + syn keyword nimException E_Base EAsynch ESynch ESystem EIO EOS + syn keyword nimException ERessourceExhausted EArithmetic EDivByZero + syn keyword nimException EOverflow EAccessViolation EAssertionFailed + syn keyword nimException EControlC EInvalidValue EOutOfMemory EInvalidIndex + syn keyword nimException EInvalidField EOutOfRange EStackOverflow + syn keyword nimException ENoExceptionToReraise EInvalidObjectAssignment + syn keyword nimException EInvalidObject EInvalidLibrary EInvalidKey + syn keyword nimException EInvalidObjectConversion EFloatingPoint + syn keyword nimException EFloatInvalidOp EFloatDivByZero EFloatOverflow + syn keyword nimException EFloatInexact EDeadThread EResourceExhausted + syn keyword nimException EFloatUnderflow +endif + +if nim_highlight_space_errors == 1 + " trailing whitespace + syn match nimSpaceError display excludenl "\S\s\+$"ms=s+1 + " any tabs are illegal in nim + syn match nimSpaceError display "\t" +endif + +syn sync match nimSync grouphere NONE "):$" +syn sync maxlines=200 +syn sync minlines=2000 + +if version >= 508 || !exists("did_nim_syn_inits") + if version <= 508 + let did_nim_syn_inits = 1 + command -nargs=+ HiLink hi link + else + command -nargs=+ HiLink hi def link + endif + + " The default methods for highlighting. Can be overridden later + HiLink nimBrackets Operator + HiLink nimKeyword Keyword + HiLink nimFunction Function + HiLink nimConditional Conditional + HiLink nimRepeat Repeat + HiLink nimString String + HiLink nimRawString String + HiLink nimBoolean Boolean + HiLink nimEscape Special + HiLink nimOperator Operator + HiLink nimPreCondit PreCondit + HiLink nimComment Comment + HiLink nimTodo Todo + HiLink nimDecorator Define + + if nim_highlight_numbers == 1 + HiLink nimNumber Number + endif + + if nim_highlight_builtins == 1 + HiLink nimBuiltin Number + endif + + if nim_highlight_exceptions == 1 + HiLink nimException Exception + endif + + if nim_highlight_space_errors == 1 + HiLink nimSpaceError Error + endif + + delcommand HiLink +endif + +let b:current_syntax = "nim" + + +endif