Welcome to tui.ninja!


expand_more
Vim's internal formatting features work great in many cases, but sometimes it can be helpful to send a buffer's content to an external program to perform the formatting, then replace the buffer contents with the result. For example, suppose we have the following JSON file in a buffer and want to sort the keys. Initial Conditions { "bananas": 12, "strawberries": 15, "apples": 34, "oranges": 24, "grapes": 33 } COMMAND Top 1:1 :%!jq -S . % We will demonstrate how to achieve this using a command-line mode command that calls jq , a popular command-line JSON processor. Obviously jq needs to be installed on a system for this to work. The command to execute is: :%!jq -S . % Let's break this down to review what is happening: To start, this follows the pattern: :[range]!{cmd} {args} where :% specifies the range as the entire file. Next, !{cmd} indicates that an external command is to be called with the specified range and arguments. In this case the external command to be called is: jq -S . % The details of this command are (mostly) outside the scope of Vim itself, but can be found by executing jq --help which indicates that the jq command is called with the format: jq [options] <jq filter> [file] where: [options] = -S (sort object keys) <jq filter> = . (pass all lines back to stdout) [file] = % (Vim inserts the current filename) Now, when we execute this command we get the following result: After External Formatting { "apples": 34, "bananas": 12, "grapes": 33, "oranges": 24, "strawberries": 15 } NORMAL Top 1:1 7 lines filtered which contains our sorted JSON object.
Vim's built-in J command can be used to join the current line with the next line, which can be a very handy tool for keeping text "clean" when editing. Let's take a quick look at the default behavior. Starting from the following buffer , execute J : Before After Before Default Line o ne Line two Line three NORMAL Top 1:6 After Default J Line o ne Line two Line three NORMAL Top 1:9 Note that the second line has merged with the first, but the cursor jumped after joining the lines, which is a bit disorienting. We would prefer to join lines without having the cursor move, so in this tip we will develop a simple keymap that achieved this. For comparison, here is a quick demo of the buffer before and after the improved keymap: Before After Before Improved Line o ne Line two Line three NORMAL Top 1:6 After Improved <A-j> Line o ne Line two Line three NORMAL Top 1:6 Let's set this up, step by step. Since the default J moves the cursor, our goal is to have the cursor return to the original location, which we can achieve by setting a mark to record the cursor position before we execute J . We can use any lower-case letter, let's use z : Set Mark mz Line o ne Line two Line three NORMAL Top 1:6 Now that we have set the mark , execute the join by executing J : Join Lines J Line o ne Line two Line three NORMAL Top 1:9 As we saw before, the cursor jumped. Now, we simply jump back to the mark we previously set using `z : Jump to Mark `z Line o ne Line two Line three NORMAL Top 1:6 Now that we have compiled the steps required to get the behavior we want, let's combine the steps into the following keymap: vim . keymap . set ( "n" , "<A-j>" , "mzJ`z" ) Note that for demonstration purposes this mapping sets the alternate keymap to A-J . If you prefer this functionality over the default, then you can also override the default keymap directly by using J instead of <A-J> .
Reorganizing lines in a buffer is a very common task. While a yank and paste sequence could be used for each line, there is a better way. The following keymaps allow a line or group of lines to be easily moved up or down using a single keystroke: -- single-line vim . keymap . set ( "n" , "<A-j>" , ":move .+1<CR>==" ) vim . keymap . set ( "n" , "<A-k>" , ":move .-2<CR>==" ) -- multi-line vim . keymap . set ( "v" , "<A-j>" , ":move '>+1<CR>gv=gv" ) vim . keymap . set ( "v" , "<A-k>" , ":move '<-2<CR>gv=gv" ) To see how they work, consider the following buffer: Initial Conditions a = [ "one", "three", "two", "four", "five", ] NORMAL Top 1:1 Note that the words are out of order, and we would like to fix that. We start by moving the cursor to the line that we want to move (i.e. the "target" line): Move to Target Line 2j a = [ "one", "three", "two", "four", "five", ] NORMAL 38% 3:1 With this keymap, hitting A-J allows us to easily move the currently down one position, swapping its position with the line below it: Move Current Line <A-j> a = [ "one", "two", "three", "four", "five", ] NORMAL 50% 4:1 To get a better idea about what happened, consider the command that executed when we hit A-J : :move .+1<CR>== The :move command has a call signature: :m[ove] {address} and moves the current line (or selected lines) to the line below the specified address. In this case, the specified address is .+1 , which we learned in the ranges chapter means "the line below the current line". The carriage return causes the preceding command to execute, which is follows by == , which formats (indents) the line after it is moved. Let's see how this works with a visual selection . First, let's move the cursor so that the selection spans three lines: Select Multiple Lines v 2j a = [ "one", "two", "three", "four", "five", ] VISUAL 75% 6:1 Note that although the selection only touches the third line, the entire line is included in the block of lines to move. Now, move these lines up: Move Multiple Lines <A-k> a = [ "one", "three", "four", "five", "two", ] VISUAL 62% 5:1 3 lines indented The selected lines have each move up one line, and the line containing "two" (which has previously been above the selection) is now located below the selection.