añadir capítulos 18 y 19 e imágenes desde upstream

This commit is contained in:
Victorhck 2020-10-16 17:53:30 +02:00
parent 733889fe59
commit addde9dd9c
9 changed files with 684 additions and 0 deletions

319
ch18_git.md Normal file
View file

@ -0,0 +1,319 @@
# Git
Vim and git are two great tools for two different things. Git is a version control tool. Vim is a text editor. In this chapter, you will learn different ways to integrate Vim and git together.
# Diffing
In the last chapter, you saw how you can run a `vimdiff` command to show differences between multiple files.
Suppose you have two files, `file1.txt` and `file2.txt`. Inside `file1.txt`:
```
pancakes
waffles
apples
milk
apple juice
yogurt
```
Inside `file2.txt`:
```
pancakes
waffles
oranges
milk
orange juice
yogurt
```
To see the differences between both files, run:
```
vimdiff file1.txt file2.txt
```
Alternatively you could run:
```
vim -d file1.txt file2.txt
```
<p align="center">
<img alt="Basic diffing with Vim" width="900" height="auto" src="./img/diffing-basic.png">
</p>
`vimdiff` displays two buffers side-by-side. On the left is `file1.txt` and on the right is `file2.txt`. The two differences (apples and oranges) are highlighted on both lines.
Suppose you want to make the second buffer to have apples, not oranges. To transfer the content from your current position, `file1.txt`, to `file2.txt`, jump to the next diff with `]c` (to jump to the previous diff, use `[c`). The cursor should be on apples now. Run `:diffput`. Both files should now have apples.
<p align="center">
<img alt="Finding files in FZF" width="900" height="auto" src="./img/diffing-apples.png">
</p>
If you need to transfer the text from the other buffer (orange juice) to replace the text on the current buffer (apple juice), first jump 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.
`:diffput` *puts out* the text from the current buffer to another buffer. `:diffget` *gets* the text from another buffer to the current buffer. If you have multiple buffers, you can run `:diffput fileN.txt` and `:diffget fileN.txt` to target the fileN buffer.
# Vim as a Merge Tool
> "I love resolving merge conflicts!" - Nobody
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.
First, change the default merge tool to use `vimdiff` by running:
```
git config merge.tool vimdiff
git config merge.conflictstyle diff3
git config mergetool.prompt false
```
Alternatively, you can modify the `~/.gitconfig` directly (by default it should be in root, but yours might be in different place). If you haven't already, make your `gitconfig` to look like:
```
[core]
editor = vim
[merge]
tool = vimdiff
conflictstyle = diff3
[difftool]
prompt = false
```
Let's create a fake merge conflict to test this out. Create a directory `/food` and make it a git repository:
```
git init
```
Add a file, `breakfast.txt`. Inside:
```
pancakes
waffles
oranges
```
Add the file and commit it:
```
git add .
git commit -m "Initial breakfast commit"
```
Next, create a new branch and call it apples branch:
```
git checkout -b apples
```
Change the `breakfast.txt`:
```
pancakes
waffles
apples
```
Save the file, then add and commit the change:
```
git add .
git commit -m "Apples not oranges"
```
Great. Now you have oranges in the master branch and apples in the apples branch. Let's return to the master branch:
```
git checkout master
```
Inside `breakfast.txt`, you should see the base text, oranges. Let's change it to grapes because they are in season right now:
```
pancakes
waffles
grapes
```
Save, add, and commit:
```
git add .
git commit -m "Grapes not oranges"
```
Phew, that's a lot of setup. Now you are ready to merge the apples branch into the master branch:
```
git merge apples
```
You should see an error:
```
Auto-merging breakfast.txt
CONFLICT (content): Merge conflict in breakfast.txt
Automatic merge failed; fix conflicts and then commit the result.
```
A conflict, great! Let's resolve the conflict using our newly-configured `mergetool`. Run:
```
git mergetool
```
<p align="center">
<img alt="Three-way mergetool with Vim" width="900" height="auto" src="./img/mergetool-initial.png">
</p>
Vim displays four windows. Pay attention to the top three:
- `LOCAL` contains `grapes`. This is the change in "local", what you are merging into.
- `BASE` contains `oranges`. This is the common ancestor between `LOCAL` and `REMOTE` to compare how they diverge.
- `REMOTE` contains `apples`. This is what is being merged into.
At the bottom (the fourth window) you see:
```
pancakes
waffles
<<<<<<< HEAD
grapes
||||||| db63958
oranges
=======
apples
>>>>>>> apples
```
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. If you want to *get* the change from `LOCAL` (`grapes`), with the cursor over the highlighted areas, from the fourth window, run `:diffget LOCAL`. Likewise, if you want to *get* the change from `BASE` (`oranges`), run `:diffget BASE` and if you want to *get* the change from `REMOTE` (`apples`), run `:diffget REMOTE`.
In this case, let's get the change from `LOCAL`. Run `:diffget LO` (short for `LOCAL`). The fourth window will now have grapes. Save and exit all files (`:qall`) when you are done. That wasn't bad, right?
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:
```
git config --global mergetool.keepBackup false
```
# Git Inside Vim
Vim does not have a native git integration. However, one way to run git commands from Vim is to use the bang operator, `!`, in the command-line mode.
Any git command can be run with `!`:
```
:!git status
:!git commit
:!git diff
:!git push origin master
```
You can also use Vim's `%` (current buffer) or `#` (other buffer) conventions:
```
:!git add % " git add current file
:!git checkout # " git checkout the other file
```
# Plugins
To integrate git inside Vim, you have to use plugins. Below is a list of popular git-related plugins for Vim:
- [vim-gitgutter](https://github.com/airblade/vim-gitgutter)
- [vim-signify](https://github.com/mhinz/vim-signify)
- [vim-fugitive](https://github.com/tpope/vim-fugitive)
- [gv.vim](https://github.com/junegunn/gv.vim)
- [vimagit](https://github.com/jreybert/vimagit)
- [vim-twiggy](https://github.com/sodapopcan/vim-twiggy)
- [rhubarb](https://github.com/tpope/vim-rhubarb)
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.
# Vim-fugitive
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.
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).
## Git status
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:
- `Ctrl-N` / `Ctrl-P` to go up or down the file list.
- `-` to stage or unstage the file name under the cursor.
- `s` to stage the file name under the cursor.
- `u` to unstage the file name under the cursor.
- `>` / `<` to display or hide an inline diff of the file name under the cursor.
<p align="center">
<img alt="Finding files in FZF" width="900" height="auto" src="./img/fugitive-git.png">
</p>
For more, check out `:h fugitive-staging-maps`.
## Git blame
When you run the `:Git blame` command from the current file, vim-fugitive displays a split blame window. This can be useful to see the person responsible for writing that buggy line of code so you can yell at him / her (that person is probably me).
Some things you can do while in this `"git blame"` mode:
- `q` to close the blame window.
- `A` to resize the author column.
- `C` to resize the commit column.
- `D` to resize the date / time column.
For more, check out `:h :Git_blame`.
<p align="center">
<img alt="Finding files in FZF" width="900" height="auto" src="./img/fugitive-git-blame.png">
</p>
## Gdiffsplit
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 <commit>`, vim-fugitive runs a `vimdiff` against that file inside `<commit>`.
<p align="center">
<img alt="Finding files in FZF" width="900" height="auto" src="./img/fugitive-gdiffsplit.png">
</p>
Because you are in a `vimdiff` mode, you can *get* or *put* the diff with `:diffput` and `:diffget`.
## Gwrite / Gread
When you run the `:Gwrite` command in a file after you make changes, vim-fugitive stages the changes. It is like running `git add <current-file>`.
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 <current-file>`. 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 <current-file>` from the CLI.
## Gclog
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`.
<p align="center">
<img alt="Finding files in FZF" width="900" height="auto" src="./img/fugitive-git-log.png">
</p>
While in this `"git log"` mode, you can do two things:
- View the tree.
- Visit the parent (the previous commit).
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"`.
## More vim-fugitive
These are only a few examples of what vim-fugitive can do. To learn more about vim-fugitive, check out `:h fugitive.txt`. The point is, most / all popular git commands probably have their vim-fugitive version. You just have to look for them in the documentation.
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!
# Learn Vim and Git the smart way
Everybody has a different git workflow. 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. Use the best tool for the job.
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 small part of it. With that, let's *git going* (pardon the pun) and talk about how to use Vim to compile your code!

365
ch19_compile.md Normal file
View file

@ -0,0 +1,365 @@
# Compile
Compiling is an important subject for many languages. In this chapter, you will learn how to compile from Vim. In addition, you will look at ways to take advantage of Vim's `:make` command.
# Compile From the Command Line
You can use the bang operator (`!`) to compile. If you need to compile your `.cpp` file with `g++`, run:
```
:!g++ hello.cpp -o hello
```
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.
# Makefile
In this section, I will briefly go over makefile basics. If you already know how to use a makefile, feel free to jump to the next section. In the current directory, create a file named `makefile`. Put this inside:
```
all:
echo "Hello all"
```
Run the `make` command from the terminal:
```
make
```
You will see:
```
echo "Hello all"
Hello all
```
The terminal outputs the echo command itself and its output. You can have multiple "targets" in a makefile. Let's add a few:
```
all:
echo "Hello all"
foo:
echo "Hello foo"
list_pls:
ls
```
Now you can run the `make` command with different targets:
```
make foo
# returns "Hello foo"
make list_pls
# returns the ls command
```
The `make` command outputs the actual command in addition to the output. To suppress the output of the actual command, append `@` before that command, for example:
```
all:
@echo "Hello all"
```
Now when you run `make`, you only see "Hello all" and not `echo "Hello all"`.
# `:make`
Vim has a `:make` command to run a makefile. When you run it, Vim looks for a makefile in the current directory to execute.
If you haven't already, create a file named `makefile` in the current directory and put these inside:
```
all:
echo "Hello all"
foo:
echo "Hello foo"
list_pls:
ls
```
Run this from Vim:
```
:make
```
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:
```
:make foo
:make list_pls
```
The `:make` command uses Vim's `quickfix` to store any error if you run a bad command. Let's run a nonexisting target:
```
:make dontexist
```
You should see an error running that command. To view that error, run the `quickfix` command `:copen` to view the ``quickfix`` window:
```
|| make: *** No rule to make target `dontexist'. Stop.
```
# Compile with `make`
Let's use the makefile to compile a basic `.cpp` program. First, let's create a `hello.cpp` file:
```
#include <iostream>
int main() {
std::cout << "Hello!\\n";
return 0;
}
```
Update your `makefile` to build and run a `.cpp` file:
```
all:
echo "build, run"
build:
g++ hello.cpp -o hello
run:
./hello
```
Now run:
```
:make build
```
The `g++` compiles `./hello.cpp` and creates `./hello`. Then run:
```
:make run
```
You should see `"Hello!"` printed on the terminal.
# `makeprg`
When you run `:make`, Vim actually runs whatever command that is set under the `makeprg` option. If you run `:set makeprg?`, you'll see:
```
makeprg=make
```
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:
```
:set makeprg=g++\\ %
```
The `\\` is to escape the space after `g++` (you need to escape the escape). The `%` symbol in Vim represents the current file. The command `g++\\ %` is equivalent to running `g++ hello.cpp`.
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:
```
:set makeprg=g++\\ %\\ -o\\ %<
```
The breakdown:
- `g++\\ %` is the same as above. It is equivalent to running `g++ <your-file>`.
- `-o` is the output option.
- `%<` in Vim represents the current file name without an extension (`hello.cpp` becomes `hello`).
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>`.
For more, check out `:h :compiler` and `:h write-compiler-plugin`.
# Auto-compile on Save
You can further make life easier by automating compilation. Recall that you can use Vim's `autocommand` to automate actions based on certain events. To automatically compile `.cpp` files on each save:
```
:autocmd BufWritePost *.cpp make
```
Each time you save inside a `.cpp` file, Vim executes the `make` command.
# Switching Compiler
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:
```
:e $VIMRUNTIME/compilers/<tab>
```
You should see a list of compilers for different programming languages.
To use the `:compiler` command, suppose you have a ruby file, `hello.rb`, and inside it has:
```
puts "Hello ruby"
```
Recall that if you run `:make`, Vim executes whatever command is assigned to `makeprg` (default is `make`). If you run:
```
:compiler ruby
```
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.
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.
You can also create your own custom compilers. Let's create two simple compilers and call them `foo` and `bar` compilers. In your `~/.vim` directory, if there isn't already, add a `/compiler` directory (`~/.vim/compiler`). Inside, create two files: `~/.vim/compiler/foo.vim` and `~/.vim/compiler/bar.vim`. Inside the `foo.vim`, add:
```
if exists("current_compiler")
finish
endif
let current_compiler = "foo"
if exists(":CompilerSet") != 2
command -nargs=* CompilerSet setlocal <args>
endif
let s:cpo_save = &cpo
set cpo-=C
CompilerSet makeprg=echo\\ \\"Hello\\ foo\\"
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: nowrap sw=2 sts=2 ts=8:
```
If most of these sound like gibberish, don't worry. Most of these lines are boilerplates that I got from `$VIMRUNTIME/compiler/<some-language>.vim`. The important line is:
```
CompilerSet makeprg=echo\\ \\"Hello\\ foo\\"
```
This line defines the `makeprg` when you call `:compiler foo`. With this, the makeprg will run an `echo "Hello foo"` command. Since the spaces and double quotes are special characters, you need to escape them.
Inside `bar.vim`, add:
```
if exists("current_compiler")
finish
endif
let current_compiler = "bar"
if exists(":CompilerSet") != 2
command -nargs=* CompilerSet setlocal <args>
endif
let s:cpo_save = &cpo
set cpo-=C
CompilerSet makeprg=cp\\ %\\ %<
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: nowrap sw=2 sts=2 ts=8:
```
For the `bar` compiler, the `makeprg` is set to run a `cp % %<` command. It copies the current file and name it with the same name minus the extension. If you're running it from `hello.txt`, it will create the file `hello`.
Let's switch to the `foo` compiler:
```
:compiler foo
```
Then run:
```
:make
```
You should see the echo output:
```
Hello foo
```
Let's quickly switch it to the `bar` compiler. Run:
```
:compiler bar
```
Then run:
```
:make
```
You should see the copy of the current file you're in *without* the extension. If you need to define your own compilers (or set of commands), you can use the `:compiler` method to quickly switch compilers.
# Async Compiler
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?
Luckily there are plugins to run async processes. The two big ones are:
- [vim-dispatch](https://github.com/tpope/vim-dispatch)
- [asyncrun.vim](https://github.com/skywind3000/asyncrun.vim)
In this chapter, I will go over vim-dispatch, but I would strongly encourage you to try all of them out there.
*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`.*
# Plugin: vim dispatch
Vim-dispatch has several commands, but the two main ones are `:Make` and `:Dispatch` commands.
## `:Make`
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:
```
:set makeprg=npm\\ t
```
If you run:
```
:make
```
Vim will execute `npm t`. Meanwhile, you are just staring at the frozen screen while your Javascript test runs. With vim-dispatch, you can just run:
```
:Make
```
Vim will run `npm t` asynchronously. This way, while `npm t` is running on a background process, you can edit your text with Vim. Neat!
## `:Dispatch`
The `:Dispatch` command works like the `:compiler` and the `:!` command.
Assume that you are inside a ruby spec file and you need to run a test. Run:
```
:Dispatch rspec %
```
Vim will asynchronously run the `rspec` command against the current file (`%`).
## Automating Dispatch
Vim-dispatch has `b:dispatch` buffer variable that you can configure to evaluate specific command. You can leverage it with `autocmd`. If you add this in your vimrc:
```
autocmd BufEnter *_spec.rb let b:dispatch = 'bundle exec rspec %'
```
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>`.
# Learn Compile the Smart Way
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.

BIN
img/diffing-apples.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
img/diffing-basic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
img/fugitive-gdiffsplit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
img/fugitive-git-blame.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
img/fugitive-git-log.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
img/fugitive-git.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
img/mergetool-initial.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB