let s:expect = themis#helper('expect') let s:Snippet = vsnip#snippet#import() let s:start_position = { \ 'line': 1, \ 'character': 1 \ } Describe vsnip#snippet Describe #init It should mark follower placeholders let l:snippet = s:Snippet.new(s:start_position, 'console.log(${1:default}, $1, $1)') call s:expect(l:snippet.children[3].follower).to_equal(v:true) call s:expect(l:snippet.children[5].follower).to_equal(v:true) End It should add final tabstop let l:snippet = s:Snippet.new(s:start_position, 'console.log($1)') call s:expect(l:snippet.children[3].is_final).to_equal(v:true) End It should convert variable to placeholder let l:snippet = s:Snippet.new(s:start_position, 'console.log(${variable:default}, $variable)') call s:expect(l:snippet.text()).to_equal('console.log(default, default)') call s:expect(l:snippet.children[3].follower).to_equal(v:true) End It should resolve known variables let l:snippet = s:Snippet.new(s:start_position, 'console.log(${CURRENT_YEAR})') call s:expect(l:snippet.text()).to_equal('console.log(' . strftime('%Y') . ')') End It should support whole word transform let l:snippet = s:Snippet.new(s:start_position, '${1:state}, set${1/(.*)/${1:/capitalize}/}') call s:expect(l:snippet.text()).to_equal('state, setState') End End Describe #sync It should sync placeholder text in same tabstop groups let l:snippet = s:Snippet.new(s:start_position, 'console.log(${1:default}, $1)') call l:snippet.follow(0, { \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 13 \ }, \ 'end': { \ 'line': 1, \ 'character': 17 \ } \ }, \ 'text': '___' \ }) call l:snippet.sync() call s:expect(l:snippet.text()).to_equal('console.log(___ult, ___ult)') End End Describe #follow It should not affect following empty diff let l:snippet = s:Snippet.new(s:start_position, "class $1 {\n\tpublic ${2:default}() {\n\t\t$0\n\t}\n}") let l:followed = l:snippet.follow(0, { \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 1 \ }, \ 'end': { \ 'line': 1, \ 'character': 1 \ } \ }, \ 'text': '' \ }) call s:expect(l:followed).to_equal(v:true) call s:expect(l:snippet.text()).to_equal("class {\n\tpublic default() {\n\t\t\n\t}\n}") End It should follow when diff range covers whole of snippet let l:snippet = s:Snippet.new(s:start_position, "class $1 {\n\tpublic ${2:default}() {\n\t\t$0\n\t}\n}") let l:followed = l:snippet.follow(0, { \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 1 \ }, \ 'end': { \ 'line': 5, \ 'character': 1 \ } \ }, \ 'text': '' \ }) call s:expect(l:followed).to_equal(v:true) call s:expect(l:snippet.text()).to_equal("") End It should squash placeholder when diff range covers multiple placeholders let l:snippet = s:Snippet.new(s:start_position, "console.log(${1:first}, ${2:second})") call s:expect(l:snippet.get_placeholder_nodes()).to_have_length(3) let l:followed = l:snippet.follow(0, { \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 13 \ }, \ 'end': { \ 'line': 1, \ 'character': 26 \ } \ }, \ 'text': '' \ }) call s:expect(l:followed).to_equal(v:true) call s:expect(l:snippet.get_placeholder_nodes()).to_have_length(2) call s:expect(l:snippet.text()).to_equal('console.log()') End It should not squash placeholder when diff range includes multiple placeholders but last one does not covered let l:snippet = s:Snippet.new(s:start_position, "console.log(${1:first}, ${2:second})") call s:expect(l:snippet.get_placeholder_nodes()).to_have_length(3) let l:followed = l:snippet.follow(0, { \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 13 \ }, \ 'end': { \ 'line': 1, \ 'character': 25 \ } \ }, \ 'text': '' \ }) call s:expect(l:followed).to_equal(v:true) call s:expect(l:snippet.get_placeholder_nodes()).to_have_length(3) call s:expect(l:snippet.text()).to_equal('console.log(d)') End It should prefer current placeholder let l:snippet = s:Snippet.new(s:start_position, 'console.log(${1}, ${2:, ${3:default}})') let l:followed = l:snippet.follow(3, { \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 17 \ }, \ 'end': { \ 'line': 1, \ 'character': 17 \ } \ }, \ 'text': '___' \ }) call s:expect(l:followed).to_equal(v:true) call s:expect(l:snippet.get_placeholder_nodes()[2].text()).to_equal('___default') End It should follow when diff range is within one node range let l:snippet = s:Snippet.new(s:start_position, "class $1 {\n\tpublic ${2:default}() {\n\t\t$0\n\t}\n}") call l:snippet.follow(0, { \ 'range': { \ 'start': { \ 'line': 2, \ 'character': 9 \ }, \ 'end': { \ 'line': 2, \ 'character': 12 \ } \ }, \ 'text': '___' \ }) call s:expect(l:snippet.text()).to_equal("class {\n\tpublic d___ult() {\n\t\t\n\t}\n}") call s:expect(l:snippet.get_next_jump_point(1).placeholder.text()).to_equal('d___ult') End It should follow when diff range included only text node let l:snippet = s:Snippet.new(s:start_position, "class $1 {\n\tpublic ${2:default}() {\n\t\t$0\n\t}\n}") call l:snippet.follow(1, { \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 1 \ }, \ 'end': { \ 'line': 1, \ 'character': 6 \ } \ }, \ 'text': 'modified' \ }) call s:expect(l:snippet.text()).to_equal("modified {\n\tpublic default() {\n\t\t\n\t}\n}") End It should prefer placeholder node than text node when both followable (left) let l:snippet = s:Snippet.new(s:start_position, '[${1:text1}][${2:text2}][${3:text3}]') call l:snippet.follow(1, { \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 9 \ }, \ 'end': { \ 'line': 1, \ 'character': 9 \ } \ }, \ 'text': '___' \ }) call s:expect(l:snippet.text()).to_equal('[text1][___text2][text3]') call s:expect(l:snippet.children[3].text()).to_equal('___text2') End It should prefer placeholder node than text node when both followable (right) let l:snippet = s:Snippet.new(s:start_position, '[${1:text1}][${2:text2}][${3:text3}]') call l:snippet.follow(1, { \ 'range': { \ 'start': { \ 'line': 1, \ 'character': 14 \ }, \ 'end': { \ 'line': 1, \ 'character': 14 \ } \ }, \ 'text': '___' \ }) call s:expect(l:snippet.text()).to_equal('[text1][text2___][text3]') call s:expect(l:snippet.children[3].text()).to_equal('text2___') End End Describe #text It should return text1 let l:snippet = s:Snippet.new(s:start_position, 'console.log($0${1:default})') call s:expect(l:snippet.text()).to_equal('console.log(default)') End It should return text2 call vsnip#selected_text('THIS_IS_SELECTED_TEXT') let l:snippet = s:Snippet.new(s:start_position, '$TM_SELECTED_TEXT') call s:expect(l:snippet.text()).to_equal('THIS_IS_SELECTED_TEXT') End It should return text2 call vsnip#selected_text('THIS_IS_SELECTED_TEXT') let l:snippet = s:Snippet.new(s:start_position, '${TM_SELECTED_TEXT}') call s:expect(l:snippet.text()).to_equal('THIS_IS_SELECTED_TEXT') End It should return text3 call vsnip#selected_text('') let l:snippet = s:Snippet.new(s:start_position, '${TM_SELECTED_TEXT:default}') call s:expect(l:snippet.text()).to_equal('default') End It should support whole word transform (upcase) on tabstop let l:snippet = s:Snippet.new(s:start_position, '${1:varName}, ${1/(.*)/${1:/upcase}/}') call s:expect(l:snippet.text()).to_equal('varName, VARNAME') End It should support whole word transform (downcase) on variable call vsnip#selected_text('varName') let l:snippet = s:Snippet.new(s:start_position, '${TM_SELECTED_TEXT/(.*)/${1:/downcase}/}') call s:expect(l:snippet.text()).to_equal('varname') End It should support whole word transform (capitalize) call vsnip#selected_text('varName') let l:snippet = s:Snippet.new(s:start_position, '${TM_SELECTED_TEXT/(.*)/${1:/capitalize}/}') call s:expect(l:snippet.text()).to_equal('VarName') End It should support whole word transform (camelcase) call vsnip#selected_text('var_name') call s:expect( \ s:Snippet.new(s:start_position, '${TM_SELECTED_TEXT/(.*)/${1:/camelcase}/}').text() \ ).to_equal('varName') call vsnip#selected_text('VAR_NAME') call s:expect( \ s:Snippet.new(s:start_position, '${TM_SELECTED_TEXT/(.*)/${1:/camelcase}/}').text() \ ).to_equal('varName') call vsnip#selected_text('VarName') call s:expect( \ s:Snippet.new(s:start_position, '${TM_SELECTED_TEXT/(.*)/${1:/camelcase}/}').text() \ ).to_equal('varName') End It should support whole word transform (pascalcase) call vsnip#selected_text('var_name') call s:expect( \ s:Snippet.new(s:start_position, '${TM_SELECTED_TEXT/(.*)/${1:/pascalcase}/}').text() \ ).to_equal('VarName') call vsnip#selected_text('VAR_NAME') call s:expect( \ s:Snippet.new(s:start_position, '${TM_SELECTED_TEXT/(.*)/${1:/pascalcase}/}').text() \ ).to_equal('VarName') call vsnip#selected_text('varName') call s:expect( \ s:Snippet.new(s:start_position, '${TM_SELECTED_TEXT/(.*)/${1:/pascalcase}/}').text() \ ).to_equal('VarName') End It should support whole word transform with additional text call vsnip#selected_text('varName') let l:snippet = s:Snippet.new(s:start_position, '${TM_SELECTED_TEXT/(.*)/start-lowercase-${1:/downcase}/}-end') call s:expect(l:snippet.text()).to_equal('start-lowercase-varname-end') End End Describe #range It should return range1 let l:snippet = s:Snippet.new(s:start_position, "01234\n56789") call s:expect(l:snippet.range()).to_equal({ \ 'start': { \ 'line': s:start_position.line, \ 'character': s:start_position.character \ }, \ 'end': { \ 'line': s:start_position.line + 1, \ 'character': 5 \ } \ }) End It should return range2 let l:snippet = s:Snippet.new(s:start_position, "012345") call s:expect(l:snippet.range()).to_equal({ \ 'start': { \ 'line': s:start_position.line, \ 'character': s:start_position.character \ }, \ 'end': { \ 'line': s:start_position.line, \ 'character': 7 \ } \ }) End End Describe #offset_to_position It should return position from offset let l:snippet = s:Snippet.new(s:start_position, "class クラス {\n\tpublic constructor() {\n\t\t$0\n\t}\n}") call s:expect(l:snippet.offset_to_position(13)).to_equal({ \ 'line': 2, \ 'character': 1 \ }) End End Describe #position_to_offset It should return offset from position let l:snippet = s:Snippet.new(s:start_position, "class クラス {\n\tpublic constructor() {\n\t\t$0\n\t}\n}") call s:expect(l:snippet.position_to_offset({ \ 'line': 2, \ 'character': 1 \ })).to_equal(13) End End Describe #normalize It should not normalize when does not exists adjacent text nodes let l:snippet = s:Snippet.new(s:start_position, 'console.log(${1:i}${2:++})') let l:text = l:snippet.text() call s:expect(len(l:snippet.children)).to_equal(5) call l:snippet.normalize() call s:expect(len(l:snippet.children)).to_equal(5) call s:expect(l:text).to_equal(l:snippet.text()) End It should normalize adjacent text nodes let l:snippet = s:Snippet.new(s:start_position, 'console.log') call insert(l:snippet.children, vsnip#snippet#node#create_text('___'), 1) let l:text = l:snippet.text() call s:expect(len(l:snippet.children)).to_equal(3) call l:snippet.normalize() call s:expect(len(l:snippet.children)).to_equal(2) call s:expect(l:text).to_equal(l:snippet.text()) End It should normalize correctly when the node has the same structure children let l:snippet = s:Snippet.new(s:start_position, '') let l:snippet.children = vsnip#snippet#node#create_from_ast([{ \ 'type': 'text', \ 'value': '*', \ 'escaped': '*', \ }, { \ 'type': 'placeholder', \ 'id': 1, \ 'children': [{ \ 'type': 'text', \ 'value': '', \ 'escaped': '', \ }] \ }, { \ 'type': 'text', \ 'value': '*', \ 'escaped': '*', \ }, { \ 'type': 'text', \ 'value': '_', \ 'escaped': '_', \ }, { \ 'type': 'placeholder', \ 'id': 1, \ 'children': [{ \ 'type': 'text', \ 'value': '', \ 'escaped': '', \ }] \ }, { \ 'type': 'text', \ 'value': '*', \ 'escaped': '*', \ }, { \ 'type': 'text', \ 'value': '__', \ 'escaped': '__', \ }]) let l:text = l:snippet.text() call l:snippet.normalize() call s:expect(l:text).to_equal(l:snippet.text()) End End Describe #insert It should insert node 1 let l:snippet = s:Snippet.new(s:start_position, 'console.log(${1}, ${2:${1}})') call l:snippet.insert({ 'line': 1, 'character': 13 }, s:Snippet.new(s:start_position, 'console.log(${3}, ${4:${3}})').children) call s:expect(l:snippet.text()).to_equal('console.log(console.log(, ), )') End It should insert node 2 let l:snippet = s:Snippet.new(s:start_position, 'console.log(${1}, ${2:${1}})') call l:snippet.insert({ 'line': 1, 'character': 15 }, s:Snippet.new(s:start_position, 'console.log()').children) call s:expect(l:snippet.text()).to_equal('console.log(, console.log())') call s:expect(len(l:snippet.get_placeholder_nodes())).to_equal(5) End It should insert node 3 let l:snippet = s:Snippet.new(s:start_position, 'console.log(aiueo)') call l:snippet.insert({ 'line': 1, 'character': 13 }, s:Snippet.new(s:start_position, '___').children) call s:expect(l:snippet.text()).to_equal('console.log(___aiueo)') End It should insert node 4 let l:snippet = s:Snippet.new(s:start_position, 'console.log(aiueo)') call l:snippet.insert({ 'line': 1, 'character': 15 }, s:Snippet.new(s:start_position, '___').children) call s:expect(l:snippet.text()).to_equal('console.log(ai___ueo)') End It should insert node 5 let l:snippet = s:Snippet.new(s:start_position, 'console.log(aiueo)') call l:snippet.insert({ 'line': 1, 'character': 18 }, [vsnip#snippet#node#create_text('___')]) call s:expect(l:snippet.text()).to_equal('console.log(aiueo___)') End End Describe #get_next_jump_point It should return next jump point 1 let l:snippet = s:Snippet.new(s:start_position, 'console.log(${1:012345})') call s:expect(l:snippet.get_next_jump_point(0).range).to_equal({ \ 'start': { \ 'line': 1, \ 'character': 13 \ }, \ 'end': { \ 'line': 1, \ 'character': 19 \ } \ }) End It should return next jump point 2 let l:snippet = s:Snippet.new(s:start_position, 'console.log(${1:0123${2:456}7890})') call s:expect(l:snippet.get_next_jump_point(0).range).to_equal({ \ 'start': { \ 'line': 1, \ 'character': 13 \ }, \ 'end': { \ 'line': 1, \ 'character': 24 \ } \ }) call s:expect(l:snippet.get_next_jump_point(1).range).to_equal({ \ 'start': { \ 'line': 1, \ 'character': 17, \ }, \ 'end': { \ 'line': 1, \ 'character': 20 \ } \ }) End It should return next jump point 3 let l:snippet = s:Snippet.new(s:start_position, 'console.log(${1:0${3:12}3${2:456}7890})') call s:expect(l:snippet.get_next_jump_point(0).range).to_equal({ \ 'start': { \ 'line': 1, \ 'character': 13 \ }, \ 'end': { \ 'line': 1, \ 'character': 24 \ } \ }) call s:expect(l:snippet.get_next_jump_point(1).range).to_equal({ \ 'start': { \ 'line': 1, \ 'character': 17, \ }, \ 'end': { \ 'line': 1, \ 'character': 20 \ } \ }) call s:expect(l:snippet.get_next_jump_point(2).range).to_equal({ \ 'start': { \ 'line': 1, \ 'character': 14, \ }, \ 'end': { \ 'line': 1, \ 'character': 16 \ } \ }) End It should return next jump point 4 let l:snippet = s:Snippet.new(s:start_position, 'console.log(0${1}123456789${1}0)') call s:expect(l:snippet.get_next_jump_point(0).range).to_equal({ \ 'start': { \ 'line': 1, \ 'character': 14 \ }, \ 'end': { \ 'line': 1, \ 'character': 14 \ } \ }) End End End