├── .bundle └── config ├── Flavorfile ├── .travis.yml ├── Flavorfile.lock ├── .gitignore ├── Gemfile ├── Rakefile ├── doc ├── tags └── vim-textobj-variable-segment.txt ├── bin ├── bundler ├── rake ├── thor ├── vim-flavor └── bundle ├── .pre-commit-config.yaml ├── plugin └── textobj │ └── variable-segment.vim ├── .github └── workflows │ └── ci.yml ├── README.md ├── COPYING ├── autoload └── textobj │ └── variable_segment.vim └── t └── variable-segment.vim /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_BIN: bin 3 | -------------------------------------------------------------------------------- /Flavorfile: -------------------------------------------------------------------------------- 1 | flavor 'kana/vim-textobj-user', '~> 0.3' 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.4 4 | script: rake ci 5 | -------------------------------------------------------------------------------- /Flavorfile.lock: -------------------------------------------------------------------------------- 1 | kana/vim-textobj-user (0.7.6) 2 | kana/vim-vspec (v1.9.2) 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | 3 | .vim-flavor/ 4 | VimFlavor.lock 5 | 6 | TODO 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'vim-flavor', '~> 4.0' 4 | gem 'rake', '~> 13.0' 5 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | 3 | task :default => [:test] 4 | task :ci => [:dump, :test] 5 | 6 | task :dump do 7 | sh 'vim --version' 8 | end 9 | 10 | task :test do 11 | sh 'bin/vim-flavor test' 12 | end 13 | -------------------------------------------------------------------------------- /doc/tags: -------------------------------------------------------------------------------- 1 | vim-textobj-variable-segment-contents vim-textobj-variable-segment.txt /*vim-textobj-variable-segment-contents* 2 | vim-textobj-variable-segment-vim-textobj-variable-segment vim-textobj-variable-segment.txt /*vim-textobj-variable-segment-vim-textobj-variable-segment* 3 | vim-textobj-variable-segment.txt vim-textobj-variable-segment.txt /*vim-textobj-variable-segment.txt* 4 | -------------------------------------------------------------------------------- /bin/bundler: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'bundler' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('bundler', 'bundler') 17 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.3.0 4 | hooks: 5 | - id: check-json 6 | - id: check-yaml 7 | - id: check-vcs-permalinks 8 | - id: end-of-file-fixer 9 | - id: trailing-whitespace 10 | 11 | - repo: https://github.com/markdownlint/markdownlint 12 | rev: v0.11.0 13 | hooks: 14 | - id: markdownlint 15 | args: ["-r", "~MD013", "-r", "~MD046"] 16 | -------------------------------------------------------------------------------- /plugin/textobj/variable-segment.vim: -------------------------------------------------------------------------------- 1 | " textobj-variable-segment: a text object for segments of variable names 2 | " Author: Julian Berman 3 | " Version: 0.5.0 4 | 5 | if exists('g:loaded_textobj_variable_segment') 6 | finish 7 | endif 8 | 9 | call textobj#user#plugin('variable', { 10 | \ '-': { 11 | \ 'sfile': expand(':p'), 12 | \ 'select-a': 'av', 'select-a-function': 'textobj#variable_segment#select_a', 13 | \ 'select-i': 'iv', 'select-i-function': 'textobj#variable_segment#select_i', 14 | \ }}) 15 | 16 | let g:loaded_textobj_variable_segment = 1 17 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | pre-commit: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: actions/setup-python@v4 11 | with: 12 | python-version: '3.x' 13 | - name: Install luacheck 14 | run: | 15 | sudo apt-get update 16 | sudo apt-get install luarocks 17 | sudo luarocks install luacheck 18 | - uses: pre-commit/action@v3.0.0 19 | 20 | ci: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v3 24 | - uses: ruby/setup-ruby@v1 25 | with: 26 | ruby-version: '3.0' 27 | bundler-cache: true 28 | - run: bundle exec rake test 29 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'rake' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("rake", "rake") 28 | -------------------------------------------------------------------------------- /bin/thor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'thor' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("thor", "thor") 28 | -------------------------------------------------------------------------------- /bin/vim-flavor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'vim-flavor' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 12 | 13 | bundle_binstub = File.expand_path("bundle", __dir__) 14 | 15 | if File.file?(bundle_binstub) 16 | if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") 17 | load(bundle_binstub) 18 | else 19 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 20 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 21 | end 22 | end 23 | 24 | require "rubygems" 25 | require "bundler/setup" 26 | 27 | load Gem.bin_path("vim-flavor", "vim-flavor") 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim-textobj-variable-segment 2 | 3 | A vim plugin providing a single text object (on `iv` and `av`) for 4 | variable segments. A variable segment is defined to be a substring in 5 | any identifier character followed by an underscore ("snake case") or 6 | a lowercase identifier character followed by an uppercase character 7 | ("camel case"). 8 | 9 | E.g.: 10 | 11 | foo_ba|r_baz -> civquux -> foo_quux_baz 12 | QU|UX_SPAM -> civLOTS_OF -> LOTS_OF_SPAM 13 | eggsAn|dCheese -> civOr -> eggsOrCheese 14 | _privat|e_thing -> civone -> _one_thing 15 | 16 | foo_ba|r_baz -> dav -> foo_baz 17 | QU|UX_SPAM -> dav -> SPAM 18 | eggsAn|dCheese -> dav -> eggsCheese 19 | _privat|e_thing -> dav -> _thing 20 | 21 | It will also preserve case for small camels when initial segments are deleted 22 | (with `av`): 23 | 24 | _g|etJiggyYo -> dav -> _jiggyYo 25 | 26 | Requires [vim-textobj-user](https://github.com/kana/vim-textobj-user). 27 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Julian Berman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /doc/vim-textobj-variable-segment.txt: -------------------------------------------------------------------------------- 1 | *vim-textobj-variable-segment.txt* A text object for variable segments 2 | 3 | ================================================================================ 4 | CONTENTS *vim-textobj-variable-segment-contents* 5 | 6 | 1. vim-textobj-variable-segment.|vim-textobj-variable-segment-vim-textobj-variable-segment| 7 | 8 | ================================================================================ 9 | VIM-TEXTOBJ-VARIABLE-SEGMENT *vim-textobj-variable-segment-vim-textobj-variable-segment* 10 | 11 | A vim plugin providing a single text object (on `iv` and `av`) for 12 | variable segments. A variable segment is defined to be a substring in 13 | any identifier character followed by an underscore ("snake case") or 14 | a lowercase identifier character followed by an uppercase character 15 | ("camel case"). 16 | 17 | E.g.: 18 | > 19 | foo_ba|r_baz -> civquux -> foo_quux_baz 20 | QU|UX_SPAM -> civLOTS_OF -> LOTS_OF_SPAM 21 | eggsAn|dCheese -> civOr -> eggsOrCheese 22 | _privat|e_thing -> civone -> _one_thing 23 | foo_ba|r_baz -> dav -> foo_baz 24 | QU|UX_SPAM -> dav -> SPAM 25 | eggsAn|dCheese -> dav -> eggsCheese 26 | _privat|e_thing -> dav -> _thing 27 | < 28 | 29 | It will also preserve case for small camels when initial segments are deleted 30 | (with `av`): 31 | > 32 | _g|etJiggyYo -> dav -> _jiggyYo 33 | < 34 | 35 | Requires vim-textobj-user (https://github.com/kana/vim-textobj-user). 36 | 37 | vim:tw=78:ts=8:ft=help:norl: 38 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'bundle' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "rubygems" 12 | 13 | m = Module.new do 14 | module_function 15 | 16 | def invoked_as_script? 17 | File.expand_path($0) == File.expand_path(__FILE__) 18 | end 19 | 20 | def env_var_version 21 | ENV["BUNDLER_VERSION"] 22 | end 23 | 24 | def cli_arg_version 25 | return unless invoked_as_script? # don't want to hijack other binstubs 26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` 27 | bundler_version = nil 28 | update_index = nil 29 | ARGV.each_with_index do |a, i| 30 | if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN) 31 | bundler_version = a 32 | end 33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ 34 | bundler_version = $1 35 | update_index = i 36 | end 37 | bundler_version 38 | end 39 | 40 | def gemfile 41 | gemfile = ENV["BUNDLE_GEMFILE"] 42 | return gemfile if gemfile && !gemfile.empty? 43 | 44 | File.expand_path("../Gemfile", __dir__) 45 | end 46 | 47 | def lockfile 48 | lockfile = 49 | case File.basename(gemfile) 50 | when "gems.rb" then gemfile.sub(/\.rb$/, ".locked") 51 | else "#{gemfile}.lock" 52 | end 53 | File.expand_path(lockfile) 54 | end 55 | 56 | def lockfile_version 57 | return unless File.file?(lockfile) 58 | lockfile_contents = File.read(lockfile) 59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ 60 | Regexp.last_match(1) 61 | end 62 | 63 | def bundler_requirement 64 | @bundler_requirement ||= 65 | env_var_version || 66 | cli_arg_version || 67 | bundler_requirement_for(lockfile_version) 68 | end 69 | 70 | def bundler_requirement_for(version) 71 | return "#{Gem::Requirement.default}.a" unless version 72 | 73 | bundler_gem_version = Gem::Version.new(version) 74 | 75 | bundler_gem_version.approximate_recommendation 76 | end 77 | 78 | def load_bundler! 79 | ENV["BUNDLE_GEMFILE"] ||= gemfile 80 | 81 | activate_bundler 82 | end 83 | 84 | def activate_bundler 85 | gem_error = activation_error_handling do 86 | gem "bundler", bundler_requirement 87 | end 88 | return if gem_error.nil? 89 | require_error = activation_error_handling do 90 | require "bundler/version" 91 | end 92 | return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) 93 | warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" 94 | exit 42 95 | end 96 | 97 | def activation_error_handling 98 | yield 99 | nil 100 | rescue StandardError, LoadError => e 101 | e 102 | end 103 | end 104 | 105 | m.load_bundler! 106 | 107 | if m.invoked_as_script? 108 | load Gem.bin_path("bundler", "bundle") 109 | end 110 | -------------------------------------------------------------------------------- /autoload/textobj/variable_segment.vim: -------------------------------------------------------------------------------- 1 | " Given three positions, return the position closest to the first one. 2 | function! s:closest_position(main, a, b) 3 | let a_distance = mapnew(a:main, {k,v->abs(v-a:a[k])}) 4 | let b_distance = mapnew(a:main, {k,v->abs(v-a:b[k])}) 5 | if a_distance[1] < b_distance[1] 6 | return a:a 7 | endif 8 | if a_distance[1] > b_distance[1] 9 | return a:b 10 | endif 11 | if a_distance[2] < b_distance[2] 12 | return a:a 13 | endif 14 | if a_distance[2] > b_distance[2] 15 | return a:b 16 | endif 17 | " Distances are the same, return either one 18 | return a:a 19 | endfunction! 20 | 21 | function! s:select(object_type, right_boundary) 22 | let left_boundaries = ['_\+\k', '\<', '\l\u', '\u\u\ze\l', '\a\d', '\d\a'] 23 | 24 | " Gather all possible matches 25 | let cursor_position = getpos('.') 26 | let all_positions = [] 27 | for boundary in left_boundaries 28 | " search() moves the cursor, so we need to reset the cursor's position 29 | " before each search 30 | call setpos('.', cursor_position) 31 | if search(boundary, 'bce') > 0 32 | call add(all_positions, getpos('.')) 33 | endif 34 | endfor 35 | 36 | " Try to find a good match on the same line and on the left of the cursor 37 | let start_position = v:null 38 | let potential_matches = filter(copy(all_positions), 39 | \ {v -> v[1] == cursor_position[1] && v[2] <= cursor_position[2]}) 40 | if len(potential_matches) > 0 41 | let start_position = reduce(potential_matches, 42 | \ {a, b -> s:closest_position(cursor_position, a, b)}) 43 | endif 44 | 45 | if type(start_position) == type(v:null) 46 | " No match found yet, try on the same line but on the right of the 47 | " cursor 48 | let potential_matches = filter(copy(all_positions), 49 | \ {v -> v[1] == cursor_position[1] && v[2] > cursor_position[2]}) 50 | if len(potential_matches) > 0 51 | let start_position = reduce(potential_matches, 52 | \ {a, b -> s:closest_position(cursor_position, a, b)}) 53 | endif 54 | endif 55 | 56 | if type(start_position) == type(v:null) 57 | " No match found yet, try to find one on lines above the cursor 58 | let potential_matches = filter(copy(all_positions), 59 | \ {v -> v[1] < cursor_position[1]}) 60 | if len(potential_matches) > 0 61 | let start_position = reduce(potential_matches, 62 | \ {a, b -> s:closest_position(cursor_position, a, b)}) 63 | endif 64 | endif 65 | 66 | if type(start_position) == type(v:null) 67 | " No match found yet, try to find one on below after the cursor 68 | let potential_matches = filter(copy(all_positions), 69 | \ {v -> v[1] > cursor_position[1]}) 70 | if len(potential_matches) > 0 71 | let start_position = reduce(potential_matches, 72 | \ {a, b -> s:closest_position(cursor_position, a, b)}) 73 | endif 74 | endif 75 | 76 | if type(start_position) == type(v:null) 77 | " The buffer must not contain any words - fall back to the cursor's 78 | " position 79 | let start_position = cursor_position 80 | endif 81 | 82 | call setpos('.', start_position) 83 | call search('\>', 'c') 84 | let word_end = getpos('.') 85 | call setpos('.', start_position) 86 | 87 | call search(a:right_boundary, 'c') 88 | for _ in range(v:count1 - 1) 89 | if getpos('.') != word_end 90 | call search(a:right_boundary) 91 | endif 92 | endfor 93 | let end_position = getpos('.') 94 | 95 | return ['v', start_position, end_position] 96 | endfunction 97 | 98 | function! s:select_a() 99 | let right_boundaries = ['_', '\l\u', '\u\u\l', '\a\d', '\d\a', '\k\>'] 100 | let right_boundary = join(right_boundaries, '\|') 101 | let [type, start_position, end_position] = s:select('a', right_boundary) 102 | let [_, start_line, start_column, _] = start_position 103 | 104 | call search('\k\>', 'c') 105 | if end_position == getpos('.') && 106 | \ getline(start_line)[start_column - 2] =~# '_' 107 | let start_position[2] -= 1 108 | endif 109 | 110 | let was_small_camel = match(expand(''), '^_*\l.*\u') != -1 111 | if was_small_camel 112 | call search('\<', 'bc') 113 | let [_, _, word_start, _] = getpos('.') 114 | 115 | if start_column - 2 <= word_start || 116 | \ getline(start_line)[:start_column - 2] =~# '^_*$' 117 | call setpos('.', end_position) 118 | let l:tildeop = &tildeop 119 | set notildeop 120 | normal! l~ 121 | let &tildeop = l:tildeop 122 | endif 123 | endif 124 | 125 | return [type, start_position, end_position] 126 | endfunction 127 | 128 | function! s:select_i() 129 | let right_boundaries = ['\k_', '\l\u', '\u\u\l', '\a\d', '\d\a', '\k\>'] 130 | return s:select('i', join(right_boundaries, '\|')) 131 | endfunction 132 | 133 | function! textobj#variable_segment#select_i() abort 134 | return s:select_i() 135 | endfunction 136 | 137 | function! textobj#variable_segment#select_a() abort 138 | return s:select_a() 139 | endfunction 140 | -------------------------------------------------------------------------------- /t/variable-segment.vim: -------------------------------------------------------------------------------- 1 | filetype plugin on 2 | runtime! plugin/textobj/variable-segment.vim 3 | 4 | 5 | describe 'iv' 6 | it 'selects between underscores' 7 | put! = 'foo_bar_baz' 8 | normal! 6| 9 | normal civquux 10 | Expect getline(1) == 'foo_quux_baz' 11 | end 12 | 13 | it 'selects leading sections' 14 | put! = 'bag_of_spam' 15 | normal! 2| 16 | normal civjar 17 | Expect getline(1) == 'jar_of_spam' 18 | end 19 | 20 | it 'selects ending sections' 21 | put! = 'bag_of_spam' 22 | normal! 9| 23 | normal civbeans 24 | Expect getline(1) == 'bag_of_beans' 25 | end 26 | 27 | it 'selects between small camels' 28 | put! = 'eggsAndCheese' 29 | normal! 6| 30 | normal civOr 31 | Expect getline(1) == 'eggsOrCheese' 32 | end 33 | 34 | it 'considers numbers to be capitals on the left boundary' 35 | put! = '22Skidoo' 36 | normal! 5| 37 | normal civProblemsButThisTestAintOne 38 | Expect getline(1) == '22ProblemsButThisTestAintOne' 39 | end 40 | 41 | it 'considers numbers to be capitals on the right boundary' 42 | put! = 'Catch22' 43 | normal! 2| 44 | normal civSplit 45 | Expect getline(1) == 'Split22' 46 | end 47 | 48 | it 'considers numbers to be a single group' 49 | put! = 'ABC123DEF' 50 | normal! 5| 51 | normal civ456 52 | Expect getline(1) == 'ABC456DEF' 53 | end 54 | 55 | it 'recognizes weird capital runs considering the last to be a new group' 56 | put! = 'MyHTMLParser' 57 | normal! 4| 58 | normal civXML 59 | Expect getline(1) == 'MyXMLParser' 60 | end 61 | 62 | it 'recognizes weird capital runs on the left' 63 | put! = 'MyHTMLParser' 64 | normal! 7| 65 | normal civNightmare 66 | Expect getline(1) == 'MyHTMLNightmare' 67 | end 68 | 69 | it 'ignores leading snake underscores' 70 | put! = '_spam_eggs' 71 | normal! 3| 72 | normal civquux 73 | Expect getline(1) == '_quux_eggs' 74 | end 75 | 76 | it 'ignores leading camel underscores' 77 | put! = '_doCoolThings' 78 | normal! 3| 79 | normal civhowMany 80 | Expect getline(1) == '_howManyCoolThings' 81 | end 82 | 83 | it 'ignores multiple leading underscores' 84 | put! = '__doCoolThings' 85 | normal! 3| 86 | normal civhowMany 87 | Expect getline(1) == '__howManyCoolThings' 88 | end 89 | 90 | it 'selects leading small camels and does not swap case' 91 | put! = 'greatQuuxThings' 92 | normal! 3| 93 | normal civdecent 94 | Expect getline(1) == 'decentQuuxThings' 95 | end 96 | 97 | it 'selects leading large camels and preserves case' 98 | put! = 'BigCat' 99 | normal! 2| 100 | normal civSmall 101 | Expect getline(1) == 'SmallCat' 102 | end 103 | 104 | it 'works on CONSTANT_SECTIONS' 105 | put! = 'SPAM_AND_EGGS' 106 | normal! 7| 107 | normal civOR 108 | Expect getline(1) == 'SPAM_OR_EGGS' 109 | end 110 | 111 | it 'works on mixed variables' 112 | put! = 'getFoo_AndBar' 113 | normal! 10| 114 | normal civOr 115 | Expect getline(1) == 'getFoo_OrBar' 116 | end 117 | 118 | it 'can match on the underscore' 119 | put! = 'hello_world' 120 | normal! 6| 121 | normal civstrange 122 | Expect getline(1) == 'strange_world' 123 | end 124 | 125 | it 'can match on the left snake boundary' 126 | put! = 'hello_world' 127 | normal! 0 128 | normal civgoodbye_cruel 129 | Expect getline(1) == 'goodbye_cruel_world' 130 | end 131 | 132 | it 'can match on the right snake boundary' 133 | put! = 'hello_world' 134 | normal! $ 135 | normal civhello 136 | Expect getline(1) == 'hello_hello' 137 | end 138 | 139 | it 'can match on the left camel boundary' 140 | put! = 'helloWorld' 141 | normal! 0 142 | normal civgoodbyeCruel 143 | Expect getline(1) == 'goodbyeCruelWorld' 144 | end 145 | 146 | it 'can match on the right camel boundary' 147 | put! = 'helloWorld' 148 | normal! $ 149 | normal civBeautiful 150 | Expect getline(1) == 'helloBeautiful' 151 | end 152 | 153 | it 'selects single letter snake case sections' 154 | put! = 'a_thing_I_like' 155 | normal! 0 156 | normal civthe 157 | Expect getline(1) == 'the_thing_I_like' 158 | end 159 | 160 | it 'selects single letter camel sections' 161 | put! = 'aThingILike' 162 | normal! 0 163 | normal civthe 164 | Expect getline(1) == 'theThingILike' 165 | end 166 | 167 | it 'selects segments after single letter snake case sections' 168 | put! = 'a_thing_I_like' 169 | normal! 3| 170 | normal civtest 171 | Expect getline(1) == 'a_test_I_like' 172 | end 173 | 174 | it 'selects segments after single letter camel sections' 175 | put! = 'aThingILike' 176 | normal! 2| 177 | normal civTest 178 | Expect getline(1) == 'aTestILike' 179 | end 180 | 181 | it 'does not cross left snake boundaries' 182 | put! = 'foo_bar baz_quux' 183 | normal! 10| 184 | normal civspaz 185 | Expect getline(1) == 'foo_bar spaz_quux' 186 | end 187 | 188 | it 'does not cross right snake boundaries' 189 | put! = 'foo_bar baz_quux' 190 | normal! 6| 191 | normal civlala 192 | Expect getline(1) == 'foo_lala baz_quux' 193 | end 194 | 195 | it 'does not cross left camel boundaries' 196 | put! = 'fooBar bazQuux' 197 | normal! 10| 198 | normal civFez 199 | Expect getline(1) == 'fooBar FezQuux' 200 | end 201 | 202 | it 'does not cross right camel boundaries' 203 | put! = 'fooBar bazQuux' 204 | normal! 6| 205 | normal civtHill 206 | Expect getline(1) == 'footHill bazQuux' 207 | end 208 | 209 | it 'supports counts' 210 | put! = 'foo_bar_baz_quux_spam' 211 | normal! 6| 212 | normal c3iveggs 213 | Expect getline(1) == 'foo_eggs_spam' 214 | end 215 | 216 | it 'works in the degenerate case' 217 | put! = 'thing another' 218 | normal 2| 219 | normal civone 220 | Expect getline(1) == 'one another' 221 | end 222 | 223 | it 'respects &iskeyword' 224 | let original = &iskeyword 225 | set iskeyword=a-z,_,' 226 | put! = 'bag_of_spam'' stuff' 227 | normal! 9| 228 | normal civeggs_n' 229 | Expect getline(1) == "bag_of_eggs_n' stuff" 230 | let &iskeyword = original 231 | end 232 | end 233 | 234 | 235 | describe 'av' 236 | it 'selects between underscores' 237 | put! = 'foo_bar_baz' 238 | normal! 6| 239 | normal dav 240 | Expect getline(1) == 'foo_baz' 241 | end 242 | 243 | it 'selects leading sections' 244 | put! = 'bag_of_spam' 245 | normal! 2| 246 | normal dav 247 | Expect getline(1) == 'of_spam' 248 | end 249 | 250 | it 'selects ending sections' 251 | put! = 'bag_of_spam' 252 | normal! 9| 253 | normal dav 254 | Expect getline(1) == 'bag_of' 255 | end 256 | 257 | it 'selects between small camels' 258 | put! = 'eggsAndCheese' 259 | normal! 6| 260 | normal dav 261 | Expect getline(1) == 'eggsCheese' 262 | end 263 | 264 | it 'considers numbers to be capitals on the left boundary' 265 | put! = '22Skidoo' 266 | normal! 5| 267 | normal dav 268 | Expect getline(1) == '22' 269 | end 270 | 271 | it 'considers numbers to be capitals on the right boundary' 272 | put! = 'Catch22' 273 | normal! 2| 274 | normal dav 275 | Expect getline(1) == '22' 276 | end 277 | 278 | it 'considers numbers to be a single group' 279 | put! = 'ABC123DEF' 280 | normal! 5| 281 | normal dav 282 | Expect getline(1) == 'ABCDEF' 283 | end 284 | 285 | it 'recognizes weird capital runs considering the last to be a new group' 286 | put! = 'MyHTMLParser' 287 | normal! 4| 288 | normal dav 289 | Expect getline(1) == 'MyParser' 290 | end 291 | 292 | it 'recognizes weird capital runs on the left' 293 | put! = 'MyHTMLParser' 294 | normal! 7| 295 | normal dav 296 | Expect getline(1) == 'MyHTML' 297 | end 298 | 299 | it 'ignores leading snake underscores' 300 | put! = '_spam_eggs' 301 | normal! 3| 302 | normal dav 303 | Expect getline(1) == '_eggs' 304 | end 305 | 306 | it 'ignores leading camel underscores' 307 | put! = '_doCoolThings' 308 | normal! 5| 309 | normal dav 310 | Expect getline(1) == '_doThings' 311 | end 312 | 313 | it 'ignores multiple leading underscores' 314 | put! = '__doCoolThings' 315 | normal! 3| 316 | normal dav 317 | Expect getline(1) == '__coolThings' 318 | end 319 | 320 | it 'selects leading small camels and swaps case' 321 | put! = 'greatQuuxThings' 322 | normal! 3| 323 | normal dav 324 | Expect getline(1) == 'quuxThings' 325 | end 326 | 327 | it 'selects leading small camels and swaps case even with underscores' 328 | put! = '_greatQuuxThings' 329 | normal! 3| 330 | normal dav 331 | Expect getline(1) == '_quuxThings' 332 | end 333 | 334 | it 'selects leading large camels and preserves case' 335 | put! = 'BigCat' 336 | normal! 2| 337 | normal dav 338 | Expect getline(1) == 'Cat' 339 | end 340 | 341 | it 'works on CONSTANT_SECTIONS' 342 | put! = 'SPAM_AND_EGGS' 343 | normal! 7| 344 | normal dav 345 | Expect getline(1) == 'SPAM_EGGS' 346 | end 347 | 348 | it 'works on mixed variables' 349 | put! = 'bathRobe_AndBody' 350 | normal! 5| 351 | normal dav 352 | Expect getline(1) == 'bathAndBody' 353 | end 354 | 355 | it 'can match on the underscore' 356 | put! = 'hello_world' 357 | normal! 6| 358 | normal dav 359 | Expect getline(1) == 'world' 360 | end 361 | 362 | it 'can match on the left snake boundary' 363 | put! = 'hello_world' 364 | normal! 0 365 | normal dav 366 | Expect getline(1) == 'world' 367 | end 368 | 369 | it 'can match on the right snake boundary' 370 | put! = 'hello_world' 371 | normal! $ 372 | normal dav 373 | Expect getline(1) == 'hello' 374 | end 375 | 376 | it 'can match on the left camel boundary' 377 | put! = 'helloWorld' 378 | normal! 0 379 | normal dav 380 | Expect getline(1) == 'world' 381 | end 382 | 383 | it 'can match on the right camel boundary' 384 | put! = 'helloWorld' 385 | normal! $ 386 | normal dav 387 | Expect getline(1) == 'hello' 388 | end 389 | 390 | it 'selects single letter camel sections' 391 | put! = 'aThingILike' 392 | normal! 0 393 | normal dav 394 | Expect getline(1) == 'thingILike' 395 | end 396 | 397 | it 'does not cross left snake boundaries' 398 | put! = 'foo_bar baz_quux' 399 | normal! 10| 400 | normal dav 401 | Expect getline(1) == 'foo_bar quux' 402 | end 403 | 404 | it 'does not cross right snake boundaries' 405 | put! = 'foo_bar baz_quux' 406 | normal! 6| 407 | normal dav 408 | Expect getline(1) == 'foo baz_quux' 409 | end 410 | 411 | it 'does not cross left camel boundaries' 412 | put! = 'fooBar bazQuux' 413 | normal! 10| 414 | normal dav 415 | Expect getline(1) == 'fooBar quux' 416 | end 417 | 418 | it 'supports counts' 419 | put! = 'foo_bar_baz_quux_spam' 420 | normal! 6| 421 | normal d3av 422 | Expect getline(1) == 'foo_spam' 423 | end 424 | 425 | it 'does not cross right camel boundaries' 426 | put! = 'fooBar bazQuux' 427 | normal! 6| 428 | normal dav 429 | Expect getline(1) == 'foo bazQuux' 430 | end 431 | 432 | it 'selects leading small camels and swaps case even with tildeop' 433 | set tildeop " Vim default is notildeop 434 | put! = 'fooBarQuux' 435 | normal! 0 436 | normal dav 437 | Expect getline(1) == 'barQuux' 438 | Expect &tildeop == 1 439 | set notildeop 440 | end 441 | 442 | it 'respects &iskeyword' 443 | let original = &iskeyword 444 | set iskeyword=a-z,_,' 445 | put! = 'bag_of_spam'' stuff' 446 | normal! 9| 447 | normal dav 448 | Expect getline(1) == "bag_of stuff" 449 | let &iskeyword = original 450 | end 451 | end 452 | --------------------------------------------------------------------------------