diff --git a/.aspell.en.pws b/.aspell.en.pws index d9e3c2f97..7f7d41ed8 100644 --- a/.aspell.en.pws +++ b/.aspell.en.pws @@ -1,4 +1,4 @@ -personal_ws-1.1 en 147 +personal_ws-1.1 en 149 grimoire IcedGrep IcedDocumentOpen @@ -42,6 +42,7 @@ sexp dir href IcedEvalOuterTopList +IcedCommandPalette lhs changelog wrapscan @@ -82,6 +83,7 @@ IcedBrowseFunction stdout Javadoc IcedExtractFunction +IcedGrimoireOpen IcedToggleTraceNs FileType IcedMoveToLet diff --git a/autoload/iced/let.vim b/autoload/iced/let.vim index 4c2c1afaf..e72fb96f9 100644 --- a/autoload/iced/let.vim +++ b/autoload/iced/let.vim @@ -6,20 +6,31 @@ function! iced#let#goto() abort let reg_save = @@ try - call iced#paredit#move_to_prev_top_element() - let stopline = line('.') - call winrestview(view) - let ret = search('(let[ \r\n]', 'b', stopline) - if ret != 0 - call search('\[') + if iced#paredit#move_to_current_element_head() == 0 + call winrestview(view) + return 0 endif - return ret + + while v:true + let head = strpart(getline('.'), col('.'), 4) + if head ==# 'let' || head ==# 'let ' + call search('\[') + break + endif + + if iced#paredit#move_to_parent_element() == 0 + call winrestview(view) + return 0 + endif + endwhile + + return col('.') finally let @@ = reg_save endtry endfunction -function! iced#let#move_to_let() abort +function! iced#let#move_to_let(...) abort let view = winsaveview() let reg_save = @@ @@ -29,15 +40,30 @@ function! iced#let#move_to_let() abort let indent = col('.')-1 let form = iced#util#del_indent(indent, form) - let name = trim(input('Name: ')) + let name = get(a:, 1, '') + if empty(name) + let name = trim(input('Name: ')) + endif + if empty(name) + return iced#message#echom('canceled') + endif if iced#let#goto() == 0 + " re-select form + call iced#paredit#get_outer_list_raw() " 6 means `len('(let [')` let form = iced#util#add_indent(len(name)+1+6, form) let @@ = iced#util#add_indent( \ indent, printf("(let [%s %s]\n %s)", name, form, name)) silent normal! gvp else + " re-select form + " FIXME too redundant!! + let tmp = winsaveview() + call winrestview(view) + call iced#paredit#get_outer_list_raw() + call winrestview(tmp) + let pos = getcurpos() let @@ = name silent normal! gvp diff --git a/autoload/iced/nrepl/debug.vim b/autoload/iced/nrepl/debug.vim index 6bc1e5fee..f164f93c0 100644 --- a/autoload/iced/nrepl/debug.vim +++ b/autoload/iced/nrepl/debug.vim @@ -25,7 +25,7 @@ function! s:apply_coordination(coordination) abort let i = c while i > 0 call sexp#move_to_adjacent_element('n', 1, 1, 0, 0) - let x = strpart(getline('.'), max([col('.')-1, 1]), 6) + let x = strpart(getline('.'), max([col('.')-1, 0]), 6) if stridx(x, '#dbg') != 0 && stridx(x, '#break') != 0 let i = i - 1 endif @@ -57,8 +57,7 @@ function! s:move_cursor_and_set_highlight(resp) abort call s:apply_coordination(a:resp['coor']) let pos = getcurpos() - let char = getline('.')[max([col('.')-1, 1])] - if char ==# '(' + if iced#util#char() ==# '(' normal! l let l = max([len(expand('')), 1]) else diff --git a/autoload/iced/nrepl/ns/refactor.vim b/autoload/iced/nrepl/ns/refactor.vim index f99a644de..ee8115919 100644 --- a/autoload/iced/nrepl/ns/refactor.vim +++ b/autoload/iced/nrepl/ns/refactor.vim @@ -160,10 +160,11 @@ function! s:add(ns_name) abort call iced#message#info_str(msg) endfunction -function! s:project_namespaces(namespaces) abort - let namespaces = (empty(a:namespaces) ? [] : a:namespaces) +function! s:ns_list(resp) abort + if !has_key(a:resp, 'ns-list') | return iced#message#error('ns_list_error') | endif + let namespaces = get(a:resp, 'ns-list', []) let favorites = get(g:iced#nrepl#ns#refactor#favorites, iced#nrepl#current_session_key(), {}) - call extend(namespaces, keys(favorites)) + let namespaces = s:L.uniq(s:L.concat([namespaces, keys(favorites)])) call ctrlp#iced#start({ \ 'candidates': namespaces, @@ -173,7 +174,7 @@ endfunction function! iced#nrepl#ns#refactor#add(ns_name) abort if empty(a:ns_name) - call iced#nrepl#op#iced#project_namespaces(funcref('s:project_namespaces')) + call iced#nrepl#op#cider#ns_list(funcref('s:ns_list')) else call s:add(a:ns_name) endif diff --git a/autoload/iced/nrepl/op/cider.vim b/autoload/iced/nrepl/op/cider.vim index 91ea5faa2..ab96a8453 100644 --- a/autoload/iced/nrepl/op/cider.vim +++ b/autoload/iced/nrepl/op/cider.vim @@ -22,6 +22,15 @@ function! iced#nrepl#op#cider#ns_path(ns, callback) abort \ }) endfunction +function! iced#nrepl#op#cider#ns_list(callback) abort + if !iced#nrepl#is_connected() | echom iced#message#get('not_connected') | return | endif + call iced#nrepl#send({ + \ 'op': 'ns-list', + \ 'session': iced#nrepl#current_session(), + \ 'callback': a:callback, + \ }) +endfunction + function! iced#nrepl#op#cider#format_code(code, callback) abort if !iced#nrepl#is_connected() | echom iced#message#get('not_connected') | return | endif call iced#nrepl#send({ diff --git a/autoload/iced/nrepl/refactor.vim b/autoload/iced/nrepl/refactor.vim index 2c16aa39b..524500e0d 100644 --- a/autoload/iced/nrepl/refactor.vim +++ b/autoload/iced/nrepl/refactor.vim @@ -13,6 +13,10 @@ function! iced#nrepl#refactor#extract_function() abort try let locals = a:resp['used-locals'] let func_name = trim(input('Function name: ')) + if empty(func_name) + return iced#message#echom('canceled') + endif + let func_body = iced#paredit#get_outer_list_raw() let @@ = printf('(%s %s)', func_name, join(locals, ' ')) diff --git a/autoload/iced/palette.vim b/autoload/iced/palette.vim new file mode 100644 index 000000000..d2c0ec0de --- /dev/null +++ b/autoload/iced/palette.vim @@ -0,0 +1,44 @@ +let s:save_cpo = &cpo +set cpo&vim + +let s:default_palette = [ + \ 'Connect', + \ 'Disconnect', + \ 'Reconnect', + \ 'Interrupt', + \ 'PrintLast', + \ 'RequireAll', + \ 'StartCljsRepl figwheel', + \ 'StartCljsRepl nashorn', + \ 'QuitCljsRepl', + \ 'TestNs', + \ 'TestAll', + \ 'TestRedo', + \ 'ToggleSrcAndTest', + \ 'BrowseNamespace', + \ 'BrowseFunction', + \ 'BrowseSpec', + \ 'CleanNs', + \ 'AddNs', + \ 'ToggleTraceVar', + \ 'ToggleTraceNs', + \ 'InReplNs', + \ 'LintCurrentFile', + \ 'LintToggle', + \ ] + +let g:iced#palette#palette = get(g:, 'iced#palette#palette', s:default_palette) + +function! s:run(candidate) abort + execute printf(':Iced%s', a:candidate) +endfunction + +function! iced#palette#show() abort + call ctrlp#iced#start({ + \ 'candidates': g:iced#palette#palette, + \ 'accept': {_, candidate -> s:run(candidate)}, + \ }) +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/autoload/iced/paredit.vim b/autoload/iced/paredit.vim index b141d7b9c..d7ad2d1c1 100644 --- a/autoload/iced/paredit.vim +++ b/autoload/iced/paredit.vim @@ -35,6 +35,31 @@ function! iced#paredit#move_to_prev_top_element() abort call sexp#move_to_adjacent_element('n', 0, 0, 0, 1) endfunction +function! iced#paredit#move_to_current_element_head() abort + if iced#util#char() ==# '(' + return col('.') + else + let pos = getcurpos() + silent normal! va(o + silent exe "normal! \" + return (pos == getcurpos() ? 0 : col('.')) + endif +endfunction + +function! iced#paredit#move_to_parent_element() abort + let view = winsaveview() + let ret = iced#paredit#move_to_current_element_head() + if ret == 0 || col('.') == 1 + call winrestview(view) + return 0 + endif + + " move to parent form head + silent normal! hva(o + silent exe "normal! \" + return col('.') +endfunction + function! iced#paredit#get_current_top_list_raw() abort let code = v:none let pos = v:none @@ -81,7 +106,11 @@ function! iced#paredit#get_current_top_list() abort endfunction function! iced#paredit#get_outer_list_raw() abort - silent normal! va(y + try + silent normal! va(y + finally + silent exe "normal! \" + endtry return @@ endfunction diff --git a/autoload/iced/util.vim b/autoload/iced/util.vim index ce4ddfa68..bdbac6318 100644 --- a/autoload/iced/util.vim +++ b/autoload/iced/util.vim @@ -86,5 +86,9 @@ function! iced#util#del_indent(n, s) abort return substitute(a:s, '\r\?\n'.spc, "\n", 'g') endfunction +function! iced#util#char() abort + return getline('.')[max([col('.')-1, 0])] +endfunction + let &cpo = s:save_cpo unlet s:save_cpo diff --git a/doc/vim-iced.txt b/doc/vim-iced.txt index 6418696b6..77fd3ebac 100644 --- a/doc/vim-iced.txt +++ b/doc/vim-iced.txt @@ -1,6 +1,6 @@ *vim-iced.txt* Clojure interactive development environment for Vim8 -Version: 0.4.2 +Version: 0.4.3 Author : Masashi Iizuka License: MIT LICENSE @@ -448,6 +448,18 @@ Commands *vim-iced-commands* If {symbol} is not passed, symbol under cursor is used. Key is mapped to |(iced_source_show)|. + *:IcedGrimoireOpen* +:IcedGrimoireOpen [{symbol}] + Open a window, and show grimoire for {symbol} + If {symbol} is not passed, symbol under cursor is used. + Key is mapped to |(iced_grimoire_open)|. + + *:IcedCommandPalette* +:IcedCommandPalette + Select command from palette. + `ctrlp.vim` is required to select namespace. + Key is mapped to |(iced_command_palette)|. + *:IcedSlurp* :IcedSlurp Slurp next element. @@ -762,6 +774,14 @@ Key Mappings *vim-iced-key-mappings* (iced_source_show) Same as |:IcedSourceShow|. + *(iced_grimoire_open)* +(iced_grimoire_open) + Same as |:IcedGrimoireOpen|. + + *(iced_command_palette)* +(iced_command_palette) + Same as |:IcedCommandPalette|. + *(iced_slurp)* (iced_slurp) Same as |:IcedSlurp|. @@ -1001,6 +1021,7 @@ g:iced_enable_default_key_mappings rml (iced_move_to_let) hs (iced_source_show) hg (iced_grimoire_open) + hh (iced_command_palette) bn (iced_browse_namespace) bf (iced_browse_function) bs (iced_browse_spec) diff --git a/ftplugin/clojure.vim b/ftplugin/clojure.vim index 5b4f3a882..09908eaee 100644 --- a/ftplugin/clojure.vim +++ b/ftplugin/clojure.vim @@ -47,6 +47,7 @@ command! IcedFormDocument call iced#nrepl#document#current_form() command! IcedDocumentClose call iced#buffer#document#close() command! -nargs=? IcedSourceShow call iced#nrepl#source#show() command! -nargs=? IcedGrimoireOpen call iced#grimoire#open() +command! IcedCommandPalette call iced#palette#show() command! IcedSlurp call iced#paredit#deep_slurp() command! IcedBarf call iced#paredit#barf() @@ -121,6 +122,7 @@ nnoremap (iced_form_document) :IcedFormDocument nnoremap (iced_document_close) :IcedDocumentClose nnoremap (iced_source_show) :IcedSourceShow nnoremap (iced_grimoire_open) :IcedGrimoireOpen +nnoremap (iced_command_palette) :IcedCommandPalette nnoremap (iced_slurp) :IcedSlurp nnoremap (iced_barf) :IcedBarf @@ -314,6 +316,10 @@ function! s:default_key_mappings() abort silent! nmap hg (iced_grimoire_open) endif + if !hasmapto('(iced_command_palette)') + silent! nmap hh (iced_command_palette) + endif + if !hasmapto('(iced_browse_namespace)') silent! nmap bn (iced_browse_namespace) endif diff --git a/message/iced/en.txt b/message/iced/en.txt index 8a52eb034..f21ff8df4 100644 --- a/message/iced/en.txt +++ b/message/iced/en.txt @@ -30,6 +30,7 @@ 'start_to_trace': 'Start to trace: %s.', 'stop_to_trace': 'Stop to trace: %s.', 'untraced': 'Untraced.', + 'ns_list_error': 'Failed to fetch ns list.', 'spec_list_error': 'Failed to fetch spec list.', 'spec_form_error': 'Failed to fetch spec form.', 'no_spec': 'No spec form is found.', @@ -42,5 +43,6 @@ 'search_hit_top': 'search hit TOP, continuing at BOTTOM...', 'search_hit_bottom': 'search hit BOTTOM, continuing at TOP...', 'used_locals_error': 'Failed to fetch used-locals.', + 'canceled': 'Canceled.', } #vim:ft=ruby diff --git a/test/let.vim b/test/let.vim new file mode 100644 index 000000000..6b973bcef --- /dev/null +++ b/test/let.vim @@ -0,0 +1,84 @@ +let s:suite = themis#suite('iced.let') +let s:assert = themis#helper('assert') +let s:buf = themis#helper('iced_buffer') + +function! s:suite.goto_test() abort + call s:buf.start_dummy([ + \ '(let [foo 123]', + \ ' (println foo)', + \ ' |)', + \ ]) + + call s:assert.equals(line('.'), 3) + call iced#let#goto() + call s:assert.equals(line('.'), 1) + call s:assert.equals(col('.'), 6) + + call s:buf.stop_dummy() + + call s:buf.start_dummy([ + \ '(let [foo 123]', + \ ' (println foo)', + \ ' (bar|))', + \ ]) + + call iced#let#goto() + call s:assert.equals(line('.'), 1) + call s:assert.equals(col('.'), 6) + + call s:buf.stop_dummy() +endfunction + +function! s:suite.goto_no_let_test() abort + call s:buf.start_dummy([ + \ '(list 123', + \ ' 456|)', + \ ]) + + let pos = getcurpos() + call iced#let#goto() + call s:assert.equals(getcurpos(), pos) + + call s:buf.stop_dummy() +endfunction + +function! s:suite.goto_no_matched_let_test() abort + call s:buf.start_dummy([ + \ '(do', + \ ' (let [foo 123]', + \ ' (println foo))', + \ ' |foo)', + \ ]) + + let pos = getcurpos() + call iced#let#goto() + call s:assert.equals(getcurpos(), pos) + + call s:buf.stop_dummy() +endfunction + +function! s:suite.move_to_let_test() abort + call s:buf.start_dummy(['(let [a 1] (inc |a))']) + call iced#let#move_to_let('b') + call s:assert.equals(s:buf.get_texts(), + \ "(let [a 1\n b (inc a)] b)") + call s:buf.stop_dummy() + + "" no let + call s:buf.start_dummy(['(do (inc |a))']) + call iced#let#move_to_let('b') + call s:assert.equals(s:buf.get_texts(), + \ "(do (let [b (inc a)]\n b))") + call s:buf.stop_dummy() + + "" no let in parent + call s:buf.start_dummy([ + \ '(do (let [a 1] a)', + \ ' (inc |a))', + \ ]) + call iced#let#move_to_let('b') + call s:assert.equals(s:buf.get_texts(), + \ "(do (let [a 1] a)\n (let [b (inc a)]\n b))") + call s:buf.stop_dummy() +endfunction + diff --git a/test/paredit.vim b/test/paredit.vim index 8ad5bcc7c..1bad8632b 100644 --- a/test/paredit.vim +++ b/test/paredit.vim @@ -60,3 +60,52 @@ function! s:suite.get_current_top_list_with_tag_test() abort call s:assert.equals(res['code'], "#?(:clj\n (foo (bar)))") call s:buf.stop_dummy() endfunction + +function! s:suite.move_to_current_element_head_test() abort + call s:buf.start_dummy(['(foo (bar (baz|)))']) + call s:assert.equals(col('.'), 15) + call s:assert.not_equals(iced#paredit#move_to_current_element_head(), 0) + call s:assert.equals(col('.'), 11) + call s:buf.stop_dummy() + + call s:buf.start_dummy(['(foo (bar |(baz)))']) + call s:assert.equals(col('.'), 11) + call s:assert.not_equals(iced#paredit#move_to_current_element_head(), 0) + call s:assert.equals(col('.'), 11) + call s:buf.stop_dummy() +endfunction + +function! s:suite.move_to_current_element_head_not_form_test() abort + call s:buf.start_dummy(['(foo (bar (baz)))', '|']) + let pos = getcurpos() + call s:assert.equals(iced#paredit#move_to_current_element_head(), 0) + call s:assert.equals(getcurpos(), pos) + call s:buf.stop_dummy() +endfunction + +function! s:suite.move_to_parent_element_test() abort + call s:buf.start_dummy([ + \ '(foo (bar (baz|))', + \ ]) + call s:assert.equals(col('.'), 15) + call iced#paredit#move_to_parent_element() + call s:assert.equals(col('.'), 6) + call s:buf.stop_dummy() + + call s:buf.start_dummy([ + \ '(do (foo bar) (baz|))', + \ ]) + call iced#paredit#move_to_parent_element() + call s:assert.equals(col('.'), 1) + call s:buf.stop_dummy() +endfunction + +function! s:suite.move_to_parent_element_no_parent_test() abort + call s:buf.start_dummy([ + \ '(foo| (bar (baz))', + \ ]) + let pos = getcurpos() + call iced#paredit#move_to_parent_element() + call s:assert.equals(getcurpos(), pos) + call s:buf.stop_dummy() +endfunction