├── .gitignore ├── LICENSE.txt ├── README.md ├── ch00_new_to_vim_read_this_first.md ├── ch01_starting_vim.md ├── ch02_buffers_windows_tabs.md ├── ch03_searching_files.md ├── ch04_vim_grammar.md ├── ch05_moving_in_file.md ├── ch06_insert_mode.md ├── ch07_the_dot_command.md ├── ch08_registers.md ├── ch09_macros.md ├── ch10_undo.md ├── ch11_visual_mode.md ├── ch12_search_and_substitute.md ├── ch13_the_global_command.md ├── ch14_external_commands.md ├── ch15_command-line_mode.md ├── ch16_tags.md ├── ch17_fold.md ├── ch18_git.md ├── ch19_compile.md ├── ch20_views_sessions_viminfo.md ├── ch21_vimrc.md ├── ch22_vim_packages.md ├── ch23_vim_runtime.md ├── ch24_vimscript_basic_data_types.md ├── ch25_vimscript_conditionals_and_loops.md ├── ch26_vimscript_variables_scopes.md ├── ch27_vimscript_functions.md └── images ├── cartesian-xy.png ├── cartesian-xyz.png ├── cartesian-z.png ├── diffing-apples.png ├── diffing-basic.png ├── fugitive-gdiffsplit.png ├── fugitive-git-blame.png ├── fugitive-git-log.png ├── fugitive-git.png ├── fzf-files.gif ├── fzf-in-files.gif ├── learn-vim-cover.png ├── mergetool-initial.png ├── screen-one-buffer-buffers-command.png ├── screen-one-buffer-file1-highlighted.png ├── screen-one-buffer.png ├── screen-split-window-vertically-and-horizontally-two-file2.png ├── screen-split-window-vertically-and-horizontally.png ├── screen-split-window.png ├── screen-tab2.png ├── screen-vscode-3-windows.png ├── session-layout.png ├── tabs-file1js.png └── tabs-file2js.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/vim 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=vim 3 | 4 | ### Vim ### 5 | # Swap 6 | [._]*.s[a-v][a-z] 7 | [._]*.sw[a-p] 8 | [._]s[a-rt-v][a-z] 9 | [._]ss[a-gi-z] 10 | [._]sw[a-p] 11 | 12 | # Session 13 | Session.vim 14 | 15 | # Temporary 16 | .netrwhist 17 | *~ 18 | # Auto-generated tag files 19 | tags 20 | # Persistent undo 21 | [._]*.un~ 22 | 23 | # End of https://www.toptal.com/developers/gitignore/api/vim 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learn Vim (the Smart Way) 2 | 3 | ## What's This? 4 | 5 | *Learn Vim (the Smart Way)* is a guide to learn the good parts of Vim. 6 | 7 | There are many places to learn Vim: the `vimtutor` is a great place to start and the `help` manual has all the references you will ever need. However, the average user needs something more than `vimtutor` and less than the `help` manual. This guide attempts to bridge that gap by highlighting only the key features to learn the most useful parts of Vim in the least time possible. 8 | 9 | This guide is written for both beginner and advanced Vimmers. It starts out with broad and simple concepts and ends with specific and advanced concepts. If you're an advanced user already, I would encourage you to read this guide from start to finish anyway, because you will learn something new! 10 | 11 | ## I Want More Vim Tips! 12 | 13 | Follow [@learnvim](https://twitter.com/learnvim) for updates, Vim tips, etc. 14 | 15 | ## Support This Project 16 | 17 | This guide is and will always be free. 18 | 19 | If you want to financially support this project, you can [purchase this guide on gumroad](https://gumroad.com/l/DADpa/learn-vim-2021). 20 | 21 | 22 | 23 | ## Table Of Contents 24 | 25 | ### Prologue 26 | 27 | - [Ch 0 - New to Vim? Read This First](./ch00_new_to_vim_read_this_first.md) 28 | 29 | ### Part 1: Learn Vim the Smart Way 30 | 31 | - [Ch 1 - Starting Vim](./ch01_starting_vim.md) 32 | - [Ch 2 - Buffers, Windows, and Tabs](./ch02_buffers_windows_tabs.md) 33 | - [Ch 3 - Opening and Searching Files](./ch03_searching_files.md) 34 | - [Ch 4 - Vim Grammar](./ch04_vim_grammar.md) 35 | - [Ch 5 - Moving in a File](./ch05_moving_in_file.md) 36 | - [Ch 6 - Insert Mode](./ch06_insert_mode.md) 37 | - [Ch 7 - The Dot command](./ch07_the_dot_command.md) 38 | - [Ch 8 - Registers](./ch08_registers.md) 39 | - [Ch 9 - Macros](./ch09_macros.md) 40 | - [Ch 10 - Undo](./ch10_undo.md) 41 | - [Ch 11 - Visual Mode](./ch11_visual_mode.md) 42 | - [Ch 12 - Search and Substitute](./ch12_search_and_substitute.md) 43 | - [Ch 13 - The Global Command](./ch13_the_global_command.md) 44 | - [Ch 14 - External Commands](./ch14_external_commands.md) 45 | - [Ch 15 - Command-line Mode](./ch15_command-line_mode.md) 46 | - [Ch 16 - Tags](./ch16_tags.md) 47 | - [Ch 17 - Fold](./ch17_fold.md) 48 | - [Ch 18 - Git](./ch18_git.md) 49 | - [Ch 19 - Compile](./ch19_compile.md) 50 | - [Ch 20 - Views, Sessions, and Viminfo](./ch20_views_sessions_viminfo.md) 51 | 52 | ### Part 2: Customize Vim the Smart Way 53 | 54 | - [Ch 21 - Vimrc](./ch21_vimrc.md) 55 | - [Ch 22 - Vim Packages](./ch22_vim_packages.md) 56 | - [Ch 23 - Vim Runtime](./ch23_vim_runtime.md) 57 | 58 | ### Part 3: Learn Vimscript the Smart Way 59 | 60 | - [Ch 24 - Vimscript Basic Data Types](./ch24_vimscript_basic_data_types.md) 61 | - [Ch 25 - Vimscript Conditionals And Loops](./ch25_vimscript_conditionals_and_loops.md) 62 | - [Ch 26 - Vimscript Variable Scopes](./ch26_vimscript_variables_scopes.md) 63 | - [Ch 27 - Vimscript Functions](./ch27_vimscript_functions.md) 64 | 65 | ## Translations 66 | - [Learn-Vim 中文翻译](https://github.com/wsdjeg/Learn-Vim_zh_cn)(`zh-CN`) 67 | - [Learn-Vim Spanish](https://github.com/victorhck/learn-Vim-es)(`es`) 68 | 69 | ## License & Copyright 70 | The materials here are all © 2020-2021 Igor Irianto. 71 | 72 | Creative Commons License
73 | 74 | This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International. 75 | -------------------------------------------------------------------------------- /ch00_new_to_vim_read_this_first.md: -------------------------------------------------------------------------------- 1 | # New To Vim? Read This First 2 | 3 | ## Why This Guide Was Written 4 | 5 | There are many places to learn Vim: the `vimtutor` is a great place to start and the `:help` manual has all the references you will ever need. 6 | 7 | However, the average user needs something more than `vimtutor` and less than the `:help` manual. This guide attempts to bridge that gap by highlighting only the key features to learn the most useful parts of Vim in the least time possible. 8 | 9 | Chances are you won't need all 100% of Vim features. You probably only need to know about 20% of them to become a powerful Vimmer. This guide will show you which Vim features you will find most useful. 10 | 11 | This is an opinionated guide. It covers techniques that I often use when using Vim. The chapters are sequenced based on what I think would make the most logical sense for a beginner to learn Vim. 12 | 13 | This guide is examples-heavy. When learning a new skill, examples are indispensable, having numerous examples will solidify these concepts more effectively. 14 | 15 | Some of you may wonder why do you need to learn Vimscript? In my first year of using Vim, I was content with just knowing how to use Vim. Time passed and I started needing Vimscript more and more to write custom commands for my specific editing needs. As you are mastering Vim, you will sooner or later need to learn Vimscript. So why not sooner? Vimscript is a small language. You can learn its basics in just four chapters of this guide. 16 | 17 | You can go far using Vim without knowing any Vimscript, but knowing it will help you excel even farther. 18 | 19 | This guide is written for both beginner and advanced Vimmers. It starts out with broad and simple concepts and ends with specific and advanced concepts. If you're an advanced user already, I would encourage you to read this guide from start to finish anyway, because you will learn something new! 20 | 21 | ## How To Transition To Vim From Using A Different Text Editor 22 | 23 | Learning Vim is a satisfying experience, albeit hard. There are two main approaches to learn Vim: 24 | 25 | 1. Cold turkey 26 | 2. Gradual 27 | 28 | Going cold turkey means to stop using whatever editor / IDE you were using and to use Vim exclusively starting now. The downside of this method is you will have a serious productivity loss during the first week or two. If you're a full-time programmer, this method may not be feasible. That's why for most people, I believe the best way to transition to Vim is to use it gradually. 29 | 30 | To gradually use Vim, during the first two weeks, spend an hour a day using Vim as your editor while the rest of the time you can use other editors. Many modern editors come with Vim plugins. When I first started, I used VSCode's popular Vim plugin for an hour per day. I gradually increased the time with the Vim plugin until I finally used it all day. Keep in mind that these plugins can only emulate a fraction of Vim features. To experience the full power of Vim like Vimscript, Command-line (Ex) Commands, and external commands integration, you will need to use Vim itself. 31 | 32 | There were two pivotal moments that made me start to use Vim 100%: when I grasped that Vim has a grammar-like structure (see chapter 4) and the [fzf.vim](https://github.com/junegunn/fzf.vim) plugin (see chapter 3). 33 | 34 | The first, when I realized Vim's grammar-like structure, was the defining moment that I finally understood what these Vim users were talking about. I didn't need to learn hundreds of unique commands. I only had to learn a small handful of commands and I could chain in a very intuitive way to do many things. 35 | 36 | The second, the ability to quickly run a fuzzy file-search was the IDE feature that I used most. When I learned how to do that in Vim, I gained a major speed boost and never looked back ever since. 37 | 38 | Everyone programs differently. Upon introspection, you will find that there are one or two features from your favorite editor / IDE that you use all the time. Maybe it was fuzzy-search, jump-to-definition, or quick compilation. Whatever they may be, identify them quickly and learn how to implement those in Vim (chances are Vim can probably do them too). Your editing speed will receive a huge boost. 39 | 40 | Once you can edit at 50% of the original speed, it's time to go full-time Vim. 41 | 42 | ## How To Read This Guide 43 | 44 | This is a practical guide. To become good in Vim you need to develop your muscle memory, not head knowledge. 45 | 46 | You don't learn how to ride a bike by reading a guide about how to ride a bike. You need to actually ride a bike. 47 | 48 | You need to type along every commands referred in this guide. Not only that, but you need to repeat them several times and try different combinations. Look up what other features the command you just learned has. The `:help` command and search engines are your best friends. Your goal is not to know everything about a command, but to be able to execute that command naturally and instinctively. 49 | 50 | As much as I try to fashion this guide to be linear, some concepts in this guide have to be presented out-of-order. For example in chapter 1, I mention the substitute command (`:s`), even though it won't be covered until chapter 12. To remedy this, whenever a new concept that has not been covered yet is mentioned early, I will provide a quick how-to guide without a detailed explanation. So please bear with me :). 51 | 52 | ## More Help 53 | 54 | Here's one extra tip to use the help manual: suppose you want to learn more about what `Ctrl-P` does in insert mode. If you merely search for `:h CTRL-P`, you will be directed to normal mode's `Ctrl-P`. This is not the `Ctrl-P` help that you're looking for. In this case, search instead for `:h i_CTRL-P`. The appended `i_` represents the insert mode. Pay attention to which mode it belongs to. 55 | 56 | ## Syntax 57 | 58 | Most of the command or code-related phrases are in code-case (`like this`). 59 | 60 | Strings are surrounded by a pair of double-quotes ("like this"). 61 | 62 | Vim commands can be abbreviated. For example, `:join` can be abbreviated as `:j`. Throughout the guide, I will be mixing the shorthand and the longhand descriptions. For commands that are not frequently used in this guide, I will use the longhand version. For commands that are frequently used, I will use the shorthand version. I apologize for the inconsistencies. In general, whenever you spot a new command, always check it on `:help` to see its abbreviations. 63 | 64 | ## Vimrc 65 | 66 | At various points in the guide, I will refer to vimrc options. If you're new to Vim, a vimrc is like a config file. 67 | 68 | Vimrc won't be covered until chapter 21. For the sake of clarity, I will show briefly here how to set it up. 69 | 70 | Suppose you need to set the number options (`set number`). If you don't have a vimrc already, create one. It is usually placed at the root directory named `.vimrc`. Depending on your OS, the location may differ. In macOS, I have it on `~/.vimrc`. To see where you should put yours, check out `:h vimrc`. 71 | 72 | Inside it, add `set number`. Save it (`:w`), then source it (`:source %`). You should now see line numbers displayed on the left side. 73 | 74 | Alternatively, if you don't want to a make permanent setting change, you can always run the `set` command inline, by running `:set number`. The downside of this approach is that this setting is temporary. When you close Vim, the option disappears. 75 | 76 | Since we are learning about Vim and not Vi, a setting that you must have is the `nocompatible` option. Add `set nocompatible` in your vimrc. Many Vim-specific features are disabled when it is running on `compatible` option. 77 | 78 | In general, whenever a passage mentions a vimrc option, just add that option into vimrc, save it, and source it. 79 | 80 | ## Future, Errors, Questions 81 | 82 | Expect more updates in the future. If you find any errors or have any questions, please feel free to reach out. 83 | 84 | I also have planned a few more upcoming chapters, so stay tuned! 85 | 86 | ## I Want More Vim Tricks 87 | 88 | To learn more about Vim, please follow [@learnvim](https://twitter.com/learnvim). 89 | 90 | ## Thank Yous 91 | 92 | This guide wouldn't be possible without Bram Moleenar for creating Vim, my wife who had been very patient and supportive throughout the journey, all the [contributors](https://github.com/iggredible/Learn-Vim/graphs/contributors) of the learn-vim project, the Vim community, and many, many others that weren't mentioned. 93 | 94 | Thank you. You all help make text editing fun :) 95 | 96 | -------------------------------------------------------------------------------- /ch01_starting_vim.md: -------------------------------------------------------------------------------- 1 | # Ch01. Starting Vim 2 | 3 | In this chapter, you will learn different ways to start Vim from the terminal. I was using Vim 8.2 when writing this guide. If you use Neovim or an older version of Vim, you should be mostly fine, but be aware that some commands might not be available. 4 | 5 | ## Installing 6 | 7 | I won't go through the detailed instruction how to install Vim in a specific machine. The good news is, most Unix-based computers should come with Vim installed. If not, most distros should have some instructions to install Vim. 8 | 9 | For download informations, check out Vim's official download website or Vim's official github repository: 10 | - [Vim website](https://www.vim.org/download.php) 11 | - [Vim github](https://github.com/vim/vim) 12 | 13 | ## The Vim Command 14 | 15 | Now that you have Vim installed, run this from the terminal: 16 | 17 | ```bash 18 | vim 19 | ``` 20 | 21 | You should see an intro screen. This is the where you will be working on your file. Unlike most text editors and IDEs, Vim is a modal editor. If you want to type "hello", you need to switch to insert mode with `i`. Press `ihello` to insert the text "hello". 22 | 23 | ## Exiting Vim 24 | 25 | There are several ways to exit Vim. The most common one is to type: 26 | 27 | ``` 28 | :quit 29 | ``` 30 | 31 | You can type `:q` for short. That command is a command-line mode command (another one of Vim modes). If you type `:` in normal mode, the cursor will move to the bottom of the screen where you can type some commands. You will learn about the command-line mode later in chapter 15. If you are in insert mode, typing `:` will literally produce the character ":" on the screen. In this case, you need to switch back to normal mode. Type `` to switch to normal mode. By the way, you can also return to normal mode from command-line mode by pressing ``. You will notice that you can "escape" out of several Vim modes back to normal mode by pressing ``. 32 | 33 | ## Saving A File 34 | 35 | To save your changes, type: 36 | 37 | ``` 38 | :write 39 | ``` 40 | 41 | You can also type `:w` for short. If this is a new file, you need to give it a name before you can save it. Let's name it `file.txt`. Run: 42 | 43 | ``` 44 | :w file.txt 45 | ``` 46 | 47 | To save and quit, you can combine the `:w` and `:q` commands: 48 | 49 | ``` 50 | :wq 51 | ``` 52 | 53 | To quit without saving any changes, add `!` after `:q` to force quit: 54 | 55 | ``` 56 | :q! 57 | ``` 58 | 59 | There are other ways to exit Vim, but these are the ones you will use daily. 60 | 61 | ## Help 62 | 63 | Throughout this guide, I will refer you to various Vim help pages. You can go to the help page by typing `:help {some-command}` (`:h` for short). You can pass to the `:h` command a topic or a command name as an argument. For example, to learn about different ways to quit Vim, type: 64 | 65 | ``` 66 | :h write-quit 67 | ``` 68 | 69 | How did I know to search for "write-quit"? I actually didn't. I just typed `:h`, then "quit", then ``. Vim displayed relevant keywords to choose from. If you ever need to look up something ("I wish Vim can do this..."), just type `:h` and try some keywords, then ``. 70 | 71 | ## Opening a File 72 | 73 | To open a file (`hello1.txt`) on Vim from the terminal, run: 74 | 75 | ```bash 76 | vim hello1.txt 77 | ``` 78 | 79 | You can also open multiple files at once: 80 | 81 | ```bash 82 | vim hello1.txt hello2.txt hello3.txt 83 | ``` 84 | 85 | Vim opens `hello1.txt`, `hello2.txt`, and `hello3.txt` in separate buffers. You will learn about buffers in the next chapter. 86 | 87 | ## Arguments 88 | 89 | You can pass the `vim` terminal command with different flags and options. 90 | 91 | To check the current Vim version, run: 92 | 93 | ```bash 94 | vim --version 95 | ``` 96 | 97 | This tells you the current Vim version and all available features marked with either `+` or `-` Some of these features in this guide require certain features to be available. For example, you will explore Vim's command-line history in a later chapter with the `:history` command. Your Vim needs to have `+cmdline_history` feature for the command to work. There is a good chance that the Vim you just installed have all the necessary features, especially if it is from a popular download source. 98 | 99 | Many things you do from the terminal can also be done from inside Vim. To see the version from *inside* Vim, you can run this: 100 | 101 | ``` 102 | :version 103 | ``` 104 | 105 | If you want to open the file `hello.txt` and immediately execute a command, you can pass to the `vim` command the `+{cmd}` option. 106 | 107 | In Vim, you can substitute texts with the `:s` command (short for `:substitute`). If you want to open `hello.txt` and substitute all "pancake" with "bagel", run: 108 | 109 | ```bash 110 | vim +%s/pancake/bagel/g hello.txt 111 | ``` 112 | 113 | The command can be stacked: 114 | 115 | ```bash 116 | vim +%s/pancake/bagel/g +%s/bagel/egg/g +%s/egg/donut/g hello.txt 117 | ``` 118 | 119 | Vim will replace all instances of "pancake" with "bagel", then replace "bagel" with "egg", then replace "egg" with "donut" (you willl learn substitution in a later chapter). 120 | 121 | You can also pass the `c` flag followed by the command instead of the `+` syntax: 122 | 123 | ```bash 124 | vim -c %s/pancake/bagel/g hello.txt 125 | vim -c %s/pancake/bagel/g -c %s/bagel/egg/g -c %s/egg/donut/g hello.txt 126 | ``` 127 | 128 | ## Opening Multiple Windows 129 | 130 | You can launch Vim on split horizontal and vertical windows with `o` and `O`, respectively. 131 | 132 | To open Vim with two horizontal windows, run: 133 | 134 | ```bash 135 | vim -o2 136 | ``` 137 | 138 | To open Vim with 5 horizontal windows, run: 139 | 140 | ```bash 141 | vim -o5 142 | ``` 143 | 144 | To open Vim with 5 horizontal windows and fill up the first two with `hello1.txt` and `hello2.txt`, run: 145 | 146 | ```bash 147 | vim -o5 hello1.txt hello2.txt 148 | ``` 149 | 150 | To open Vim with two vertical windows, 5 vertical windows, and 5 vertical windows with 2 files: 151 | 152 | ```bash 153 | vim -O 154 | vim -O5 155 | vim -O5 hello1.txt hello2.txt 156 | ``` 157 | 158 | ## Suspending 159 | 160 | If you need to suspend Vim while in the middle of editing, you can press `Ctrl-z`. You can also run either the `:stop` or `:suspend` command. To return to the suspended Vim, run `fg` from the terminal. 161 | 162 | ## Starting Vim The Smart Way 163 | 164 | You can pass the `vim` command with different options and flags, just like any terminal commands. One of the options is the command-line command (`+{cmd}` or `c cmd`). As you learn more commands throughout this guide, see if you can apply it on start. Also being a terminal command, you can combine `vim` with many other terminal commands. For example, you can redirect the output of the `ls` command to be edited in Vim with `ls -l | vim -`. 165 | 166 | To learn more about Vim terminal command, check out `man vim`. To learn more about the Vim editor, continue reading this guide along with the `:help` command. 167 | -------------------------------------------------------------------------------- /ch02_buffers_windows_tabs.md: -------------------------------------------------------------------------------- 1 | # Ch02. Buffers, Windows, and Tabs 2 | 3 | If you have used a modern text editor, you are probably familiar with windows and tabs. Vim uses three display abstractions instead of two: buffers, windows, and tabs. In this chapter, I will explain what buffers, windows, and tabs are and how they work in Vim. 4 | 5 | Before you start, make sure you have the `set hidden` option in vimrc. Without it, whenever you switch buffers and your current buffer is not saved, Vim will prompt you to save the file (you don't want that if you want to move quickly). I haven't cover vimrc yet. If you don't have a vimrc, create one. It is usually placed at the root directory and named `.vimrc`. I have mine on `~/.vimrc`. To see where you should create your vimrc, check out `:h vimrc`. Inside it, add: 6 | 7 | ``` 8 | set hidden 9 | ``` 10 | 11 | Save it, then source it (run `:source %` from inside the vimrc). 12 | 13 | ## Buffers 14 | 15 | What is a *buffer*? 16 | 17 | A buffer is an in-memory space where you can write and edit some text. When you open a file in Vim, the data is bound to a buffer. When you open 3 files in Vim, you have 3 buffers. 18 | 19 | Have two empty files, `file1.js` and `file2.j` (if possible, create them with Vim) available. Run this in the terminal: 20 | 21 | ```bash 22 | vim file1.js 23 | ``` 24 | 25 | ![one buffer displayed with highlight](images/screen-one-buffer-file1-highlighted.png) 26 | 27 | What you are seeing is `file1.js` *buffer*. Whenever you open a new file, Vim creates a new buffer. 28 | 29 | Exit Vim. This time, open two new files: 30 | 31 | ```bash 32 | vim file1.js file2.js 33 | ``` 34 | 35 | ![one buffer displayed.png](images/screen-one-buffer.png) 36 | 37 | Vim displays `file1.js` buffer, but it actually creates two buffers: `file1.js` buffer and `file2.js` buffer. Run `:buffers` to see all the buffers (alternatively, you can use `:ls` or `:files` too). 38 | 39 | ![buffers command showing 2 buffers](images/screen-one-buffer-buffers-command.png) 40 | 41 | There are several ways you can traverse buffers: 42 | - `:bnext` to go to the next buffer (`:bprevious` to go to the previous buffer). 43 | - `:buffer` + filename. Vim can autocomplete filename with ``. 44 | - `:buffer` + `n`, where `n` is the buffer number. For example, typing `:buffer 2` will take you to buffer #2. 45 | - Jump to the older position in jump list with `Ctrl-O` and to the newer position with `Ctrl-I`. These are not buffer specific methods, but they can be used to jump between different buffers. I will talk more about jumps in Chapter 5. 46 | - Go to the previously edited buffer with `Ctrl-^`. 47 | 48 | Once Vim creates a buffer, it will remain in your buffers list. To remove it, you can type `:bdelete`. It accepts either a buffer number (`:bdelete 3` to delete buffer #3) or a filename (`:bdelete` then use `` to autocomplete). 49 | 50 | The hardest thing for me when learning about buffer was visualizing how buffers worked. Imagine a deck of playing cards. If I have 2 buffers, I have a stack of 2 cards. The card on top is the card I see. If I see `file1.js` buffer displayed then the `file1.js` card is on the top of the deck. I can't see the other card, `file2.js`. If I switch buffers to `file2.js`, that `file2.js` card is now on the top of the deck and `file1.js` card is at the bottom. 51 | 52 | If you haven't used Vim before, this is a new concept. Take your time to understand it. 53 | 54 | ## Exiting Vim 55 | 56 | By the way, if you have multiple buffers opened, you can close all of them with quit-all: 57 | 58 | ``` 59 | :qall 60 | ``` 61 | 62 | If you want to close without saving your changes, just add `!` at the end: 63 | 64 | ``` 65 | :qall! 66 | ``` 67 | 68 | To save and quit all, run: 69 | 70 | ``` 71 | :wqall 72 | ``` 73 | 74 | ## Windows 75 | 76 | A window is a viewport on a buffer. You can have multiple windows. Most text editors have the ability to display multiple windows. Below you see a VSCode with 3 windows: 77 | 78 | ![VSCode showing 3 windows](images/screen-vscode-3-windows.png) 79 | 80 | Let's open `file1.js` from the terminal again: 81 | 82 | ```bash 83 | vim file1.js 84 | ``` 85 | 86 | ![one buffer displayed.png](images/screen-one-buffer.png) 87 | 88 | Earlier I said that you're looking at `file1.js` buffer. While that was correct, it was incomplete. You are looking at `file1.js` buffer displayed through **a window**. A window is what you are seeing a buffer through. 89 | 90 | Don't quit Vim yet. Run: 91 | 92 | ``` 93 | :split file2.js 94 | ``` 95 | 96 | ![split window horizontally](images/screen-split-window.png) 97 | 98 | Now you are looking at two buffers through **two windows**. The top window displays `file2.js` buffer. The bottom window displays `file1.js` buffer. 99 | 100 | If you want to navigate between windows, use these shortcuts: 101 | 102 | ``` 103 | Ctrl-W H Moves the cursor to the left window 104 | Ctrl-W J Moves the cursor to the window below 105 | Ctrl-W K Moves the cursor to the window upper 106 | Ctrl-W L Moves the cursor to the right window 107 | ``` 108 | 109 | Now run: 110 | 111 | ``` 112 | :vsplit file3.js 113 | ``` 114 | 115 | ![split window vertically and horizontally](images/screen-split-window-vertically-and-horizontally.png) 116 | 117 | 118 | You are now seeing three windows displaying three buffers. The top left window displays `file3.js` buffer, the top right window displays `file2.js` buffer, and the bottom window displays `file1.js` buffer. 119 | 120 | You can have multiple windows displaying the same buffer. While you're on the top left window, type: 121 | ``` 122 | :buffer file2.js 123 | ``` 124 | ![split window vertically and horizontally with two file2.js](images/screen-split-window-vertically-and-horizontally-two-file2.png) 125 | 126 | 127 | Now both top left and top right windows are displaying `file2.js` buffer. If you start typing on the top left, you can see that the content on both top left and top right window are being updated in real-time. 128 | 129 | To close the current window, you can run `Ctrl-W C` or type `:quit`. When you close a window, the buffer will still be there (run `:buffers` to confirm this). 130 | 131 | Here are some useful normal-mode window commands: 132 | 133 | ``` 134 | Ctrl-W V Opens a new vertical split 135 | Ctrl-W S Opens a new horizontal split 136 | Ctrl-W C Closes a window 137 | Ctrl-W O Makes the current window the only one on screen and closes other windows 138 | ``` 139 | And here is a list of useful window command-line commands: 140 | 141 | ``` 142 | :vsplit filename Split window vertically 143 | :split filename Split window horizontally 144 | :new filename Create new window 145 | ``` 146 | 147 | Take your time to understand them. For more, check out `:h window`. 148 | 149 | ## Tabs 150 | 151 | A tab is a collection of windows. Think of it like a layout for windows. In most modern text editors (and modern internet browsers), a tab means an open file / page and when you close it, that file / page goes away. In Vim, a tab does not represent an open file. When you close a tab in Vim, you are not closing a file. You are only closing the layout. The data for those files are stored in-memory in buffers. The buffers are still there. 152 | 153 | Let's see Vim tabs in action. Open `file1.js`: 154 | 155 | ```bash 156 | vim file1.js 157 | ``` 158 | 159 | To open `file2.js` in a new tab: 160 | 161 | ``` 162 | :tabnew file2.js 163 | ``` 164 | 165 | ![screen displays tab 2](images/screen-tab2.png) 166 | 167 | You can also let Vim autocomplete the file you want to open in a *new tab* by pressing `` (no pun intended). 168 | 169 | Below is a list of useful tab navigations: 170 | 171 | ``` 172 | :tabnew file.txt Open file.txt in a new tab 173 | :tabclose Close the current tab 174 | :tabnext Go to next tab 175 | :tabprevious Go to previous tab 176 | :tablast Go to last tab 177 | :tabfirst Go to first tab 178 | ``` 179 | 180 | You can also run `gt` to go to next tab page (you can go to previous tab with `gT`). You can pass count as argument to `gt`, where count is tab number. To go to the third tab, do `3gt`. 181 | 182 | One advantage of having multiple tabs is you can have different window arrangements in different tabs. Maybe you want your first tab to have 3 vertical windows and second tab to have a mixed horizontal and vertical windows layout. Tab is the perfect tool for the job! 183 | 184 | ![first tab with multiple windows](images/tabs-file1js.png) 185 | 186 | ![second tab with multiple windows](images/tabs-file2js.png) 187 | 188 | To start Vim with multiple tabs, you can do this from the terminal: 189 | 190 | ```bash 191 | vim -p file1.js file2.js file3.js 192 | ``` 193 | 194 | ## Moving In 3D 195 | 196 | Moving between windows is like traveling two-dimensionally along X-Y axis in a Cartesian coordinate. You can move to the top, right, bottom, and left window with `Ctrl-W H/J/K/L`. 197 | 198 | ![cartesian movement in x and y axis](images/cartesian-xy.png) 199 | 200 | Moving between buffers is like traveling across the Z axis in a Cartesian coordinate. Imagine your buffer files lining up across the Z axis. You can traverse the Z axis one buffer at a time with `:bnext` and `:bprevious`. You can jump to any coordinate in Z axis with `:buffer filename/buffernumber`. 201 | 202 | ![cartesian movement in z axis](images/cartesian-z.png) 203 | 204 | You can move in *three-dimensional space* by combining window and buffer movements. You can move to the top, right, bottom, or left window (X-Y navigations) with window navigations. Since each window contains buffers, you can move forward and backward (Z navigations) with buffer movements. 205 | 206 | ![cartesian movement in x, y, and z axis](images/cartesian-xyz.png) 207 | 208 | ## Using Buffers, Windows, and Tabs The Smart Way 209 | 210 | You have learned what buffers, windows, and tabs are and how they work in Vim. Now that you understand them better, you can use them in your own workflow. 211 | 212 | Everyone has a different workflow, here is mine for example: 213 | - First I use buffers to store all the required files for the current task. Vim can handle many open buffers before it starts slowing down. Plus having many buffers opened won't crowd my screen. I am only seeing one buffer (assuming I have only one window) at any time, allowing me to focus on one screen. When I need to go somewhere, I can quickly fly to any open buffer anytime. 214 | - I use multiple windows to view multiple buffers at once, usually when diffing files, reading docs, or following a code flow. I try to keep the number of windows opened to no more than three because my screen will get crowded (I use a small laptop). When I am done, I close any extra windows. Fewer windows means less distractions. 215 | - Instead of tabs, I use [tmux](https://github.com/tmux/tmux/wiki) windows. I usually use multiple tmux windows at once. For example, one tmux window for client-side codes and another for backend codes. 216 | 217 | My workflow may look different than yours based on your editing style and that's fine. Experiment around to discover your own flow suited for your coding style. 218 | -------------------------------------------------------------------------------- /ch04_vim_grammar.md: -------------------------------------------------------------------------------- 1 | # Ch04. Vim Grammar 2 | 3 | It is easy to get intimidated by the complexity of Vim commands. If you see a Vim user doing `gUfV` or `1GdG`, you may not immediately know what these commands do. In this chapter, I will break down the general structure of Vim commands into a simple grammar rule. 4 | 5 | This is the most important chapter in the entire guide. Once you understand the underlying grammatical structure, you will be able to "speak" to Vim. By the way, when I say *Vim language* in this chapter, I am not talking about Vimscript language (Vim's built-in programming language, you will learn that in later chapters). 6 | 7 | ## How To Learn A Language 8 | 9 | I am not a native English speaker. I learned English when I was 13 when I moved to the US. There are three things you need to do to learn to speak a new language: 10 | 11 | 1. Learn grammar rules. 12 | 2. Increase vocabulary. 13 | 3. Practice, practice, practice. 14 | 15 | Likewise, to speak Vim language, you need to learn the grammar rules, increase vocabulary, and practice until you can run the commands without thinking. 16 | 17 | ## Grammar Rule 18 | 19 | There is only one grammar rule in Vim language: 20 | 21 | ``` 22 | verb + noun 23 | ``` 24 | 25 | That's it! 26 | 27 | This is like saying these English phrases: 28 | 29 | - *"Eat (verb) a donut (noun)"* 30 | - *"Kick (verb) a ball (noun)"* 31 | - *"Learn (verb) the Vim editor (noun)"* 32 | 33 | Now you need to build up your vocabulary with basic Vim verbs and nouns. 34 | 35 | ## Nouns (Motions) 36 | 37 | Nouns are Vim motions. Motions are used to move around in Vim. Below is a list of some of Vim motions: 38 | 39 | ``` 40 | h Left 41 | j Down 42 | k Up 43 | l Right 44 | w Move forward to the beginning of the next word 45 | } Jump to the next paragraph 46 | $ Go to the end of the line 47 | ``` 48 | 49 | You will learn more about motions in the next chapter, so don't worry too much if you don't understand some of them. 50 | 51 | ## Verbs (Operators) 52 | 53 | According to `:h operator`, Vim has 16 operators. However, in my experience, learning these 3 operators is enough for 80% of my editing needs: 54 | 55 | ``` 56 | y Yank text (copy) 57 | d Delete text and save to register 58 | c Delete text, save to register, and start insert mode 59 | ``` 60 | 61 | Btw, after you yank a text, you can paste it with `p` (after the cursor) or `P` (before the cursor). 62 | 63 | ## Verb And Noun 64 | 65 | Now that you know basic nouns and verbs, let's apply the grammar rule, verb + noun! Suppose you have this expression: 66 | 67 | ```javascript 68 | const learn = "vim"; 69 | ``` 70 | 71 | - To yank everything from your current location to the end of the line: `y$`. 72 | - To delete from your current location to the beginning of the next word: `dw`. 73 | - To change from your current location to the end of the current paragraph, say `c}`. 74 | 75 | Motions also accept count number as arguments (I will discourse this in the next chapter). If you need to go up 3 lines, instead of pressing `k` 3 times, you can do `3k`. Count works with Vim grammar. 76 | - To yank two characters to the left: `y2h`. 77 | - To delete the next two words: `d2w`. 78 | - To change the next two lines: `c2j`. 79 | 80 | Right now, you may have to think long and hard to do even a simple command. You're not alone. When I first started, I had similar struggles but I got faster in time. So will you. Repetition, repetition, repetition. 81 | 82 | As a side note, linewise operations (operations affecting the entire line) are common operations in text editing. In general, by typing an operator command twice, Vim performs a linewise operation for that action. For example, `dd`, `yy`, and `cc` perform **deletion**, **yank**, and **change** on the entire line. Try this with other operators! 83 | 84 | This is really cool. I am seeing a pattern here. But I am not quite done yet. Vim has one more type of noun: text objects. 85 | 86 | ## More Nouns (Text Objects) 87 | 88 | Imagine you are somewhere inside a pair of parentheses like `(hello Vim)` and you need to delete the entire phrase inside the parentheses. How can you quickly do it? Is there a way to delete the "group" you are inside of? 89 | 90 | The answer is yes. Texts often come structured. They often contain parentheses, quotes, brackets, braces, and more. Vim has a way to capture this structure with text objects. 91 | 92 | Text objects are used with operators. There are two types of text objects: inner and outer text objects. 93 | 94 | ``` 95 | i + object Inner text object 96 | a + object Outer text object 97 | ``` 98 | 99 | Inner text object selects the object inside *without* the white space or the surrounding objects. Outer text object selects the object inside *including* the white space or the surrounding objects. Generally, an outer text object always selects more text than an inner text object. If your cursor is somewhere inside the parentheses in the expression `(hello vim)`: 100 | - To delete the text inside the parentheses without deleting the parentheses: `di(`. 101 | - To delete the parentheses and the text inside: `da(`. 102 | 103 | Let's look at a different example. Suppose you have this Javascript function and your cursor is on the "H" in "Hello": 104 | 105 | ```javascript 106 | const hello = function() { 107 | console.log("Hello Vim"); 108 | return true; 109 | } 110 | ``` 111 | 112 | - To delete the entire "Hello Vim": `di(`. 113 | - To delete the content of function (surrounded by `{}`): `di{`. 114 | - To delete the "Hello" string: `diw`. 115 | 116 | Text objects are powerful because you can target different objects from one location. You can delete the objects inside the parentheses, the function block, or the current word. Mnemonically, when you see `di(`, `di{`, and `diw`, you get a pretty good idea which text objects they represent: a pair of parentheses, a pair of braces, and a word. 117 | 118 | Let's look at one last example. Suppose you have these HTML tags: 119 | 120 | ```html 121 |
122 |

Header1

123 |

Paragraph1

124 |

Paragraph2

125 |
126 | ``` 127 | 128 | If your cursor is on "Header1" text: 129 | - To delete "Header1": `dit`. 130 | - To delete `

Header1

`: `dat`. 131 | 132 | If your cursor is on "div": 133 | - To delete `h1` and both `p` lines: `dit`. 134 | - To delete everything: `dat`. 135 | - To delete "div": `di<`. 136 | 137 | Below is a list of common text objects: 138 | 139 | ``` 140 | w A word 141 | p A paragraph 142 | s A sentence 143 | ( or ) A pair of ( ) 144 | { or } A pair of { } 145 | [ or ] A pair of [ ] 146 | < or > A pair of < > 147 | t XML tags 148 | " A pair of " " 149 | ' A Pair of ' ' 150 | ` A pair of ` ` 151 | ``` 152 | 153 | To learn more, check out `:h text-objects`. 154 | 155 | ## Composability And Grammar 156 | 157 | Vim grammar is subset of Vim's composability feature. Let's discuss composability in Vim and why this is a great feature to have in a text editor. 158 | 159 | Composability means having a set of general commands that can be combined (composed) to perform more complex commands. Just like in programming where you can create more complex abstractions from simpler abstractions, in Vim you can execute complex commands from simpler commands. Vim grammar is the manifestation of Vim's composable nature. 160 | 161 | The true power of Vim's composability shines when it integrates with external programs. Vim has a filter operator (`!`) to use external programs as filters for our texts. Suppose you have this messy text below and you want to tabularize it: 162 | 163 | ``` 164 | Id|Name|Cuteness 165 | 01|Puppy|Very 166 | 02|Kitten|Ok 167 | 03|Bunny|Ok 168 | ``` 169 | 170 | This cannot be easily done with Vim commands, but you can get it done quickly with `column` terminal command (assuming your terminal has `column` command). With your cursor on "Id", run `!}column -t -s "|"`. Voila! Now you have this pretty tabular data with just one quick command. 171 | 172 | ``` 173 | Id Name Cuteness 174 | 01 Puppy Very 175 | 02 Kitten Ok 176 | 03 Bunny Ok 177 | ``` 178 | 179 | Let's break down the command. The verb was `!` (filter operator) and the noun was `}` (go to next paragraph). The filter operator `!` accepted another argument, a terminal command, so I gave it `column -t -s "|"`. I won't go through how `column` worked, but in effect, it tabularized the text. 180 | 181 | Suppose you want to not only tabularize your text, but to display only the rows with "Ok". You know that `awk` can do the job easily. You can do this instead: 182 | 183 | ``` 184 | !}column -t -s "|" | awk 'NR > 1 && /Ok/ {print $0}' 185 | ``` 186 | 187 | Result: 188 | 189 | ``` 190 | 02 Kitten Ok 191 | 03 Bunny Ok 192 | ``` 193 | 194 | Great! The external command operator can also use pipe (`|`). 195 | 196 | This is the power of Vim's composability. The more you know your operators, motions, and terminal commands, your ability to compose complex actions is *multiplied*. 197 | 198 | Suppose you only know four motions, `w, $, }, G` and only one operator, `d`. You can do 8 actions: *move* 4 different ways (`w, $, }, G`) and *delete* 4 different targets (`dw, d$, d}, dG`). Then one day you learn about the uppercase (`gU`) operator. You have added not just one new ability to your Vim tool belt, but *four*: `gUw, gU$, gU}, gUG`. This makes at 12 tools in your Vim tool belt. Each new knowledge is a multiplier to your current abilities. If you know 10 motions and 5 operators, you have 60 moves (50 operations + 10 motions) in your arsenal. Vim has a line-number motion (`nG`) that gives you `n` motions, where `n` is how many lines you have in your file (to go to line 5, run `5G`). The search motion (`/`) practically gives you near unlimited number motions because you can search for anything. External command operator (`!`) gives you as many filtering tools as the number of terminal commands you know. Using a composable tool like Vim, everything you know can be linked together to do operations with increasing complexity. The more you know, the more powerful you become. 199 | 200 | This composable behavior echoes Unix philosophy: *do one thing well*. An operator has one job: do Y. A motion has one job: go to X. By combining an operator with a motion, you predictably get YX: do Y on X. 201 | 202 | Motions and operators are extendable. You can create custom motions and operators to add to your Vim toolbelt. The [`vim-textobj-user`](https://github.com/kana/vim-textobj-user) plugin allows you to create your own text objects. It also contains a [list](https://github.com/kana/vim-textobj-user/wiki) of user-made custom text objects. 203 | 204 | ## Learn Vim Grammar The Smart Way 205 | 206 | You just learned about Vim grammar's rule: `verb + noun`. One of my biggest Vim "AHA!" moments was when I had just learned about the uppercase (`gU`) operator and wanted to uppercase the current word, I *instinctively* ran `gUiw` and it worked! The word was uppercased. At that moment, I finally began to understand Vim. My hope is that you will have your own "AHA!" moment soon, if not already. 207 | 208 | The goal is this chapter is to show you the `verb + noun` pattern in Vim so you will approach learning Vim like learning a new language instead of memorizing every command combinations. 209 | 210 | Learn the pattern and understand the implications. That's the smart way to learn. 211 | -------------------------------------------------------------------------------- /ch06_insert_mode.md: -------------------------------------------------------------------------------- 1 | # Ch06. Insert Mode 2 | 3 | Insert mode is the default mode of many text editors. In this mode, what you type is what you get. 4 | 5 | However, that does not mean there isn't much to learn. Vim's insert mode contains many useful features. In this chapter, you will learn how to use these insert mode features in Vim to improve your typing efficiency. 6 | 7 | ## Ways To Go To Insert Mode 8 | 9 | There are many ways to get into insert mode from the normal mode. Here are some of them: 10 | 11 | ``` 12 | i Insert text before the cursor 13 | I Insert text before the first non-blank character of the line 14 | a Append text after the cursor 15 | A Append text at the end of line 16 | o Starts a new line below the cursor and insert text 17 | O Starts a new line above the cursor and insert text 18 | s Delete the character under the cursor and insert text 19 | S Delete the current line and insert text 20 | gi Insert text in same position where the last insert mode was stopped 21 | gI Insert text at the start of line (column 1) 22 | ``` 23 | 24 | Notice the lowercase / uppercase pattern. For each lowercase command, there is an uppercase counterpart. If you are new, don't worry if you don't remember the whole list above. Start with `i` and `o`. They should be enough to get you started. Gradually learn more over time. 25 | 26 | ## Different Ways To Exit Insert Mode 27 | 28 | There are a few different ways to return to the normal mode while in the insert mode: 29 | 30 | ``` 31 | Exits insert mode and go to normal mode 32 | Ctrl-[ Exits insert mode and go to normal mode 33 | Ctrl-C Like Ctrl-[ and , but does not check for abbreviation 34 | ``` 35 | 36 | I find `` key too far to reach, so I map my computer `` to behave like ``. If you search for Bill Joy's ADM-3A keyboard (Vi creator), you will see that the `` key is not located on far top left like modern keyboards, but to the left of `q` key. This is why I think it makes sense to map `` to ``. 37 | 38 | Another common convention I have seen Vim users do is mapping `` to `jj` or `jk` in insert mode. If you prefer this option add this one of those lines (or both) in your vimrc file. 39 | 40 | ``` 41 | inoremap jj 42 | inoremap jk 43 | ``` 44 | 45 | ## Repeating Insert Mode 46 | 47 | You can pass a count parameter before entering insert mode. For example: 48 | 49 | ``` 50 | 10i 51 | ``` 52 | 53 | If you type "hello world!" and exit insert mode, Vim will repeat the text 10 times. This will work with any insert mode method (ex: `10I`, `11a`, `12o`). 54 | 55 | ## Deleting Chunks In Insert Mode 56 | 57 | When you make a typing mistake, it can be cumbersome to type `` repeatedly. It may make more sense to go to normal mode and delete your mistake. You can also delete several characters at a time while in insert mode. 58 | 59 | ``` 60 | Ctrl-H Delete one character 61 | Ctrl-W Delete one word 62 | Ctrl-U Delete the entire line 63 | ``` 64 | 65 | ## Insert From Register 66 | 67 | Vim registers can store texts for future use. To insert a text from any named register while in insert mode, type `Ctrl-R` plus the register symbol. There are many symbols you can use, but for this section, let's cover only the named registers (a-z). 68 | 69 | To see it in action, first you need to yank a word to register a. Move your cursor on any word. Then type: 70 | 71 | ``` 72 | "ayiw 73 | ``` 74 | 75 | - `"a` tells Vim that the target of your next action will go to register a. 76 | - `yiw` yanks inner word. Review the chapter on Vim grammar for a refresher. 77 | 78 | Register a now contains the word you just yanked. While in insert mode, to paste the text stored in register a: 79 | 80 | ``` 81 | Ctrl-R a 82 | ``` 83 | 84 | There are multiple types of registers in Vim. I will cover them in greater detail in a later chapter. 85 | 86 | ## Scrolling 87 | 88 | Did you know that you can scroll while inside insert mode? While in insert mode, if you go to `Ctrl-X` sub-mode, you can do additional operations. Scrolling is one of them. 89 | 90 | ``` 91 | Ctrl-X Ctrl-Y Scroll up 92 | Ctrl-X Ctrl-E Scroll down 93 | ``` 94 | 95 | ## Autocompletion 96 | 97 | As mentioned above, if you press `Ctrl-X` from insert mode, Vim will enter a sub-mode. You can do text autocompletion while in this insert mode sub-mode. Although it is not as good as [intellisense](https://code.visualstudio.com/docs/editor/intellisense) or any other Language Server Protocol (LSP), but for something that is available right out of the box, it is a very capable feature. 98 | 99 | Here are some useful autocomplete commands to get started: 100 | 101 | ``` 102 | Ctrl-X Ctrl-L Insert a whole line 103 | Ctrl-X Ctrl-N Insert a text from current file 104 | Ctrl-X Ctrl-I Insert a text from included files 105 | Ctrl-X Ctrl-F Insert a file name 106 | ``` 107 | 108 | When you trigger autocompletion, Vim will display a pop-up window. To navigate up and down the pop-up window, use `Ctrl-N` and `Ctrl-P`. 109 | 110 | Vim also has two autocompletion shortcuts that don't involve the `Ctrl-X` sub-mode: 111 | 112 | ``` 113 | Ctrl-N Find the next word match 114 | Ctrl-P Find the previous word match 115 | ``` 116 | 117 | In general, Vim looks at the text in all available buffers for autocompletion source. If you have an open buffer with a line that says "Chocolate donuts are the best": 118 | - When you type "Choco" and do `Ctrl-X Ctrl-L`, it will match and print the entire line. 119 | - When you type "Choco" and do `Ctrl-P`, it will match and print the word "Chocolate". 120 | 121 | Autocomplete is a vast topic in Vim. This is just the tip of the iceberg. To learn more, check out `:h ins-completion`. 122 | 123 | ## Executing A Normal Mode Command 124 | 125 | Did you know Vim can execute a normal mode command while in insert mode? 126 | 127 | While in insert mode, if you press `Ctrl-O`, you'll be in insert-normal sub-mode. If you look at the mode indicator on bottom left, normally you will see `-- INSERT --`, but pressing `Ctrl-O` changes it to `-- (insert) --`. In this mode, you can do *one* normal mode command. Some things you can do: 128 | 129 | **Centering and jumping** 130 | 131 | ``` 132 | Ctrl-O zz Center window 133 | Ctrl-O H/M/L Jump to top/middle/bottom window 134 | Ctrl-O 'a Jump to mark a 135 | ``` 136 | 137 | **Repeating text** 138 | 139 | ``` 140 | Ctrl-O 100ihello Insert "hello" 100 times 141 | ``` 142 | 143 | **Executing terminal commands** 144 | 145 | ``` 146 | Ctrl-O !! curl https://google.com Run curl 147 | Ctrl-O !! pwd Run pwd 148 | ``` 149 | 150 | **Deleting faster** 151 | 152 | ``` 153 | Ctrl-O dtz Delete from current location till the letter "z" 154 | Ctrl-O D Delete from current location to the end of the line 155 | ``` 156 | 157 | ## Learn Insert Mode The Smart Way 158 | 159 | If you are like me and you come from another text editor, it can be tempting to stay in insert mode. However, staying in insert mode when you're not entering a text is an anti-pattern. Develop a habit to go to normal mode when your fingers aren't typing new texts. 160 | 161 | When you need to insert a text, first ask yourself if that text already exists. If it does, try to yank or move that text instead of typing it. If you have to use insert mode, see if you can autocomplete that text whenever possible. Avoid typing the same word more than once if you can. 162 | -------------------------------------------------------------------------------- /ch07_the_dot_command.md: -------------------------------------------------------------------------------- 1 | # Ch07. The Dot Command 2 | 3 | In general, you should try to avoid redoing what you just did whenever possible. In this chapter, you will learn how to use the dot command to easily redo the previous change. It is a versatile command for reducing simple repetitions. 4 | 5 | ## Usage 6 | 7 | Just like its name, you can use the dot command by pressing the dot key (`.`). 8 | 9 | For example, if you want to replace all "let" with "const" in the following expressions: 10 | 11 | ``` 12 | let one = "1"; 13 | let two = "2"; 14 | let three = "3"; 15 | ``` 16 | 17 | - Search with `/let` to go to the match. 18 | - Change with `cwconst` to replace "let" with "const". 19 | - Navigate with `n` to find the next match using the previous search. 20 | - Repeat what you just did with the dot command (`.`). 21 | - Continue pressing `n . n .` until you replace every word. 22 | 23 | Here the dot command repeated the `cwconst` sequence. It saved you from typing eight keystrokes in exchange for just one. 24 | 25 | ## What Is A Change? 26 | 27 | If you look at the definition of the dot command (`:h .`), it says that the dot command repeats the last change. What is a change? 28 | 29 | Any time you update (add, modify, or delete) the content of the current buffer, you are making a change. The exceptions are updates done by command-line commands (the commands starting with `:`) do not count as a change. 30 | 31 | In the first example, `cwconst` was the change. Now suppose you have this text: 32 | 33 | ``` 34 | pancake, potatoes, fruit-juice, 35 | ``` 36 | 37 | To delete the text from the start of the line to the next occurrence of a comma, first delete to the comma, then repeat twice it with `df,..`. 38 | 39 | Let's try another example: 40 | 41 | ``` 42 | pancake, potatoes, fruit-juice, 43 | ``` 44 | 45 | This time, your task is to delete the comma, not the breakfast items. Go to the first comma using, delete it, then repeat two more times with `f,x..` Easy, right? Wait a minute, it didn't work! Why? 46 | 47 | A change excludes motions because it does not update buffer content. The command `f,x` consisted of two actions: the command `f,` to move the cursor to "," and `x` to delete a character. Only the latter, `x`, caused a change. Contrast that with `df,` from the earlier example. In it, `f,` is a directive to the delete operator `d`, not a motion to move the cursor. The `f,` in `df,` and `f,x` have two very different roles. 48 | 49 | Let's finish the last task. After you run `f,` then `x`, go to the next comma with `;` to repeat the latest `f`. Finally, use `.` to delete the character under the cursor. Repeat `; . ; .` until everything is deleted. The full command is `f,x;.;.`. 50 | 51 | Let's try another one: 52 | 53 | ``` 54 | pancake 55 | potatoes 56 | fruit-juice 57 | ``` 58 | 59 | Lets add a comma at the end of each line. Starting at the first line, do `A,j`. By now, you realize that `j` does not cause a change. The change here is only `A,`. You can move and repeat the change with `j . j .`. The full command is `A,j.j.`. 60 | 61 | Every action from the moment you press the insert command operator (`A`) until you exit the insert command (``) is considered as a change. 62 | 63 | ## Multi-line Repeat 64 | 65 | Suppose you have this text: 66 | 67 | ``` 68 | let one = "1"; 69 | let two = "2"; 70 | let three = "3"; 71 | const foo = "bar'; 72 | let four = "4"; 73 | let five = "5"; 74 | let six = "6"; 75 | let seven = "7"; 76 | let eight = "8"; 77 | let nine = "9"; 78 | ``` 79 | 80 | Your goal is to delete all lines except the "foo" line. First, delete the first three lines with `d2j`, then to the line below the "foo" line. On the next line, use the dot command twice. The full command is `d2jj..`. 81 | 82 | Here the change was `d2j`. In this context, `2j` was not a motion, but a part of the delete operator. 83 | 84 | Let's look at another example: 85 | 86 | ``` 87 | zlet zzone = "1"; 88 | zlet zztwo = "2"; 89 | zlet zzthree = "3"; 90 | let four = "4"; 91 | ``` 92 | 93 | Let's remove all the z's. Starting from the first character on the first line, visually select the only the first z from the first three lines with blockwise visual mode (`Ctrl-Vjj`). If you're not familiar with blockwise visual mode, I will cover them in a later chapter. Once you have the three z's visually selected, delete them with the delete operator (`d`). Then move to the next word (`w`) to the next z. Repeat the change two more times (`..`). The full command is `Ctrl-vjjdw..`. 94 | 95 | When you deleted a column of three z's (`Ctrl-vjjd`), it was counted as a change. Visual mode operation can be used to target multiple lines as part of a change. 96 | 97 | ## Including A Motion In A Change 98 | 99 | Let's revisit the first example in this chapter. Recall that the command `/letcwconst` followed by `n . n .` replaced all "let" with "const" in the following expressions: 100 | 101 | ``` 102 | let one = "1"; 103 | let two = "2"; 104 | let three = "3"; 105 | ``` 106 | 107 | There is a faster way to accomplish this. After you searched `/let`, run `cgnconst` then `. .`. 108 | 109 | `gn` is a motion that searches forward for the last search pattern (in this case, `/let`) and automatically does a visual highlight. To replace the next occurrence, you no longer have to move and repeat the change ( `n . n .`), but only repeat (`. .`). You do not have to use search motions anymore because searching the next match is now part of the change! 110 | 111 | When you are editing, always be on the lookout for motions that can do several things at once like `gn` whenever possible. 112 | 113 | ## Learn The Dot Command The Smart Way 114 | 115 | The dot command's power comes from exchanging several keystrokes for one. It is probably not a profitable exchange to use the dot command for single key operations like `x`. If your last change requires a complex operation like `cgnconst`, the dot command reduces nine keypresses into one, a very profitable trade-off. 116 | 117 | When editing, think about repeatability. For example, if I need to remove the next three words, is it more economical to use `d3w` or to do `dw` then `.` two times? Will you be deleting a word again? If so, then it makes sense to use `dw` and repeat it several times instead of `d3w` because `dw` is more reusable than `d3w`. 118 | 119 | The dot command is the simpversatile command for automating single changes. In a later chapter, you will learn how to automate more complex actions with Vim macros. But first, let's learn about registers to store and retrieve text. 120 | -------------------------------------------------------------------------------- /ch08_registers.md: -------------------------------------------------------------------------------- 1 | # Ch08. Registers 2 | 3 | Learning Vim registers is like learning algebra for the first time. You didn't think you need it until you needed it. 4 | 5 | You've probably used Vim registers when you yanked or deleted a text then pasted it with `p` or `P`. However, did you know that Vim has 10 different types of registers? Used correctly, Vim registers can save you from repetitive typing. 6 | 7 | In this chapter, I will go over all Vim register types and how to use them efficiently. 8 | 9 | ## The Ten Register Types 10 | 11 | Here are the 10 Vim register types: 12 | 13 | 1. The unnamed register (`""`). 14 | 2. The numbered registers (`"0-9`). 15 | 3. The small delete register (`"-`). 16 | 4. The named registers (`"a-z`). 17 | 5. The read-only registers (`":`, `".`,and `"%`). 18 | 6. The alternate file register (`"#`). 19 | 7. The expression register (`"=`). 20 | 8. The selection registers (`"*` and `"+`). 21 | 9. The black hole register (`"_`). 22 | 10. The last search pattern register (`"/`). 23 | 24 | 25 | ## Register Operators 26 | 27 | To use registers, you need to first store them with operators. Here are some operators that store values to registers: 28 | 29 | ``` 30 | y Yank (copy) 31 | c Delete text and start insert mode 32 | d Delete text 33 | ``` 34 | 35 | There are more operators (like `s` or `x`), but the above are the useful ones. The rule of thumb is, if an operator can remove a text, it probably stores the text to registers. 36 | 37 | To paste a text from registers, you can use: 38 | 39 | ``` 40 | p Paste the text after the cursor 41 | P Paste the text before the cursor 42 | ``` 43 | 44 | Both `p` and `P` accept a count and a register symbol as arguments. For example, to paste ten times, do `10p`. To paste the text from register a, do `"ap`. To paste the text from register a ten times, do `10"ap`. By the way, the `p` actually technically stands for "put", not "paste", but I figure paste is a more conventional word. 45 | 46 | The general syntax to get the content from a specific register is `"a`, where `a` is the register symbol. 47 | 48 | ## Calling Registers From Insert Mode 49 | 50 | Everything you learn in this chapter can also be executed in insert mode. To get the text from register a, normally you do `"ap`. But if you are in insert mode, run `Ctrl-R a`. The syntax to call registers from insert mode is: 51 | 52 | ``` 53 | Ctrl-R a 54 | ``` 55 | 56 | Where `a` is the register symbol. Now that you know how to store and retrieve registers, let's dive in! 57 | 58 | 59 | ## The Unnamed Register 60 | 61 | To get the text from the unnamed register, do `""p`. It stores the last text you yanked, changed, or deleted. If you do another yank, change, or delete, Vim will automatically replace the old text. The unnamed register is like a computer's standard copy / paste operation. 62 | 63 | By default, `p` (or `P`) is connected to the unnamed register (from now on I will refer to the unnamed register with `p` instead of `""p`). 64 | 65 | ## The Numbered Registers 66 | 67 | Numbered registers automatically fill themselves up in ascending order. There are 2 different numbered registers: the yanked register (`0`) and the numbered registers (`1-9`). Let's discuss the yanked register first. 68 | 69 | ### The Yanked Register 70 | 71 | If you yank an entire line of text (`yy`), Vim actually saves that text in two registers: 72 | 73 | 1. The unnamed register (`p`). 74 | 2. The yanked register (`"0p`). 75 | 76 | When you yank a different text, Vim will update both the yanked register and the unnamed register. Any other operations (like delete) will not be stored in register 0. This can be used to your advantage, because unless you do another yank, the yanked text will always be there, no matter how many changes and deletions you do. 77 | 78 | For example, if you: 79 | 1. Yank a line (`yy`) 80 | 2. Delete a line (`dd`) 81 | 3. Delete another line (`dd`) 82 | 83 | The yanked register will have the text from step one. 84 | 85 | If you: 86 | 1. Yank a line (`yy`) 87 | 2. Delete a line (`dd`) 88 | 3. Yank another line (`yy`) 89 | 90 | The yanked register will have the text from step three. 91 | 92 | One last tip, while in insert mode, you can quickly paste the text you just yanked using `Ctrl-R 0`. 93 | 94 | ### The Non-zero Numbered Registers 95 | 96 | When you change or delete a text that is at least one line long, that text will be stored in the numbered registers 1-9 sorted by the most recent. 97 | 98 | For example, if you have these lines: 99 | 100 | ``` 101 | line three 102 | line two 103 | line one 104 | ``` 105 | 106 | With your cursor on "line three", delete them one by one with `dd`. Once all lines are deleted, register 1 should contain "line one" (most recent), register two "line two" (second most recent), and register three "line three" (oldest). To get the content from register one, do `"1p`. 107 | 108 | As a side note, these numbered registers are automatically incremented when using the dot command. If your numbered register one (`"1`) contains "line one", register two (`"2`) "line two", and register three (`"3`) "line three", you can paste them sequentially with this trick: 109 | - Do `"1P` to paste the content from the numbered register one ("1). 110 | - Do `.` to paste the content from the numbered register two ("2). 111 | - Do `.` to paste the content from the numbered register three ("3). 112 | 113 | This trick works with any numbered register. If you started with `"5P`, `.` would do `"6P`, `.` again would do `"7P`, and so on. 114 | 115 | Small deletions like a word deletion (`dw`) or word change (`cw`) do not get stored in the numbered registers. They are stored in the small delete register (`"-`), which I will discuss next. 116 | 117 | ## The Small Delete Register 118 | 119 | Changes or deletions less than one line are not stored in the numbered registers 0-9, but in the small delete register (`"-`). 120 | 121 | For example: 122 | 1. Delete a word (`diw`) 123 | 2. Delete a line (`dd`) 124 | 3. Delete a line (`dd`) 125 | 126 | `"-p` gives you the deleted word from step one. 127 | 128 | Another example: 129 | 1. I delete a word (`diw`) 130 | 2. I delete a line (`dd`) 131 | 3. I delete a word (`diw`) 132 | 133 | `"-p` gives you the deleted word from step three. `"1p` gives you the deleted line from step two. Unfortunately, there is no way to retrieve the deleted word from step one because the small delete register only stores one item. However, if you want to preserve the text from step one, you can do it with the named registers. 134 | 135 | ## The Named Register 136 | 137 | The named registers are Vim's most versatile register. It can store yanked, changed, and deleted texts into registers a-z. Unlike the previous 3 register types you've seen which automatically stores texts into registers, you have to explicitly tell Vim to use the named register, giving you full control. 138 | 139 | To yank a word into register a, you can do it with `"ayiw`. 140 | - `"a` tells Vim that the next action (delete / change / yank) will be stored in register a. 141 | - `yiw` yanks the word. 142 | 143 | To get the text from register a, run `"ap`. You can use all twenty-six alphabetical characters to store twenty-six different texts with named registers. 144 | 145 | Sometimes you may want to add to your existing named register. In this case, you can append your text instead of starting all over. To do that, you can use the uppercase version of that register. For example, suppose you have the word "Hello " already stored in register a. If you want to add "world" into register a, you can find the text "world" and yank it using A register (`"Ayiw`). 146 | 147 | ## The Read-Only Registers 148 | 149 | Vim has three read-only registers: `.`, `:`, and `%`. They are pretty simple to use: 150 | 151 | ``` 152 | . Stores the last inserted text 153 | : Stores the last executed command-line 154 | % Stores the name of current file 155 | ``` 156 | 157 | If the last text you wrote was "Hello Vim", running `".p` will print out the text "Hello Vim". If you want to get the name of current file, run `"%p`. If you run `:s/foo/bar/g` command, running `":p` will print out the literal text "s/foo/bar/g". 158 | 159 | ## The Alternate File Register 160 | 161 | In Vim, `#` usually represents the alternate file. An alternative file is the last file you opened. To insert the name of the alternate file, you can use `"#p`. 162 | 163 | ## The Expression Register 164 | 165 | Vim has an expression register, `"=`, to evaluate expressions. 166 | 167 | To evaluate mathematical expressions `1 + 1`, run: 168 | 169 | ``` 170 | "=1+1p 171 | ``` 172 | 173 | Here, you are telling Vim that you are using the expression register with `"=`. Your expression is (`1 + 1`). You need to type `p` to get the result. As mentioned earlier, you can also access the register from insert mode. To evaluate mathematical expression from insert mode, you can do: 174 | 175 | ``` 176 | Ctrl-R =1+1 177 | ``` 178 | 179 | You can also get the values from any register via the expression register when appended with `@`. If you wish to get the text from register a: 180 | 181 | ``` 182 | "=@a 183 | ``` 184 | 185 | Then press ``, then `p`. Similarly, to get values from register a while in insert mode: 186 | 187 | ``` 188 | Ctrl-r =@a 189 | ``` 190 | 191 | Expression is a vast topic in Vim, so I will only cover the basics here. I will address expressions in more details in later Vimscript chapters. 192 | 193 | ## The Selection Registers 194 | 195 | Don't you sometimes wish that you can copy a text from external programs and paste it locally in Vim, and vice versa? With Vim's selection registers, you can. Vim has two selection registers: `quotestar` (`"*`) and `quoteplus` (`"+`). You can use them to access copied text from external programs. 196 | 197 | If you are on an external program (like Chrome browser) and you copy a block of text with `Ctrl-C` (or `Cmd-C`, depending on your OS), normally you wouldn't be able to use `p` to paste the text in Vim. However, both Vim's `"+` and `"*` are connected to your clipboard, so you can actually paste the text with `"+p` or `"*p`. Conversely, if you yank a word from Vim with `"+yiw` or `"*yiw`, you can paste that text in the external program with `Ctrl-V` (or `Cmd-V`). Note that this only works if your Vim program comes with the `+clipboard` option (to check it, run `:version`). 198 | 199 | You may wonder if `"*` and `"+` do the same thing, why does Vim have two different registers? Some machines use X11 window system. This system has 3 types of selections: primary, secondary, and clipboard. If your machine uses X11, Vim uses X11's *primary* selection with the `quotestar` (`"*`) register and X11's *clipboard* selection with the `quoteplus` (`"+`) register. This is only applicable if you have `+xterm_clipboard` option available in your Vim build. If your Vim doesn't have `xterm_clipboard`, it's not a big deal. It just means that both `quotestar` and `quoteplus` are interchangeable (mine doesn't either). 200 | 201 | I find doing `=*p` or `=+p` to be cumbersome. To make Vim to paste copied text from the external program with just `p`, you can add this in your vimrc: 202 | 203 | ``` 204 | set clipboard=unnamed 205 | ``` 206 | 207 | Now when I copy a text from an external program, I can paste it with the unnamed register, `p`. I can also copy a text from Vim and paste it to an external program. If you have `+xterm_clipboard` on, you may want to use both `unnamed` and `unnamedplus` clipboard options. 208 | 209 | ## The Black Hole Register 210 | 211 | Each time you delete or change a text, that text is stored in Vim register automatically. There will be times when you don't want to save anything into the register. How can you do that? 212 | 213 | You can use the black hole register (`"_`). To delete a line and not have Vim store the deleted line into any register, use `"_dd`. 214 | 215 | The black hole register is like the `/dev/null` of registers. 216 | 217 | ## The Last Search Pattern Register 218 | 219 | To paste your last search (`/` or `?`), you can use the last search pattern register (`"/`). To paste the last search term, use `"/p`. 220 | 221 | ## Viewing The Registers 222 | 223 | To view all your registers, use the `:register` command. To view only registers "a, "1, and "-, use `:register a 1 -`. 224 | 225 | There is a plugin called [vim-peekaboo](https://github.com/junegunn/vim-peekaboo) that lets you to peek into the contents of the registers when you hit `"` or `@` in normal mode and `Ctrl-R` in insert mode. I find this plugin very useful because most times, I can't remember the content in my registers. Give it a try! 226 | 227 | ## Executing A Register 228 | 229 | The named registers are not just for storing texts. They can also execute macros with `@`. I will go over macros in the next chapter. 230 | 231 | Keep in mind since macros are stored inside Vim registers, you can accidentally overwrite the stored text with macros. If you store the text "Hello Vim" in register a and you later record a macro in the same register (`qa{macro-sequence}q`), that macro will overwrite your "Hello Vim" text stored earlier. 232 | 233 | ## Clearing A Register 234 | 235 | Technically, there is no need to clear any register because the next register you store under the same name will overwrite it. However, you can quickly clear any named register by recording an empty macro. For example, if you run `qaq`, Vim will record an empty macro in the register a. 236 | 237 | Another alternative is to run the command `:call setreg('a', '')` where "a is the register a. 238 | 239 | One more way to clear register is to set the content of "a register to an empty string with the expression `:let @a = ''`. 240 | 241 | ## Putting The Content Of A Register 242 | 243 | You can use the `:put` command to paste the content of any one register. For example, if you run `:put a`, Vim will print the content of register a below the current line. This behaves much like `"ap`, with the difference that the normal mode command `p` prints the register content after the cursor and the command `:put` prints the register content at newline. 244 | 245 | Since `:put` is a command-line command, you can pass it an address. `:10put a` will paste text from register a to below line 10. 246 | 247 | One cool trick to pass `:put` with the black hole register (`"_`). Since the black hole register does not store any text, `:put _` will insert a blank line instead. You can combine this with the global command to insert multiple blank lines. For example, to insert blank lines below all lines that contain the text "end", run `:g/end/put _`. You will learn about the global command later. 248 | 249 | ## Learning Registers The Smart Way 250 | 251 | You made it to the end. Congratulations! If you are feeling overwhelmed by the sheer information, you are not alone. When I first started learning about Vim registers, there were way too much information to take at once. 252 | 253 | I don't think you should memorize all the registers immediately. To become productive, you can start by using only these 3 registers: 254 | 1. The unnamed register (`""`). 255 | 2. The named registers (`"a-z`). 256 | 3. The numbered registers (`"0-9`). 257 | 258 | Since the unnamed register defaults to `p` and `P`, you only have to learn two registers: the named registers and the numbered registers. Gradually learn more registers when you need them. Take your time. 259 | 260 | The average human has a limited short-term memory capacity, about 5 - 7 items at once. That is why in my everyday editing, I only use about 5 - 7 named registers. There is no way I can remember all twenty-six in my head. I normally start with register a, then b, ascending the alphabetical order. Try it and experiment around to see what technique works best for you. 261 | 262 | Vim registers are powerful. Used strategically, it can save you from typing countless repeating texts. Next, let's learn about macros. 263 | -------------------------------------------------------------------------------- /ch09_macros.md: -------------------------------------------------------------------------------- 1 | # Ch09. Macros 2 | 3 | When editing files, you may find yourself repeating the same actions. Wouldn't it be nice if you can do those actions once and replay them whenever you need it? With Vim macros, you can record actions and store them inside Vim registers to be executed whenever you need it. 4 | 5 | In this chapter, you will learn how to use macros to automate mundane tasks (plus it looks cool to watch your file edit itself). 6 | 7 | ## Basic Macros 8 | 9 | Here is the basic syntax of a Vim macro: 10 | 11 | ``` 12 | qa Start recording a macro in register a 13 | q (while recording) Stop recording macro 14 | ``` 15 | 16 | You can choose any lowercase letters (a-z) to store macros. Here is how you can execute a macro: 17 | 18 | ``` 19 | @a Execute macro from register a 20 | @@ Execute the last executed macros 21 | ``` 22 | 23 | Suppose you have this text and you want to uppercase everything on each line: 24 | 25 | ``` 26 | hello 27 | vim 28 | macros 29 | are 30 | awesome 31 | ``` 32 | 33 | With your cursor at the start of the line "hello", run: 34 | 35 | ``` 36 | qa0gU$jq 37 | ``` 38 | 39 | The breakdown: 40 | - `qa` starts recording a macro in the "a register. 41 | - `0` goes to beginning of the line. 42 | - `gU$` uppercases the text from your current location to the end of the line. 43 | - `j` goes down one line. 44 | - `q` stops recording. 45 | 46 | To replay it, run `@a`. Just like many other Vim commands, you can pass a count argument to macros. For example, running `3@a` executes the macro three times. 47 | 48 | ## Safety Guard 49 | 50 | Macro execution automatically ends when it encounters an error. Suppose you have this text: 51 | 52 | ``` 53 | a. chocolate donut 54 | b. mochi donut 55 | c. powdered sugar donut 56 | d. plain donut 57 | ``` 58 | 59 | If you want to uppercase the first word on each line, this macro should work: 60 | 61 | ``` 62 | qa0W~jq 63 | ``` 64 | 65 | Here's the breakdown of the command above: 66 | - `qa` starts recording a macro in the "a register. 67 | - `0` goes to the beginning of the line. 68 | - `W` goes to the next WORD. 69 | - `~` toggles the case of the character under the cursor. 70 | - `j` goes down one line. 71 | - `q` stops recording. 72 | 73 | I prefer to overcount my macro execution than undercount it, so I usually call it ninety-nine times (`99@a`). With this command, Vim does not actually run this macro ninety-nine times. When Vim reaches the last line and runs `j` motion, it finds no more line to go down to, throws an error, and stops the macro execution. 74 | 75 | The fact that macro execution stops upon the first error encounter is a good feature, otherwise Vim will continue to execute this macro ninety-nine times even though it already reaches the end of the line. 76 | 77 | ## Command Line Macro 78 | 79 | Running `@a` in normal mode is not the only way you can execute macros in Vim. You can also run `:normal @a` command line. `:normal` allows the user to execute any normal mode command passed as argument. In the case above, it is the same as running `@a` from normal mode. 80 | 81 | The `:normal` command accepts range as arguments. You can use this to run macro in select ranges. If you want to execute your macro between lines 2 and 3, you can run `:2,3 normal @a`. 82 | 83 | ## Executing A Macro Across Multiple Files 84 | 85 | Suppose you have multiple `.txt` files, each contains some texts. Your task is to uppercase the first word only on lines containing the word "donut". Assume you have `0W~j` in register a (the same macro as before). How can you quickly accomplish this? 86 | 87 | First file: 88 | 89 | ``` 90 | ## savory.txt 91 | a. cheddar jalapeno donut 92 | b. mac n cheese donut 93 | c. fried dumpling 94 | ``` 95 | 96 | Second file: 97 | 98 | ``` 99 | ## sweet.txt 100 | a. chocolate donut 101 | b. chocolate pancake 102 | c. powdered sugar donut 103 | ``` 104 | 105 | Third file: 106 | 107 | ``` 108 | ## plain.txt 109 | a. wheat bread 110 | b. plain donut 111 | ``` 112 | 113 | Here is how you can do it: 114 | - `:args *.txt` to find all `.txt` files in your current directory. 115 | - `:argdo g/donut/normal @a` executes the global command `g/donut/normal @a` on each file inside `:args`. 116 | - `:argdo update` executes `update` command to save each file inside `:args` when the buffer has been modified. 117 | 118 | If you are not familiar with the global command `:g/donut/normal @a`, it executes the command you give (`normal @a`) on lines that match the pattern (`/donut/`). I will go over the global command in a later chapter. 119 | 120 | ## Recursive Macro 121 | 122 | You can recursively execute a macro by calling the same macro register while recording that macro. Suppose you have this list again and you need to toggle the case of the first word: 123 | 124 | ``` 125 | a. chocolate donut 126 | b. mochi donut 127 | c. powdered sugar donut 128 | d. plain donut 129 | ``` 130 | 131 | This time, let's do it recursively. Run: 132 | 133 | ``` 134 | qaqqa0W~j@aq 135 | ``` 136 | 137 | Here is the breakdown of the steps: 138 | - `qaq` records an empty macro "a. It is necessary to start with an empty register because when you recursively call the macro, it will run whatever is in that register. 139 | - `qa` starts recording on register a. 140 | - `0` goes to the first character in the current line. 141 | - `W` goes to the next WORD. 142 | - `~` toggles the case of the character under the cursor. 143 | - `j` goes down one line. 144 | - `@a` executes macro "a. 145 | - `q` stops recording. 146 | 147 | Now you can just run `@a` and watch Vim execute the macro recursively. 148 | 149 | How did the macro know when to stop? When the macro was on the last line, it triedto run `j`, since there was no more line to go to, it stopped the macro execution. 150 | 151 | ## Appending A Macro 152 | 153 | If you need to add actions to an existing macro, instead of recreating the macro from scratch, you can append actions to an existing one. In the register chapter, you learned that you can append a named register by using its uppercased symbol. The same rule applies. To append actions to register a macro, use register "A. 154 | 155 | Record a macro in register a: `qa0W~q` (this sequence toggles the case of the next WORD in a line). If you want to append a new sequence to also add a dot at the end of the line, run: 156 | 157 | ``` 158 | qAA.q 159 | ``` 160 | 161 | The breakdown: 162 | - `qA` starts recording the macro in register "A. 163 | - `A.` inserts at the end of the line (here `A` is the insert mode command, not to be confused with the macro "A) a dot, then exits insert mode. 164 | - `q` stops recording macro. 165 | 166 | Now when you execute `@a`, it not only toggles the case of the next WORD, it also adds a dot at the end of the line. 167 | 168 | ## Amending A Macro 169 | 170 | What if you need to add new actions in the middle of a macro? 171 | 172 | Assume that you have a macro that toggles the first actual word and adding a period at the end of the line, `0W~A.` in register a. Suppose that between uppercasing the first word and adding a period at the end of the line, you need to add the word "deep fried" right before the word "donut" *(because the only thing better than regular donuts are deep fried donuts)*. 173 | 174 | I will reuse the text from earlier section: 175 | ``` 176 | a. chocolate donut 177 | b. mochi donut 178 | c. powdered sugar donut 179 | d. plain donut 180 | ``` 181 | 182 | First, let's call the existing macro (assume you have kept the macro from the previous section in register a) with `:put a`: 183 | 184 | ``` 185 | 0W~A.^[ 186 | ``` 187 | 188 | What is this `^[`? Didn't you do `0W~A.`? Where is the ``? `^[` is Vim's *internal code* representation of ``. With certain special keys, Vim prints the representation of those keys in the form of internal codes. Some common keys that have internal code representations are ``, ``, and ``. There are more special keys, but they are not within the scope of this chapter. 189 | 190 | Back to the macro, right after the toggle case operator (`~`), let's add the instructions to go to the end of the line (`$`), go back one word (`b`), go to the insert mode (`i`), type "deep fried " (don't forget the space after "fried "), and exit insert mode (``). 191 | 192 | Here is what you will end up with: 193 | 194 | ``` 195 | 0W~$bideep fried A.^[ 196 | ``` 197 | 198 | There is a small problem. Vim does not understand ``. You can't literally type ``. You will have to write the internal code representation for the `` key. While in insert mode, you press `Ctrl-V` followed by ``. Vim will print `^[`.` Ctrl-V` is an insert mode operator to insert the next non-digit character *literally*. Your macro code should look like this now: 199 | 200 | ``` 201 | 0W~$bideep fried ^[A.^[ 202 | ``` 203 | 204 | To add the amended instruction into register a, you can do it the same way as adding a new entry into a named register. At the start of the line, run `"ay$` to store the yanked text in register a. 205 | 206 | Now when you execute `@a`, your macro will toggle the case of the first word, add "deep fried " before "donut", and add a "." at the end of the line. Yum! 207 | 208 | An alternative way to amend a macro is to use a command line expression. Do `:let @a="`, then do `Ctrl-R Ctrl-R a`, this will literally paste the content of register a. Finally, don't forget to close the double quotes (`"`). You might have something like `:let @a="0W~$bideep fried ^[A.^["`. 209 | 210 | ## Macro Redundancy 211 | 212 | You can easily duplicate macros from one register to another. For example, to duplicate a macro in register a to register z, you can do `:let @z = @a`. `@a` represents the content of register a. Now if you run `@z`, it does the exact same actions as `@a`. 213 | 214 | I find creating a redundancy useful on my most frequently used macros. In my workflow, I usually record macros in the first seven alphabetical letters (a-g) and I often replace them without much thought. If I move the useful macros towards the end of the alphabets, I can preserve them without worrying that I might accidentally replace them. 215 | 216 | ## Series Vs Parallel Macro 217 | 218 | Vim can execute macros in series and parallel. Suppose you have this text: 219 | 220 | ``` 221 | import { FUNC1 } from "library1"; 222 | import { FUNC2 } from "library2"; 223 | import { FUNC3 } from "library3"; 224 | import { FUNC4 } from "library4"; 225 | import { FUNC5 } from "library5"; 226 | ``` 227 | 228 | If you want to record a macro to lowercase all the uppercased "FUNC", this macro should work: 229 | 230 | ``` 231 | qa0f{gui{jq 232 | ``` 233 | 234 | The breakdown: 235 | - `qa` starts recording in register a. 236 | - `0` goes to first line. 237 | - `f{` finds the first instance of "{". 238 | - `gui{` lowercases (`gu`) the text inside the bracket text-object (`i{`). 239 | - `j` goes down one line. 240 | - `q` stops macro recording. 241 | 242 | Now you can run `99@a` to execute it on the remaining lines. However, what if you have this import expression inside your file? 243 | 244 | ``` 245 | import { FUNC1 } from "library1"; 246 | import { FUNC2 } from "library2"; 247 | import { FUNC3 } from "library3"; 248 | import foo from "bar"; 249 | import { FUNC4 } from "library4"; 250 | import { FUNC5 } from "library5"; 251 | ``` 252 | 253 | Running `99@a`, only executes the macro three times. It does not execute the macro on last two lines because the execution fails to run `f{` on the "foo" line. This is expected when running the macro in series. You can always go to the next line where "FUNC4" is and replay that macro again. But what if you want to get everything done in one go? 254 | 255 | Run the macro in parallel. 256 | 257 | Recall from earlier section that macros can be executed using the command line command `:normal` (ex: `:3,5 normal @a` executes macro "a on lines 3-5). If you run `:1,$ normal @a`, you will see that the macro is being executed on all lines except the "foo" line. It works! 258 | 259 | Although internally Vim does not actually run the macros in parallel, outwardly, it behaves like it. Vim executes `@a` *independently* on each line from the first to the last line (`1,$`). Since Vim executes these macros independently, each line does not know that one of the macro executions had failed on the "foo" line. 260 | 261 | ## Learn Macros The Smart Way 262 | 263 | Many things you do in editing are repetitive. To get better at editing, get into the habit of detecting repetitive actions. Use macros (or dot command) so you don't have to perform the same action twice. Almost everything that you can do in Vim can be replicated with macros. 264 | 265 | In the beginning, I find it very awkward to write macros, but don't give up. With enough practice, you will get into the habit of automating everything. 266 | 267 | You might find it helpful to use mnemonics to help remember your macros. If you have a macro that creates a function, use the "f register (`qf`). If you have a macro for numerical operations, the "n register should work (`qn`). Name it with the *first named register* that comes to your mind when you think of that operation. I also find that the "q register makes a good default macro register because `qq` requires less brain power to come up with. Lastly, I also like to increment my macros in alphabetical orders, like `qa`, then `qb`, then `qc`, and so on. 268 | 269 | Find a method that works best for you. 270 | -------------------------------------------------------------------------------- /ch10_undo.md: -------------------------------------------------------------------------------- 1 | # Ch10. Undo 2 | 3 | We all make all sorts of typing mistakes. That's why undo is an essential feature in any modern software. Vim's undo system is not only capable of undoing and redoing simple mistakes, but also accessing different text states, giving you control to all the texts you have ever typed. In this chapter, you will learn how to undo, redo, navigate an undo branch, persist undo, and travel across time. 4 | 5 | ## Undo, Redo, And UNDO 6 | 7 | To perform a basic undo, you can use `u` or run `:undo`. 8 | 9 | If you have this text (note the empty line below "one"): 10 | 11 | ``` 12 | one 13 | 14 | ``` 15 | 16 | Then you add another text: 17 | 18 | ``` 19 | one 20 | two 21 | ``` 22 | 23 | If you press `u`, Vim undoes the text "two". 24 | 25 | How does Vim know how much to undo? Vim undoes a single "change" at a time, similar to a dot command's change (unlike the dot command, command-line command also count as a change). 26 | 27 | To redo the last change, press `Ctrl-R` or run `:redo`. After you undo the text above to remove "two", running `Ctrl-R` will get the removed text back. 28 | 29 | Vim also has UNDO that you can run with `U`. It undoes all latest changes. 30 | 31 | How is `U` different from `u`? First, `U` removes *all* the changes on the latest changed line, while `u` only removes one change at a time. Second, while doing `u` does not count as a change, doing `U` counts as a change. 32 | 33 | Back to this example: 34 | 35 | ``` 36 | one 37 | two 38 | ``` 39 | 40 | Change the second line to "three": 41 | 42 | ``` 43 | one 44 | three 45 | ``` 46 | 47 | Change the second line again and replace it with "four": 48 | 49 | ``` 50 | one 51 | four 52 | ``` 53 | 54 | If you press `u`, you will see "three". If you press `u` again, you'll see "two". If instead of pressing `u` when you still had the text "four", you had pressed `U`, you will see: 55 | 56 | ``` 57 | one 58 | 59 | ``` 60 | 61 | `U` bypasses all the intermediary changes and goes to the original state when you started (an empty line). In addition, since UNDO actually creates a new change in Vim, you can UNDO your UNDO. `U` followed by `U` will undo itself. You can press `U`, then `U`, then `U`, etc. You will see the same two text states toggling back and forth. 62 | 63 | I personally do not use `U` because it is hard to remember the original state (I seldom ever need it). 64 | 65 | Vim sets a maximum number of how many times you can undo in `undolevels` option variable. You can check it with `:echo &undolevels`. I have mine set to be 1000. To change yours to 1000, run `:set undolevels=1000`. Feel free to set it to any number you like. 66 | 67 | ## Breaking The Blocks 68 | 69 | I mentioned earlier that `u` undoes a single "change" similar to the dot command's change: the texts inserted from when you enter the insert mode until you exit it count as a change. 70 | 71 | If you do `ione two three` then press `u`, Vim removes the entire "one two three" text because the whole thing counts as a change. This is not a big deal if you have written short texts, but what if you have written several paragraphs within one insert mode session without exiting and later you realized you made a mistake? If you press `u`, everything you had written would be removed. Wouldn't it be useful if you can press `u` to remove only a section of your text? 72 | 73 | Luckily, you can break the undo blocks. When you are typing in insert mode, pressing `Ctrl-G u` creates an undo breakpoint. For example, if you do `ione two three`, then press `u`, you will only lose the text "three" (press `u` one more time to remove "two"). When you write a long text, use `Ctrl-G u` strategically. The end of each sentence, between two paragraphs, or after each line of code are prime locations to add undo breakpoints to make it easier to undo your mistakes if you ever make one. 74 | 75 | It is also useful to create an undo breakpoint when deleting chunks in insert mode with `Ctrl-W` (delete the word before the cursor) and `Ctrl-U` (delete all text before the cursor). A friend suggested to use the following maps: 76 | 77 | ``` 78 | inoremap u 79 | inoremap u 80 | ``` 81 | 82 | With these, you can easily recover the deleted texts. 83 | 84 | ## Undo Tree 85 | 86 | Vim stores every change ever written in an undo tree. Start a new empty file. Then add a new text: 87 | 88 | ``` 89 | one 90 | 91 | ``` 92 | 93 | Add a new text: 94 | 95 | ``` 96 | one 97 | two 98 | ``` 99 | 100 | Undo once: 101 | 102 | ``` 103 | one 104 | 105 | ``` 106 | 107 | Add a different text: 108 | 109 | ``` 110 | one 111 | three 112 | ``` 113 | 114 | Undo again: 115 | 116 | ``` 117 | one 118 | 119 | ``` 120 | 121 | And add another different text: 122 | 123 | ``` 124 | one 125 | four 126 | ``` 127 | 128 | 129 | Now if you undo, you will lose the text "four" you just added: 130 | 131 | ``` 132 | one 133 | 134 | ``` 135 | 136 | If you undo one more time: 137 | 138 | ``` 139 | 140 | ``` 141 | 142 | You will lose the text "one". In most text editor, getting the texts "two" and "three" back would have been impossible, but not with Vim! Press `g+` and you'll get your text "one" back: 143 | 144 | ``` 145 | one 146 | 147 | ``` 148 | 149 | Type `g+` again and you will see an old friend: 150 | 151 | ``` 152 | one 153 | two 154 | ``` 155 | 156 | Let's keep going. Press `g+` again: 157 | 158 | ``` 159 | one 160 | three 161 | ``` 162 | 163 | Press `g+` one more time: 164 | 165 | ``` 166 | one 167 | four 168 | ``` 169 | 170 | In Vim, every time you press `u` and then make a different change, Vim stores the previous state's text by creating an "undo branch". In this example, after you typed "two", then pressed `u`, then typed "three", you created an leaf branch that stores the state containing the text "two". At that moment, the undo tree contained at least two leaf nodes: the main node containing the text "three" (most recent) and the undo branch node containing the text "two". If you had done another undo and typed the text "four", you would have at three nodes: a main node containing the text "four" and two nodes containing the texts "three" and "two". 171 | 172 | To traverse each undo tree nodes, you can use `g+` to go to a newer state and `g-` to go to an older state. The difference between `u`, `Ctrl-R`, `g+`, and `g-` is that both `u` and `Ctrl-R` traverse only the *main* nodes in undo tree while `g+` and `g-` traverse *all* nodes in the undo tree. 173 | 174 | Undo tree is not easy to visualize. I find [vim-mundo](https://github.com/simnalamburt/vim-mundo) plugin to be very useful to help visualize Vim's undo tree. Give it some time to play around with it. 175 | 176 | ## Persistent Undo 177 | 178 | If you start Vim, open a file, and immediately press `u`, Vim will probably display "*Already at oldest change*" warning. There is nothing to undo because you haven't made any changes. 179 | 180 | To rollover the undo history from the last editing session, Vim can preserve your undo history with an undo file with `:wundo`. 181 | 182 | Create a file `mynumbers.txt`. Type: 183 | 184 | ``` 185 | one 186 | ``` 187 | 188 | Then type another line (make sure each line counts as a change): 189 | 190 | ``` 191 | one 192 | two 193 | ``` 194 | 195 | Type another line: 196 | 197 | ``` 198 | one 199 | two 200 | three 201 | ``` 202 | 203 | Now create your undo file with `:wundo {my-undo-file}`. If you need to overwrite an existing undo file, you can add `!` after `wundo`. 204 | 205 | ``` 206 | :wundo! mynumbers.undo 207 | ``` 208 | 209 | Then exit Vim. 210 | 211 | By now you should have `mynumbers.txt` and `mynumbers.undo` files in your directory. Open up `mynumbers.txt` again and try pressing `u`. You can't. You haven't made any changes since you opened the file. Now load your undo history by reading the undo file with `:rundo`: 212 | 213 | ``` 214 | :rundo mynumbers.undo 215 | ``` 216 | 217 | Now if you press `u`, Vim removes "three". Press `u` again to remove "two". It is like you never even closed Vim! 218 | 219 | If you want to have an automatic undo persistence, one way to do it is by adding these in vimrc: 220 | 221 | ``` 222 | set undodir=~/.vim/undo_dir 223 | set undofile 224 | ``` 225 | 226 | The setting above will put all the undofile in one centralized directory, the `~/.vim` directory. The name `undo_dir` is arbitrary. `set undofile` tells Vim to turn on `undofile` feature because it is off by default. Now whenever you save, Vim automatically creates and updates the relevant file inside the `undo_dir` directory (make sure that you create the actual `undo_dir` directory inside `~/.vim` directory before running this). 227 | 228 | ## Time Travel 229 | 230 | Who says that time travel doesn't exist? Vim can travel to a text state in the past with `:earlier` command-line command. 231 | 232 | If you have this text: 233 | 234 | ``` 235 | one 236 | 237 | ``` 238 | Then later you add: 239 | 240 | ``` 241 | one 242 | two 243 | ``` 244 | 245 | If you had typed "two" less than ten seconds ago, you can go back to the state where "two" didn't exist ten seconds ago with: 246 | 247 | ``` 248 | :earlier 10s 249 | ``` 250 | 251 | You can use `:undolist` to see when the last change was made. `:earlier` also accepts different arguments: 252 | 253 | ``` 254 | :earlier 10s Go to the state 10 seconds before 255 | :earlier 10m Go to the state 10 minutes before 256 | :earlier 10h Go to the state 10 hours before 257 | :earlier 10d Go to the state 10 days before 258 | ``` 259 | 260 | In addition, it also accepts a regular `count` as argument to tell Vim to go to the older state `count` times. For example, if you do `:earlier 2`, Vim will go back to an older text state two changes ago. It is the same as doing `g-` twice. You can also tell it to go to the older text state 10 saves ago with `:earlier 10f`. 261 | 262 | The same set of arguments work with `:earlier` counterpart: `:later`. 263 | 264 | ``` 265 | :later 10s go to the state 10 seconds later 266 | :later 10m go to the state 10 minutes later 267 | :later 10h go to the state 10 hours later 268 | :later 10d go to the state 10 days later 269 | :later 10 go to the newer state 10 times 270 | :later 10f go to the state 10 saves later 271 | ``` 272 | 273 | ## Learn Undo The Smart Way 274 | 275 | `u` and `Ctrl-R` are two indispensable Vim commands for correcting mistakes. Learn them first. Next, learn how to use `:earlier` and `:later` using the time arguments first. After that, take your time to understand the undo tree. The [vim-mundo](https://github.com/simnalamburt/vim-mundo) plugin helped me a lot. Type along the texts in this chapter and check the undo tree as you make each change. Once you grasp it, you will never see undo system the same way again. 276 | 277 | Prior to this chapter, you learned how to find any text in a project space, with undo, you can now find any text in a time dimension. You are now able to search for any text by its location and time written. You have achieved Vim-omnipresence. 278 | 279 | -------------------------------------------------------------------------------- /ch11_visual_mode.md: -------------------------------------------------------------------------------- 1 | # Ch11. Visual Mode 2 | 3 | Highlighting and applying changes to a body of text is a common feature in many text editors and word processors. Vim can do this using visual mode. In this chapter, you will learn how to use the visual mode to manipulate texts efficiently. 4 | 5 | ## The Three Types Of Visual Modes 6 | 7 | Vim has three different visual modes. They are: 8 | 9 | ``` 10 | v Character-wise visual mode 11 | V Line-wise visual mode 12 | Ctrl-v Block-wise visual mode 13 | ``` 14 | 15 | If you have the text: 16 | 17 | ``` 18 | one 19 | two 20 | three 21 | ``` 22 | 23 | Character-wise visual mode works with individual characters. Press `v` on the first character. Then go down to the next line with `j`. It highlights all texts from "one" up to your cursor location. If you press `gU`, Vim uppercases the highlighted characters. 24 | 25 | Line-wise visual mode works with lines. Press `V` and watch Vim selects the entire line your cursor is on. Just like character-wise visual mode, if you run `gU`, Vim uppercases the highlighted characters. 26 | 27 | Block-wise visual mode works with rows and columns. It gives you more freedom of movement than the other two modes. If you press `Ctrl-v`, Vim highlights the character under the cursor just like character-wise visual mode, except instead of highlighting each character until the end of the line before going down to the next line, it goes to the next line with minimal highlighting. Try moving around with `h/j/k/l` and watch the cursor moves. 28 | 29 | On the bottom left of your Vim window, you will see either `-- VISUAL --`, `-- VISUAL LINE --`, or `-- VISUAL BLOCK --` displayed to indicate which visual mode you are in. 30 | 31 | While you are inside a visual mode, you can switch to another visual mode by pressing either `v`, `V`, or `Ctrl-v`. For example, if you are in line-wise visual mode and you want to switch to block-wise visual mode, run `Ctrl-v`. Try it! 32 | 33 | There are three ways to exit the visual mode: ``, `Ctrl-C`, and the same key as your current visual mode. What the latter means is if you are currently in the line-wise visual mode (`V`), you can exit it by pressing `V` again. If you are in the character-wise visual mode, you can exit it by pressing `v`. 34 | 35 | There is actually one more way to enter the visual mode: 36 | 37 | ``` 38 | gv Go to the previous visual mode 39 | ``` 40 | 41 | It will start the same visual mode on the same highlighted text block as you did last time. 42 | 43 | ## Visual Mode Navigation 44 | 45 | While in a visual mode, you can expand the highlighted text block with Vim motions. 46 | 47 | Let's use the same text you used earlier: 48 | 49 | ``` 50 | one 51 | two 52 | three 53 | ``` 54 | 55 | This time let's start from the line "two". Press `v` to go to the character-wise visual mode (here the square brackets `[]` represents the character highlights): 56 | 57 | ``` 58 | one 59 | [t]wo 60 | three 61 | ``` 62 | 63 | Press `j` and Vim will highlight all the text from the line "two" down to the first character of the line "three". 64 | 65 | ``` 66 | one 67 | [two 68 | t]hree 69 | ``` 70 | 71 | Assume from this position, you want to add the line "one" too. If you press `k`, to your dismay, the highlight moves away from the line "three". 72 | 73 | ``` 74 | one 75 | [t]wo 76 | three 77 | ``` 78 | 79 | Is there a way to freely expand visual selection to go to any direction you want? Definitely. Let's back up a little bit to where you have the line "two" and "three" highlighted. 80 | 81 | ``` 82 | one 83 | [two 84 | t]hree <-- cursor 85 | ``` 86 | 87 | Visual highlight follows the cursor movement. If you want to expand it upward to line "one", you need to move the cursor up to the line "two". Right now the cursor is on the line "three". You can toggle the cursor location with either `o` or `O`. 88 | 89 | ``` 90 | one 91 | [two <-- cursor 92 | t]hree 93 | ``` 94 | 95 | Now when you press `k`, it no longer reduces the selection, but expands it upward. 96 | 97 | ``` 98 | [one 99 | two 100 | t]hree 101 | ``` 102 | 103 | With `o` or `O` in visual mode, the cursor jumps from the beginning to the end of the highlighted block, allowing you to expand the highlight area. 104 | 105 | ## Visual Mode Grammar 106 | 107 | The visual mode shares many operations with normal mode. 108 | 109 | For example, if you have the following text and you want to delete the first two lines from visual mode: 110 | 111 | ``` 112 | one 113 | two 114 | three 115 | ``` 116 | 117 | Highlight the lines "one" and "two" with the line-wise visual mode (`V`): 118 | 119 | ``` 120 | [one 121 | two] 122 | three 123 | ``` 124 | 125 | Pressing `d` will delete the selection, similar to normal mode. Notice the grammar rule from normal mode, verb + noun, does not apply. The same verb is still there (`d`), but there is no noun in visual mode. The grammar rule in visual mode is noun + verb, where noun is the highlighted text. Select the text block first, then the command follows. 126 | 127 | In normal mode, there are some commands that do not require a motion, like `x` to delete a single character under the cursor and `r` to replace the character under the cursor (`rx` replaces the character under the cursor with "x"). In visual mode, these commands are now being applied to the entire highlighted text instead of a single character. Back at the highlighted text: 128 | 129 | ``` 130 | [one 131 | two] 132 | three 133 | ``` 134 | 135 | Running `x` deletes all highlighted texts. 136 | 137 | You can use this behavior to quickly create a header in markdown text. Suppose you need to quickly turn the following text into a first-level markdown header ("==="): 138 | 139 | ``` 140 | Chapter One 141 | ``` 142 | 143 | First, copy the text with `yy`, then paste it with `p`: 144 | 145 | ``` 146 | Chapter One 147 | Chapter One 148 | ``` 149 | 150 | Now go to the second line, select it with line-wise visual mode: 151 | 152 | ``` 153 | Chapter One 154 | [Chapter One] 155 | ``` 156 | 157 | A first-level header is a series of "=" below a text. Run `r=`, voila! This saves you from typing "=" manually. 158 | 159 | ``` 160 | Chapter One 161 | =========== 162 | ``` 163 | 164 | To learn more about operators in visual mode, check out `:h visual-operators`. 165 | 166 | ## Visual Mode And Command-line Commands 167 | 168 | You can selectively apply command-line commands on a highlighted text block. If you have these statements and you want to substitute "const" with "let" only on the first two lines: 169 | 170 | ``` 171 | const one = "one"; 172 | const two = "two"; 173 | const three = "three"; 174 | ``` 175 | 176 | Highlight the first two lines with *any* visual mode and run the substitute command `:s/const/let/g`: 177 | 178 | ``` 179 | let one = "one"; 180 | let two = "two"; 181 | const three = "three"; 182 | ``` 183 | 184 | Notice I said you can do this with *any* visual mode. You do not have to highlight the entire line to run the command on that line. As long as you select at least a character on each line, the command is applied. 185 | 186 | ## Adding Text On Multiple Lines 187 | 188 | You can add text on multiple lines in Vim using the block-wise visual mode. If you need to add a semicolon at the end of each line: 189 | 190 | ``` 191 | const one = "one" 192 | const two = "two" 193 | const three = "three" 194 | ``` 195 | 196 | With your cursor on the first line: 197 | - Run block-wise visual mode and go down two lines (`Ctrl-v jj`). 198 | - Highlight to the end of the line (`$`). 199 | - Append (`A`) then type ";". 200 | - Exit visual mode (``). 201 | 202 | You should see the appended ";" on each line now. Pretty cool! There are two ways to enter the insert mode from block-wise visual mode: `A` to enter the text after the cursor or `I` to enter the text before the cursor. Do not confuse them with `A` (append text at the end of the line) and `I` (insert text before the first non-blank line) from normal mode. 203 | 204 | Alternatively, you can also use the `:normal` command to add text on multiple lines: 205 | - Highlight all 3 lines (`vjj`). 206 | - Type `:normal! A;`. 207 | 208 | Remember, `:normal` command executes normal mode commands. You can instruct it to run `A;` to append text ";" at the end of the line. 209 | 210 | ## Incrementing Numbers 211 | 212 | Vim has `Ctrl-X` and `Ctrl-A` commands to decrement and increment numbers. When used with visual mode, you can increment numbers across multiple lines. 213 | 214 | If you have these HTML elements: 215 | 216 | ``` 217 |
218 |
219 |
220 |
221 |
222 | ``` 223 | 224 | It is a bad practice to have several ids having the same name, so let's increment them to make them unique: 225 | - Move your cursor to the "1" on the second line. 226 | - Start block-wise visual mode and go down 3 lines (`Ctrl-v 3j`). This highlights the remaining "1"s. Now all "1" should be highlighted. 227 | - Run `g Ctrl-a`. 228 | 229 | You should see this result: 230 | 231 | ``` 232 |
233 |
234 |
235 |
236 |
237 | ``` 238 | 239 | `g Ctrl-a` increments numbers on multiple lines. `Ctrl-X/Ctrl-A` can increment letters too, with the number formats option: 240 | 241 | ``` 242 | set nrformats+=alpha 243 | ``` 244 | 245 | The `nrformats` option instructs Vim which bases are considered as "numbers" for `Ctrl-A` and `Ctrl-X` to increment and decrement. By adding `alpha`, an alphabetical character is now considered as a number. If you have the following HTML elements: 246 | 247 | ``` 248 |
249 |
250 |
251 |
252 |
253 | ``` 254 | 255 | Put your cursor on the second "app-a". Use the same technique as above (`Ctrl-v 3j` then `g Ctrl-a`) to increment the ids. 256 | 257 | ``` 258 |
259 |
260 |
261 |
262 |
263 | ``` 264 | 265 | ## Selecting The Last Visual Mode Area 266 | 267 | Earlier in this chapter I mentioned that `gv` can quickly highlight the last visual mode highlight. You can also go to the location of the start and the end of the last visual mode with these two special marks: 268 | 269 | ``` 270 | `< Go to the last place of the previous visual mode highlight 271 | `> Go to the first place of the previous visual mode highlight 272 | ``` 273 | 274 | Earlier, I also mentioned that you can selectively execute command-line commands on a highlighted text, like `:s/const/let/g`. When you did that, you'd see this below: 275 | 276 | ``` 277 | :`<,`>s/const/let/g 278 | ``` 279 | 280 | You were actually executing a *ranged* `s/const/let/g` command (with the two marks as the addresses). Interesting! 281 | 282 | You can always edit these marks anytime you wish. If instead you needed to substitute from the start of the highlighted text to the end of the file, you just change the command to: 283 | 284 | ``` 285 | :`<,$s/const/let/g 286 | ``` 287 | 288 | ## Entering Visual Mode From Insert Mode 289 | 290 | You can also enter visual mode from the insert mode. To go to character-wise visual mode while you are in insert mode: 291 | 292 | ``` 293 | Ctrl-O v 294 | ``` 295 | 296 | Recall that running `Ctrl-O` while in the insert mode lets you to execute a normal mode command. While in this normal-mode-command-pending mode, run `v` to enter character-wise visual mode. Notice that on the bottom left of the screen, it says `--(insert) VISUAL--`. This trick works with any visual mode operator: `v`, `V`, and `Ctrl-v`. 297 | 298 | ## Select Mode 299 | 300 | Vim has a mode similar to visual mode called the select mode. Like the visual mode, it also has three different modes: 301 | 302 | ``` 303 | gh Character-wise select mode 304 | gH Line-wise select mode 305 | gCtrl-h Block-wise select mode 306 | ``` 307 | 308 | Select mode emulates a regular editor's text highlighting behavior closer than Vim's visual mode does. 309 | 310 | In a regular editor, after you highlight a text block and type a letter, say the letter "y", it will delete the highlighted text and insert the letter "y". If you highlight a line with line-wise select mode (`gH`) and type "y", it will delete the highlighted text and insert the letter "y". 311 | 312 | Contrast this select mode with visual mode: if you highlight a line of text with line-wise visual mode (`V`) and type "y", the highlighted text will not be deleted and replaced by the literal letter "y", it will be yanked. You can't execute normal mode commands on highlighted text in select mode. 313 | 314 | I personally never used select mode, but it's good to know that it exists. 315 | 316 | ## Learn Visual Mode The Smart Way 317 | 318 | The visual mode is Vim's representation of the text highlighting procedure. 319 | 320 | If you find yourself using visual mode operation far more often than normal mode operations, be careful. This is an anti-pattern. It takes more keystrokes to run a visual mode operation than its normal mode counterpart. For example, if you need to delete an inner word, why use four keystrokes, `viwd` (visually highlight an inner word then delete), if you can accomplish it with just three keystrokes (`diw`)? The latter is more direct and concise. Of course, there will be times when visual modes are appropriate, but in general, favor a more direct approach. 321 | -------------------------------------------------------------------------------- /ch14_external_commands.md: -------------------------------------------------------------------------------- 1 | # Ch14. External Commands 2 | 3 | Inside the Unix system, you will find many small, hyper-specialized commands that does one thing (and does it well). You can chain these commands to work together to solve a complex problem. Wouldn't it be great if you can use these commands from inside Vim? 4 | 5 | Definitely. In this chapter, you will learn how extend Vim to work seamlessly with external commands. 6 | 7 | ## The Bang Command 8 | 9 | Vim has a bang (`!`) command that can do three things: 10 | 11 | 1. Read the STDOUT of an external command into the current buffer. 12 | 2. Write the content of your buffer as the STDIN to an external command. 13 | 3. Execute an external command from inside Vim. 14 | 15 | Let's go through each of them. 16 | 17 | ## Reading The STDOUT Of A Command Into Vim 18 | 19 | The syntax to read the STDOUT of an external command into the current buffer is: 20 | 21 | ``` 22 | :r !{cmd} 23 | ``` 24 | 25 | `:r` is Vim's read command. If you use it without `!`, you can use it to get the content of a file. If you have a file `file1.txt` in the current directory and you run: 26 | 27 | ``` 28 | :r file1.txt 29 | ``` 30 | 31 | Vim will put the content of `file1.txt` into the current buffer. 32 | 33 | If you run the `:r` command followed by a `!` and an external command, the output of that commmand will be inserted into the current buffer. To get the result of the `ls` command, run: 34 | 35 | ``` 36 | :r !ls 37 | ``` 38 | 39 | It returns something like: 40 | 41 | ``` 42 | file1.txt 43 | file2.txt 44 | file3.txt 45 | ``` 46 | 47 | You can read the data from the `curl` command: 48 | 49 | ``` 50 | :r !curl -s 'https://jsonplaceholder.typicode.com/todos/1' 51 | ``` 52 | 53 | The `r` command also accepts an address: 54 | 55 | ``` 56 | :10r !cat file1.txt 57 | ``` 58 | 59 | Now the STDOUT from running `cat file1.txt` will be inserted after line 10. 60 | 61 | ## Writing The Buffer Content Into An External Command 62 | 63 | The command `:w`, in addition to saving a file, can be used to pass the text in the current buffer as the STDIN for an external command. The syntax is: 64 | 65 | ``` 66 | :w !{cmd} 67 | ``` 68 | 69 | If you have these expressions: 70 | 71 | ``` 72 | console.log("Hello Vim"); 73 | console.log("Vim is awesome"); 74 | ``` 75 | 76 | Make sure you have [node](https://nodejs.org/en/) installed in your machine, then run: 77 | 78 | ``` 79 | :w !node 80 | ``` 81 | 82 | Vim will use `node` to execute the JavaScript expressions to print "Hello Vim" and "Vim is awesome". 83 | 84 | When using the `:w` command, Vim uses all texts in the current buffer, similar to the global command (most command-line commands, if you don't pass it a range, only executes the command against the current line). If you pass `:w` a specific address: 85 | 86 | ``` 87 | :2w !node 88 | ``` 89 | 90 | Vim only uses the text from the second line into the `node` interpreter. 91 | 92 | There is a subtle but significant difference between `:w !node` and `:w! node`. With `:w !node`, you are "writing" the text in the current buffer into the external command `node`. With `:w! node`, you are force-saving a file and naming the file "node". 93 | 94 | ## Executing An External Command 95 | 96 | You can execute an external command from inside Vim with the bang command. The syntax is: 97 | 98 | ``` 99 | :!cmd 100 | ``` 101 | 102 | To see the content of the current directory in the long format, run: 103 | 104 | ``` 105 | :!ls -ls 106 | ``` 107 | 108 | To kill a process that is running on PID 3456, you can run: 109 | 110 | ``` 111 | :!kill -9 3456 112 | ``` 113 | 114 | You can run any external command without leaving Vim so you can stay focused on your task. 115 | 116 | ## Filtering Texts 117 | 118 | If you give `!` a range, it can be used to filter texts. Suppose you have the following texts: 119 | 120 | ``` 121 | hello vim 122 | hello vim 123 | ``` 124 | 125 | Let's uppercase the current line using the `tr` (translate) command. Run: 126 | 127 | ``` 128 | :.!tr '[:lower:]' '[:upper:]' 129 | ``` 130 | 131 | The result: 132 | 133 | ``` 134 | HELLO VIM 135 | hello vim 136 | ``` 137 | 138 | The breakdown: 139 | - `.!` executes the filter command on the current line. 140 | - `!tr '[:lower:]' '[:upper:]'` calls the `tr` command to replace all lowercase characters with uppercase ones. 141 | 142 | It is imperative to pass a range to run the external command as a filter. If you try running the command above without the `.` (`:!tr '[:lower:]' '[:upper:]'`), you will see an error. 143 | 144 | Let's assume that you need to remove the second column on both lines with the `awk` command: 145 | 146 | ``` 147 | :%!awk "{print $1}" 148 | ``` 149 | 150 | The result: 151 | 152 | ``` 153 | hello 154 | hello 155 | ``` 156 | 157 | The breakdown: 158 | - `:%!` executes the filter command on all lines (`%`). 159 | - `awk "{print $1}"` prints only the first column of the match. 160 | 161 | You can chain multiple commands with the chain operator (`|`) just like in the terminal. Let's say you have a file with these delicious breakfast items: 162 | 163 | ``` 164 | name price 165 | chocolate pancake 10 166 | buttermilk pancake 9 167 | blueberry pancake 12 168 | ``` 169 | 170 | If you need to sort them based on the price and display only the menu with an even spacing, you can run: 171 | 172 | ``` 173 | :%!awk 'NR > 1' | sort -nk 3 | column -t 174 | ``` 175 | 176 | The result: 177 | ``` 178 | buttermilk pancake 9 179 | chocolate pancake 10 180 | blueberry pancake 12 181 | ``` 182 | 183 | The breakdown: 184 | - `:%!` applies the filter to all lines (`%`). 185 | - `awk 'NR > 1'` displays the texts only from row number two onwards. 186 | - `|` chains the next command. 187 | - `sort -nk 3` sorts numerically (`n`) using the values from column 3 (`k 3`). 188 | - `column -t` organizes the text with even spacing. 189 | 190 | ## Normal Mode Command 191 | 192 | Vim has a filter operator (`!`) in the normal mode. If you have the following greetings: 193 | 194 | ``` 195 | hello vim 196 | hola vim 197 | bonjour vim 198 | salve vim 199 | ``` 200 | 201 | To uppercase the current line and the line below, you can run: 202 | ``` 203 | !jtr '[a-z]' '[A-Z]' 204 | ``` 205 | 206 | The breakdown: 207 | - `!j` runs the normal command filter operator (`!`) targetting the current line and the line below it. Recall that because it is a normal mode operator, the grammar rule `verb + noun` applies. `!` is the verb and `j` is the noun. 208 | - `tr '[a-z]' '[A-Z]'` replaces the lowercase letters with the uppercase letters. 209 | 210 | The filter normal command only works on motions / text objects that are at least one line or longer. If you had tried running `!iwtr '[a-z]' '[A-Z]'` (execute `tr` on inner word), you will find that it applies the `tr` command on the entire line, not the word your cursor is on. 211 | 212 | ## Learn External Commands The Smart Way 213 | 214 | Vim is not an IDE. It is a lightweight modal editor that is highly extensible by design. Because of this extensibility, you have an easy access to any external command in your system. Armed with these external commands, Vim is one step closer from becoming an IDE. Someone said that the Unix system is the first IDE ever. 215 | 216 | The bang command is as useful as how many external commands you know. Don't worry if your external command knowledge is limited. I still have a lot to learn too. Take this as a motivation for continuous learning. Whenever you need to modify a text, look if there is an external command that can solve your problem. Don't worry about mastering everything, just learn the ones you need to complete the current task. 217 | -------------------------------------------------------------------------------- /ch15_command-line_mode.md: -------------------------------------------------------------------------------- 1 | # Ch15. Command-line Mode 2 | 3 | In the last three chapters, you learned how to use the search commands (`/`, `?`), substitute command (`:s`), global command (`:g`), and external command (`!`). These are examples of command-line mode commands. 4 | 5 | In this chapter, you will learn various tips and tricks for the command-line mode. 6 | 7 | ## Entering And Exiting The Command-line Mode 8 | 9 | The command-line mode is a mode in itself, just like normal mode, insert mode, and visual mode. When you are in this mode, the cursor goes to the bottom of the screen where you can type in different commands. 10 | 11 | There are 4 different commands you can use to enter the command-line mode: 12 | - Search patterns (`/`, `?`) 13 | - Command-line commands (`:`) 14 | - External commands (`!`) 15 | 16 | You can enter the command-line mode from the normal mode or the visual mode. 17 | 18 | To leave the command-line mode, you can use ``, `Ctrl-c, or Ctrl-[`. 19 | 20 | *Other literatures might refer the "Command-line command" as "Ex command" and the "External command" as "filter command" or "bang operator".* 21 | 22 | ## Repeating The Previous Command 23 | 24 | You can repeat the previous command-line command or external command with `@:`. 25 | 26 | If you just ran `:s/foo/bar/g`, running `@:` repeats that substitution. If you just ran `:.!tr '[a-z]' '[A-Z]'`, running `@:` repeats the last external command translation filter. 27 | 28 | ## Command-line Mode Shortcuts 29 | 30 | While in the command-line mode, you can move to the left or to the right, one character at a time, with the `Left` or `Right` arrow. 31 | 32 | If you need to move word-wise, use `Shift-Left` or `Shift-Right` (in some OS, you might have to use `Ctrl` instead of `Shift`). 33 | 34 | To go to the start of the line, use `Ctrl-b`. To go to the end of the line, use `Ctrl-e`. 35 | 36 | Similar to the insert mode, inside the command-line mode, you have three ways to delete characters: 37 | 38 | ``` 39 | Ctrl-H Delete one character 40 | Ctrl-W Delete one word 41 | Ctrl-U Delete the entire line 42 | ``` 43 | Finally, if you want to edit the command like you would a normal textfile use `Ctrl-f`. 44 | 45 | This also allows you to search through the previous commands, edit them and rerun them by pressing `` in "command-line editing normal mode". 46 | 47 | ## Register And Autocomplete 48 | 49 | While in the command-line mode, you can insert texts from Vim register with `Ctrl-R` the same way as the insert mode. If you have the string "foo" saved in the register a, you can insert it by running `Ctrl-R a`. Everything that you can get from the register in the insert mode, you can do the same from the command-line mode. 50 | 51 | In addition, you can also get the word under the cursor with `Ctrl-R Ctrl-W` (`Ctrl-R Ctrl-A` for the WORD under cursor). To get the line under the cursor, use `Ctrl-R Ctrl-L`. To get the filename under the cursor, use `Ctrl-R Ctrl-F`. 52 | 53 | You can also autocomplete existing commands. To autocomplete the `echo` command, while in the command-line mode, type "ec", then press ``. You should see on the bottom left Vim commands starting with "ec" (example: `echo echoerr echohl echomsg econ`). To go to the next option, press either `` or `Ctrl-N`. To go the previous option, press either `` or `Ctrl-P`. 54 | 55 | Some command-line commands accept file names as arguments. One example is `edit`. You can autocomplete here too. After typing the command, `:e ` (don't forget the space), press ``. Vim will list all the relevant file names that you can choose from so you don't have to type it from scratch. 56 | 57 | ## History Window And Command-line Window 58 | 59 | You can view the histoy of command-line commands and search terms (this requires the `+cmdline_hist` feature). 60 | 61 | To open the command-line history, run `:his :`. You should see something like the following: 62 | 63 | ``` 64 | ## cmd History 65 | 2 e file1.txt 66 | 3 g/foo/d 67 | 4 s/foo/bar/g 68 | ``` 69 | 70 | Vim lists the history of all the `:` commands you run. By default, Vim stores the last 50 commands. To change the amount of the entries that Vim remembers to 100, you run `set history=100`. 71 | 72 | A more useful use of the command-line history is through the command-line window,`q:`. This will open a searchable, editable history window. Suppose you have these expressions in the history when you press `q:`: 73 | 74 | ``` 75 | 51 s/verylongsubstitutionpattern/pancake/g 76 | 52 his : 77 | 53 wq 78 | ``` 79 | 80 | If your current task is to do `s/verylongsubstitutionpattern/donut/g`, instead of typing the command from scratch, why don't you reuse `s/verylongsubstitutionpattern/pancake/g`? After all, the only thing that's different is the word substitute, "donut" vs "pancake". Everything else is the same. 81 | 82 | After you ran `q:`, find that `s/verylongsubstitutionpattern/pancake/g` in the history (you can use the Vim navigation in this environment) and edit it directly! Change "pancake" to "donut" inside the history window, then press ``. Boom! Vim executes `s/verylongsubstitutionpattern/donut/g` for you. Super convenient! 83 | 84 | Similarly, to view the search history, run `:his /` or `:his ?`. To open the search history window where you can search and edit past history, run `q/` or `q?`. 85 | 86 | To quit this window, press `Ctrl-C`, `Ctrl-W C`, or type `:quit`. 87 | 88 | ## More Command-line Commands 89 | 90 | Vim has hundreds of built-in commands. To see all the commands Vim have, check out `:h ex-cmd-index` or `:h :index`. 91 | 92 | ## Learn Command-line Mode The Smart Way 93 | 94 | Compared to the other three modes, the command-line mode is like the Swiss Army knife of text editing. You can edit text, modify files, and execute commands, just to name a few. This chapter is a collection of odds and ends of the command-line mode. It also brings Vim modes into closure. Now that you know how to use the normal, insert, visual, and command-line mode you can edit text with Vim faster than ever. 95 | 96 | It's time to move away from Vim modes and learn how to do an even faster navigation with Vim tags. 97 | -------------------------------------------------------------------------------- /ch17_fold.md: -------------------------------------------------------------------------------- 1 | # Ch17. Fold 2 | 3 | When you read a file, often there are many irrelevant texts that hinder you from understanding what that file does. To hide the unnecessary noise, use Vim fold. 4 | 5 | In this chapter, you will learn different ways to fold a file. 6 | 7 | ## Manual Fold 8 | 9 | Imagine that you are folding a sheet of paper to cover some text. The actual text does not go away, it is still there. Vim fold works the same way. It folds a range of text, hiding it from display without actually deleting it. 10 | 11 | The fold operator is `z` (when a paper is folded, it is shaped like the letter z). 12 | 13 | Suppose you have this text: 14 | 15 | ``` 16 | Fold me 17 | Hold me 18 | ``` 19 | 20 | With the cursor in the first line, type `zfj`. Vim folds both lines into one. You should see something like this: 21 | 22 | ``` 23 | +-- 2 lines: Fold me ----- 24 | ``` 25 | 26 | Here is the breakdown: 27 | - `zf` is the fold operator. 28 | - `j` is the motion for the fold operator. 29 | 30 | You can open a folded text with `zo`. To close the fold, use `zc`. 31 | 32 | Fold is an operator, so it follows the grammar rule (`verb + noun`). You can pass the fold operator with a motion or text object. To fold an inner paragraph, run `zfip`. To fold to the end of a file, run `zfG`. To fold the texts between `{` and `}`, run `zfa{`. 33 | 34 | You can fold from the visual mode. Highlight the area you want to fold (`v`, `V`, or `Ctrl-v`), then run `zf`. 35 | 36 | You can execute a fold from the command-line mode with the `:fold` command. To fold the current line and the line after it, run: 37 | 38 | ``` 39 | :,+1fold 40 | ``` 41 | 42 | `,+1` is the range. If you don't pass parameters to the range, it defaults to the current line. `+1` is the range indicator for the next line. To fold the lines 5 to 10, run `:5,10fold`. To fold from the current position to the end of the line, run `:,$fold`. 43 | 44 | There are many other fold and unfold commands. I find them too many to remember when starting out. The most useful ones are: 45 | - `zR` to open all folds. 46 | - `zM` to close all folds. 47 | - `za` toggle a fold. 48 | 49 | You can run `zR` and `zM` on any line, but `za` only works when you are on a folded / unfolded line. To learn more folding commands, check out `:h fold-commands`. 50 | 51 | ## Different Fold Methods 52 | 53 | The section above covers Vim's manual fold. There are six different folding methods in Vim: 54 | 1. Manual 55 | 2. Indent 56 | 3. Expression 57 | 4. Syntax 58 | 5. Diff 59 | 6. Marker 60 | 61 | To see which folding method you are currently using, run `:set foldmethod?`. By default, Vim uses the `manual` method. 62 | 63 | In the rest of the chapter, you will learn the other five folding methods. Let's get started with the indent fold. 64 | 65 | ## Indent Fold 66 | 67 | To use an indent fold, change the `'foldmethod'` to indent: 68 | 69 | ``` 70 | :set foldmethod=indent 71 | ``` 72 | 73 | Suppose that you have the text: 74 | 75 | ``` 76 | One 77 | Two 78 | Two again 79 | ``` 80 | 81 | If you run `:set foldmethod=indent`, you will see: 82 | 83 | ``` 84 | One 85 | +-- 2 lines: Two ----- 86 | ``` 87 | 88 | With indent fold, Vim looks at how many spaces each line has at the beginning and compares it with the `'shiftwidth'` option to determine its foldability. `'shiftwidth'` returns the number of spaces required for each step of the indent. If you run: 89 | 90 | ``` 91 | :set shiftwidth? 92 | ``` 93 | 94 | Vim's default `'shiftwidth'` value is 2. On the text above, there are two spaces between the start of the line and the text "Two" and "Two again". When Vim sees the number of spaces and that the `'shiftwidth'` value is 2, Vim considers that line to have an indent fold level of one. 95 | 96 | Suppose this time you only one space between the start of the line and the text: 97 | 98 | ``` 99 | One 100 | Two 101 | Two again 102 | ``` 103 | 104 | Right now if you run `:set foldmethod=indent`, Vim does not fold the indented line because there isn't sufficient space on each line. One space is not considered an indentation. However, if you change the `'shiftwidth'` to 1: 105 | 106 | ``` 107 | :set shiftwidth=1 108 | ``` 109 | 110 | The text is now foldable. It is now considered an indentation. 111 | 112 | Restore the `shiftwidth` back to 2 and the spaces between the texts to two again. In addition, add two additional texts: 113 | 114 | ``` 115 | One 116 | Two 117 | Two again 118 | Three 119 | Three again 120 | ``` 121 | 122 | Run fold (`zM`), you will see: 123 | 124 | ``` 125 | One 126 | +-- 4 lines: Two ----- 127 | ``` 128 | 129 | Unfold the folded lines (`zR`), then put your cursor on "Three" and toggle the text's folding state (`za`): 130 | 131 | ``` 132 | One 133 | Two 134 | Two again 135 | +-- 2 lines: Three ----- 136 | ``` 137 | 138 | What's this? A fold within a fold? 139 | 140 | Nested folds are valid. The text "Two" and "Two again" have fold level of one. The text "Three" and "Three again" have fold level of two. If you have a foldable text with a higher fold level within a foldable text, you will have multiple fold layers. 141 | 142 | ## Expression Fold 143 | 144 | Expression fold allows you to define an expression to match for a fold. After you define the fold expressions, Vim scans each line for the value of `'foldexpr'`. This is the variable that you have to configure to return the appropriate value. If the `'foldexpr'` returns 0, then the line is not folded. If it returns 1, then that line has a fold level of 1. If it returns 2, then that line has a fold level of 2. There are more values other than integers, but I won't go over them. If you are curious, check out `:h fold-expr`. 145 | 146 | First, let's change the foldmethod: 147 | 148 | ``` 149 | :set foldmethod=expr 150 | ``` 151 | 152 | Suppose you have a list of breakfast foods and you want to fold all breakfast items starting with "p": 153 | 154 | ``` 155 | donut 156 | pancake 157 | pop-tarts 158 | protein bar 159 | salmon 160 | scrambled eggs 161 | ``` 162 | 163 | Next, change the `foldexpr` to capture the expressions starting with "p": 164 | 165 | ``` 166 | :set foldexpr=getline(v:lnum)[0]==\\"p\\" 167 | ``` 168 | 169 | The expression above looks complicated. Let's break it down: 170 | - `:set foldexpr` sets up the `'foldexpr'` option to accept a custom expression. 171 | - `getline()` is a Vimscript function that returns the content of any given line. If you run `:echo getline(5)`, it will return the content of line 5. 172 | - `v:lnum` is Vim's special variable for the `'foldexpr'` expression. Vim scans each line and at that moment stores each line's number in `v:lnum` variable. On line 5, `v:lnum` has value of 5. On line 10, `v:lnum` has value of 10. 173 | - `[0]` in the context of `getline(v:lnum)[0]` is the first character of each line. When Vim scans a line, `getline(v:lnum)` returns the content of each line. `getline(v:lnum)[0]` returns the first character of each line. On the first line of our list, "donut", `getline(v:lnum)[0]` returns "d". On the second line of our list, "pancake", `getline(v:lnum)[0]` returns "p". 174 | - `==\\"p\\"` is the second half of the equality expression. It checks if the expression you just evaluated is equal to "p". If it is true, it returns 1. If it is false, it returns 0. In Vim, 1 is truthy and 0 is falsy. So on the lines that start with an "p", it returns 1. Recall if a `'foldexpr'` has a value of 1, then it has a fold level of 1. 175 | 176 | After running this expression, you should see: 177 | 178 | ``` 179 | donut 180 | +-- 3 lines: pancake ----- 181 | salmon 182 | scrambled eggs 183 | ``` 184 | 185 | ## Syntax Fold 186 | 187 | Syntax fold is determined by syntax language highlighting. If you use a language syntax plugin like [vim-polyglot](https://github.com/sheerun/vim-polyglot), the syntax fold will work right out of the box. Just change the fold method to syntax: 188 | 189 | ``` 190 | :set foldmethod=syntax 191 | ``` 192 | 193 | Let's assume you are editing a JavaScript file and you have vim-polyglot installed. If you have an array like the following: 194 | 195 | ``` 196 | const nums = [ 197 | one, 198 | two, 199 | three, 200 | four 201 | ] 202 | ``` 203 | 204 | It will be folded with a syntax fold. When you define a syntax highlighting for a particular language (typically inside the `syntax/` directory), you can add a `fold` attribute to make it foldable. Below is a snippet from vim-polyglot JavaScript syntax file. Notice the `fold` keyword at the end. 205 | 206 | ``` 207 | syntax region jsBracket matchgroup=jsBrackets start=/\[/ end=/\]/ contains=@jsExpression,jsSpreadExpression extend fold 208 | ``` 209 | 210 | This guide won't cover the `syntax` feature. If you're curious, check out `:h syntax.txt`. 211 | 212 | ## Diff Fold 213 | 214 | Vim can do a diff procedure to compare two or more files. 215 | 216 | If you have `file1.txt`: 217 | 218 | ``` 219 | vim is awesome 220 | vim is awesome 221 | vim is awesome 222 | vim is awesome 223 | vim is awesome 224 | vim is awesome 225 | vim is awesome 226 | vim is awesome 227 | vim is awesome 228 | vim is awesome 229 | ``` 230 | 231 | And `file2.txt`: 232 | 233 | ``` 234 | vim is awesome 235 | vim is awesome 236 | vim is awesome 237 | vim is awesome 238 | vim is awesome 239 | vim is awesome 240 | vim is awesome 241 | vim is awesome 242 | vim is awesome 243 | emacs is ok 244 | ``` 245 | 246 | Run `vimdiff file1.txt file2.txt`: 247 | 248 | ``` 249 | +-- 3 lines: vim is awesome ----- 250 | vim is awesome 251 | vim is awesome 252 | vim is awesome 253 | vim is awesome 254 | vim is awesome 255 | vim is awesome 256 | [vim is awesome] / [emacs is ok] 257 | ``` 258 | 259 | Vim automatically folds some of the identical lines. When you are running the `vimdiff` command, Vim automatically uses `foldmethod=diff`. If you run `:set foldmethod?`, it will return `diff`. 260 | 261 | ## Marker Fold 262 | 263 | To use a marker fold, run: 264 | 265 | ``` 266 | :set foldmethod=marker 267 | ``` 268 | 269 | Suppose you have the text: 270 | 271 | ``` 272 | Hello 273 | 274 | {{{ 275 | world 276 | vim 277 | }}} 278 | ``` 279 | 280 | Run `zM`, you will see: 281 | 282 | ``` 283 | hello 284 | 285 | +-- 4 lines: ----- 286 | ``` 287 | 288 | Vim sees `{{{` and `}}}` as fold indicators and folds the texts between them. With the marker fold, Vim looks for special markers, defined by `'foldmarker'` option, to mark folding areas. To see what markers Vim uses, run: 289 | 290 | ``` 291 | :set foldmarker? 292 | ``` 293 | 294 | By default, Vim uses `{{{` and `}}}` as indicators. If you want to change the indicator to another texts, like "coffee1" and "coffee2": 295 | 296 | ``` 297 | :set foldmarker=coffee1,coffee2 298 | ``` 299 | 300 | If you have the text: 301 | 302 | ``` 303 | hello 304 | 305 | coffee1 306 | world 307 | vim 308 | coffee2 309 | ``` 310 | 311 | Now Vim uses `coffee1` and `coffee2` as the new folding markers. As a side note, an indicator must be a literal string and cannot be a regex. 312 | 313 | ## Persisting Fold 314 | 315 | You loses all fold information when you close the Vim session. If you have this file, `count.txt`: 316 | 317 | ``` 318 | one 319 | two 320 | three 321 | four 322 | five 323 | ``` 324 | 325 | Then do a manual fold from line "three" down (`:3,$fold`): 326 | 327 | ``` 328 | one 329 | two 330 | +-- 3 lines: three --- 331 | ``` 332 | 333 | When you exit Vim and reopen `count.txt`, the folds are no longer there! 334 | 335 | To preserve the folds, after folding, run: 336 | 337 | ``` 338 | :mkview 339 | ``` 340 | 341 | Then when you open up `count.txt`, run: 342 | 343 | ``` 344 | :loadview 345 | ``` 346 | 347 | Your folds are restored. However, you have to manually run `mkview` and `loadview`. I know that one of these days, I will forget to run `mkview` before closing the file and I will lose all the folds. How can we automate this process? 348 | 349 | To automatically run `mkview` when you close a `.txt` file and run `loadview` when you open a `.txt` file, add this in your vimrc: 350 | 351 | ``` 352 | autocmd BufWinLeave *.txt mkview 353 | autocmd BufWinEnter *.txt silent loadview 354 | ``` 355 | 356 | Recall that `autocmd` is used to execute a command on an event trigger. The two events here are: 357 | - `BufWinLeave` for when you remove a buffer from a window. 358 | - `BufWinEnter` for when you load a buffer in a window. 359 | 360 | Now after you fold inside a `.txt` file and exit Vim, the next time you open that file, your fold information will be restored. 361 | 362 | By default, Vim saves the fold information when running `mkview` inside `~/.vim/view` for the Unix system. For more information, check out `:h 'viewdir'`. 363 | 364 | ## Learn Fold The Smart Way 365 | 366 | When I first started Vim, I neglected ot learn fold because I didn't think it was useful. However, the longer I code, the more useful I find folding is. Strategically placed folds can give you a better overview of the text structure, like a book's table of content. 367 | 368 | When you learn fold, start with the manual fold because that can be used on-the-go. Then gradually learn different tricks to do indent and marker folds. Finally, learn how to do syntax and expression folds. You can even use the latter two to write your own Vim plugins. 369 | -------------------------------------------------------------------------------- /ch18_git.md: -------------------------------------------------------------------------------- 1 | # Ch18. Git 2 | 3 | Vim and git are two great tools for two different things. Git is a version control tool. Vim is a text editor. 4 | 5 | In this chapter, you will learn different ways to integrate Vim and git together. 6 | 7 | ## Diffing 8 | 9 | Recall in the previous chapter, you can run a `vimdiff` command to show the differences between multiple files. 10 | 11 | Suppose you have two files, `file1.txt` and `file2.txt`. 12 | 13 | Inside `file1.txt`: 14 | 15 | ``` 16 | pancakes 17 | waffles 18 | apples 19 | 20 | milk 21 | apple juice 22 | 23 | yogurt 24 | ``` 25 | 26 | Inside `file2.txt`: 27 | 28 | ``` 29 | pancakes 30 | waffles 31 | oranges 32 | 33 | milk 34 | orange juice 35 | 36 | yogurt 37 | ``` 38 | 39 | To see the differences between both files, run: 40 | 41 | ``` 42 | vimdiff file1.txt file2.txt 43 | ``` 44 | 45 | Alternatively you could run: 46 | 47 | ``` 48 | vim -d file1.txt file2.txt 49 | ``` 50 | 51 |

52 | Basic diffing with Vim 53 |

54 | 55 | `vimdiff` displays two buffers side-by-side. On the left is `file1.txt` and on the right is `file2.txt`. The first differences (apples and oranges) are highlighted on both lines. 56 | 57 | Suppose you want to make the second buffer to have apples, not oranges. To transfer the content from your current position (you're currently on `file1.txt`) to `file2.txt`, first go to the next diff with `]c` (to jump to the previous diff window, use `[c`). The cursor should be on apples now. Run `:diffput`. Both files should now have apples. 58 | 59 |

60 | Diffing Apples 61 |

62 | 63 | If you need to transfer the text from the other buffer (orange juice, `file2.txt`) to replace the text on the current buffer (apple juice, `file1.txt`), with your cursor still on `file1.txt` window, first go to the next diff with `]c`. Your cursor now should be on apple juice. Run `:diffget` to get the orange juice from another buffer to replace apple juice in our buffer. 64 | 65 | `:diffput` *puts out* the text from the current buffer to another buffer. `:diffget` *gets* the text from another buffer to the current buffer. 66 | 67 | If you have multiple buffers, you can run `:diffput fileN.txt` and `:diffget fileN.txt` to target the the fileN buffer. 68 | 69 | ## Vim As A Merge Tool 70 | 71 | > "I love resolving merge conflicts!" - Nobody 72 | 73 | I don't know anyone who likes resolving merge conflicts. However, they are inevitable. In this section, you will learn how to leverage Vim as a merge conflict resolution tool. 74 | 75 | First, change the default merge tool to use `vimdiff` by running: 76 | 77 | ``` 78 | git config merge.tool vimdiff 79 | git config merge.conflictstyle diff3 80 | git config mergetool.prompt false 81 | ``` 82 | 83 | Alternatively, you can modify the `~/.gitconfig` directly (by default it should be in root, but yours might be in different place). The commands above should modify your gitconfig to look like the setting below, if you haven't run them already, you can also manually edit your gitconfig. 84 | 85 | ``` 86 | [core] 87 | editor = vim 88 | [merge] 89 | tool = vimdiff 90 | conflictstyle = diff3 91 | [difftool] 92 | prompt = false 93 | ``` 94 | 95 | Let's create a fake merge conflict to test this out. Create a directory `/food` and make it a git repository: 96 | 97 | ``` 98 | git init 99 | ``` 100 | 101 | Add a file, `breakfast.txt`. Inside: 102 | 103 | ``` 104 | pancakes 105 | waffles 106 | oranges 107 | ``` 108 | 109 | Add the file and commit it: 110 | 111 | ``` 112 | git add . 113 | git commit -m "Initial breakfast commit" 114 | ``` 115 | 116 | Next, create a new branch and call it apples branch: 117 | 118 | ``` 119 | git checkout -b apples 120 | ``` 121 | 122 | Change the `breakfast.txt`: 123 | 124 | ``` 125 | pancakes 126 | waffles 127 | apples 128 | ``` 129 | 130 | Save the file, then add and commit the change: 131 | 132 | ``` 133 | git add . 134 | git commit -m "Apples not oranges" 135 | ``` 136 | 137 | Great. Now you have oranges in the master branch and apples in the apples branch. Let's return to the master branch: 138 | 139 | ``` 140 | git checkout master 141 | ``` 142 | 143 | Inside `breakfast.txt`, you should see the base text, oranges. Let's change it to grapes because they are in season right now: 144 | 145 | ``` 146 | pancakes 147 | waffles 148 | grapes 149 | ``` 150 | 151 | Save, add, and commit: 152 | 153 | ``` 154 | git add . 155 | git commit -m "Grapes not oranges" 156 | ``` 157 | 158 | Now you are ready to merge the apples branch into the master branch: 159 | 160 | ``` 161 | git merge apples 162 | ``` 163 | 164 | You should see an error: 165 | 166 | ``` 167 | Auto-merging breakfast.txt 168 | CONFLICT (content): Merge conflict in breakfast.txt 169 | Automatic merge failed; fix conflicts and then commit the result. 170 | ``` 171 | 172 | A conflict, great! Let's resolve the conflict using our newly-configured `mergetool`. Run: 173 | 174 | ``` 175 | git mergetool 176 | ``` 177 | 178 |

179 | Three-way mergetool with Vim 180 |

181 | 182 | Vim displays four windows. Pay attention to the top three: 183 | 184 | - `LOCAL` contains `grapes`. This is the change in "local", what you are merging into. 185 | - `BASE` contains `oranges`. This is the common ancestor between `LOCAL` and `REMOTE` to compare how they diverge. 186 | - `REMOTE` contains `apples`. This is what is being merged into. 187 | 188 | At the bottom (the fourth window) you see: 189 | 190 | ``` 191 | pancakes 192 | waffles 193 | <<<<<<< HEAD 194 | grapes 195 | ||||||| db63958 196 | oranges 197 | ======= 198 | apples 199 | >>>>>>> apples 200 | ``` 201 | 202 | The fourth window contains the merge conflict texts. With this setup, it is easier to see what change each environment has. You can see the content from `LOCAL`, `BASE`, and `REMOTE` at the same time. 203 | 204 | Your cursor should be on the fourth windows, on the highlighted area. To get the change from `LOCAL` (grapes), run `:diffget LOCAL`. To get the change from `BASE` (oranges), run `:diffget BASE` and to get the change from `REMOTE` (apples), run `:diffget REMOTE`. 205 | 206 | In this case, let's get the change from `LOCAL`. Run `:diffget LOCAL`. The fourth window will now have grapes. Save and exit all files (`:wqall`) when you are done. That wasn't bad, right? 207 | 208 | If you notice, you also have a file `breakfast.txt.orig` now. Git creates a backup file in case things don't go well. If you don't want git to create a backup during a merge, run: 209 | 210 | ``` 211 | git config --global mergetool.keepBackup false 212 | ``` 213 | 214 | ## Git Inside Vim 215 | 216 | Vim does not have a native git feature built-in. One way to run git commands from Vim is to use the bang operator, `!`, in the command-line mode. 217 | 218 | Any git command can be run with `!`: 219 | 220 | ``` 221 | :!git status 222 | :!git commit 223 | :!git diff 224 | :!git push origin master 225 | ``` 226 | 227 | You can also use Vim's `%` (current buffer) or `#` (other buffer) conventions: 228 | 229 | ``` 230 | :!git add % " git add current file 231 | :!git checkout # " git checkout the other file 232 | ``` 233 | 234 | One Vim trick you can use to add multiple files in different Vim window is to run: 235 | 236 | ``` 237 | :windo !git add % 238 | ``` 239 | 240 | Then make a commit: 241 | 242 | ``` 243 | :!git commit "Just git-added everything in my Vim window, cool" 244 | ``` 245 | 246 | The `windo` command is one of Vim's "do" commands, similar to `argdo` that you saw previously. `windo` executes the command on each window. 247 | 248 | Alternatively, you can also use `bufdo !git add %` to git add all buffers or `argdo !git add %` to git add all the file arguments, depending on your workflow. 249 | 250 | ## Plugins 251 | 252 | There are many Vim plugins for git support. Below is a list of some of the popular git-related plugins for Vim (there is probably more at the time you read this): 253 | 254 | - [vim-gitgutter](https://github.com/airblade/vim-gitgutter) 255 | - [vim-signify](https://github.com/mhinz/vim-signify) 256 | - [vim-fugitive](https://github.com/tpope/vim-fugitive) 257 | - [gv.vim](https://github.com/junegunn/gv.vim) 258 | - [vimagit](https://github.com/jreybert/vimagit) 259 | - [vim-twiggy](https://github.com/sodapopcan/vim-twiggy) 260 | - [rhubarb](https://github.com/tpope/vim-rhubarb) 261 | 262 | One of the most popular ones is vim-fugitive. For the remaining of the chapter, I will go over a several git workflow using this plugin. 263 | 264 | ## Vim-fugitive 265 | 266 | The vim-fugitive plugin allows you to run the git CLI without leaving the Vim editor. You will find that some commands are better when executed from inside Vim. 267 | 268 | To get started, install the vim-fugitive with a Vim plugin manager ([vim-plug](https://github.com/junegunn/vim-plug), [vundle](https://github.com/VundleVim/Vundle.vim), [dein.vim](https://github.com/Shougo/dein.vim), etc). 269 | 270 | ## Git Status 271 | 272 | When you run the `:Git` command without any parameters, vim-fugitive displays a git summary window. It shows the untracked, unstaged, and staged file(s). While in this "`git status`" mode, you can do several things: 273 | - `Ctrl-N` / `Ctrl-P` to go up or down the file list. 274 | - `-` to stage or unstage the file name under the cursor. 275 | - `s` to stage the file name under the cursor. 276 | - `u` to unstage the file name under the cursor. 277 | - `>` / `<` to display or hide an inline diff of the file name under the cursor. 278 | 279 |

280 | Fugitive Git 281 |

282 | 283 | For more, check out `:h fugitive-staging-maps`. 284 | 285 | ## Git Blame 286 | 287 | When you run the `:Git blame` command from the current file, vim-fugitive displays a split blame window. This can be useful to find the person responsible for writing that buggy line of code so you can yell at him / her (just kidding). 288 | 289 | Some things you can do while in this `"git blame"` mode: 290 | - `q` to close the blame window. 291 | - `A` to resize the author column. 292 | - `C` to resize the commit column. 293 | - `D` to resize the date / time column. 294 | 295 | For more, check out `:h :Git_blame`. 296 | 297 |

298 | Fugitive Git Blame 299 |

300 | 301 | ## Gdiffsplit 302 | 303 | When you run the `:Gdiffsplit` command, vim-fugitive runs a `vimdiff` of the current file's latest changes against the index or work tree. If you run `:Gdiffsplit `, vim-fugitive runs a `vimdiff` against that file inside ``. 304 | 305 |

306 | Fugitive Gdiffsplit 307 |

308 | 309 | Because you are in a `vimdiff` mode, you can *get* or *put* the diff with `:diffput` and `:diffget`. 310 | 311 | ## Gwrite And Gread 312 | 313 | When you run the `:Gwrite` command in a file after you make changes, vim-fugitive stages the changes. It is like running `git add `. 314 | 315 | When you run the `:Gread` command in a file after you make changes, vim-fugitive restores the file to the state prior to the changes. It is like running `git checkout `. One advantage of running `:Gread` is the action is undo-able. If, after you run `:Gread`, you change your mind and want to keep the old change, you can just run undo (`u`) and Vim will undo the `:Gread` action. This would not have been possible if you had run `git checkout ` from the CLI. 316 | 317 | ## Gclog 318 | 319 | When you run the `:Gclog` command, vim-fugitive displays the commit history. It is like running the `git log` command. Vim-fugitive uses Vim's quickfix to accomplish this, so you can use `:cnext` and `:cprevious` to traverse to the next or previous log information. You can open and close the log list with `:copen` and `:cclose`. 320 | 321 |

322 | Fugitive Git Log 323 |

324 | 325 | While in this `"git log"` mode, you can do two things: 326 | - View the tree. 327 | - Visit the parent (the previous commit). 328 | 329 | You can pass to `:Gclog` arguments just like the `git log` command. If your project has a long commit history and you only need to view the last three commits, you can run `:Gclog -3`. If you need to filter it based on the committer's date, you can run something like `:Gclog --after="January 1" --before="March 14"`. 330 | 331 | ## More Vim-Fugitive 332 | 333 | These are only a few examples of what vim-fugitive can do. To learn more about vim-fugitive, check out `:h fugitive.txt`. Most of the popular git commands are probably optimized with vim-fugitive. You just have to look for them in the documentation. 334 | 335 | If you are inside one of vim-fugitive's "special mode" (for example, inside `:Git` or `:Git blame` mode) and you want to learn what shortcuts are available, press `g?`. Vim-fugitive will display the appropriate `:help` window for the mode you are in. Neat! 336 | 337 | ## Learn Vim And Git The Smart Way 338 | 339 | You may find vim-fugitive to be a good compliment to your workflow (or not). Regardless, I would strongly encourage you to check out all the plugins listed above. There are probably others I didn't list. Go try them out. 340 | 341 | One obvious way to get better with Vim-git integration is to read more about git. Git, on its own, is a vast topic and I am only showing a fraction of it. With that, let's *git going* (pardon the pun) and talk about how to use Vim to compile your code! 342 | -------------------------------------------------------------------------------- /ch19_compile.md: -------------------------------------------------------------------------------- 1 | # Ch19. Compile 2 | 3 | Compiling is an important subject for many languages. In this chapter, you will learn how to compile from Vim. You will also look at ways to take advantage of Vim's `:make` command. 4 | 5 | ## Compile From the Command Line 6 | 7 | You can use the bang operator (`!`) to compile. If you need to compile your `.cpp` file with `g++`, run: 8 | 9 | ``` 10 | :!g++ hello.cpp -o hello 11 | ``` 12 | 13 | However, having to manually type the filename and the output filename each time is error-prone and tedious. A makefile is the way to go. 14 | 15 | ## The Make Command 16 | 17 | Vim has a `:make` command to run a makefile. When you run it, Vim looks for a makefile in the current directory to execute. 18 | 19 | Create a file named `makefile` in the current directory and put these inside: 20 | 21 | ``` 22 | all: 23 | echo "Hello all" 24 | foo: 25 | echo "Hello foo" 26 | list_pls: 27 | ls 28 | ``` 29 | 30 | Run this from Vim: 31 | 32 | ``` 33 | :make 34 | ``` 35 | 36 | Vim executes it the same way as when you're running it from the terminal. The `:make` command accepts parameter just like the terminal make command. Run: 37 | 38 | ``` 39 | :make foo 40 | " Outputs "Hello foo" 41 | 42 | :make list_pls 43 | " Outputs the ls command result 44 | ``` 45 | 46 | The `:make` command uses Vim's quickfix to store any error if you run a bad command. Let's run a nonexisting target: 47 | 48 | ``` 49 | :make dontexist 50 | ``` 51 | 52 | You should see an error running that command. To view that error, run the quickfix command `:copen` to view the quickfix window: 53 | 54 | ``` 55 | || make: *** No rule to make target `dontexist'. Stop. 56 | ``` 57 | 58 | ## Compiling With Make 59 | 60 | Let's use the makefile to compile a basic `.cpp` program. First, let's create a `hello.cpp` file: 61 | 62 | ``` 63 | #include 64 | 65 | int main() { 66 | std::cout << "Hello!\\n"; 67 | return 0; 68 | } 69 | ``` 70 | 71 | Update your makefile to build and run a `.cpp` file: 72 | 73 | ``` 74 | all: 75 | echo "build, run" 76 | build: 77 | g++ hello.cpp -o hello 78 | run: 79 | ./hello 80 | ``` 81 | 82 | Now run: 83 | 84 | ``` 85 | :make build 86 | ``` 87 | 88 | The `g++` compiles `./hello.cpp` and creates `./hello`. Then run: 89 | 90 | ``` 91 | :make run 92 | ``` 93 | 94 | You should see `"Hello!"` printed on the terminal. 95 | 96 | ## Different Make Program 97 | 98 | When you run `:make`, Vim actually runs whatever command that is set under the `makeprg` option. If you run `:set makeprg?`, you'll see: 99 | 100 | ``` 101 | makeprg=make 102 | ``` 103 | 104 | The default `:make` command is the `make` external command. To change the `:make` command to execute `g++ {your-file-name}` each time you run it, run: 105 | 106 | ``` 107 | :set makeprg=g++\ % 108 | ``` 109 | 110 | The `\` is to escape the space after `g++`. The `%` symbol in Vim represents the current file. The command `g++\\ %` is equivalent to running `g++ hello.cpp`. 111 | 112 | Go to `./hello.cpp` then run `:make`. Vim compiles `hello.cpp` and creates `a.out` because you didn't specify the output. Let's refactor it so it will name the compiled output with the name of the original file minus the extension. Run or add this to vimrc: 113 | 114 | ``` 115 | set makeprg=g++\ %\ -o\ %< 116 | ``` 117 | 118 | The breakdown: 119 | - `g++\ %` is the same as above. It is equivalent to running `g++ `. 120 | - `-o` is the output option. 121 | - `%<` in Vim represents the current file name without an extension (`hello.cpp` becomes `hello`). 122 | 123 | When you run `:make` from inside `./hello.cpp`, it is compiled into `./hello`. To quickly execute `./hello` from inside `./hello.cpp`, run `:!./%<`. Again, this is the same as running `:!./{current-file-name-minus-the-extension}`. 124 | 125 | For more, check out `:h :compiler` and `:h write-compiler-plugin`. 126 | 127 | ## Auto-compile On Save 128 | 129 | You can make life even easier by automating compilation. Recall that you can use Vim's `autocmd` to trigger automatic actions based on certain events. To automatically compile `.cpp` files on each save add this on your vimrc: 130 | 131 | ``` 132 | autocmd BufWritePost *.cpp make 133 | ``` 134 | 135 | Each time you save inside a `.cpp` file, Vim executes the `make` command. 136 | 137 | ## Switching Compiler 138 | 139 | Vim has a `:compiler` command to quickly switch compilers. Your Vim build probably comes with several pre-built compiler configurations. To check what compilers you have, run: 140 | 141 | ``` 142 | :e $VIMRUNTIME/compiler/ 143 | ``` 144 | 145 | You should see a list of compilers for different programming languages. 146 | 147 | To use the `:compiler` command, suppose you have a ruby file, `hello.rb` and inside it has: 148 | 149 | ``` 150 | puts "Hello ruby" 151 | ``` 152 | 153 | Recall that if you run `:make`, Vim executes whatever command is assigned to `makeprg` (default is `make`). If you run: 154 | 155 | ``` 156 | :compiler ruby 157 | ``` 158 | 159 | Vim runs the `$VIMRUNTIME/compiler/ruby.vim` script and changes the `makeprg` to use the `ruby` command. Now if you run `:set makeprg?`, it should say `makeprg=ruby` (this depends on what is inside your `$VIMRUNTIME/compiler/ruby.vim` file or if you have another custom ruby compilers. Yours might be different). The `:compiler {your-lang}` command allows you to switch to different compilers quickly. This is useful if your project uses multiple languages. 160 | 161 | You don't have to use the `:compiler` and `makeprg` to compile a program. You can run a test script, lint a file, send a signal, or anything you want. 162 | 163 | ## Creating A Custom Compiler 164 | 165 | Let's create a simple Typescript compiler. Install Typescript (`npm install -g typescript`) to your machine. You should now have the `tsc` command. If you haven't played with typescript before, `tsc` compiles a Typescript file into a Javascript file. Suppose that you have a file, `hello.ts`: 166 | 167 | ``` 168 | const hello = "hello"; 169 | console.log(hello); 170 | ``` 171 | 172 | If you run `tsc hello.ts`, it will compile into `hello.js`. However, if you have the following expressions inside `hello.ts`: 173 | 174 | ``` 175 | const hello = "hello"; 176 | hello = "hello again"; 177 | console.log(hello); 178 | ``` 179 | 180 | This will throw an error because you can't mutate a `const` variable. Running `tsc hello.ts` will throw an error: 181 | 182 | ``` 183 | hello.ts:2:1 - error TS2588: Cannot assign to 'person' because it is a constant. 184 | 185 | 2 person = "hello again"; 186 | ~~~~~~ 187 | 188 | 189 | Found 1 error. 190 | ``` 191 | 192 | To create a simple Typescript compiler, in your `~/.vim/` directory, add a `compiler` directory (`~/.vim/compiler/`), then create a `typescript.vim` file (`~/.vim/compiler/typescript.vim`). Put this inside: 193 | 194 | ``` 195 | CompilerSet makeprg=tsc 196 | CompilerSet errorformat=%f:\ %m 197 | ``` 198 | 199 | The first line sets the `makeprg` to run the `tsc` command. The second line sets the error format to display the file (`%f`), followed by a literal colon (`:`) and an escaped space (`\ `), followed by the error message (`%m`). To learn more about the error formatting, check out `:h errorformat`. 200 | 201 | You should also read some of the pre-made compilers to see how others do it. Check out `:e $VIMRUNTIME/compiler/.vim`. 202 | 203 | Because some plugins may interfere with the Typescript file, let's open the `hello.ts` without any plugin, using the `--noplugin` flag: 204 | 205 | ``` 206 | vim --noplugin hello.ts 207 | ``` 208 | 209 | Check the `makeprg`: 210 | 211 | ``` 212 | :set makeprg? 213 | ``` 214 | 215 | It should say the default `make` program. To use the new Typescript compiler, run: 216 | 217 | ``` 218 | :compiler typescript 219 | ``` 220 | 221 | When you run `:set makeprg?`, it should say `tsc` now. Let's put it to the test. Run: 222 | 223 | ``` 224 | :make % 225 | ``` 226 | 227 | Recall that `%` means the current file. Watch your Typescript compiler work as expected! To see the list of error(s), run `:copen`. 228 | 229 | ## Async Compiler 230 | 231 | Sometimes compiling can take a long time. You don't want to be staring at a frozen Vim while waiting for your compilation process to finish. Wouldn't it be nice if you can compile asynchronously so you can still use Vim during compilation? 232 | 233 | Luckily there are plugins to run async processes. The two big ones are: 234 | 235 | - [vim-dispatch](https://github.com/tpope/vim-dispatch) 236 | - [asyncrun.vim](https://github.com/skywind3000/asyncrun.vim) 237 | 238 | In the remaining of this chapter, I will go over vim-dispatch, but I would strongly encourage you to try all of them out there. 239 | 240 | *Vim and NeoVim actually supports async jobs, but they are beyond the scope of this chapter. If you're curious, check out `:h job-channel-overview.txt`.* 241 | 242 | ## Plugin: Vim-dispatch 243 | 244 | Vim-dispatch has several commands, but the two main ones are `:Make` and `:Dispatch` commands. 245 | 246 | ### Async Make 247 | 248 | Vim-dispatch's `:Make` command is similar to Vim's `:make`, but it runs asynchronously. If you are in a Javascript project and you need to run `npm t`, you might attempt to set your makeprg to be: 249 | 250 | ``` 251 | :set makeprg=npm\\ t 252 | ``` 253 | 254 | If you run: 255 | 256 | ``` 257 | :make 258 | ``` 259 | 260 | Vim will execute `npm t`, but you will be staring at the frozen screen while your JavaScript test runs. With vim-dispatch, you can just run: 261 | 262 | ``` 263 | :Make 264 | ``` 265 | 266 | Vim will run `npm t` asynchronously. This way, while `npm t` is running on a background process, you can continue doing whatever you were doing. Awesome! 267 | 268 | ### Async Dispatch 269 | 270 | The `:Dispatch` command is like the `:compiler` and the `:!` command. It can run any external command asynchronously in Vim. 271 | 272 | Assume that you are inside a ruby spec file and you need to run a test. Run: 273 | 274 | ``` 275 | :Dispatch bundle exec rspec % 276 | ``` 277 | 278 | Vim will asynchronously run the `rspec` command against the current file (`%`). 279 | 280 | ### Automating Dispatch 281 | 282 | Vim-dispatch has `b:dispatch` buffer variable that you can configure to evaluate specific command automatically. You can leverage it with `autocmd`. If you add this in your vimrc: 283 | 284 | ``` 285 | autocmd BufEnter *_spec.rb let b:dispatch = 'bundle exec rspec %' 286 | ``` 287 | 288 | Now each time you enter a file (`BufEnter`) that ends with `_spec.rb`, running `:Dispatch` automatically executes `bundle exec rspec {your-current-ruby-spec-file}`. 289 | 290 | ## Learn Compile The Smart Way 291 | 292 | In this chapter, you learned that you can use the `make` and `compiler` commands to run *any* process from inside Vim asynchronously to complement your programming workflow. Vim's ability to extend itself with other programs makes it powerful. 293 | 294 | -------------------------------------------------------------------------------- /ch20_views_sessions_viminfo.md: -------------------------------------------------------------------------------- 1 | # Ch20. Views, Sessions, And Viminfo 2 | 3 | After you worked on a project for a while, you may find the project to gradually take shape with its own settings, folds, buffers, layouts, etc. It's like decorating your apartment after living in it for a while. The problem is, when you close Vim, you lose those changes. Wouldn't it be nice if you can keep those changes so the next time you open Vim, it looks just like you had never left? 4 | 5 | In this chapter, you will learn how use View, Session, and Viminfo to preserve a "snapshot" of your projects. 6 | 7 | ## View 8 | 9 | A View is the smallest subset of the three (View, Session, Viminfo). It is a collection of settings for one window. If you spend a long time working on a window and you want to preserve the maps and folds, you can use a View. 10 | 11 | Let's create a file called `foo.txt`: 12 | 13 | ``` 14 | foo1 15 | foo2 16 | foo3 17 | foo4 18 | foo5 19 | foo6 20 | foo7 21 | foo8 22 | foo9 23 | foo10 24 | ``` 25 | 26 | In this file, create three changes: 27 | 1. On line 1, create a manual fold `zf4j` (fold the next 4 lines). 28 | 2. Change the `number` setting: `setlocal nonumber norelativenumber`. This will remove the number indicators on the left side of the window. 29 | 3. Create a local mapping to go down two lines each time you press `j` instead of one: `:nnoremap j jj`. 30 | 31 | Your file should look like this: 32 | 33 | ``` 34 | +-- 5 lines: foo1 ----- 35 | foo6 36 | foo7 37 | foo8 38 | foo9 39 | foo10 40 | ``` 41 | 42 | ### Configuring View Attributes 43 | 44 | Run: 45 | 46 | ``` 47 | :set viewoptions? 48 | ``` 49 | 50 | By default it should say (yours may look different depending on your vimrc): 51 | 52 | ``` 53 | viewoptions=folds,cursor,curdir 54 | ``` 55 | 56 | Let's configure `viewoptions`. The three attributes you want to preserve are the folds, the maps, and the local set options. If your setting looks like mine, you already have the `folds` option. You need to tell View to remember the `localoptions`. Run: 57 | 58 | ``` 59 | :set viewoptions+=localoptions 60 | ``` 61 | 62 | To learn what other options are available for `viewoptions`, check out `:h viewoptions`. Now if you run `:set viewoptions?`, you should see: 63 | 64 | ``` 65 | viewoptions=folds,cursor,curdir,localoptions 66 | ``` 67 | 68 | ### Saving The View 69 | 70 | With the `foo.txt` window properly folded and having `nonumber norelativenumber` options, let's save the View. Run: 71 | 72 | ``` 73 | :mkview 74 | ``` 75 | 76 | Vim creates a View file. 77 | 78 | ### View Files 79 | 80 | You might wonder, "Where did Vim save this View file?" To see where Vim saves it, run: 81 | 82 | ``` 83 | :set viewdir? 84 | ``` 85 | 86 | The default should say `~/.vim/view` (if you have a different OS, it might show a different path. Check out `:h viewdir` for more). If you want to change it to a different path, add this into your vimrc: 87 | 88 | ``` 89 | set viewdir=$HOME/else/where 90 | ``` 91 | 92 | ### Loading The View File 93 | 94 | Close the `foo.txt` if you haven't, then open `foo.txt` again. **You should see the original text without the changes.** That's expected. 95 | 96 | To restore the state, you need to load the View file. Run: 97 | 98 | ``` 99 | :loadview 100 | ``` 101 | 102 | Now you should see: 103 | 104 | ``` 105 | +-- 5 lines: foo1 ----- 106 | foo6 107 | foo7 108 | foo8 109 | foo9 110 | foo10 111 | ``` 112 | 113 | The folds, local settings, and local mappings are restored. If you notice, your cursor should also be on the line where you left it when you ran `:mkview`. As long as you have the `cursor` option, View also remembers your cursor position. 114 | 115 | ### Multiple Views 116 | 117 | Vim lets you save 9 numbered Views (1-9). 118 | 119 | Suppose you want to make an additional fold (say you want to fold the last two lines) with `:9,10 fold`. Let's save this as View 1. Run: 120 | 121 | ``` 122 | :mkview 1 123 | ``` 124 | 125 | If you want to make one more fold with `:6,7 fold` and save it as a different View, run: 126 | 127 | ``` 128 | :mkview 2 129 | ``` 130 | 131 | Close the file. When you open `foo.txt` and you want to load View 1, run: 132 | 133 | ``` 134 | :loadview 1 135 | ``` 136 | 137 | To load View 2, run: 138 | 139 | ``` 140 | :loadview 2 141 | ``` 142 | 143 | To load the original View, run: 144 | 145 | ``` 146 | :loadview 147 | ``` 148 | 149 | ### Automating View Creation 150 | 151 | One of the worst things that can happen is, after spending countless hours organizing a large file with folds, you accidentally close the window and lose all fold information. To prevent this, you might want to automatically create a View each time you close a buffer. Add this in your vimrc: 152 | 153 | ``` 154 | autocmd BufWinLeave *.txt mkview 155 | ``` 156 | 157 | Additionally, it might be nice to load View when you open a buffer: 158 | 159 | ``` 160 | autocmd BufWinEnter *.txt silent loadview 161 | ``` 162 | 163 | Now you don't have to worry about creating and loading View anymore when you are working with `txt` files. Keep in mind that over time, your `~/.vim/view` might start to accumulate View files. It's good to clean it up once every few months. 164 | 165 | ## Sessions 166 | 167 | If a View saves the settings of a window, a Session saves the information of all windows (including the layout). 168 | 169 | ### Creating A New Session 170 | 171 | Suppose you are working with these 3 files in a `foobarbaz` project: 172 | 173 | Inside `foo.txt`: 174 | 175 | ``` 176 | foo1 177 | foo2 178 | foo3 179 | foo4 180 | foo5 181 | foo6 182 | foo7 183 | foo8 184 | foo9 185 | foo10 186 | ``` 187 | 188 | Inside `bar.txt`: 189 | 190 | ``` 191 | bar1 192 | bar2 193 | bar3 194 | bar4 195 | bar5 196 | bar6 197 | bar7 198 | bar8 199 | bar9 200 | bar10 201 | ``` 202 | 203 | Inside `baz.txt`: 204 | 205 | ``` 206 | baz1 207 | baz2 208 | baz3 209 | baz4 210 | baz5 211 | baz6 212 | baz7 213 | baz8 214 | baz9 215 | baz10 216 | ``` 217 | 218 | Let's say that your windows layout look like the following (using strategically placed `split` and `vsplit`): 219 | 220 | ![Session Layout](images/session-layout.png) 221 | 222 | To preserve this look, you need to save the Session. Run: 223 | 224 | ``` 225 | :mksession 226 | ``` 227 | 228 | Unlike `mkview` where it saves to `~/.vim/view` by default, `mksession` saves a Session file (`Session.vim`) in the current directory. Check out the file if you're curious what's inside. 229 | 230 | If you want to save the Session file somewhere else, you can pass an argument to `mksession`: 231 | 232 | ``` 233 | :mksession ~/some/where/else.vim 234 | ``` 235 | 236 | If you want to overwrite the existing Session file, call the command with a `!` (`:mksession! ~/some/where/else.vim`). 237 | 238 | ### Loading A Session 239 | 240 | To load a Session, run: 241 | 242 | ``` 243 | :source Session.vim 244 | ``` 245 | 246 | Now Vim looks like just the way you left it! Alternatively, you can also load a Session file from the terminal: 247 | 248 | ``` 249 | vim -S Session.vim 250 | ``` 251 | 252 | ### Configuring Session Attributes 253 | 254 | You can configure the attributes Session saves. To see what is currently being saved, run: 255 | 256 | ``` 257 | :set sessionoptions? 258 | ``` 259 | 260 | Mine says: 261 | 262 | ``` 263 | blank,buffers,curdir,folds,help,tabpages,winsize,terminal 264 | ``` 265 | 266 | If you don't want to save `terminal` when you save a Session, remove it from the session options. Run: 267 | 268 | ``` 269 | :set sessionoptions-=terminal 270 | ``` 271 | 272 | If you want to add an `options` when you save a Session, run: 273 | 274 | ``` 275 | :set sessionoptions+=options 276 | ``` 277 | 278 | Here are some attributes that `sessionoptions` can store: 279 | - `blank` stores empty windows 280 | - `buffers` stores buffers 281 | - `folds` stores folds 282 | - `globals` stores global variables (must start with an uppercase letter and contain at least one lowercase letter) 283 | - `options` stores options and mappings 284 | - `resize` stores window lines and columns 285 | - `winpos` stores window position 286 | - `winsize` stores window sizes 287 | - `tabpages` stores tabs 288 | - `unix` stores files in Unix format 289 | 290 | For the complete list check out `:h 'sessionoptions'`. 291 | 292 | Session is a useful tool to preserve your project's external attributes. However, some internal attributes aren't saved by Session, like local marks, registers, histories, etc. To save them, you need to use Viminfo! 293 | 294 | ## Viminfo 295 | 296 | If you notice, after yanking a word into register a and quitting Vim, the next time you open Vim you still that text stored in the register. This is actually a work of Viminfo. Without it, Vim won't remember the register after you close Vim. 297 | 298 | If you use Vim 8 or higher, Vim enables Viminfo by default, so you may have been using Viminfo this whole time without knowing it! 299 | 300 | You might ask: "What does Viminfo save? How does it differ from Session?" 301 | 302 | To use Viminfo, first you need to have `+viminfo` feature available (`:version`). Viminfo stores: 303 | - The command-line history. 304 | - The search string history. 305 | - The input-line history. 306 | - Contents of non-empty registers. 307 | - Marks for several files. 308 | - File marks, pointing to locations in files. 309 | - Last search / substitute pattern (for 'n' and '&'). 310 | - The buffer list. 311 | - Global variables. 312 | 313 | In general, Session stores the "external" attributes and Viminfo the "internal" attributes. 314 | 315 | Unlike Session where you can have one Session file per project, you normally will use one Viminfo file per computer. Viminfo is project-agnostic. 316 | 317 | The default Viminfo location for Unix is `$HOME/.viminfo` (`~/.viminfo`). If you use a different OS, your Viminfo location might be different. Check out `:h viminfo-file-name`. Each time you make "internal" changes, like yanking a text into a register, Vim automatically updates the Viminfo file. 318 | 319 | *Make sure that you have `nocompatible` option set (`set nocompatible`), otherwise your Viminfo will not work.* 320 | 321 | ### Writing And Reading Viminfo 322 | 323 | Although you will use only one Viminfo file, you can create multiple Viminfo files. To write a Viminfo file, use the `:wviminfo` command (`:wv` for short). 324 | 325 | ``` 326 | :wv ~/.viminfo_extra 327 | ``` 328 | 329 | To overwrite an existing Viminfo file, add a bang to the `wv` command: 330 | 331 | ``` 332 | :wv! ~/.viminfo_extra 333 | ``` 334 | 335 | By default Vim will read from `~/.viminfo` file. To read from a different Viminfo file, run `:rviminfo`, or `:rv` for short: 336 | 337 | ``` 338 | :rv ~/.viminfo_extra 339 | ``` 340 | 341 | To start Vim with a different Viminfo file from the terminal, use the `i` flag: 342 | 343 | ``` 344 | vim -i viminfo_extra 345 | ``` 346 | 347 | If you use Vim for different tasks, like coding and writing, you can create a Viminfo optimized for writing and another for coding. 348 | 349 | ``` 350 | vim -i viminfo_writing 351 | 352 | vim -i viminfo_coding 353 | ``` 354 | 355 | ### Starting Vim Without Viminfo 356 | 357 | To start Vim without Viminfo, you can run from the terminal: 358 | 359 | ``` 360 | vim -i NONE 361 | ``` 362 | 363 | To make it permanent, you can add this in your vimrc file: 364 | 365 | ``` 366 | set viminfo="NONE" 367 | ``` 368 | 369 | ### Configuring Viminfo Attributes 370 | 371 | Similar to `viewoptions` and `sessionoptions`, you can instruct what attributes to save with the `viminfo` option. Run: 372 | 373 | ``` 374 | :set viminfo? 375 | ``` 376 | 377 | You will get: 378 | 379 | ``` 380 | !,'100,<50,s10,h 381 | ``` 382 | 383 | This looks cryptic. Let's break it down: 384 | - `!` saves global variables that start with an uppercase letter and don't contain lowercase letters. Recall that `g:` indicates a global variable. For example, if at some point you wrote the assignment `let g:FOO = "foo"`, Viminfo will save the global variable `FOO`. However if you did `let g:Foo = "foo"`, Viminfo will not save this global variable because it contains lowercase letters. Without `!`, Vim won't safe those global variables. 385 | - `'100` represents marks. In this case, Viminfo will save the local marks (a-z) of the last 100 files. Be aware that if you tell Viminfo to save too many files, Vim can start slowing down. 1000 is a good number to have. 386 | - `<50` tells Viminfo how many maximum lines are saved for each registers (50 in this case). If I yank 100 lines of text into register a (`"ay99j`) and close Vim, the next time I open Vim and paste from register a (`"ap`), Vim will only paste 50 lines max. If you don't give maximum line number, *all* lines will be saved. If you give it 0, nothing will be saved. 387 | - `s10` sets a size limit (in kb) for a register. In this case, any register greater than 10kb size will be excluded. 388 | - `h` disables highlighting (from `hlsearch`) when Vim starts. 389 | 390 | There are other options that you can pass. To learn more, check out `:h 'viminfo'`. 391 | 392 | ## Using Views, Sessions, And Viminfo The Smart Way 393 | 394 | Vim has View, Session, and Viminfo to take different level of your Vim environment snapshots. For micro projects, use Views. For larger projects, use Sessions. You should take your time to check out all the options that View, Session, and Viminfo offers. 395 | 396 | Create your own View, Session, and Viminfo for your own editing style. If you ever need to use Vim outside of your computer, you can just load your settings and you will immediately feel at home! 397 | -------------------------------------------------------------------------------- /ch22_vim_packages.md: -------------------------------------------------------------------------------- 1 | # Ch22. Vim Packages 2 | 3 | In the previous chapter, I mentioned using an external plugin manager to install plugins. Since version 8, Vim comes with its own built-in plugin manager called *packages*. In this chapter, you will learn how to use Vim packages to install plugins. 4 | 5 | To see if your Vim build has the ability to use packages, run `:version` and look for `+packages` attribute. Alternatively, you can also run `:echo has('packages')` (if it returns 1, then it has the packages ability). 6 | 7 | ## Pack Directory 8 | 9 | Check if you have a `~/.vim/` directory in the root path. If you don't, create one. Inside it, create a directory called `pack` (`~/.vim/pack/)`. Vim automatically knows to search inside this directory for packages. 10 | 11 | ## Two Types Of Loading 12 | 13 | Vim package has two loading mechanisms: automatic and manual loading. 14 | 15 | ### Automatic Loading 16 | 17 | To load plugins automatically when Vim starts, you need to put them in the `start/` directory. The path looks like this: 18 | 19 | ``` 20 | ~/.vim/pack/*/start/ 21 | ``` 22 | 23 | Now you may ask, "What is the `*` between `pack/` and `start/`?" `*` is an arbitrary name and can be anything you want. let's name it `packdemo/`: 24 | 25 | ``` 26 | ~/.vim/pack/packdemo/start/ 27 | ``` 28 | 29 | Keep in mind that if you skip it and do something like this instead: 30 | 31 | ``` 32 | ~/.vim/pack/start/ 33 | ``` 34 | 35 | The package system won't work. It is imperative to put a name between `pack/` and `start/`. 36 | 37 | For this demo, let's try to install the [NERDTree](https://github.com/preservim/nerdtree) plugin. Go all the way to the `start/` directory (`cd ~/.vim/pack/packdemo/start/`) and clone the NERDTree repository: 38 | 39 | ``` 40 | git clone https://github.com/preservim/nerdtree.git 41 | ``` 42 | 43 | That's it! You are all set. The next time you start Vim, you can immediately execute NERDTree commands like `:NERDTreeToggle`. 44 | 45 | You can clone as many plugin repositories as you want inside the `~/.vim/pack/*/start/` path. Vim will automatically load each one. If you remove the cloned repository (`rm -rf nerdtree/`), that plugin will not be available anymore. 46 | 47 | ### Manual Loading 48 | 49 | To load plugins manually when Vim starts, you need to put them in the `opt/` directory. Similar to automatic loading, the path looks like this: 50 | 51 | ``` 52 | ~/.vim/pack/*/opt/ 53 | ``` 54 | 55 | Let's use the same `packdemo/` directory from earlier: 56 | 57 | ``` 58 | ~/.vim/pack/packdemo/opt/ 59 | ``` 60 | 61 | This time, let's install the [killersheep](https://github.com/vim/killersheep) game (this requires Vim 8.2). Go to the `opt/` directory (`cd ~/.vim/pack/packdemo/opt/`) and clone the repository: 62 | 63 | ``` 64 | git clone https://github.com/vim/killersheep.git 65 | ``` 66 | 67 | Start Vim. The command to execute the game is `:KillKillKill`. Try running it. Vim will complain that it is not a valid editor command. You need to *manually* load the plugin first. Let's do that: 68 | 69 | ``` 70 | :packadd killersheep 71 | ``` 72 | 73 | Now try running the command again `:KillKillKill`. The command should work now. 74 | 75 | You may wonder, "Why would I ever want to manually load packages? Isn't it better to automatically load everything at the start?" 76 | 77 | Great question. Sometimes there are plugins that you won't use all the time, like that KillerSheep game. You probably don't need to load 10 different games and slow down Vim startup time. However, once in a while, when you are bored, you might want to play a few games. Use manual loading for nonessential plugins. 78 | 79 | You can also use this to conditionally add plugins. Maybe you use both Neovim and Vim and there are plugins optimized for Neovim. You can add something like this in your vimrc: 80 | 81 | ``` 82 | if has('nvim') 83 | packadd! neovim-only-plugin 84 | else 85 | packadd! generic-vim-plugin 86 | endif 87 | ``` 88 | 89 | ## Organizing packages 90 | 91 | Recall that the requirement to use Vim's package system is to have either: 92 | 93 | ``` 94 | ~/.vim/pack/*/start/ 95 | ``` 96 | 97 | Or: 98 | 99 | ``` 100 | ~/.vim/pack/*/opt/ 101 | ``` 102 | 103 | The fact that `*` can be *any* name can be used to organize your packages. Suppose you want to group your plugins based on categories (colors, syntax, and games): 104 | 105 | ``` 106 | ~/.vim/pack/colors/ 107 | ~/.vim/pack/syntax/ 108 | ~/.vim/pack/games/ 109 | ``` 110 | 111 | You can still use `start/` and `opt/` inside each of the directories. 112 | 113 | ``` 114 | ~/.vim/pack/colors/start/ 115 | ~/.vim/pack/colors/opt/ 116 | 117 | ~/.vim/pack/syntax/start/ 118 | ~/.vim/pack/syntax/opt/ 119 | 120 | ~/.vim/pack/games/start/ 121 | ~/.vim/pack/games/opt/ 122 | ``` 123 | 124 | ## Adding Packages The Smart Way 125 | 126 | You may wonder if Vim package will make popular plugin managers like vim-pathogen, vundle.vim, dein.vim, and vim-plug obsolete. 127 | 128 | The answer is, as always, "it depends." 129 | 130 | I still use vim-plug because it makes it easy to add, remove or update plugins. If you use many plugins, it may be more convenient to use plugin managers because it is easy to update many simultaneously. Some plugin managers also offer asynchronous functionalities. 131 | 132 | If you are a minimalist, try out Vim packages. If you a heavy plugin user, you may want to consider using a plugin manager. 133 | -------------------------------------------------------------------------------- /ch23_vim_runtime.md: -------------------------------------------------------------------------------- 1 | # Ch23. Vim Runtime 2 | 3 | In the previous chapters, I mentioned that Vim automatically looks for special paths like `pack/` (Ch. 22) and `compiler/` (Ch. 19) inside the `~/.vim/` directory. These are examples of Vim runtime paths. 4 | 5 | Vim has more runtime paths than these two. In this chapter, you will learn a high-level overview of these runtime paths. The goal of this chapter is to show you when they are called. Knowing this will allow you to understand and customize Vim further. 6 | 7 | ## Runtime Path 8 | 9 | In a Unix machine, one of your Vim runtime paths is `$HOME/.vim/` (if you have a different OS like Windows, your path might be different). To see what the runtime paths for different OS are, check out `:h 'runtimepath'`. In this chapter, I will use `~/.vim/` as the default runtime path. 10 | 11 | ## Plugin Scripts 12 | 13 | Vim has a plugin runtime path that executes any scripts in this directory once each time Vim starts. Do not confuse the name "plugin" with Vim external plugins (like NERDTree, fzf.vim, etc). 14 | 15 | Go to `~/.vim/` directory and create a `plugin/` directory. Create two files: `donut.vim` and `chocolate.vim`. 16 | 17 | Inside `~/.vim/plugin/donut.vim`: 18 | 19 | ``` 20 | echo "donut!" 21 | ``` 22 | 23 | Inside `~/.vim/plugin/chocolate.vim`: 24 | 25 | ``` 26 | echo "chocolate!" 27 | ``` 28 | 29 | Now close Vim. The next time you start Vim, you will see both `"donut!"` and `"chocolate!"` echoed. The plugin runtime path can be used for initializations scripts. 30 | 31 | ## Filetype Detection 32 | 33 | Before you start, to ensure that these detections work, make sure that your vimrc contains at least the following line: 34 | 35 | ``` 36 | filetype plugin indent on 37 | ``` 38 | 39 | Check out `:h filetype-overview` for more context. Essentially this turns on Vim's filetype detection. 40 | 41 | When you open a new file, Vim usually knows what kind of file it is. If you have a file `hello.rb`, running `:set filetype?` returns the correct response `filetype=ruby`. 42 | 43 | Vim knows how to detect "common" file types (Ruby, Python, Javascript, etc). But what if you have a custom file? You need to teach Vim to detect it and assign it with the correct file type. 44 | 45 | There are two methods of detection: using file name and file content. 46 | 47 | ### File Name Detection 48 | 49 | File name detection detects a file type using the name of that file. When you open the `hello.rb` file, Vim knows it is a Ruby file from the `.rb` extension. 50 | 51 | There are two ways you can do file name detection: using `ftdetect/` runtime directory and using `filetype.vim` runtime file. Let's explore both. 52 | 53 | #### `ftdetect/` 54 | 55 | Let's create an obscure (yet tasty) file, `hello.chocodonut`. When you open it and you run `:set filetype?`, since it is not a common file name extension Vim doesn't know what to make of it. It returns `filetype=`. 56 | 57 | You need to instruct Vim to set all files ending with `.chocodonut` as a "chocodonut" file type. Create a directory named `ftdetect/` in the runtime root (`~/.vim/`). Inside, create a file and name it `chocodonut.vim` (`~/.vim/ftdetect/chocodonut.vim`). Inside this file, add: 58 | 59 | ``` 60 | autocmd BufNewFile,BufRead *.chocodonut set filetype=chocodonut 61 | ``` 62 | 63 | `BufNewFile` and `BufRead` are triggered whenever you create a new buffer and open a new buffer. `*.chocodonut` means that this event will only be triggered if the opened buffer has a `.chocodonut` filename extension. Finally, `set filetype=chocodonut` command sets the file type to be a chocodonut type. 64 | 65 | Restart Vim. Now open `hello.chocodonut` file and run `:set filetype?`. It returns `filetype=chocodonut`. 66 | 67 | Scrumptious! You can put as many files as you want inside `ftdetect/`. In the future, you can maybe add `ftdetect/strawberrydonut.vim`, `ftdetect/plaindonut.vim`, etc., if you ever decide to expand your donut file types. 68 | 69 | There are actually two ways to set a file type in Vim. One is what you just used `set filetype=chocodonut`. The other way is to run `setfiletype chocodonut`. The former command `set filetype=chocodonut` will *always* set the file type to chocodonut type, while the latter command `setfiletype chocodonut` will only set the file type if no file type was set yet. 70 | 71 | #### Filetype File 72 | 73 | The second file detection method requires you to create a `filetype.vim` in the root directory (`~/.vim/filetype.vim`). Add this inside: 74 | 75 | ``` 76 | autocmd BufNewFile,BufRead *.plaindonut set filetype=plaindonut 77 | ``` 78 | 79 | Create a `hello.plaindonut` file. When you open it and run `:set filetype?`, Vim displays the correct custom file type `filetype=plaindonut`. 80 | 81 | Holy pastry, it works! By the way, if you play around with `filetype.vim`, you may notice that this file is being run multiple times when you open `hello.plaindonut`. To prevent this, you can add a guard so the main script is run only once. Update the `filetype.vim`: 82 | 83 | ``` 84 | if exists("did_load_filetypes") 85 | finish 86 | endif 87 | 88 | augroup donutfiletypedetection 89 | autocmd! BufRead,BufNewFile *.plaindonut setfiletype plaindonut 90 | augroup END 91 | ``` 92 | 93 | `finish` is a Vim command to stop running the rest of the script. The `"did_load_filetypes"` expression is *not* a built-in Vim function. It is actually a global variable from inside `$VIMRUNTIME/filetype.vim`. If you're curious, run `:e $VIMRUNTIME/filetype.vim`. You will find these lines inside: 94 | 95 | ``` 96 | if exists("did_load_filetypes") 97 | finish 98 | endif 99 | 100 | let did_load_filetypes = 1 101 | ``` 102 | 103 | When Vim calls this file, it defines `did_load_filetypes` variable and sets is to 1. 1 is truthy in Vim. You should read the rest of the `filetype.vim` too. See if you can understand what it does when Vim calls it. 104 | 105 | ### File Type Script 106 | 107 | Let's learn how to detect and assign a file type based on the file content. 108 | 109 | Suppose you have a collection of files without an agreeable extension. The only thing these files have in common is that they all start with the word "donutify" on the first line. You want to assign these files to a `donut` file type. Create new files named `sugardonut`, `glazeddonut`, and `frieddonut` (without extension). Inside each file, add this line: 110 | 111 | ``` 112 | donutify 113 | ``` 114 | 115 | When you run the `:set filetype?` from inside `sugardonut`, Vim doesn't know what file type to assign this file with. It returns `filetype=`. 116 | 117 | In the runtime root path, add a `scripts.vim` file (`~/.vim/scripts.vim`). Inside it, add these: 118 | 119 | ``` 120 | if did_filetype() 121 | finish 122 | endif 123 | 124 | if getline(1) =~ '^\\' 125 | setfiletype donut 126 | endif 127 | ``` 128 | 129 | The function `getline(1)` returns the text on the first line. It checks if the first line starts with the word "donutify". The function `did_filetype()` is a Vim built-in function. It will return true when a file type related event is triggered at least once. It is used as a guard to stop re-running file type event. 130 | 131 | Open the `sugardonut` file and run `:set filetype?`, Vim now returns `filetype=donut`. If you open another donut files (`glazeddonut` and `frieddonut`), Vim also identifies their file types as `donut` types. 132 | 133 | Note that `scripts.vim` is only run when Vim opens a file with an unknown file type. If Vim opens a file with a known file type, `scripts.vim` won't run. 134 | 135 | ## File Type Plugin 136 | 137 | What if you want Vim to run chocodonut-specific scripts when you open a chocodonut file and to not run those scripts when opening plaindonut file? 138 | 139 | You can do this with file type plugin runtime path (`~/.vim/ftplugin/`). Vim looks inside this directory for a file with the same name as the file type you just opened. Create a `chocodonut.vim` (`~/.vim/ftplugin/chocodonut.vim`): 140 | 141 | ``` 142 | echo "Calling from chocodonut ftplugin" 143 | ``` 144 | 145 | Create another ftplugin file, `plaindonut.vim` (`~/.vim/ftplugin/plaindonut.vim`): 146 | 147 | ``` 148 | echo "Calling from plaindonut ftplugin" 149 | ``` 150 | 151 | Now each time you open a chocodonut file type, Vim runs the scripts from `~/.vim/ftplugin/chocodonut.vim`. Each time you open a plaindonut file type, Vim runs the scripts from `~/.vim/ftplugin/plaindonut.vim`. 152 | 153 | One warning: these files are run each time a buffer file type is set (`set filetype=chocodonut` for example). If you open 3 different chocodonut files, the scripts will be run a *total* of three times. 154 | 155 | ## Indent Files 156 | 157 | Vim has an indent runtime path that works similar to ftplugin, where Vim looks for a file named the same as the opened file type. The purpose of these indent runtime paths is to store indent-related codes. If you the file `~/.vim/indent/chocodonut.vim`, it will be executed only when you open a chocodonut file type. You can store indent-related codes for chocodonut files here. 158 | 159 | ## Colors 160 | 161 | Vim has a colors runtime path (`~/.vim/colors/`) to store color schemes. Any file that goes inside the directory will be displayed in the `:color` command-line command. 162 | 163 | If you have a `~/.vim/colors/beautifulprettycolors.vim` file, when you run `:color` and press tab, you will see `beautifulprettycolors` as one of the color options. If you prefer to add your own color scheme, this is the place to go. 164 | 165 | If you want to check out the color schemes other people made, a good place to visit is [vimcolors](https://vimcolors.com/). 166 | 167 | ## Syntax Highlighting 168 | 169 | Vim has a syntax runtime path (`~/.vim/syntax/`) to define syntax highlighting. 170 | 171 | Suppose you have a `hello.chocodonut` file, inside it you have the following expressions: 172 | 173 | ``` 174 | (donut "tasty") 175 | (donut "savory") 176 | ``` 177 | 178 | Although Vim now knows the correct file type, all texts have the same color. Let's add a syntax highlighting rule to highlight the "donut" keyword. Create a new chocodonut syntax file, `~/.vim/syntax/chocodonut.vim`. Inside it add: 179 | 180 | ``` 181 | syntax keyword donutKeyword donut 182 | 183 | highlight link donutKeyword Keyword 184 | ``` 185 | 186 | Now reopen `hello.chocodonut` file. The `donut` keywords are now highlighted. 187 | 188 | This chapter won't go over syntax highlighting in depth. It is a vast topic. If you are curious, check out `:h syntax.txt`. 189 | 190 | The [vim-polyglot](https://github.com/sheerun/vim-polyglot) plugin is a great plugin that provides highlights for many popular programming languages. 191 | 192 | ## Documentation 193 | 194 | If you create a plugin, you will have to create your own documentation. You use the doc runtime path for that. 195 | 196 | Let's create a basic documentation for chocodonut and plaindonut keywords. Create a `donut.txt` (`~/.vim/doc/donut.txt`). Inside, add these texts: 197 | 198 | ``` 199 | *chocodonut* Delicious chocolate donut 200 | 201 | *plaindonut* No choco goodness but still delicious nonetheless 202 | ``` 203 | 204 | If you try to search for `chocodonut` and `plaindonut` (`:h chocodonut` and `:h plaindonut`), you won't find anything. 205 | 206 | First, you need to run `:helptags` to generate new help entries. Run `:helptags ~/.vim/doc/` 207 | 208 | Now if you run `:h chocodonut` and `:h plaindonut`, you will find these new help entries. Notice that the file is now read-only and has a "help" file type. 209 | 210 | ## Lazy Loading Scripts 211 | 212 | All of the runtime paths that you learned in this chapter were run automatically. If you want to manually load a script, use the autoload runtime path. 213 | 214 | Create an autoload directory (`~/.vim/autoload/`). Inside that directory, create a new file and name it `tasty.vim` (`~/.vim/autoload/tasty.vim`). Inside it: 215 | 216 | ``` 217 | echo "tasty.vim global" 218 | 219 | function tasty#donut() 220 | echo "tasty#donut" 221 | endfunction 222 | ``` 223 | 224 | Note that the function name is `tasty#donut`, not `donut()`. The pound sign (`#`) is required when using the autoload feature. The function naming convention for the autoload feature is: 225 | 226 | ``` 227 | function fileName#functionName() 228 | ... 229 | endfunction 230 | ``` 231 | 232 | In this case, the file name is `tasty.vim` and the function name is (technically) `donut`. 233 | 234 | To invoke a function, you need the `call` command. Let's call that function with `:call tasty#donut()`. 235 | 236 | The first time you call the function, you should see *both* echo messages ("tasty.vim global" and "tasty#donut"). The subsequent calls to` tasty#donut` function will only display "testy#donut" echo. 237 | 238 | When you open a file in Vim, unlike the previous runtime paths, autoload scripts aren't loaded automatically. Only when you explicitly call `tasty#donut()`, Vim looks for the `tasty.vim` file and loads everything inside it, including the `tasty#donut()` function. Autoload is the perfect mechanism for functions that use extensive resources but you don't use often. 239 | 240 | You can add as many nested directories with autoload as you want. If you have the runtime path `~/.vim/autoload/one/two/three/tasty.vim`, you can call the function with `:call one#two#three#tasty#donut()`. 241 | 242 | ## After Scripts 243 | 244 | Vim has an after runtime path (`~/.vim/after/`) that mirrors the structure of `~/.vim/`. Anything in this path is executed last, so developers usually use these paths for script overrides. 245 | 246 | For example, if you want to overwrite the scripts from `plugin/chocolate.vim`, you can create `~/.vim/after/plugin/chocolate.vim` to put the override scripts. Vim will run the `~/.vim/after/plugin/chocolate.vim` *after* `~/.vim/plugin/chocolate.vim`. 247 | 248 | ## $VIMRUNTIME 249 | 250 | Vim has an environment variable `$VIMRUNTIME` for default scripts and support files. You can check it out by running `:e $VIMRUNTIME`. 251 | 252 | The structure should look familiar. It contains many runtime paths you learned in this chapter. 253 | 254 | Recall in Chapter 21, you learned that when you open Vim, it looks for a vimrc files in seven different locations. I said that the last location Vim checks is `$VIMRUNTIME/default.vim`. If Vim fails to find any uservimrc files, Vim uses a `default.vim` as vimrc. 255 | 256 | Have you ever tried running Vim without syntax plugin like vim-polyglot and yet your file is still syntatically highlighted? That is because when Vim fails to find a syntax file from the runtime path, Vim looks for a syntax file from `$VIMRUNTIME` syntax directory. 257 | 258 | To learn more, check out `:h $VIMRUNTIME`. 259 | 260 | ## Runtimepath Option 261 | 262 | To check your runtimepath, run `:set runtimepath?` 263 | 264 | If you use Vim-Plug or popular external plugin managers, it should display a list of directories. For example, mine shows: 265 | 266 | ``` 267 | runtimepath=~/.vim,~/.vim/plugged/vim-signify,~/.vim/plugged/base16-vim,~/.vim/plugged/fzf.vim,~/.vim/plugged/fzf,~/.vim/plugged/vim-gutentags,~/.vim/plugged/tcomment_vim,~/.vim/plugged/emmet-vim,~/.vim/plugged/vim-fugitive,~/.vim/plugged/vim-sensible,~/.vim/plugged/lightline.vim, ... 268 | ``` 269 | 270 | One of the things plugin managers does is adding each plugin into the runtime path. Each runtime path can have its own directory structure similar to `~/.vim/`. 271 | 272 | If you have a directory `~/box/of/donuts/` and you want to add that directory to your runtime path, you can add this to your vimrc: 273 | 274 | ``` 275 | set rtp+=$HOME/box/of/donuts/ 276 | ``` 277 | 278 | If inside `~/box/of/donuts/`, you have a plugin directory (`~/box/of/donuts/plugin/hello.vim`) and a ftplugin (`~/box/of/donuts/ftplugin/chocodonut.vim`), Vim will run all scripts from `plugin/hello.vim` when you open Vim. Vim will also run `ftplugin/chocodonut.vim` when you open a chocodonut file. 279 | 280 | Try this yourself: create an arbitrary path and add it to your runtimepath. Add some of the runtime paths you learned from this chapter. Make sure they work as expected. 281 | 282 | ## Learn Runtime The Smart Way 283 | 284 | Take your time reading it and play around with these runtime paths. To see how runtime paths are being used in the wild, go to the repository of one of your favorite Vim plugins and study its directory structure. You should be able to understand most of them now. Try to follow along and discern the big picture. Now that you understand Vim directory structure, you're ready to learn Vimscript. 285 | -------------------------------------------------------------------------------- /ch25_vimscript_conditionals_and_loops.md: -------------------------------------------------------------------------------- 1 | # Ch25. Vimscript Conditionals And Loops 2 | 3 | After learning what the basic data types are, the next step is to learn how to combine them together to start writing a basic program. A basic program consists of conditionals and loops. 4 | 5 | In this chapter, you will learn how to use Vimscript data types to write conditionals and loops. 6 | 7 | ## Relational Operators 8 | 9 | Vimscript relational operators are similar to many programming languages: 10 | 11 | ``` 12 | a == b equal to 13 | a != b not equal to 14 | a > b greater than 15 | a >= b greater than or equal to 16 | a < b less than 17 | a <= b less than or equal to 18 | ``` 19 | 20 | For example: 21 | 22 | ``` 23 | :echo 5 == 5 24 | :echo 5 != 5 25 | :echo 10 > 5 26 | :echo 10 >= 5 27 | :echo 10 < 5 28 | :echo 5 <= 5 29 | ``` 30 | 31 | Recall that strings are coerced into numbers in an arithmetic expression. Here Vim also coerces strings into numbers in an equality expression. "5foo" is coerced into 5 (truthy): 32 | 33 | ``` 34 | :echo 5 == "5foo" 35 | " returns true 36 | ``` 37 | 38 | Also recall that if you start a string with a non-numerical character like "foo5", the string is converted into number 0 (falsy). 39 | 40 | ``` 41 | echo 5 == "foo5" 42 | " returns false 43 | ``` 44 | 45 | ### String Logic Operators 46 | 47 | Vim has more relational operators for comparing strings: 48 | 49 | ``` 50 | a =~ b 51 | a !~ b 52 | ``` 53 | 54 | For examples: 55 | 56 | ``` 57 | let str = "hearty breakfast" 58 | 59 | echo str =~ "hearty" 60 | " returns true 61 | 62 | echo str =~ "dinner" 63 | " returns false 64 | 65 | echo str !~ "dinner" 66 | " returns true 67 | ``` 68 | 69 | The `=~` operator performs a regex match against the given string. In the example above, `str =~ "hearty"` returns true because `str` *contains* the "hearty" pattern. You can always use `==` and `!=`, but using them will compare the expression against the entire string. `=~` and `!~` are more flexible choices. 70 | 71 | ``` 72 | echo str == "hearty" 73 | " returns false 74 | 75 | echo str == "hearty breakfast" 76 | " returns true 77 | ``` 78 | 79 | Let's try this one. Note the uppercase "H": 80 | 81 | ``` 82 | echo str =~ "Hearty" 83 | " true 84 | ``` 85 | 86 | It returns true even though "Hearty" is capitalized. Interesting... It turns out that my Vim setting is set to ignore case (`set ignorecase`), so when Vim checks for equality, it uses my Vim setting and ignores the case. If I were to turn off ignore case (`set noignorecase`), the comparison now returns false. 87 | 88 | ``` 89 | set noignorecase 90 | echo str =~ "Hearty" 91 | " returns false because case matters 92 | 93 | set ignorecase 94 | echo str =~ "Hearty" 95 | " returns true because case doesn't matter 96 | ``` 97 | 98 | If you are writing a plugin for others, this is a tricky situation. Does the user use `ignorecase` or `noignorecase`? You definitely do *not* want to force your users to change their ignore case option. So what do you do? 99 | 100 | Luckily, Vim has an operator that can *always* ignore or match case. To always match case, add a `#` at the end. 101 | 102 | ``` 103 | set ignorecase 104 | echo str =~# "hearty" 105 | " returns true 106 | 107 | echo str =~# "HearTY" 108 | " returns false 109 | 110 | set noignorecase 111 | echo str =~# "hearty" 112 | " true 113 | 114 | echo str =~# "HearTY" 115 | " false 116 | 117 | echo str !~# "HearTY" 118 | " true 119 | ``` 120 | 121 | To always ignore case when comparing, append it with `?`: 122 | 123 | ``` 124 | set ignorecase 125 | echo str =~? "hearty" 126 | " true 127 | 128 | echo str =~? "HearTY" 129 | " true 130 | 131 | set noignorecase 132 | echo str =~? "hearty" 133 | " true 134 | 135 | echo str =~? "HearTY" 136 | " true 137 | 138 | echo str !~? "HearTY" 139 | " false 140 | ``` 141 | 142 | I prefer to use `#` to always match the case and be on the safe side. 143 | 144 | ## If 145 | 146 | Now that you have seen Vim's equality expressions, let's touch a fundamental conditional operator, the `if` statement. 147 | 148 | At minimum, the syntax is: 149 | 150 | ``` 151 | if {clause} 152 | {some expression} 153 | endif 154 | ``` 155 | 156 | You can extend the case analysis with `elseif` and `else`. 157 | 158 | ``` 159 | if {predicate1} 160 | {expression1} 161 | elseif {predicate2} 162 | {expression2} 163 | elseif {predicate3} 164 | {expression3} 165 | else 166 | {expression4} 167 | endif 168 | ``` 169 | 170 | For example, the plugin [vim-signify](https://github.com/mhinz/vim-signify) uses a different installation method depending on your Vim settings. Below is the installation instruction from their `readme`, using the `if` statement: 171 | 172 | ``` 173 | if has('nvim') || has('patch-8.0.902') 174 | Plug 'mhinz/vim-signify' 175 | else 176 | Plug 'mhinz/vim-signify', { 'branch': 'legacy' } 177 | endif 178 | ``` 179 | 180 | ## Ternary Expression 181 | 182 | Vim has a ternary expression for a one-liner case analysis: 183 | 184 | ``` 185 | {predicate} ? expressiontrue : expressionfalse 186 | ``` 187 | 188 | For example: 189 | 190 | ``` 191 | echo 1 ? "I am true" : "I am false" 192 | ``` 193 | 194 | Since 1 is truthy, Vim echoes "I am true". Suppose you want to conditionally set the `background` to dark if you are using Vim past a certain hour. Add this to vimrc: 195 | 196 | ``` 197 | let &background = strftime("%H") < 18 ? "light" : "dark" 198 | ``` 199 | 200 | `&background` is the `'background'` option in Vim. `strftime("%H")` returns the current time in hours. If it is not yet 6 PM, use a light background. Otherwise, use a dark background. 201 | 202 | ## Or 203 | 204 | The logical "or" (`||`) works like many programming languages. 205 | 206 | ``` 207 | {Falsy expression} || {Falsy expression} false 208 | {Falsy expression} || {Truthy expression} true 209 | {Truthy expression} || {Falsy expression} true 210 | {Truthy expression} || {Truthy expression} true 211 | ``` 212 | 213 | Vim evaluates the expression and return either 1 (truthy) or 0 (falsy). 214 | 215 | ``` 216 | echo 5 || 0 217 | " returns 1 218 | 219 | echo 5 || 5 220 | " returns 1 221 | 222 | echo 0 || 0 223 | " returns 0 224 | 225 | echo "foo5" || "foo5" 226 | " returns 0 227 | 228 | echo "5foo" || "foo5" 229 | " returns 1 230 | ``` 231 | 232 | If the current expression evaluates to truthy, the subsequent expression won't be evaluated. 233 | 234 | ``` 235 | let one_dozen = 12 236 | 237 | echo one_dozen || two_dozen 238 | " returns 1 239 | 240 | echo two_dozen || one_dozen 241 | " returns error 242 | ``` 243 | 244 | Note that `two_dozen` is never defined. The expression `one_dozen || two_dozen` doesn't throw any error because `one_dozen` is evaluated first found to be truthy, so Vim doesn't evaluate `two_dozen`. 245 | 246 | ## And 247 | 248 | The logical "and" (`&&`) is the complement of the logical or. 249 | 250 | ``` 251 | {Falsy Expression} && {Falsy Expression} false 252 | {Falsy expression} && {Truthy expression} false 253 | {Truthy Expression} && {Falsy Expression} false 254 | {Truthy expression} && {Truthy expression} true 255 | ``` 256 | 257 | For example: 258 | 259 | ``` 260 | echo 0 && 0 261 | " returns 0 262 | 263 | echo 0 && 10 264 | " returns 0 265 | ``` 266 | 267 | Unlike "or", "and" will evaluate the subsequent expression after it reaches the first falsy expression. It will continue to evaluate the subsequent truthy expressions until the end or when it sees the first falsy expression. 268 | 269 | ``` 270 | let one_dozen = 12 271 | echo one_dozen && 10 272 | " returns 1 273 | 274 | echo one_dozen && v:false 275 | " returns 0 276 | 277 | echo one_dozen && two_dozen 278 | " returns error 279 | 280 | echo exists("one_dozen") && one_dozen == 12 281 | " returns 1 282 | ``` 283 | 284 | ## For 285 | 286 | The `for` loop is commonly used with the list data type. 287 | 288 | ``` 289 | let breakfasts = ["pancakes", "waffles", "eggs"] 290 | 291 | for breakfast in breakfasts 292 | echo breakfast 293 | endfor 294 | ``` 295 | 296 | It works with nested list: 297 | 298 | ``` 299 | let meals = [["breakfast", "pancakes"], ["lunch", "fish"], ["dinner", "pasta"]] 300 | 301 | for [meal_type, food] in meals 302 | echo "I am having " . food . " for " . meal_type 303 | endfor 304 | ``` 305 | 306 | You can technically use the `for` loop with a dictionary using the `keys()` method. 307 | 308 | ``` 309 | let beverages = #{breakfast: "milk", lunch: "orange juice", dinner: "water"} 310 | for beverage_type in keys(beverages) 311 | echo "I am drinking " . beverages[beverage_type] . " for " . beverage_type 312 | endfor 313 | ``` 314 | 315 | ## While 316 | 317 | Another common loop is the `while` loop. 318 | 319 | ``` 320 | let counter = 1 321 | while counter < 5 322 | echo "Counter is: " . counter 323 | let counter += 1 324 | endwhile 325 | ``` 326 | 327 | To get the content of the current line to the last line: 328 | 329 | ``` 330 | let current_line = line(".") 331 | let last_line = line("$") 332 | 333 | while current_line <= last_line 334 | echo getline(current_line) 335 | let current_line += 1 336 | endwhile 337 | ``` 338 | 339 | ## Error Handling 340 | 341 | Often your program doesn't run the way you expect it to. As a result, it throws you for a loop (pun intended). What you need is a proper error handling. 342 | 343 | ### Break 344 | 345 | When you use `break` inside a `while` or `for` loop, it stops the loop. 346 | 347 | To get the texts from the start of the file to the current line, but stop when you see the word "donut": 348 | 349 | ``` 350 | let line = 0 351 | let last_line = line("$") 352 | let total_word = "" 353 | 354 | while line <= last_line 355 | let line += 1 356 | let line_text = getline(line) 357 | if line_text =~# "donut" 358 | break 359 | endif 360 | echo line_text 361 | let total_word .= line_text . " " 362 | endwhile 363 | 364 | echo total_word 365 | ``` 366 | 367 | If you have the text: 368 | 369 | ``` 370 | one 371 | two 372 | three 373 | donut 374 | four 375 | five 376 | ``` 377 | 378 | Running the above `while` loop gives "one two three" and not the rest of the text because the loop breaks once it matches "donut". 379 | 380 | ### Continue 381 | 382 | The `continue` method is similar to `break`, where it is invoked during a loop. The difference is that instead of breaking out of the loop, it just skips that current iteration. 383 | 384 | Suppose you have the same text but instead of `break`, you use `continue`: 385 | 386 | ``` 387 | let line = 0 388 | let last_line = line("$") 389 | let total_word = "" 390 | 391 | while line <= last_line 392 | let line += 1 393 | let line_text = getline(line) 394 | if line_text =~# "donut" 395 | continue 396 | endif 397 | echo line_text 398 | let total_word .= line_text . " " 399 | endwhile 400 | 401 | echo total_word 402 | ``` 403 | 404 | This time it returns `one two three four five`. It skips the line with the word "donut", but the loop continues. 405 | 406 | ### Try, Finally, And Catch 407 | 408 | Vim has a `try`, `finally`, and `catch` to handle errors. To simulate an error, you can use the `throw` command. 409 | 410 | ``` 411 | try 412 | echo "Try" 413 | throw "Nope" 414 | endtry 415 | ``` 416 | 417 | Run this. Vim will complain with `"Exception not caught: Nope` error. 418 | 419 | Now add a catch block: 420 | 421 | ``` 422 | try 423 | echo "Try" 424 | throw "Nope" 425 | catch 426 | echo "Caught it" 427 | endtry 428 | ``` 429 | 430 | Now there is no longer any error. You should see "Try" and "Caught it" displayed. 431 | 432 | Let's remove the `catch` and add a `finally`: 433 | 434 | ``` 435 | try 436 | echo "Try" 437 | throw "Nope" 438 | echo "You won't see me" 439 | finally 440 | echo "Finally" 441 | endtry 442 | ``` 443 | 444 | Run this. Now Vim displays the error and "Finally". 445 | 446 | Let's put all of them together: 447 | 448 | ``` 449 | try 450 | echo "Try" 451 | throw "Nope" 452 | catch 453 | echo "Caught it" 454 | finally 455 | echo "Finally" 456 | endtry 457 | ``` 458 | 459 | This time Vim displays both "Caught it" and "Finally". No error is displayed because Vim caught it. 460 | 461 | Errors come from different places. Another source of error is calling a nonexistent function, like `Nope()` below: 462 | 463 | ``` 464 | try 465 | echo "Try" 466 | call Nope() 467 | catch 468 | echo "Caught it" 469 | finally 470 | echo "Finally" 471 | endtry 472 | ``` 473 | 474 | The difference between `catch` and `finally` is that `finally` is always run, error or not, where a catch is only run when your code gets an error. 475 | 476 | You can catch specific error with `:catch`. According to `:h :catch`: 477 | 478 | ``` 479 | catch /^Vim:Interrupt$/. " catch interrupts (CTRL-C) 480 | catch /^Vim\\%((\\a\\+)\\)\\=:E/. " catch all Vim errors 481 | catch /^Vim\\%((\\a\\+)\\)\\=:/. " catch errors and interrupts 482 | catch /^Vim(write):/. " catch all errors in :write 483 | catch /^Vim\\%((\\a\\+)\\)\\=:E123:/ " catch error E123 484 | catch /my-exception/. " catch user exception 485 | catch /.*/ " catch everything 486 | catch. " same as /.*/ 487 | ``` 488 | 489 | Inside a `try` block, an interrupt is considered a catchable error. 490 | 491 | ``` 492 | try 493 | catch /^Vim:Interrupt$/ 494 | sleep 100 495 | endtry 496 | ``` 497 | 498 | In your vimrc, if you use a custom colorscheme, like [gruvbox](https://github.com/morhetz/gruvbox), and you accidentally delete the colorscheme directory but still have the line `colorscheme gruvbox` in your vimrc, Vim will throw an error when you `source` it. To fix this, I added this in my vimrc: 499 | 500 | ``` 501 | try 502 | colorscheme gruvbox 503 | catch 504 | colorscheme default 505 | endtry 506 | ``` 507 | 508 | Now if you `source` vimrc without `gruvbox` directory, Vim will use the `colorscheme default`. 509 | 510 | ## Learn conditionals the smart way 511 | 512 | In the previous chapter, you learned about Vim basic data types. In this chapter, you learned how to combine them to write basic programs using conditionals and loops. These are the building blocks of programming. 513 | 514 | Next, let's learn about variable scopes. 515 | -------------------------------------------------------------------------------- /ch26_vimscript_variables_scopes.md: -------------------------------------------------------------------------------- 1 | # Ch26. Vimscript Variables And Scopes 2 | 3 | Before diving into Vimscript functions, let's learn about the different sources and scopes of Vim variables. 4 | 5 | ## Mutable And Immutable Variables 6 | 7 | You can assign a value to a variable in Vim with `let`: 8 | 9 | ``` 10 | let pancake = "pancake" 11 | ``` 12 | 13 | Later you can call that variable any time. 14 | 15 | ``` 16 | echo pancake 17 | " returns "pancake" 18 | ``` 19 | 20 | `let` is mutable, meaning you can change the value at any time in the future. 21 | 22 | ``` 23 | let pancake = "pancake" 24 | let pancake = "not waffles" 25 | 26 | echo pancake 27 | " returns "not waffles" 28 | ``` 29 | 30 | Notice that when you want to change the value of a set variable, you still need to use `let`. 31 | 32 | ``` 33 | let beverage = "milk" 34 | 35 | beverage = "orange juice" 36 | " throws an error 37 | ``` 38 | 39 | You can define an immutable variable with `const`. Being immutable, once a variable value is assigned, you cannot reassign it with a different value. 40 | 41 | ``` 42 | const waffle = "waffle" 43 | const waffle = "pancake" 44 | " throws an error 45 | ``` 46 | 47 | ## Variable Sources 48 | 49 | There are three sources for variables: environment variable, option variable, and register variable. 50 | 51 | ### Environment Variable 52 | 53 | Vim can access your terminal environment variable. For example, if you have the `SHELL` environment variable available in your terminal, you can access it from Vim with: 54 | 55 | ``` 56 | echo $SHELL 57 | " returns $SHELL value. In my case, it returns /bin/bash 58 | ``` 59 | 60 | ### Option Variable 61 | 62 | You can access Vim options with `&` (these are the settings you access with `set`). 63 | 64 | For example, to see what background Vim uses, you can run: 65 | 66 | ``` 67 | echo &background 68 | " returns either "light" or "dark" 69 | ``` 70 | 71 | Alternatively, you can always run `set background?` to see the value of the `background` option. 72 | 73 | ### Register Variable 74 | 75 | You can access Vim registers (Ch. 08) with `@`. 76 | 77 | Suppose the value "chocolate" is already saved in register a. To access it, you can use `@a`. You can also update it with `let`. 78 | 79 | ``` 80 | echo @a 81 | " returns chocolate 82 | 83 | let @a .= " donut" 84 | 85 | echo @a 86 | " returns "chocolate donut" 87 | ``` 88 | 89 | Now when you paste from register `a` (`"ap`), it will return "chocolate donut". The operator `.=` concatenates two strings. The expression `let @a .= " donut"` is the same as `let @a = @a . " donut"` 90 | 91 | ## Variable Scopes 92 | 93 | There are 9 different variable scopes in Vim. You can recognize them from their prepended letter: 94 | 95 | ``` 96 | g: Global variable 97 | {nothing} Global variable 98 | b: Buffer-local variable 99 | w: Window-local variable 100 | t: Tab-local variable 101 | s: Sourced Vimscript variable 102 | l: Function local variable 103 | a: Function formal parameter variable 104 | v: Built-in Vim variable 105 | ``` 106 | 107 | ### Global variable 108 | 109 | When you are declaring a "regular" variable: 110 | 111 | ``` 112 | let pancake = "pancake" 113 | ``` 114 | 115 | `pancake` is actually a global variable. When you define a global variable, you can call them from anywhere. 116 | 117 | Prepending `g:` to a variable also creates a global variable. 118 | 119 | ``` 120 | let g:waffle = "waffle" 121 | ``` 122 | 123 | In this case both `pancake` and `g:waffle` have the same scope. You can call each of them with or without `g:`. 124 | 125 | ``` 126 | echo pancake 127 | " returns "pancake" 128 | 129 | echo g:pancake 130 | "returns "pancake" 131 | 132 | echo waffle 133 | " returns "waffle" 134 | 135 | echo g:waffle 136 | " returns "waffle" 137 | ``` 138 | 139 | ### Buffer Variable 140 | 141 | A variable preceded with `b:` is a buffer variable. A buffer variable is a variable that is local to the current buffer (Ch. 02). If you have multiple buffers open, each buffer will have their own separate list of buffer variables. 142 | 143 | In buffer 1: 144 | 145 | ``` 146 | const b:donut = "chocolate donut" 147 | ``` 148 | 149 | In buffer 2: 150 | 151 | ``` 152 | const b:donut = "blueberry donut" 153 | ``` 154 | 155 | If you run `echo b:donut` from buffer 1, it will return "chocolate donut". If you run it from buffer 2, it will return "blueberry donut". 156 | 157 | On the side note, Vim has a *special* buffer variable `b:changedtick` that keeps track of all the changes done to the current buffer. 158 | 159 | 1. Run `echo b:changedtick` and note the number it returns.. 160 | 2. Make changes in Vim. 161 | 3. Run `echo b:changedtick` again and note the number it now returns. 162 | 163 | ### Window Variable 164 | 165 | A variable preceded with `w:` is a window variable. It exists only in that window. 166 | 167 | In window 1: 168 | 169 | ``` 170 | const w:donut = "chocolate donut" 171 | ``` 172 | 173 | In window 2: 174 | 175 | ``` 176 | const w:donut = "raspberry donut" 177 | ``` 178 | 179 | On each window, you can call `echo w:donut` to get unique values. 180 | 181 | ### Tab Variable 182 | 183 | A variable preceded with `t:` is a tab variable. It exists only in that tab. 184 | 185 | In tab 1: 186 | 187 | ``` 188 | const t:donut = "chocolate donut" 189 | ``` 190 | 191 | In tab 2: 192 | 193 | ``` 194 | const t:donut = "blackberry donut" 195 | ``` 196 | 197 | On each tab, you can call `echo t:donut` to get unique values. 198 | 199 | ### Script variable 200 | 201 | A variable preceded with `s:` is a script variable. These variables can only be accessed from inside that script. 202 | 203 | If you have an arbitrary file `dozen.vim` and inside it you have: 204 | 205 | ``` 206 | let s:dozen = 12 207 | 208 | function Consume() 209 | let s:dozen -= 1 210 | echo s:dozen " is left" 211 | endfunction 212 | ``` 213 | 214 | Source the file with `:source dozen.vim`. Now call the `Consume` function: 215 | 216 | ``` 217 | :call Consume() 218 | " returns "11 is left" 219 | 220 | :call Consume() 221 | " returns "10 is left" 222 | 223 | :echo s:dozen 224 | " Undefined variable error 225 | ``` 226 | 227 | When you call `Consume`, you see it decrements the `s:dozen` value as expected. When you try to get `s:dozen` value directly, Vim won't find it because you are out of scope. `s:dozen` is only accessible from inside `dozen.vim`. 228 | 229 | Each time you source the `dozen.vim` file, it resets the `s:dozen` counter. If you are in the middle of decrementing `s:dozen` value and you run `:source dozen.vim`, the counter resets back to 12. This can be a problem for unsuspecting users. To fix this issue, refactor the code: 230 | 231 | ``` 232 | if !exists("s:dozen") 233 | let s:dozen = 12 234 | endif 235 | 236 | function Consume() 237 | let s:dozen -= 1 238 | echo s:dozen 239 | endfunction 240 | ``` 241 | 242 | Now when you source `dozen.vim` while in the middle of decrementing, Vim reads `!exists("s:dozen")`, finds that it is true, and doesn't reset the value back to 12. 243 | 244 | ### Function Local And Function Formal Parameter variable 245 | 246 | Both the function local variable (`l:`) and the function formal variable (`a:`) will be covered in the next chapter. 247 | 248 | ### Built-in Vim Variables 249 | 250 | A variable prepended with `v:` is a special built-in Vim variable. You cannot define these variables. You have seen some of them already. 251 | - `v:version` tells you what Vim version you are using. 252 | - `v:key` contains the current item value when iterating through a dictionary. 253 | - `v:val` contains the current item value when running a `map()` or `filter()` operation. 254 | - `v:true`, `v:false`, `v:null`, and `v:none` are special data types. 255 | 256 | There are other variables. For a list of Vim built-in variables, check out `:h vim-variable` or `:h v:`. 257 | 258 | ## Using Vim Variable Scopes The Smart Way 259 | 260 | Being able to quickly access environment, option, and register variables give you a broad flexibility to customize your editor and terminal environment. You also learned that Vim has 9 different variable scopes, each existing under a certain constraints. You can take advantage of these unique variable types to decouple your program. 261 | 262 | You made it this far. You learned about data types, means of combinations, and variable scopes. Only one thing is left: functions. 263 | -------------------------------------------------------------------------------- /images/cartesian-xy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/cartesian-xy.png -------------------------------------------------------------------------------- /images/cartesian-xyz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/cartesian-xyz.png -------------------------------------------------------------------------------- /images/cartesian-z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/cartesian-z.png -------------------------------------------------------------------------------- /images/diffing-apples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/diffing-apples.png -------------------------------------------------------------------------------- /images/diffing-basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/diffing-basic.png -------------------------------------------------------------------------------- /images/fugitive-gdiffsplit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/fugitive-gdiffsplit.png -------------------------------------------------------------------------------- /images/fugitive-git-blame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/fugitive-git-blame.png -------------------------------------------------------------------------------- /images/fugitive-git-log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/fugitive-git-log.png -------------------------------------------------------------------------------- /images/fugitive-git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/fugitive-git.png -------------------------------------------------------------------------------- /images/fzf-files.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/fzf-files.gif -------------------------------------------------------------------------------- /images/fzf-in-files.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/fzf-in-files.gif -------------------------------------------------------------------------------- /images/learn-vim-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/learn-vim-cover.png -------------------------------------------------------------------------------- /images/mergetool-initial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/mergetool-initial.png -------------------------------------------------------------------------------- /images/screen-one-buffer-buffers-command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/screen-one-buffer-buffers-command.png -------------------------------------------------------------------------------- /images/screen-one-buffer-file1-highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/screen-one-buffer-file1-highlighted.png -------------------------------------------------------------------------------- /images/screen-one-buffer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/screen-one-buffer.png -------------------------------------------------------------------------------- /images/screen-split-window-vertically-and-horizontally-two-file2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/screen-split-window-vertically-and-horizontally-two-file2.png -------------------------------------------------------------------------------- /images/screen-split-window-vertically-and-horizontally.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/screen-split-window-vertically-and-horizontally.png -------------------------------------------------------------------------------- /images/screen-split-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/screen-split-window.png -------------------------------------------------------------------------------- /images/screen-tab2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/screen-tab2.png -------------------------------------------------------------------------------- /images/screen-vscode-3-windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/screen-vscode-3-windows.png -------------------------------------------------------------------------------- /images/session-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/session-layout.png -------------------------------------------------------------------------------- /images/tabs-file1js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/tabs-file1js.png -------------------------------------------------------------------------------- /images/tabs-file2js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satya-nutella/Learn-Vim/207fd33011728007874fd75ae029c0c3f57dbb08/images/tabs-file2js.png --------------------------------------------------------------------------------