summaryrefslogtreecommitdiffstats
path: root/autoload/unison.vim
diff options
context:
space:
mode:
Diffstat (limited to 'autoload/unison.vim')
-rw-r--r--autoload/unison.vim108
1 files changed, 108 insertions, 0 deletions
diff --git a/autoload/unison.vim b/autoload/unison.vim
new file mode 100644
index 00000000..cdc2efe5
--- /dev/null
+++ b/autoload/unison.vim
@@ -0,0 +1,108 @@
+if polyglot#init#is_disabled(expand('<sfile>:p'), 'unison', 'autoload/unison.vim')
+ finish
+endif
+
+" Unison functionality for Vim, including type/term omnicompletion.
+"
+" Maintainer: Unison Computing
+" Original Author: Cody Allen (ceedubs)
+
+if exists('g:autoloaded_unison')
+ finish
+endif
+let g:autoloaded_unison = 1
+
+let s:required_config_value = "!REQUIRED!"
+
+" adapted from https://github.com/rust-lang/rust.vim/blob/4aa69b84c8a58fcec6b6dad6fe244b916b1cf830/autoload/rust.vim#L9-L18
+function! s:config(name, default) abort
+ let name = 'unison_' . a:name
+ " Local buffer variable with same name takes predeence over global
+ if has_key(b:, name)
+ return get(b:, name)
+ elseif has_key(g:, name)
+ return get(g:, name)
+ elseif a:default == s:required_config_value
+ throw 'Missing required configuration value: ' . name
+ else
+ return a:default
+ endif
+endfunction
+
+function! s:curl_path() abort
+ return s:config('curl_path', "curl")
+endfunction
+
+function! s:jq_path() abort
+ return s:config('jq_path', "jq")
+endfunction
+
+function! unison#SetBufferDefaults() abort
+ if s:config('set_buffer_defaults', 1)
+ " Since Unison completion is fuzzy and not prefix-based, 'longest' doesn't
+ " work well, and 'noinsert' behaves a little better.
+ setlocal completeopt=menuone,noinsert,preview
+
+ setlocal omnifunc=unison#Complete
+ endif
+endfunction
+
+" Unison completion satisfying the standard vim completion signature, such
+" that it can be assigned to omnifunc.
+" vim will first call this to find the base input that should be completed,
+" and then will call it again with the base input.
+function! unison#Complete(findstart, base) abort
+ if a:findstart
+ " locate the start of the word
+ let line = getline('.')
+ let start = col('.') - 1
+ while start > 0 && line[start - 1] !~ '\s' && line[start - 1] != '(' && line[start - 1] != ')'
+ let start -= 1
+ endwhile
+ return start
+ else
+ return unison#CompleteForBase(a:base)
+ endif
+endfunction
+
+" Return an array of completion items for the provided base input. For example
+" base could be 'List.foldL', in which case the top result would probably be
+" 'List.foldLeft'.
+function! unison#CompleteForBase(base) abort
+ let resultLimit = s:config('complete_result_limit', 20)
+ let apiHost = s:config('api_host', 'localhost')
+ let apiPort = s:config('api_port', s:required_config_value)
+ let apiToken = s:config('api_token', s:required_config_value)
+ let apiUri = 'http://' . apiHost . ':' . apiPort . '/' . apiToken . '/api/find'
+
+ let curlCommand = s:curl_path() . " -Gfs
+ \ --data-urlencode 'limit=" . resultLimit . "'
+ \ --data-urlencode 'query=" . a:base . "' "
+ \ . apiUri
+
+ let jqFilter = '
+ \ def prettyTermType: .termType|[(.[] | .segment)]|add;
+ \ def prettyTypeDef: if .tag == "BuiltinObject" then "builtin type " else "" end + (.contents|[(.[] | .segment)]|add);
+ \ def termToMatch: {
+ \ word: .bestFoundTermName,
+ \ info: (.namedTerm.termName + " : " + (.namedTerm|prettyTermType)),
+ \ menu: .namedTerm|prettyTermType
+ \ };
+ \ def typeToMatch: {
+ \ word: .bestFoundTypeName,
+ \ info: (.namedType.typeName + " : " + (.typeDef|prettyTypeDef)),
+ \ menu: .typeDef|prettyTypeDef
+ \ };
+ \ .[][1]|(
+ \ (select(.tag == "FoundTermResult")|.contents|termToMatch),
+ \ (select(.tag == "FoundTypeResult")|.contents|typeToMatch)
+ \ )'
+
+ let command = curlCommand . " | " . s:jq_path() . " -c '" . jqFilter . "'"
+ let lines = system(command)
+ let resultObjects = split(lines, "\n")
+ call map(resultObjects, {_, val -> json_decode(val)})
+ return resultObjects
+endfunction
+
+" vim: set et sw=2 sts=2 ts=2: