surrounding.vim 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. " File: surrounding.vim
  2. " Author: Michael Sanders
  3. " Description: A heavily modified, simplified fork of surround.vim by Tim Pope.
  4. if exists('g:did_surrounding') || &cp || version < 700
  5. finish
  6. endif
  7. let g:did_surrounding = 1
  8. nn cs :<c-u>call <SID>ModifySurround(<SID>Input(), <SID>Input())<cr>
  9. nn ds :<c-u>call <SID>ModifySurround(<SID>Input())<cr>
  10. xno s :<c-u>call <SID>AddSurround(visualmode(), 1)<cr>
  11. nn <silent> ys :<c-u>set opfunc=<SID>AddSurround<cr>g@
  12. nm yss 0ys$
  13. " Return next character typed by user.
  14. fun s:Input()
  15. echoh ModeMsg | echo '-- SURROUND --' | echoh None
  16. let c = nr2char(getchar())
  17. if c == ' '
  18. let c .= nr2char(getchar())
  19. endif
  20. echo '' | redraw
  21. return c == "\<esc>" || c == "\<cr>" ? '' : c
  22. endf
  23. " Surround selected text with input from user.
  24. " If a:0 is given, visual mode is assumed to be used; otherwise, an opfunc
  25. fun s:AddSurround(type, ...)
  26. " Deal with motions, if used in normal mode.
  27. if !a:0
  28. let sel_save = &sel | let &sel = 'inclusive'
  29. exe 'norm! `['.(a:type == 'char' ? 'v' : 'V')."`]\<esc>"
  30. let sel_save = &sel
  31. endif
  32. let char = s:Input()
  33. if a:type ==# 'V' " Line mode
  34. let chars = s:Wrap('', char) | let half = len(chars)/2
  35. call setline("'<", strpart(chars, 0, half).getline("'<"))
  36. call setline("'>", getline("'>").strpart(chars, half))
  37. else " Just visual mode
  38. let old = @"
  39. norm! gvc
  40. let @" = s:Wrap(@", char)
  41. exe 'norm! '.(virtcol('.') == 1 ? 'P' : 'p')
  42. let @" = old
  43. endif
  44. if a:0
  45. sil! call repeat#set('1vs'.char)
  46. " NOTE: This needs to be fixed -- it needs to save the motion/char used
  47. " An opfunc does not appear to give us access to the motion
  48. " used, so I'm not sure how to do this.
  49. sil! call repeat#set('ys')
  50. endif
  51. endf
  52. " Returns list with pair of characters matching given character.
  53. "
  54. " If character is not supported, a list with two empty strings ["", ""]
  55. " is returned.
  56. "
  57. " If character is a "t", the user is asked to input an XML tag and the
  58. " beginning and end tags are returned in a list. If the character is a
  59. " capitalized "T", the beginning and end tags returned are separated
  60. " by newlines.
  61. fun s:MatchChar(char)
  62. if a:char ==# 'b' || a:char == '(' || a:char == ')'
  63. return ['(', ')']
  64. elseif a:char ==# 'r' || a:char == ']' || a:char == '['
  65. return ['[', ']']
  66. elseif a:char ==# 'B' || a:char == '{' || a:char == '}'
  67. return ['{', '}']
  68. elseif a:char ==# 'a' || a:char == '<' || a:char == '>'
  69. return ['<', '>']
  70. elseif a:char == 't' " Insert tag
  71. if !hasmapto('>', 'c')
  72. let remapped = 1
  73. cno > <cr>
  74. endif
  75. let tag = input('<')
  76. if !remapped | let tag = substitute(tag, '>*$', '', '') | endif
  77. echo '<'.tag.'>'
  78. if remapped
  79. sil! cunmap >
  80. endif
  81. let cl = '</'.substitute(tag, ' .*', '', '').'>'
  82. return a:char ==# 'T' ? ['<'.tag.">\n", "\n".cl] : ['<'.tag.'>', cl]
  83. endif
  84. return a:char =~ '\W' ? [a:char, a:char] : ['', '']
  85. endf
  86. " Change or delete surrounding characters.
  87. " "orig" should be the original character to be changed, and a:1 should be the
  88. " new character is substituting or not given if deleting.
  89. fun s:ModifySurround(orig, ...)
  90. if a:orig == '' | return | endif
  91. if a:0 " Save position if replacing characters.
  92. if a:1 == '' | return | endif
  93. let line = line('.') | let col = col('.')
  94. endif
  95. let char = a:orig
  96. if len(char) > 1
  97. let space = ' '
  98. let char = strpart(char, 1)
  99. endif
  100. if char ==# 'a'
  101. let char = '>'
  102. elseif char ==# 'r'
  103. let char = ']'
  104. endif
  105. let old = @"
  106. let @" = ''
  107. exe 'norm! d'.v:count1.'i'.char
  108. " If di<char> failed, quit.
  109. if @" == ''
  110. let @" = old | return
  111. elseif exists('space')
  112. let @" = substitute(@",'^\s+\|\s+$', '', 'g')
  113. endif
  114. let oldLine = getline('.')
  115. let oldLnum = line('.')
  116. let oldLineLen = col('$')
  117. if char == '"' || char == "'" || char == '`'
  118. exe "norm! i \<esc>\"_d2i".char
  119. else
  120. exe 'norm! "_da'.char
  121. endif
  122. if a:0 | let @" = s:Wrap(@", a:1) | endif
  123. let endLine = col('$')
  124. sil exe 'norm! ""'.(col("']") == endLine && col('.')+1 == endLine ? 'p' : 'P')
  125. let @" = old
  126. if exists('space') | let char = ' '.char | endif
  127. if a:0
  128. sil! call repeat#set('cs'.char.a:1)
  129. call cursor(line, col)
  130. else
  131. sil! call repeat#set('ds'.char)
  132. endif
  133. endf
  134. " Returns string wrapped in the pair of the given character.
  135. " If character contains a space, it is also wrapped appropriately.
  136. fun s:Wrap(string, char)
  137. if len(a:char) > 1 " Parsing character with a space
  138. let space = ' '
  139. let chars = s:MatchChar(strpart(a:char, 1))
  140. else
  141. let space = ''
  142. let chars = s:MatchChar(a:char)
  143. endif
  144. return chars[0].space.a:string.space.chars[1]
  145. endf
  146. " vim:noet:sw=4:ts=4:ft=vim