• Stars
    star
    648
  • Rank 69,480 (Top 2 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 12 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A simple Vim plugin to switch segments of text with predefined replacements

GitHub version Build Status

Screencast!

This plugin is easier to demonstrate than explain. You can find a screencast here.

Usage

The main entry point of the plugin is a single command, :Switch. When the command is executed, the plugin looks for one of a few specific patterns under the cursor and performs a substitution depending on the pattern. For example, if the cursor is on the "true" in the following code:

flag = true

Then, upon executing :Switch, the "true" will turn into "false".

There's a default mapping to trigger the command, gs. Note that this is already a Vim built-in, but it doesn't seem particularly useful.

If you'd like to change the mapping, change the value of g:switch_mapping. For example, to map it to "-", place the following in your .vimrc:

let g:switch_mapping = "-"

To avoid the default mapping completely, set the variable to an empty string:

let g:switch_mapping = ""

See the "customization" section below for information on how to create several mappings with different definitions.

There are three main principles that the substitution follows:

  1. The cursor needs to be on the match. Regardless of the pattern, the plugin only performs the substitution if the cursor is positioned in the matched text.

  2. When several patterns match, the shortest match is performed. For example, in ruby, the following switch is defined:

    { :foo => true }
    # switches into:
    { foo: true }

    This works if the cursor is positioned somewhere on the ":foo =>" part, but if it's on top of "true", the abovementioned true -> false substitution will be performed instead. If you want to perform a "larger" substitution instead, you could move your cursor away from the "smaller" match. In this case, move the cursor away from the "true" keyword.

  3. When several patterns with the same size match, the order of the definitions is respected. For instance, in eruby, the following code can be transformed:

    <% if foo? %>
    could switch into:
    <%# if foo? %>
    but instead, it would switch into:
    <% if true or (foo?) %>

    The second switch will be performed, simply because in the definition list, the pattern was placed at a higher spot. In this case, this seems to make sense to prioritize one over the other. If it's needed to prioritize in a different way, the definition list should be redefined by the user.

Advanced usage

Instead of using the :Switch and :SwitchReverse commands, you can use the autoloaded function switch#Switch. Calling it without any arguments is the same as calling the :Switch command:

:call switch#Switch()
" equivalent to:
:Switch

However, you can also call the function with a dict of options. Instead of :SwitchReverse, you can invoke it with the reverse option:

:call switch#Switch({'reverse': 1})
" or,
:call switch#Switch({'reverse': v:true})
" equivalent to:
:SwitchReverse

The other option you can provide is definitions to set an explicit list of definitions that are different from the built-ins.

:call switch#Switch({'definitions': list_of_definitions})

The switch#Switch() function returns 1 if it succeeded, and 0 if it failed. You can use the return value to decide if you'd like to apply some other mapping.

See below in "Customization" for more details and examples on how to write use this function.

Customization

Note: for more switches by the community, take a look at the wiki

There are two variables that hold the global definition list and the buffer-local definition list -- g:switch_definitions and b:switch_definitions, respectively. These contain the definitions for the built-ins provided by the plugin. In order to add the switches you want, you should override g:switch_custom_definitions and b:switch_custom_definitions instead.

The format of the variables is a simple List of items. Each item can be either a List or a Dict.

List definitions

let g:switch_custom_definitions =
    \ [
    \   ['foo', 'bar', 'baz']
    \ ]

With this definition list, if the plugin encounters "foo" under the cursor, it will be changed to "bar". If it sees "bar", it will change it to "baz", and "baz" would be turned into "foo". This is the simple case of a definition that is implemented (in a slightly different way) by the "toggle.vim" plugin.

You might want this to work for different capitalizations, like with true and True and TRUE. You might also want to also affect only word boundaries. While you could use the more complicated dict definition, a simple way to tackle these scenarios is with modifier functions:

  • switch#NormalizedCase
  • switch#Words
  • switch#NormalizedCaseWords

Here's how you might use these:

let g:switch_custom_definitions =
    \ [
    \   switch#NormalizedCase(['one', 'two']),
    \   switch#Words(['three', 'four']),
    \   switch#NormalizedCaseWords(['five', 'six']),
    \ ]

The result of this is that:

  • The first definition would switch between "one" and "two", between "One" and "Two", and between "ONE" and "TWO".
  • The second definition would switch between "three" and "four" only at word boundaries, as if the patterns have \< and \> modifiers added to them.
  • The third would switch between "five"/"six", "Five"/"Six", "FIVE"/"SIX" only at word boundaries with a combination of the above.

See :help switch-internals for some information on the underlying data format if you'd like to use a different method to generate definitions (like, say, loading JSON).

Leaving lists aside, the more complicated (and more powerful) way to define a switch pattern is by using a Dict. In fact, a list definition is processed into three dict definitions, one for each pair of switches.

Dict definitions

autocmd FileType eruby let b:switch_custom_definitions =
    \ [
    \   {
    \     ':\(\k\+\)\s\+=>': '\1:',
    \     '\<\(\k\+\):':     ':\1 =>',
    \   },
    \ ]

When in the eruby filetype, the hash will take effect. The plugin will look for something that looks like :foo => and replace it with foo: , or the reverse -- foo: , so it could turn it into :foo =>. The search string is fed to the search() function, so all special patterns like \%l have effect in it. And the replacement string is used in the :substitute command, so all of its replacement patterns work as well.

Notice the use of autocmd FileType eruby to set the buffer-local variable whenever an eruby file is loaded. The same effect could be achieved by placing this definition in ftplugin/eruby.vim.

Another interesting example is the following definition:

autocmd FileType php let b:switch_custom_definitions =
      \ [
      \   {
      \     '<?php echo \(.\{-}\) ?>':        '<?php \1 ?>',
      \     '<?php \%(echo\)\@!\(.\{-}\) ?>': '<?php echo \1 ?>',
      \   }
      \ ]

In this case, when in the "php" filetype, the plugin will attempt to remove the "echo" in "" or vice-versa. However, the second pattern wouldn't work properly if it didn't contain "%(echo)@!". This pattern asserts that, in this place of the text, there is no "echo". Otherwise, the second pattern would match as well. Using the \@! pattern in strategic places is important in many cases.

For even more complicated substitutions, you can use the nested form.

Nested dict definitions

The following expression replaces underscored identifier names with their camelcased versions.

let b:switch_custom_definitions = [
      \   {
      \     '\<[a-z0-9]\+_\k\+\>': {
      \       '_\(.\)': '\U\1'
      \     },
      \     '\<[a-z0-9]\+[A-Z]\k\+\>': {
      \       '\([A-Z]\)': '_\l\1'
      \     },
      \   }
      \ ]

If the cursor is on "foo_bar_baz", then switching would produce "fooBarBaz" and vice-versa. The logic is as follows:

  • The keys of the dict are patterns, just like the "normal" dict version.
  • The values of the dict are dicts with patterns for keys and replacements for values.

The goal of this form is to enable substituting several different kinds of patterns within the limits of another one. In this example, there's no way to define this switch using the simpler form, since there's an unknown number of underscores in the variable name and all of them need to be replaced in order to make the switch complete.

The nested patterns differ from the simple one in that each one of them is replaced globally, only within the limits of the "parent" pattern.

Note that this particular example is NOT included as a built-in, since it may overshadow other ones and is probably not that useful, either (it's rare that a language would require changing between the two forms). An example usage may be within javascript, if your server-side variables are underscored and the client-side ones need to be camelcased. For something more complete, you can take a look at this gist.

You could also use a separate mapping for that.

Separate mappings

While there's a default mapping for :Switch, you could actually define several mappings with your own custom definitions:

let g:variable_style_switch_definitions = [
      \   {
      \     '\<[a-z0-9]\+_\k\+\>': {
      \       '_\(.\)': '\U\1'
      \     },
      \     '\<[a-z0-9]\+[A-Z]\k\+\>': {
      \       '\([A-Z]\)': '_\l\1'
      \     },
      \   }
      \ ]
nnoremap + :call switch#Switch({'definitions': g:variable_style_switch_definitions})<cr>
nnoremap - :Switch<cr>

With this, typing - would invoke the built-in switch definitions, while typing + would switch between camelcase and underscored variable styles. This may be particularly useful if you have several clashing switches on patterns that match similar things.

More complicated mappings

By using the switch#Switch() function, you can also write more complicated mappings that check if a switch succeeded, and apply some fallback if it didn't. The function returns 1 for success and 0 for failure.

For example, if you want to switch, or fall back to activating the speeddating plugin, you could map <c-a> and <c-x> like so:

" Don't use default mappings
let g:speeddating_no_mappings = 1

" Avoid issues because of us remapping <c-a> and <c-x> below
nnoremap <Plug>SpeedDatingFallbackUp <c-a>
nnoremap <Plug>SpeedDatingFallbackDown <c-x>

" Manually invoke speeddating in case switch didn't work
nnoremap <c-a> :if !switch#Switch() <bar>
      \ call speeddating#increment(v:count1) <bar> endif<cr>
nnoremap <c-x> :if !switch#Switch({'reverse': 1}) <bar>
      \ call speeddating#increment(-v:count1) <bar> endif<cr>

Builtins

Here's a list of all the built-in switch definitions. To see the actual definitions with their patterns and replacements, look at the file plugin/switch.vim.

Global

  • Boolean conditions:

    foo && bar
    foo || bar
    
  • Boolean constants:

    flag = true
    flag = false
    
    flag = True
    flag = False
    

Ruby

  • Hash style:

    foo = { :one => 'two' }
    foo = { one: 'two' }
  • If-clauses:

    if predicate?
      puts 'Hello, World!'
    end
    
    if true or (predicate?)
      puts 'Hello, World!'
    end
    
    if false and (predicate?)
      puts 'Hello, World!'
    end
  • Rspec should/should_not:

    1.should eq 1
    1.should_not eq 1
  • Tap:

    foo = user.comments.map(&:author).first
    foo = user.comments.tap { |o| puts o.inspect }.map(&:author).first
  • String style:

    foo = 'bar'
    foo = "baz"
    foo = :baz

    (Note that it only works for single-word strings.)

  • Ruby block shorthands:

    do_something { |x| x.some_work! }
    do_something(&:some_work!)
  • Array shorthands:

    ['one', 'two', 'three']
    %w(one two three)
    [:one, :two, :three]
    %i(one two three)

    (In this case, be careful to not have the cursor on one of the strings/symbols, or you'll trigger the string switch as seen above.)

PHP "echo" in tags:

<?php "Text" ?>
<?php echo "Text" ?>

Eruby

  • If-clauses:

    <% if predicate? %>
      <%= 'Hello, World!' %>
    <% end %>
    
    <% if true or (predicate?) %>
      <%= 'Hello, World!' %>
    <% end %>
    
    <% if false and (predicate?) %>
      <%= 'Hello, World!' %>
    <% end %>
  • Tag type:

    <% something %>
    <%# something %>
    <%= something %>
  • Hash style:

    <% foo = { :one => 'two' } %>
    <% foo = { one: 'two' } %>

Haml

  • If-clauses:

    - if predicate?
      Hello, World!
    
    - if true or (predicate?)
      Hello, World!
    
    - if false and (predicate?)
      Hello, World!
  • Tag type:

    - something
    -# something
    = something
  • Hash style:

    %a{:href => '/example'}
    %a{href: '/example'}

C++ pointer dots/arrows:

Object* foo = bar.baz;
Object* foo = bar->baz;

JavaScript

  • Function definitions:

    function example(one, two) { }
    var example = function(one, two) { }
  • ES6-style arrow functions:

    var example = function(one, two) { }
    var example = (one, two) => { }
  • ES6-style variable declarations:

    var example
    let example
    const example
    // var -> let
    // let -> const
    // const -> let

    Switching to var from const or let is unsupported, since it's assumed to be an unlikely case.

CoffeeScript arrows

functionCall (foo) ->
functionCall (foo) =>

CoffeeScript dictionary shorthands

foo = {one, two}
foo = {one: one, two}

Clojure

  • String style:

    "baz"
    'bar
    :baz

    (Note that it only works for single-word strings, such as baz, b-a-z, or **.)

  • If-clauses:

    (if predicate?
      (prn "Hello, world!")
      (prn "oh..."))
    
    (if (or true predicate?)
      (prn "Hello, world!")
      (prn "oh..."))
    
    (if (and false predicate?)
      (prn "Hello, world!")
      (prn "oh..."))

    (Note that it also works for if-not, when, and when-not.)

Scala

  • String style:
    "foo bar"
    s"foo bar"
    f"foo bar"
    """foo bar"""
    s"""foo bar"""
    f"""foo bar"""

Git Rebase

  • Git Rebase Commands
      pick -> fixup -> reword -> edit -> squash -> exec -> break -> drop -> label -> reset -> merge -> (loops back to pick)
    
      p -> fixup
      f -> reword
      r -> edit
      e -> squash
      s -> exec
      x -> break
      b -> drop
      d -> label
      l -> reset
      t -> merge
      m -> pick
    

Elixir

Charlist -> Binary -> Atom

foo = 'bar'
foo = "bar"
foo = :bar

Elixir list shorthands

["one", "two", "three"]
~w(one two three)

[:one, :two, :three]
~w(one two three)a

Capitalized boolean constants :

flag = True
flag = False

Rust

Void typecheck

let value = complicated_expression();
let value: () = complicated_expression();

TOML

Particularly for files named Cargo.toml with the toml filetype (not built-in, but there are plugins for it):

structopt = "0.3.5"
structopt = { version = "0.3.5" }

Markdown

Task items

- [ ] Task 1
- [x] Task 1

Similar work

This plugin is very similar to two other ones:

Both of these work on replacing a specific word under the cursor with a different one. The benefit of switch.vim is that it works for much more complicated patterns. The drawback is that this makes extending it more involved. I encourage anyone that doesn't need the additional power in switch.vim to take a look at one of these two.

Contributing

If you'd like to hack on the plugin, please see CONTRIBUTING.md first.

Issues

Any issues and suggestions are very welcome on the github bugtracker.

More Repositories

1

splitjoin.vim

Switch between single-line and multiline forms of code
Vim Script
1,918
star
2

sideways.vim

A Vim plugin to move function arguments (and other delimited-by-something items) left and right.
Ruby
480
star
3

linediff.vim

A vim plugin to perform diffs on blocks of code
Vim Script
463
star
4

tagalong.vim

Change an HTML(ish) opening tag and take the closing one along as well
Vim Script
412
star
5

vimrunner

Control a vim instance through ruby code
Ruby
237
star
6

inline_edit.vim

Edit code that's embedded within other code
Vim Script
150
star
7

typewriter.vim

Make cool typewriter sounds in insert mode
Vim Script
88
star
8

bufferize.vim

Execute a :command and show the output in a temporary buffer
Vim Script
83
star
9

Vimfiles

My .vim folder
Vim Script
79
star
10

deleft.vim

Delete a wrapping if-clause, try-catch block, etc. and shift left.
Vim Script
71
star
11

quickpeek.vim

Show a preview popup on quickfix entries
Vim Script
70
star
12

undoquit.vim

Undo a :quit -- reopen the last window you closed
Ruby
64
star
13

diffurcate.vim

Split a git diff into separate files
Ruby
59
star
14

dsf.vim

Delete surrounding function call
Ruby
52
star
15

id3.vim

"Edit" mp3 files with Vim, or rather, their ID3 tags
Vim Script
51
star
16

ember_tools.vim

Tools for working with ember projects
Vim Script
51
star
17

writable_search.vim

Grep for something, then write the original files directly through the search results.
Vim Script
51
star
18

quickmd

Quickly preview a markdown file
Rust
37
star
19

gnugo.vim

Play a game of Go in your text editor, using GnuGo
Vim Script
33
star
20

discotheque.vim

Emphasize pieces of text, with style.
Vim Script
32
star
21

simple_bookmarks.vim

A small plugin to create named bookmarks in Vim
Vim Script
31
star
22

gapply.vim

Before committing, edit a git diff and apply it directly to the index
Vim Script
28
star
23

whitespaste.vim

Automatically adjust number of blank lines when pasting
Vim Script
26
star
24

image-processing

Some experiments with simple image processing algorithms
Ruby
22
star
25

rails_extra.vim

Some extra tools for working with Rails projects, on top of vim-rails
Vim Script
21
star
26

id3-image

A tool to embed images into mp3 files
Rust
18
star
27

vim-eco

Eco (embedded coffee-script) support for Vim
Vim Script
17
star
28

strftime.vim

Make it easier to read and write strftime strings
Vim Script
16
star
29

exercism.vim

Vim plugin to help out with exercism.io. A thin wrapper around the `exercism` command-line
Vim Script
15
star
30

dealwithit.vim

Show the "deal with it" dog animation in Vim
Vim Script
14
star
31

tagfinder.vim

A simple vim plugin to look for tags of specific kinds: classes, functions, etc.
Vim Script
13
star
32

whatif.vim

What if we could see which if-else branch gets executed? Haha jk... unless?
Ruby
12
star
33

ghundle

A package manager for git hooks
Ruby
11
star
34

yankwin.vim

Yank and paste windows around
Ruby
9
star
35

waiting-on-rails

Bored of waiting on "rails server"? No more!
Ruby
9
star
36

cucumber-vimscript

Cucumber step definitions for testing vimscript
Ruby
9
star
37

scripts

Small scripts to solve minor problems
Ruby
9
star
38

qftools.vim

Tools to manipulate the quickfix window
Vim Script
8
star
39

vim-lectures

Lectures for the Vim course in FMI
Vim Script
8
star
40

id3-json

Read and write ID3 tags with machine-readable input and output
Rust
7
star
41

progressor

Measure iterations in a long-running task
Ruby
7
star
42

popup_scrollbar.vim

A scrollbar for Vim windows built with the popup API
Vim Script
7
star
43

coffee_tools.vim

A work-in-progress plugin with tools for working with coffeescript
Vim Script
7
star
44

dotfiles

My linux configuration files
Shell
6
star
45

awesome-config

My awesome configuration ("awesome" as in the window manager, not as a quality)
Lua
5
star
46

better_netrw.vim

A better file manager for Vim
Vim Script
5
star
47

vim-fmi

The site for the Vim course at Sofia University: https://vim-fmi.bg/
Ruby
5
star
48

dot-shell

My zsh configuration
Shell
5
star
49

xmonad-config

My personal xmonad configuration (not maintained, switched to awesome)
Haskell
5
star
50

andrews_nerdtree.vim

My personal collection of NERDTree extensions
Vim Script
4
star
51

ginitpull.vim

Open a pull request directly from Vim
Vim Script
4
star
52

iseven.vim

Check if a number is even or not
Vim Script
4
star
53

rtranslate.vim

Easier translations with rails
Vim Script
3
star
54

archivitor.vim

Vim plugin for editing the contents of archives
Vim Script
3
star
55

libmarks

A small website to bookmark programming libraries. Inspired by The Ruby Toolbox
Ruby
3
star
56

daily-sites

A website to administer links to regularly visited sites
Ruby
3
star
57

simple_gl

A thin wrapper around ruby's opengl bindings
Ruby
3
star
58

rimplement.vim

Implement a ruby class or method.
Vim Script
2
star
59

rust-shooter

A toy game in Rust
Rust
2
star
60

egui-mp3s

A simple egui demo app
Rust
2
star
61

vim-learning-website

A website that uses the vimgolf gem for vim exercises.
Ruby
2
star
62

modsearch.vim

A command that lets you change the previous search in predefined ways
Ruby
2
star
63

rust-hangman

A simple game of hangman, built for learning purposes
Rust
2
star
64

do-after

A small C program to execute something after a set amount of time.
C
2
star
65

ctags_reader

Read ctags files, provide a ruby interface to get data out of them
Ruby
2
star
66

rust-spotiferris

A Rust web experiment -- a music management app (very incomplete)
Rust
2
star
67

subtitles.vim

(Not working) A vim plugin that helps you with editing subtitles
Vim Script
2
star
68

randfiles

A tool that outputs a list of random files in given directories
Ruby
2
star
69

rustbucket.vim

[WIP] A collection of Rust tools for Vim
Vim Script
2
star
70

gtk4-example

A very basic Rust example of using GTK4
Rust
2
star
71

rust_wrap.vim

Ok-wrap (Some-wrap, Rc-wrap, etc.) Rust functions
Vim Script
2
star
72

bioinformatics-experiments

Sandbox to play around with bioinformatics-related stuff
Python
2
star
73

onchange.vim

Trigger an autocommand upon a code change
Vim Script
2
star
74

digits

Simple experimental project that recognizes a digit in a given BMP image
C++
1
star
75

vsnips.vim

An early stage experiment with snippets
Vim Script
1
star
76

hello-rusty-web

Some example code for a web application in Rust
Rust
1
star
77

rust-bookworm

A toy project that indexes txt-formatted books and allows searching through them
Rust
1
star
78

mount_archive

Mount an archive as a virtual filesystem
Ruby
1
star
79

declarators

Useful method decorators for ruby
Ruby
1
star
80

vim-fmi-cli

The command-line tool for https://vim-fmi.bg
Rust
1
star
81

rust-eliza

A simple chatbot in Rust. Not intended for "serious" use (whatever the serious use for a chatbot might be), just an experiment.
Rust
1
star
82

tdd_workshop

Python
1
star
83

sticky_line.vim

[WIP] Pin lines to the window to always keep them visible while scrolling
Vim Script
1
star
84

protein-runway

Integrated Bioinformatics Project
Python
1
star