summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Stankiewicz <sheerun@sher.pl>2020-08-24 10:56:09 +0200
committerAdam Stankiewicz <sheerun@sher.pl>2020-08-24 10:56:09 +0200
commit63a175357a139ebbecf724b5d8229d383a08afef (patch)
treed338eeedf6ba4271c1a6d74b470815c8b1cf6424
parent25db54573ad9d436ffff97124887c68a74b07e66 (diff)
downloadvim-polyglot-63a175357a139ebbecf724b5d8229d383a08afef.tar.gz
vim-polyglot-63a175357a139ebbecf724b5d8229d383a08afef.zip
Remove some custom dirs config
-rw-r--r--README.md8
-rw-r--r--autoload/fsharp.vim517
-rw-r--r--autoload/ledger.vim744
-rw-r--r--autoload/nim.vim244
-rw-r--r--packages.yaml18
-rwxr-xr-xscripts/build5
6 files changed, 1523 insertions, 13 deletions
diff --git a/README.md b/README.md
index 44997f0f..ce13dcd1 100644
--- a/README.md
+++ b/README.md
@@ -79,13 +79,13 @@ If you need full functionality of any plugin, please use it directly with your p
- [ferm](https://github.com/vim-scripts/ferm.vim) (syntax)
- [fish](https://github.com/georgewitteman/vim-fish) (syntax, indent, compiler, autoload, ftplugin)
- [flatbuffers](https://github.com/dcharbon/vim-flatbuffers) (syntax)
-- [fsharp](https://github.com/ionide/Ionide-vim) (syntax, indent)
+- [fsharp](https://github.com/ionide/Ionide-vim) (syntax, indent, autoload)
- [gdscript](https://github.com/calviken/vim-gdscript3) (syntax, indent)
- [git](https://github.com/tpope/vim-git) (syntax, indent, ftplugin)
- [glsl](https://github.com/tikhomirov/vim-glsl) (syntax, indent)
- [gmpl](https://github.com/maelvalais/gmpl.vim) (syntax)
- [gnuplot](https://github.com/vim-scripts/gnuplot-syntax-highlighting) (syntax)
-- [go](https://github.com/fatih/vim-go) (syntax, compiler, indent)
+- [go](https://github.com/fatih/vim-go) (syntax, indent, compiler)
- [gradle](https://github.com/tfnico/vim-gradle) (compiler)
- [graphql](https://github.com/jparise/vim-graphql) (syntax, indent, autoload, ftplugin, after)
- [haml](https://github.com/sheerun/vim-haml) (syntax, indent, compiler, ftplugin)
@@ -112,7 +112,7 @@ If you need full functionality of any plugin, please use it directly with your p
- [jsx](https://github.com/MaxMEllon/vim-jsx-pretty) (autoload, after)
- [julia](https://github.com/JuliaEditorSupport/julia-vim) (syntax, indent, autoload, ftplugin)
- [kotlin](https://github.com/udalov/kotlin-vim) (syntax, indent, ftplugin)
-- [ledger](https://github.com/ledger/vim-ledger) (syntax, compiler, indent)
+- [ledger](https://github.com/ledger/vim-ledger) (syntax, indent, compiler, autoload)
- [less](https://github.com/groenewege/vim-less) (syntax, indent, ftplugin)
- [lilypond](https://github.com/anowlcalledjosh/vim-lilypond) (syntax, indent, compiler, ftplugin)
- [livescript](https://github.com/gkz/vim-ls) (syntax, indent, compiler, ftplugin)
@@ -126,7 +126,7 @@ If you need full functionality of any plugin, please use it directly with your p
- [meson](https://github.com/mesonbuild/meson) (syntax, indent, ftplugin)
- [moonscript](https://github.com/leafo/moonscript-vim) (syntax, indent, ftplugin)
- [nginx](https://github.com/chr4/nginx.vim) (syntax, indent, ftplugin)
-- [nim](https://github.com/zah/nim.vim) (syntax, compiler, indent)
+- [nim](https://github.com/zah/nim.vim) (syntax, indent, compiler, autoload)
- [nix](https://github.com/LnL7/vim-nix) (syntax, indent, compiler, ftplugin)
- [objc](https://github.com/b4winckler/vim-objc) (ftplugin, syntax, indent)
- [ocaml](https://github.com/rgrinberg/vim-ocaml) (syntax, indent, compiler, ftplugin)
diff --git a/autoload/fsharp.vim b/autoload/fsharp.vim
new file mode 100644
index 00000000..6816d822
--- /dev/null
+++ b/autoload/fsharp.vim
@@ -0,0 +1,517 @@
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'fsharp') == -1
+
+" Vim autoload functions
+
+if exists('g:loaded_autoload_fsharp')
+ finish
+endif
+let g:loaded_autoload_fsharp = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+function! s:prompt(msg)
+ let height = &cmdheight
+ if height < 2
+ set cmdheight=2
+ endif
+ echom a:msg
+ let &cmdheight = height
+endfunction
+
+function! s:PlainNotification(content)
+ return { 'Content': a:content }
+endfunction
+
+function! s:TextDocumentIdentifier(path)
+ let usr_ss_opt = &shellslash
+ set shellslash
+ let uri = fnamemodify(a:path, ":p")
+ if uri[0] == "/"
+ let uri = "file://" . uri
+ else
+ let uri = "file:///" . uri
+ endif
+ let &shellslash = usr_ss_opt
+ return { 'Uri': uri }
+endfunction
+
+function! s:Position(line, character)
+ return { 'Line': a:line, 'Character': a:character }
+endfunction
+
+function! s:TextDocumentPositionParams(documentUri, line, character)
+ return {
+ \ 'TextDocument': s:TextDocumentIdentifier(a:documentUri),
+ \ 'Position': s:Position(a:line, a:character)
+ \ }
+endfunction
+
+function! s:DocumentationForSymbolRequest(xmlSig, assembly)
+ return {
+ \ 'XmlSig': a:xmlSig,
+ \ 'Assembly': a:assembly
+ \ }
+endfunction
+
+function! s:ProjectParms(projectUri)
+ return { 'Project': s:TextDocumentIdentifier(a:projectUri) }
+endfunction
+
+function! s:WorkspacePeekRequest(directory, deep, excludedDirs)
+ return {
+ \ 'Directory': fnamemodify(a:directory, ":p"),
+ \ 'Deep': a:deep,
+ \ 'ExcludedDirs': a:excludedDirs
+ \ }
+endfunction
+
+function! s:WorkspaceLoadParms(files)
+ let prm = []
+ for file in a:files
+ call add(prm, s:TextDocumentIdentifier(file))
+ endfor
+ return { 'TextDocuments': prm }
+endfunction
+
+function! s:FsdnRequest(query)
+ return { 'Query': a:query }
+endfunction
+
+function! s:call(method, params, cont)
+ call LanguageClient#Call(a:method, a:params, a:cont)
+endfunction
+
+function! s:signature(filePath, line, character, cont)
+ return s:call('fsharp/signature', s:TextDocumentPositionParams(a:filePath, a:line, a:character), a:cont)
+endfunction
+function! s:signatureData(filePath, line, character, cont)
+ return s:call('fsharp/signatureData', s:TextDocumentPositionParams(a:filePath, a:line, a:character), a:cont)
+endfunction
+function! s:lineLens(projectPath, cont)
+ return s:call('fsharp/lineLens', s:ProjectParms(a:projectPath), a:cont)
+endfunction
+function! s:compilerLocation(cont)
+ return s:call('fsharp/compilerLocation', {}, a:cont)
+endfunction
+function! s:compile(projectPath, cont)
+ return s:call('fsharp/compile', s:ProjectParms(a:projectPath), a:cont)
+endfunction
+function! s:workspacePeek(directory, depth, excludedDirs, cont)
+ return s:call('fsharp/workspacePeek', s:WorkspacePeekRequest(a:directory, a:depth, a:excludedDirs), a:cont)
+endfunction
+function! s:workspaceLoad(files, cont)
+ return s:call('fsharp/workspaceLoad', s:WorkspaceLoadParms(a:files), a:cont)
+endfunction
+function! s:project(projectPath, cont)
+ return s:call('fsharp/project', s:ProjectParms(a:projectPath), a:cont)
+endfunction
+function! s:fsdn(signature, cont)
+ return s:call('fsharp/fsdn', s:FsdnRequest(a:signature), a:cont)
+endfunction
+function! s:f1Help(filePath, line, character, cont)
+ return s:call('fsharp/f1Help', s:TextDocumentPositionParams(a:filePath, a:line, a:character), a:cont)
+endfunction
+function! fsharp#documentation(filePath, line, character, cont)
+ return s:call('fsharp/documentation', s:TextDocumentPositionParams(a:filePath, a:line, a:character), a:cont)
+endfunction
+function! s:documentationSymbol(xmlSig, assembly, cont)
+ return s:call('fsharp/documentationSymbol', s:DocumentationForSymbolRequest(a:xmlSig, a:assembly), a:cont)
+endfunction
+
+" FSharpConfigDto from https://github.com/fsharp/FsAutoComplete/blob/master/src/FsAutoComplete/LspHelpers.fs
+"
+" * The following options seems not working with workspace/didChangeConfiguration
+" since the initialization has already completed?
+" 'AutomaticWorkspaceInit',
+" 'WorkspaceModePeekDeepLevel',
+"
+" * Changes made to linter/unused analyzer settings seems not reflected after sending them to FSAC?
+"
+let s:config_keys_camel =
+ \ [
+ \ {'key': 'AutomaticWorkspaceInit', 'default': 1},
+ \ {'key': 'WorkspaceModePeekDeepLevel', 'default': 2},
+ \ {'key': 'ExcludeProjectDirectories', 'default': []},
+ \ {'key': 'keywordsAutocomplete', 'default': 1},
+ \ {'key': 'ExternalAutocomplete', 'default': 0},
+ \ {'key': 'Linter', 'default': 1},
+ \ {'key': 'UnionCaseStubGeneration', 'default': 1},
+ \ {'key': 'UnionCaseStubGenerationBody'},
+ \ {'key': 'RecordStubGeneration', 'default': 1},
+ \ {'key': 'RecordStubGenerationBody'},
+ \ {'key': 'InterfaceStubGeneration', 'default': 1},
+ \ {'key': 'InterfaceStubGenerationObjectIdentifier', 'default': 'this'},
+ \ {'key': 'InterfaceStubGenerationMethodBody'},
+ \ {'key': 'UnusedOpensAnalyzer', 'default': 1},
+ \ {'key': 'UnusedDeclarationsAnalyzer', 'default': 1},
+ \ {'key': 'SimplifyNameAnalyzer', 'default': 0},
+ \ {'key': 'ResolveNamespaces', 'default': 1},
+ \ {'key': 'EnableReferenceCodeLens', 'default': 1},
+ \ {'key': 'EnableAnalyzers', 'default': 0},
+ \ {'key': 'AnalyzersPath'},
+ \ {'key': 'DisableInMemoryProjectReferences', 'default': 0},
+ \ {'key': 'LineLens', 'default': {'enabled': 'replaceCodeLens', 'prefix': '//'}},
+ \ {'key': 'UseSdkScripts', 'default': 1},
+ \ {'key': 'dotNetRoot'},
+ \ {'key': 'fsiExtraParameters', 'default': []},
+ \ ]
+let s:config_keys = []
+
+function! fsharp#toSnakeCase(str)
+ let sn = substitute(a:str, '\(\<\u\l\+\|\l\+\)\(\u\)', '\l\1_\l\2', 'g')
+ if sn == a:str | return tolower(a:str) | endif
+ return sn
+endfunction
+
+function! s:buildConfigKeys()
+ if len(s:config_keys) == 0
+ for key_camel in s:config_keys_camel
+ let key = {}
+ let key.snake = fsharp#toSnakeCase(key_camel.key)
+ let key.camel = key_camel.key
+ if has_key(key_camel, 'default')
+ let key.default = key_camel.default
+ endif
+ call add(s:config_keys, key)
+ endfor
+ endif
+endfunction
+
+function! g:fsharp#getServerConfig()
+ let fsharp = {}
+ call s:buildConfigKeys()
+ for key in s:config_keys
+ if exists('g:fsharp#' . key.snake)
+ let fsharp[key.camel] = g:fsharp#{key.snake}
+ elseif exists('g:fsharp#' . key.camel)
+ let fsharp[key.camel] = g:fsharp#{key.camel}
+ elseif has_key(key, 'default') && g:fsharp#use_recommended_server_config
+ let g:fsharp#{key.snake} = key.default
+ let fsharp[key.camel] = key.default
+ endif
+ endfor
+ return fsharp
+endfunction
+
+function! g:fsharp#updateServerConfig()
+ let fsharp = fsharp#getServerConfig()
+ let settings = {'settings': {'FSharp': fsharp}}
+ call LanguageClient#Notify('workspace/didChangeConfiguration', settings)
+endfunction
+
+function! s:findWorkspace(dir, cont)
+ let s:cont_findWorkspace = a:cont
+ function! s:callback_findWorkspace(result)
+ let result = a:result
+ let content = json_decode(result.result.content)
+ if len(content.Data.Found) < 1
+ return []
+ endif
+ let workspace = { 'Type': 'none' }
+ for found in content.Data.Found
+ if workspace.Type == 'none'
+ let workspace = found
+ elseif found.Type == 'solution'
+ if workspace.Type == 'project'
+ let workspace = found
+ else
+ let curLen = len(workspace.Data.Items)
+ let newLen = len(found.Data.Items)
+ if newLen > curLen
+ let workspace = found
+ endif
+ endif
+ endif
+ endfor
+ if workspace.Type == 'solution'
+ call s:cont_findWorkspace([workspace.Data.Path])
+ else
+ call s:cont_findWorkspace(workspace.Data.Fsprojs)
+ endif
+ endfunction
+ call s:workspacePeek(a:dir, g:fsharp#workspace_mode_peek_deep_level, g:fsharp#exclude_project_directories, function("s:callback_findWorkspace"))
+endfunction
+
+let s:workspace = []
+
+function! s:load(arg)
+ let s:loading_workspace = a:arg
+ function! s:callback_load(_)
+ echo "[FSAC] Workspace loaded: " . join(s:loading_workspace, ', ')
+ let s:workspace = s:workspace + s:loading_workspace
+ endfunction
+ call s:workspaceLoad(a:arg, function("s:callback_load"))
+endfunction
+
+function! fsharp#loadProject(...)
+ let prjs = []
+ for proj in a:000
+ call add(prjs, fnamemodify(proj, ':p'))
+ endfor
+ call s:load(prjs)
+endfunction
+
+function! fsharp#loadWorkspaceAuto()
+ if &ft == 'fsharp'
+ call fsharp#updateServerConfig()
+ if g:fsharp#automatic_workspace_init
+ echom "[FSAC] Loading workspace..."
+ let bufferDirectory = fnamemodify(resolve(expand('%:p')), ':h')
+ call s:findWorkspace(bufferDirectory, function("s:load"))
+ endif
+ endif
+endfunction
+
+function! fsharp#reloadProjects()
+ if len(s:workspace) > 0
+ function! s:callback_reloadProjects(_)
+ call s:prompt("[FSAC] Workspace reloaded.")
+ endfunction
+ call s:workspaceLoad(s:workspace, function("s:callback_reloadProjects"))
+ else
+ echom "[FSAC] Workspace is empty"
+ endif
+endfunction
+
+function! fsharp#OnFSProjSave()
+ if &ft == "fsharp_project" && exists('g:fsharp#automatic_reload_workspace') && g:fsharp#automatic_reload_workspace
+ call fsharp#reloadProjects()
+ endif
+endfunction
+
+function! fsharp#showSignature()
+ function! s:callback_showSignature(result)
+ let result = a:result
+ if exists('result.result.content')
+ let content = json_decode(result.result.content)
+ if exists('content.Data')
+ echom substitute(content.Data, '\n\+$', ' ', 'g')
+ endif
+ endif
+ endfunction
+ call s:signature(expand('%:p'), line('.') - 1, col('.') - 1, function("s:callback_showSignature"))
+endfunction
+
+function! fsharp#OnCursorMove()
+ if g:fsharp#show_signature_on_cursor_move
+ call fsharp#showSignature()
+ endif
+endfunction
+
+function! fsharp#showF1Help()
+ let result = s:f1Help(expand('%:p'), line('.') - 1, col('.') - 1)
+ echo result
+endfunction
+
+function! fsharp#showTooltip()
+ function! s:callback_showTooltip(result)
+ let result = a:result
+ if exists('result.result.content')
+ let content = json_decode(result.result.content)
+ if exists('content.Data')
+ call LanguageClient#textDocument_hover()
+ endif
+ endif
+ endfunction
+ " show hover only if signature exists for the current position
+ call s:signature(expand('%:p'), line('.') - 1, col('.') - 1, function("s:callback_showTooltip"))
+endfunction
+
+let s:script_root_dir = expand('<sfile>:p:h') . "/../"
+let s:fsac = fnamemodify(s:script_root_dir . "fsac/fsautocomplete.dll", ":p")
+let g:fsharp#languageserver_command =
+ \ ['dotnet', s:fsac,
+ \ '--background-service-enabled'
+ \ ]
+
+function! s:download(branch)
+ echom "[FSAC] Downloading FSAC. This may take a while..."
+ let zip = s:script_root_dir . "fsac.zip"
+ call system(
+ \ 'curl -fLo ' . zip . ' --create-dirs ' .
+ \ '"https://ci.appveyor.com/api/projects/fsautocomplete/fsautocomplete/artifacts/bin/pkgs/fsautocomplete.netcore.zip?branch=' . a:branch . '"'
+ \ )
+ if v:shell_error == 0
+ call system('unzip -o -d ' . s:script_root_dir . "/fsac " . zip)
+ echom "[FSAC] Updated FsAutoComplete to version " . a:branch . ""
+ else
+ echom "[FSAC] Failed to update FsAutoComplete"
+ endif
+endfunction
+
+function! fsharp#updateFSAC(...)
+ if len(a:000) == 0
+ let branch = "master"
+ else
+ let branch = a:000[0]
+ endif
+ call s:download(branch)
+endfunction
+
+let s:fsi_buffer = -1
+let s:fsi_job = -1
+let s:fsi_width = 0
+let s:fsi_height = 0
+
+function! s:win_gotoid_safe(winid)
+ function! s:vimReturnFocus(window)
+ call win_gotoid(a:window)
+ redraw!
+ endfunction
+ if has('nvim')
+ call win_gotoid(a:winid)
+ else
+ call timer_start(1, { -> s:vimReturnFocus(a:winid) })
+ endif
+endfunction
+
+function! s:get_fsi_command()
+ let cmd = g:fsharp#fsi_command
+ for prm in g:fsharp#fsi_extra_parameters
+ let cmd = cmd . " " . prm
+ endfor
+ return cmd
+endfunction
+
+function! fsharp#openFsi(returnFocus)
+ if bufwinid(s:fsi_buffer) <= 0
+ let fsi_command = s:get_fsi_command()
+ " Neovim
+ if exists('*termopen') || exists('*term_start')
+ let current_win = win_getid()
+ execute g:fsharp#fsi_window_command
+ if s:fsi_width > 0 | execute 'vertical resize' s:fsi_width | endif
+ if s:fsi_height > 0 | execute 'resize' s:fsi_height | endif
+ " if window is closed but FSI is still alive then reuse it
+ if s:fsi_buffer >= 0 && bufexists(str2nr(s:fsi_buffer))
+ exec 'b' s:fsi_buffer
+ normal G
+ if !has('nvim') && mode() == 'n' | execute "normal A" | endif
+ if a:returnFocus | call s:win_gotoid_safe(current_win) | endif
+ " open FSI: Neovim
+ elseif has('nvim')
+ let s:fsi_job = termopen(fsi_command)
+ if s:fsi_job > 0
+ let s:fsi_buffer = bufnr("%")
+ else
+ close
+ echom "[FSAC] Failed to open FSI."
+ return -1
+ endif
+ " open FSI: Vim
+ else
+ let options = {
+ \ "term_name": "F# Interactive",
+ \ "curwin": 1,
+ \ "term_finish": "close"
+ \ }
+ let s:fsi_buffer = term_start(fsi_command, options)
+ if s:fsi_buffer != 0
+ if exists('*term_setkill') | call term_setkill(s:fsi_buffer, "term") | endif
+ let s:fsi_job = term_getjob(s:fsi_buffer)
+ else
+ close
+ echom "[FSAC] Failed to open FSI."
+ return -1
+ endif
+ endif
+ setlocal bufhidden=hide
+ normal G
+ if a:returnFocus | call s:win_gotoid_safe(current_win) | endif
+ return s:fsi_buffer
+ else
+ echom "[FSAC] Your Vim does not support terminal".
+ return 0
+ endif
+ endif
+ return s:fsi_buffer
+endfunction
+
+function! fsharp#toggleFsi()
+ let fsiWindowId = bufwinid(s:fsi_buffer)
+ if fsiWindowId > 0
+ let current_win = win_getid()
+ call win_gotoid(fsiWindowId)
+ let s:fsi_width = winwidth('%')
+ let s:fsi_height = winheight('%')
+ close
+ call win_gotoid(current_win)
+ else
+ call fsharp#openFsi(0)
+ endif
+endfunction
+
+function! fsharp#quitFsi()
+ if s:fsi_buffer >= 0 && bufexists(str2nr(s:fsi_buffer))
+ if has('nvim')
+ let winid = bufwinid(s:fsi_buffer)
+ if winid > 0 | execute "close " . winid | endif
+ call jobstop(s:fsi_job)
+ else
+ call job_stop(s:fsi_job, "term")
+ endif
+ let s:fsi_buffer = -1
+ let s:fsi_job = -1
+ endif
+endfunction
+
+function! fsharp#resetFsi()
+ call fsharp#quitFsi()
+ return fsharp#openFsi(1)
+endfunction
+
+function! fsharp#sendFsi(text)
+ if fsharp#openFsi(!g:fsharp#fsi_focus_on_send) > 0
+ " Neovim
+ if has('nvim')
+ call chansend(s:fsi_job, a:text . ";;". "\n")
+ " Vim 8
+ else
+ call term_sendkeys(s:fsi_buffer, a:text . ";;" . "\<cr>")
+ call term_wait(s:fsi_buffer)
+ endif
+ endif
+endfunction
+
+" https://stackoverflow.com/a/6271254
+function! s:get_visual_selection()
+ let [line_start, column_start] = getpos("'<")[1:2]
+ let [line_end, column_end] = getpos("'>")[1:2]
+ let lines = getline(line_start, line_end)
+ if len(lines) == 0
+ return ''
+ endif
+ let lines[-1] = lines[-1][: column_end - (&selection == 'inclusive' ? 1 : 2)]
+ let lines[0] = lines[0][column_start - 1:]
+ return lines
+endfunction
+
+function! s:get_complete_buffer()
+ return join(getline(1, '$'), "\n")
+endfunction
+
+function! fsharp#sendSelectionToFsi() range
+ let lines = s:get_visual_selection()
+ exec 'normal' len(lines) . 'j'
+ let text = join(lines, "\n")
+ return fsharp#sendFsi(text)
+endfunction
+
+function! fsharp#sendLineToFsi()
+ let text = getline('.')
+ exec 'normal j'
+ return fsharp#sendFsi(text)
+endfunction
+
+function! fsharp#sendAllToFsi()
+ let text = s:get_complete_buffer()
+ return fsharp#sendFsi(text)
+endfunction
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+
+" vim: sw=4 et sts=4
+
+endif
diff --git a/autoload/ledger.vim b/autoload/ledger.vim
new file mode 100644
index 00000000..225f96a3
--- /dev/null
+++ b/autoload/ledger.vim
@@ -0,0 +1,744 @@
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'ledger') == -1
+
+scriptencoding utf-8
+" vim:ts=2:sw=2:sts=2:foldmethod=marker
+function! ledger#transaction_state_toggle(lnum, ...) abort
+ if a:0 == 1
+ let chars = a:1
+ else
+ let chars = ' *'
+ endif
+ let trans = s:transaction.from_lnum(a:lnum)
+ if empty(trans) || has_key(trans, 'expr')
+ return
+ endif
+
+ let old = has_key(trans, 'state') ? trans['state'] : ' '
+ let i = stridx(chars, old) + 1
+ let new = chars[i >= len(chars) ? 0 : i]
+
+ call trans.set_state(new)
+
+ call setline(trans['head'], trans.format_head())
+endf
+
+function! ledger#transaction_state_set(lnum, char) abort
+ " modifies or sets the state of the transaction at the cursor,
+ " removing the state altogether if a:char is empty
+ let trans = s:transaction.from_lnum(a:lnum)
+ if empty(trans) || has_key(trans, 'expr')
+ return
+ endif
+
+ call trans.set_state(a:char)
+
+ call setline(trans['head'], trans.format_head())
+endf
+
+function! ledger#transaction_date_set(lnum, type, ...) abort
+ let time = a:0 == 1 ? a:1 : localtime()
+ let trans = s:transaction.from_lnum(a:lnum)
+ if empty(trans) || has_key(trans, 'expr')
+ return
+ endif
+
+ let formatted = strftime(g:ledger_date_format, time)
+ if has_key(trans, 'date') && ! empty(trans['date'])
+ let date = split(trans['date'], '=')
+ else
+ let date = [formatted]
+ endif
+
+ if a:type =~? 'effective\|actual'
+ echoerr 'actual/effective arguments were replaced by primary/auxiliary'
+ return
+ endif
+
+ if a:type ==? 'primary'
+ let date[0] = formatted
+ elseif a:type ==? 'auxiliary'
+ if time < 0
+ " remove auxiliary date
+ let date = [date[0]]
+ else
+ " set auxiliary date
+ if len(date) >= 2
+ let date[1] = formatted
+ else
+ call add(date, formatted)
+ endif
+ endif
+ elseif a:type ==? 'unshift'
+ let date = [formatted, date[0]]
+ endif
+
+ let trans['date'] = join(date[0:1], '=')
+
+ call setline(trans['head'], trans.format_head())
+endf
+
+" == get transactions ==
+
+function! ledger#transaction_from_lnum(lnum) abort
+ return s:transaction.from_lnum(a:lnum)
+endf
+
+function! ledger#transactions(...) abort
+ if a:0 == 2
+ let lnum = a:1
+ let end = a:2
+ elseif a:0 == 0
+ let lnum = 1
+ let end = line('$')
+ else
+ throw 'wrong number of arguments for get_transactions()'
+ return []
+ endif
+
+ " safe view / position
+ let view = winsaveview()
+ let fe = &foldenable
+ set nofoldenable
+
+ let transactions = []
+ call cursor(lnum, 0)
+ while lnum && lnum < end
+ let trans = s:transaction.from_lnum(lnum)
+ if ! empty(trans)
+ call add(transactions, trans)
+ call cursor(trans['tail'], 0)
+ endif
+ let lnum = search('^[~=[:digit:]]', 'cW')
+ endw
+
+ " restore view / position
+ let &foldenable = fe
+ call winrestview(view)
+
+ return transactions
+endf
+
+" == transaction object implementation ==
+
+let s:transaction = {} "{{{1
+function! s:transaction.new() abort dict
+ return copy(s:transaction)
+endf
+
+function! s:transaction.from_lnum(lnum) abort dict "{{{2
+ let [head, tail] = s:get_transaction_extents(a:lnum)
+ if ! head
+ return {}
+ endif
+
+ let trans = copy(s:transaction)
+ let trans['head'] = head
+ let trans['tail'] = tail
+
+ " split off eventual comments at the end of line
+ let line = split(getline(head), '\ze\s*\%(\t\| \);', 1)
+ if len(line) > 1
+ let trans['appendix'] = join(line[1:], '')
+ endif
+
+ " parse rest of line
+ " FIXME (minor): will not preserve spacing (see 'join(parts)')
+ let parts = split(line[0], '\s\+')
+ if parts[0] ==# '~'
+ let trans['expr'] = join(parts[1:])
+ return trans
+ elseif parts[0] ==# '='
+ let trans['auto'] = join(parts[1:])
+ return trans
+ elseif parts[0] !~# '^\d'
+ " this case is avoided in s:get_transaction_extents(),
+ " but we'll check anyway.
+ return {}
+ endif
+
+ for part in parts
+ if ! has_key(trans, 'date') && part =~# '^\d'
+ let trans['date'] = part
+ elseif ! has_key(trans, 'code') && part =~# '^([^)]*)$'
+ let trans['code'] = part[1:-2]
+ elseif ! has_key(trans, 'state') && part =~# '^[[:punct:]]$'
+ " the first character by itself is assumed to be the state of the transaction.
+ let trans['state'] = part
+ else
+ " everything after date/code or state belongs to the description
+ break
+ endif
+ call remove(parts, 0)
+ endfor
+
+ let trans['description'] = join(parts)
+ return trans
+endf "}}}
+
+function! s:transaction.set_state(char) abort dict "{{{2
+ if has_key(self, 'state') && a:char =~# '^\s*$'
+ call remove(self, 'state')
+ else
+ let self['state'] = a:char
+ endif
+endf "}}}
+
+function! s:transaction.parse_body(...) abort dict "{{{2
+ if a:0 == 2
+ let head = a:1
+ let tail = a:2
+ elseif a:0 == 0
+ let head = self['head']
+ let tail = self['tail']
+ else
+ throw 'wrong number of arguments for parse_body()'
+ return []
+ endif
+
+ if ! head || tail <= head
+ return []
+ endif
+
+ let lnum = head
+ let tags = {}
+ let postings = []
+ while lnum <= tail
+ let line = split(getline(lnum), '\s*\%(\t\| \);', 1)
+
+ if line[0] =~# '^\s\+[^[:blank:];]'
+ " posting
+ let [state, rest] = matchlist(line[0], '^\s\+\([*!]\?\)\s*\(.*\)$')[1:2]
+ if rest =~# '\t\| '
+ let [account, amount] = matchlist(rest, '^\(.\{-}\)\%(\t\| \)\s*\(.\{-}\)\s*$')[1:2]
+ else
+ let amount = ''
+ let account = matchstr(rest, '^\s*\zs.\{-}\ze\s*$')
+ endif
+ call add(postings, {'account': account, 'amount': amount, 'state': state})
+ end
+
+ " where are tags to be stored?
+ if empty(postings)
+ " they belong to the transaction
+ let tag_container = tags
+ else
+ " they belong to last posting
+ if ! has_key(postings[-1], 'tags')
+ let postings[-1]['tags'] = {}
+ endif
+ let tag_container = postings[-1]['tags']
+ endif
+
+ let comment = join(line[1:], ' ;')
+ if comment =~# '^\s*:'
+ " tags without values
+ for t in s:findall(comment, ':\zs[^:[:blank:]]\([^:]*[^:[:blank:]]\)\?\ze:')
+ let tag_container[t] = ''
+ endfor
+ elseif comment =~# '^\s*[^:[:blank:]][^:]\+:'
+ " tag with value
+ let key = matchstr(comment, '^\s*\zs[^:]\+\ze:')
+ if ! empty(key)
+ let val = matchstr(comment, ':\s*\zs.*\ze\s*$')
+ let tag_container[key] = val
+ endif
+ endif
+ let lnum += 1
+ endw
+ return [tags, postings]
+endf "}}}
+
+function! s:transaction.format_head() abort dict "{{{2
+ if has_key(self, 'expr')
+ return '~ '.self['expr']
+ elseif has_key(self, 'auto')
+ return '= '.self['auto']
+ endif
+
+ let parts = []
+ if has_key(self, 'date') | call add(parts, self['date']) | endif
+ if has_key(self, 'state') | call add(parts, self['state']) | endif
+ if has_key(self, 'code') | call add(parts, '('.self['code'].')') | endif
+ if has_key(self, 'description') | call add(parts, self['description']) | endif
+
+ let line = join(parts)
+ if has_key(self, 'appendix') | let line .= self['appendix'] | endif
+
+ return line
+endf "}}}
+"}}}
+
+" == helper functions ==
+
+" get a list of declared accounts in the buffer
+function! ledger#declared_accounts(...) abort
+ if a:0 == 2
+ let lnum = a:1
+ let lend = a:2
+ elseif a:0 == 0
+ let lnum = 1
+ let lend = line('$')
+ else
+ throw 'wrong number of arguments for ledger#declared_accounts()'
+ return []
+ endif
+
+ " save view / position
+ let view = winsaveview()
+ let fe = &foldenable
+ set nofoldenable
+
+ let accounts = []
+ call cursor(lnum, 0)
+ while 1
+ let lnum = search('^account\s', 'cW', lend)
+ if !lnum || lnum > lend
+ break
+ endif
+
+ " remove comments at the end and "account" at the front
+ let line = split(getline(lnum), '\s\+;')[0]
+ let line = matchlist(line, 'account\s\+\(.\+\)')[1]
+
+ if len(line) > 1
+ call add(accounts, line)
+ endif
+
+ call cursor(lnum+1,0)
+ endw
+
+ " restore view / position
+ let &foldenable = fe
+ call winrestview(view)
+
+ return accounts
+endf
+
+function! s:get_transaction_extents(lnum) abort
+ if ! (indent(a:lnum) || getline(a:lnum) =~# '^[~=[:digit:]]')
+ " only do something if lnum is in a transaction
+ return [0, 0]
+ endif
+
+ " safe view / position
+ let view = winsaveview()
+ let fe = &foldenable
+ set nofoldenable
+
+ call cursor(a:lnum, 0)
+ let head = search('^[~=[:digit:]]', 'bcnW')
+ let tail = search('^[^;[:blank:]]\S\+', 'nW')
+ let tail = tail > head ? tail - 1 : line('$')
+
+ " restore view / position
+ let &foldenable = fe
+ call winrestview(view)
+
+ return head ? [head, tail] : [0, 0]
+endf
+
+function! ledger#find_in_tree(tree, levels) abort
+ if empty(a:levels)
+ return []
+ endif
+ let results = []
+ let currentlvl = a:levels[0]
+ let nextlvls = a:levels[1:]
+ let branches = ledger#filter_items(keys(a:tree), currentlvl)
+ for branch in branches
+ let exact = empty(nextlvls)
+ call add(results, [branch, exact])
+ if ! empty(nextlvls)
+ for [result, exact] in ledger#find_in_tree(a:tree[branch], nextlvls)
+ call add(results, [branch.':'.result, exact])
+ endfor
+ endif
+ endfor
+ return results
+endf
+
+function! ledger#filter_items(list, keyword) abort
+ " return only those items that start with a specified keyword
+ return filter(copy(a:list), 'v:val =~ ''^\V'.substitute(a:keyword, '\\', '\\\\', 'g').'''')
+endf
+
+function! s:findall(text, rx) abort
+ " returns all the matches in a string,
+ " there will be overlapping matches according to :help match()
+ let matches = []
+
+ while 1
+ let m = matchstr(a:text, a:rx, 0, len(matches)+1)
+ if empty(m)
+ break
+ endif
+
+ call add(matches, m)
+ endw
+
+ return matches
+endf
+
+" Move the cursor to the specified column, filling the line with spaces if necessary.
+" Ensure that at least min_spaces are added, and go to the end of the line if
+" the line is already too long
+function! s:goto_col(pos, min_spaces) abort
+ exec 'normal!' '$'
+ let diff = max([a:min_spaces, a:pos - virtcol('.')])
+ if diff > 0 | exec 'normal!' diff . 'a ' | endif
+endf
+
+" Return character position of decimal separator (multibyte safe)
+function! s:decimalpos(expr) abort
+ let pos = match(a:expr, '\V' . g:ledger_decimal_sep)
+ if pos > 0
+ let pos = strchars(a:expr[:pos]) - 1
+ endif
+ return pos
+endf
+
+" Align the amount expression after an account name at the decimal point.
+"
+" This function moves the amount expression of a posting so that the decimal
+" separator is aligned at the column specified by g:ledger_align_at.
+"
+" For example, after selecting:
+"
+" 2015/05/09 Some Payee
+" Expenses:Other $120,23 ; Tags here
+" Expenses:Something $-4,99
+" Expenses:More ($12,34 + $16,32)
+"
+" :'<,'>call ledger#align_commodity() produces:
+"
+" 2015/05/09 Some Payee
+" Expenses:Other $120,23 ; Tags here
+" Expenses:Something $-4,99
+" Expenses:More ($12,34 + $16,32)
+"
+function! ledger#align_commodity() abort
+ " Extract the part of the line after the account name (excluding spaces):
+ let l:line = getline('.')
+ let rhs = matchstr(l:line, '\m^\s\+[^;[:space:]].\{-}\(\t\| \)\s*\zs.*$')
+ if rhs !=# ''
+ " Remove everything after the account name (including spaces):
+ call setline('.', substitute(l:line, '\m^\s\+[^[:space:]].\{-}\zs\(\t\| \).*$', '', ''))
+ let pos = -1
+ if g:ledger_decimal_sep !=# ''
+ " Find the position of the first decimal separator:
+ let pos = s:decimalpos(rhs)
+ endif
+ if pos < 0
+ " Find the position after the first digits
+ let pos = matchend(rhs, '\m\d[^[:space:]]*')
+ endif
+ " Go to the column that allows us to align the decimal separator at g:ledger_align_at:
+ if pos > 0
+ call s:goto_col(g:ledger_align_at - pos - 1, 2)
+ else
+ call s:goto_col(g:ledger_align_at - strdisplaywidth(rhs) - 2, 2)
+ endif " Append the part of the line that was previously removed:
+ exe 'normal! a' . rhs
+ endif
+endf
+
+" Align the amount under the cursor and append/prepend the default currency.
+function! ledger#align_amount_at_cursor() abort
+ " Select and cut text:
+ normal! viWd
+ " Find the position of the decimal separator
+ let pos = s:decimalpos(@") " Returns zero when the separator is the empty string
+ if pos <= 0
+ let pos = len(@")
+ endif
+ " Paste text at the correct column and append/prepend default commodity:
+ if g:ledger_commodity_before
+ call s:goto_col(g:ledger_align_at - pos - len(g:ledger_default_commodity) - len(g:ledger_commodity_sep) - 1, 2)
+ exe 'normal! a' . g:ledger_default_commodity . g:ledger_commodity_sep
+ normal! p
+ else
+ call s:goto_col(g:ledger_align_at - pos - 1, 2)
+ exe 'normal! pa' . g:ledger_commodity_sep . g:ledger_default_commodity
+ endif
+endf
+
+" Report generation {{{1
+
+" Helper functions and variables {{{2
+" Position of report windows
+let s:winpos_map = {
+ \ 'T': 'to new', 't': 'abo new', 'B': 'bo new', 'b': 'bel new',
+ \ 'L': 'to vnew', 'l': 'abo vnew', 'R': 'bo vnew', 'r': 'bel vnew'
+ \ }
+
+function! s:error_message(msg) abort
+ redraw " See h:echo-redraw
+ echohl ErrorMsg
+ echo "\r"
+ echomsg a:msg
+ echohl NONE
+endf
+
+function! s:warning_message(msg) abort
+ redraw " See h:echo-redraw
+ echohl WarningMsg
+ echo "\r"
+ echomsg a:msg
+ echohl NONE
+endf
+
+" Open the quickfix/location window when it is not empty,
+" closes it if it is empty.
+"
+" Optional parameters:
+" a:1 Quickfix window title.
+" a:2 Message to show when the window is empty.
+"
+" Returns 0 if the quickfix window is empty, 1 otherwise.
+function! s:quickfix_toggle(...) abort
+ if g:ledger_use_location_list
+ let l:list = 'l'
+ let l:open = (len(getloclist(winnr())) > 0)
+ else
+ let l:list = 'c'
+ let l:open = (len(getqflist()) > 0)
+ endif
+
+ if l:open
+ execute (g:ledger_qf_vertical ? 'vert' : 'botright') l:list.'open' g:ledger_qf_size
+ " Set local mappings to quit the quickfix window or lose focus.
+ nnoremap <silent> <buffer> <tab> <c-w><c-w>
+ execute 'nnoremap <silent> <buffer> q :' l:list.'close<CR>'
+ " Note that the following settings do not persist (e.g., when you close and re-open the quickfix window).
+ " See: https://superuser.com/questions/356912/how-do-i-change-the-quickix-title-status-bar-in-vim
+ if g:ledger_qf_hide_file
+ setl conceallevel=2
+ setl concealcursor=nc
+ syntax match qfFile /^[^|]*/ transparent conceal
+ endif
+ if a:0 > 0
+ let w:quickfix_title = a:1
+ endif
+ return 1
+ endif
+
+ execute l:list.'close'
+ call s:warning_message((a:0 > 1) ? a:2 : 'No results')
+ return 0
+endf
+
+" Populate a quickfix/location window with data. The argument must be a String
+" or a List.
+function! s:quickfix_populate(data) abort
+ " Note that cexpr/lexpr always uses the global value of errorformat
+ let l:efm = &errorformat " Save global errorformat
+ set errorformat=%EWhile\ parsing\ file\ \"%f\"\\,\ line\ %l:,%ZError:\ %m,%-C%.%#
+ set errorformat+=%tarning:\ \"%f\"\\,\ line\ %l:\ %m
+ " Format to parse command-line errors:
+ set errorformat+=Error:\ %m
+ " Format to parse reports:
+ set errorformat+=%f:%l\ %m
+ set errorformat+=%-G%.%#
+ execute (g:ledger_use_location_list ? 'l' : 'c').'getexpr' 'a:data'
+ let &errorformat = l:efm " Restore global errorformat
+ return
+endf
+
+" Build a ledger command to process the given file.
+function! s:ledger_cmd(file, args) abort
+ let l:options = g:ledger_extra_options
+ if len(g:ledger_date_format) > 0 && !g:ledger_is_hledger
+ let l:options = join([l:options, '--date-format', g:ledger_date_format,
+ \ '--input-date-format', g:ledger_date_format])
+ endif
+ return join([g:ledger_bin, l:options, '-f', shellescape(expand(a:file)), a:args])
+endf
+" }}}
+
+function! ledger#autocomplete_and_align() abort
+ if pumvisible()
+ return "\<c-n>"
+ endif
+ " Align an amount only if there is a digit immediately before the cursor and
+ " such digit is preceded by at least one space (the latter condition is
+ " necessary to avoid situations where a date starting at the first column is
+ " confused with a commodity to be aligned).
+ if match(getline('.'), '\s.*\d\%'.col('.').'c') > -1
+ normal! h
+ call ledger#align_amount_at_cursor()
+ return "\<c-o>A"
+ endif
+ return "\<c-x>\<c-o>"
+endf
+
+" Use current line as input to ledger entry and replace with output. If there
+" are errors, they are echoed instead.
+function! ledger#entry() abort
+ let l:output = systemlist(s:ledger_cmd(g:ledger_main, join(['entry', getline('.')])))
+ " Filter out warnings
+ let l:output = filter(l:output, "v:val !~? '^Warning: '")
+ " Errors may occur
+ if v:shell_error
+ echomsg join(l:output)
+ return
+ endif
+ " Append output so we insert instead of overwrite, then delete line
+ call append('.', l:output)
+ normal! "_dd
+endfunc
+
+" Run an arbitrary ledger command and show the output in a new buffer. If
+" there are errors, no new buffer is opened: the errors are displayed in a
+" quickfix window instead.
+"
+" Parameters:
+" file The file to be processed.
+" args A string of Ledger command-line arguments.
+"
+" Returns:
+" Ledger's output as a String.
+function! ledger#report(file, args) abort
+ let l:output = systemlist(s:ledger_cmd(a:file, a:args))
+ if v:shell_error " If there are errors, show them in a quickfix/location list.
+ call s:quickfix_populate(l:output)
+ call s:quickfix_toggle('Errors', 'Unable to parse errors')
+ endif
+ return l:output
+endf
+
+" Open the output of a Ledger's command in a new buffer.
+"
+" Parameters:
+" report A String containing the output of a Ledger's command.
+"
+" Returns:
+" 1 if a new buffer is created; 0 otherwise.
+function! ledger#output(report) abort
+ if empty(a:report)
+ call s:warning_message('No results')
+ return 0
+ endif
+ " Open a new buffer to show Ledger's output.
+ execute get(s:winpos_map, g:ledger_winpos, 'bo new')
+ setlocal buftype=nofile bufhidden=wipe modifiable nobuflisted noswapfile nowrap
+ call append(0, a:report)
+ setlocal nomodifiable
+ " Set local mappings to quit window or lose focus.
+ nnoremap <silent> <buffer> <tab> <c-w><c-p>
+ nnoremap <silent> <buffer> q <c-w><c-p>@=winnr('#')<cr><c-w>c
+ " Add some coloring to the report
+ syntax match LedgerNumber /-\@1<!\d\+\([,.]\d\+\)*/
+ syntax match LedgerNegativeNumber /-\d\+\([,.]\d\+\)*/
+ syntax match LedgerImproperPerc /\d\d\d\+%/
+ return 1
+endf
+
+" Show an arbitrary register report in a quickfix list.
+"
+" Parameters:
+" file The file to be processed
+" args A string of Ledger command-line arguments.
+function! ledger#register(file, args) abort
+ let l:cmd = s:ledger_cmd(a:file, join([
+ \ 'register',
+ \ "--format='" . g:ledger_qf_register_format . "'",
+ \ "--prepend-format='%(filename):%(beg_line) '",
+ \ a:args
+ \ ]))
+ call s:quickfix_populate(systemlist(l:cmd))
+ call s:quickfix_toggle('Register report')
+endf
+
+" Reconcile the given account.
+" This function accepts a file path as a third optional argument.
+" The default is to use the value of g:ledger_main.
+"
+" Parameters:
+" file The file to be processed
+" account An account name (String)
+" target_amount The target amount (Float)
+function! ledger#reconcile(file, account, target_amount) abort
+ let l:cmd = s:ledger_cmd(a:file, join([
+ \ 'register',
+ \ '--uncleared',
+ \ "--format='" . g:ledger_qf_reconcile_format . "'",
+ \ "--prepend-format='%(filename):%(beg_line) %(pending ? \"P\" : \"U\") '",
+ \ shellescape(a:account)
+ \ ]))
+ let l:file = expand(a:file) " Needed for #show_balance() later
+ call s:quickfix_populate(systemlist(l:cmd))
+ if s:quickfix_toggle('Reconcile ' . a:account, 'Nothing to reconcile')
+ let g:ledger_target_amount = a:target_amount
+ " Show updated account balance upon saving, as long as the quickfix window is open
+ augroup reconcile
+ autocmd!
+ execute "autocmd BufWritePost *.ldg,*.ledger call ledger#show_balance('" . l:file . "','" . a:account . "')"
+ autocmd BufWipeout <buffer> call <sid>finish_reconciling()
+ augroup END
+ " Add refresh shortcut
+ execute "nnoremap <silent> <buffer> <c-l> :<c-u>call ledger#reconcile('"
+ \ . l:file . "','" . a:account . "'," . string(a:target_amount) . ')<cr>'
+ call ledger#show_balance(l:file, a:account)
+ endif
+endf
+
+function! s:finish_reconciling() abort
+ unlet g:ledger_target_amount
+ augroup reconcile
+ autocmd!
+ augroup END
+ augroup! reconcile
+endf
+
+" Show the pending/cleared balance of an account.
+" This function has an optional parameter:
+"
+" a:1 An account name
+"
+" If no account if given, the account in the current line is used.
+function! ledger#show_balance(file, ...) abort
+ let l:account = a:0 > 0 && !empty(a:1) ? a:1 : matchstr(getline('.'), '\m\( \|\t\)\zs\S.\{-}\ze\( \|\t\|$\)')
+ if empty(l:account)
+ call s:error_message('No account found')
+ return
+ endif
+ let l:cmd = s:ledger_cmd(a:file, join([
+ \ 'cleared',
+ \ shellescape(l:account),
+ \ '--empty',
+ \ '--collapse',
+ \ "--format='%(scrub(get_at(display_total, 0)))|%(scrub(get_at(display_total, 1)))|%(quantity(scrub(get_at(display_total, 1))))'",
+ \ (empty(g:ledger_default_commodity) ? '' : '-X ' . shellescape(g:ledger_default_commodity))
+ \ ]))
+ let l:output = systemlist(l:cmd)
+ " Errors may occur, for example, when the account has multiple commodities
+ " and g:ledger_default_commodity is empty.
+ if v:shell_error
+ call s:quickfix_populate(l:output)
+ call s:quickfix_toggle('Errors', 'Unable to parse errors')
+ return
+ endif
+ let l:amounts = split(l:output[-1], '|')
+ redraw " Necessary in some cases to overwrite previous messages. See :h echo-redraw
+ if len(l:amounts) < 3
+ call s:error_message('Could not determine balance. Did you use a valid account?')
+ return
+ endif
+ echo g:ledger_pending_string
+ echohl LedgerPending
+ echon l:amounts[0]
+ echohl NONE
+ echon ' ' g:ledger_cleared_string
+ echohl LedgerCleared
+ echon l:amounts[1]
+ echohl NONE
+ if exists('g:ledger_target_amount')
+ echon ' ' g:ledger_target_string
+ echohl LedgerTarget
+ echon printf('%.2f', (g:ledger_target_amount - str2float(l:amounts[2])))
+ echohl NONE
+ endif
+endf
+" }}}
+
+endif
diff --git a/autoload/nim.vim b/autoload/nim.vim
new file mode 100644
index 00000000..59f9122d
--- /dev/null
+++ b/autoload/nim.vim
@@ -0,0 +1,244 @@
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'nim') == -1
+
+let g:nim_log = []
+let s:plugin_path = escape(expand('<sfile>: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
+
+if has("python3")
+ exe 'py3file ' . fnameescape(s:plugin_path) . '/nim_vim.py'
+elseif has("python")
+ exe 'pyfile ' . fnameescape(s:plugin_path) . '/nim_vim.py'
+endif
+
+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 && expand("%:e") == "nim"
+ let false = 0 " Needed for eval of json
+ let true = 1 " Needed for eval of json
+ 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()
+ if has("python3") || has("python")
+ " au QuitPre * :py nimTerminateAll()
+ au VimLeavePre * :py nimTerminateAll()
+ endif
+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
+ let line = getline('.')
+ let start = col('.') - 1
+ while start > 0 && line[start - 1] =~? '\w'
+ let start -= 1
+ endwhile
+ return start
+ 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 word = split(lineData[2], '\.')[-1]
+ if a:base ==? '' || word =~# '^' . a:base
+ let kind = get(g:nim_symbol_types, lineData[1], '')
+ let c = { 'word': word, 'kind': kind, 'menu': lineData[3], 'dup': 1 }
+ call add(result, c)
+ endif
+ 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*'
+
+if !exists('g:neocomplete#sources#omni#input_patterns')
+ let g:neocomplete#sources#omni#input_patterns = {}
+endif
+let g:neocomplete#sources#omni#input_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
+
+endif
diff --git a/packages.yaml b/packages.yaml
index cc857195..8fa39a1a 100644
--- a/packages.yaml
+++ b/packages.yaml
@@ -262,7 +262,8 @@ filetypes:
---
name: fsharp
remote: ionide/Ionide-vim
-dirs: :basic
+ignored_dirs:
+- ftplugin
filetypes:
- name: fsharp
linguist: F#
@@ -321,7 +322,11 @@ filetypes:
---
name: go
remote: fatih/vim-go
-dirs: :go
+ignored_dirs:
+- autoload
+- ftplugin
+extra_dirs:
+- autoload/go/config.vim
filetypes:
- name: go
linguist: Go
@@ -542,7 +547,8 @@ filetypes:
---
name: ledger
remote: ledger/vim-ledger
-dirs: :basic
+ignored_dirs:
+- ftplugin
filetypes:
- name: ledger
extensions:
@@ -655,7 +661,8 @@ filetypes:
---
name: nim
remote: zah/nim.vim
-dirs: :basic
+ignored_dirs:
+- ftplugin
filetypes:
- name: nim
linguist: Nim
@@ -851,7 +858,8 @@ filetypes:
---
name: r-lang
remote: vim-scripts/R.vim
-dirs: :basic
+ignored_dirs:
+- ftplugin
filetypes:
- name: r
linguist: R
diff --git a/scripts/build b/scripts/build
index a3aeb604..a90f3e94 100755
--- a/scripts/build
+++ b/scripts/build
@@ -15,11 +15,8 @@ BASE_URL = 'https://raw.githubusercontent.com/github/linguist/master'
DIRS = {
default: %w(syntax indent compiler autoload ftplugin ctags after/syntax after/indent after/ftplugin),
all: %w(syntax indent compiler autoload ftplugin after extras ctags),
- basic: %w(syntax compiler indent after/syntax after/indent),
syntax: %w(syntax indent after/syntax after/indent),
- noafter: %w(syntax compiler indent autoload ftplugin),
- javascript: %w(syntax indent compiler autoload ftplugin ctags after/syntax after/indent after/ftplugin extras),
- go: %w(autoload/go/config.vim syntax compiler indent after/syntax after/indent)
+ noafter: %w(syntax compiler indent autoload ftplugin)
}
def parallel(*procs)