Collect markdown headlines in vim? - vim

I am trying to come up with a TOC (table of contents) of a text document:
lines starting with # (hashtag) VIM would "collect" into the buffer or maybe place them at the TOP OF FILE.
(Or - other idea - erase all lines in a duplicate file NOT starting with a #)
There must be solutions to this question...

I think you are looking for the :global command.
You can see all lines starting with # with :g/^#/p
and put them in a file using the :redir command :
:redir > toc.txt
:g/^#/p
:redir END
Note that toc.txt will contain the line numbers if you have set number. There is also this post on the subject.
Alternatively, you can delete all lines not starting with # with :v or :g! and save it to another file.
:v/\#/d
:w! toc.txt | undo
Look also for :copy (:t) and :move.
:global applies the command you provide to each matched lines in the order they appear in the file, so :g/^#/t0 will copy headers to the top of the file but in reverse. We could do :v/^#/m$ to move all non-header lines to the end, leaving the headers on top but stripped from the original text. Another way is to place some marker to mark the end of the table of content and copy header lines one line above this marker :
:g/^#/t?end-toc?-1

Related

Vim advanced multiline edits

I'm trying to getting into using more advanced vim features.
How would people go about for the following edit?
from this:
ssn=token_payload.fnr,
fname=token_payload.displayName,
email=token_payload.email,
login=token_payload.username,
to this:
ssn=token_payload['fnr'],
fname=token_payload['displayName'],
email=token_payload['email'],
login=token_payload['username'],
Command line :norm command
I would apply the following normal commands to all lines in the file:
" note that in the real command, <Esc> would be a literal
" press of the escape key (see explanation below)
:%norm f.s['<Esc>f,i']
apply to the whole file: %
the following normal mode commands:norm
move to the period: f.
substitute with opening square bracket and quote: s['
escape insert mode (press ctrl+v to enter a literal character, then escape -
you'll see a gray symbol appear): ^[
move to the comma: f,
insert the quote and closing square bracket: i']
I started using the command line way instead of macros recently since I find
that you can think it over more easily (particularly if you compose the command
in the command buffer with q: - see :help command-buffer).
Use a macro
Another way is to record a macro:
qa0f.s['<Esc>f,i']<Esc>jq
Which you can then deploy on the current line with #a and repeat with ##.
Or use :%norm #a to run the macro on each line.
It's basically the same as above, but instead of :%norm you use qa to
record into the a register (you can use any letter). Then perform the edit. I
added a drop down one line with j before stopping the recording with q.
You can edit the macro after recording it by pasting the contents of the
register ("ap), edit them, and yank them back ("ay$) before replaying it.
Using an external tool
If I wanted to perform multiple substitutions with a single command, I would
filter the text through an external program like sed:
:%!sed "s/\./['/; s/,$/'],/"
One more g[ood] thing
An extremely powerful tool is the :g[lobal] command! (see :help :g) I've
been using it a lot in combination with the norm command. For example, if I
wanted to get all the paragraphs in a document formatted nicely, but not affect
indented text (which could be code blocks, or tables etc.) I would do:
:%g/^\w/norm gqap
This means, for any line with a letter at the very start of the line, apply the
command gqap which applies the normal mode command gq to 'a paragraph'.
You might also want to capitalise the first word and increase the header level
of all the markdown headings like so:
:%g/^#/norm w~I#
This would change this:
# a heading
some text.
## another heading
some more text
```sh
# and a comment in some code will be unaffected
print('hello world')
```
## a further heading
some text
# conclusion
into this:
## A heading
some text.
### Another heading
some more text
```sh
# and a comment in some code will be unaffected
print('hello world')
```
### A further heading
some text
## Conclusion
see these videos for 'advanced' vim stuff
I'd implement this as an :s command. For example, this command would make the requested changes:
:%s/\.\(.*\),/['\1'],/
That operates on all lines %, matches the dot and comma and puts everything in between into a group (\(.*\)), and then replaces it with the desired value, matching the first group (\1).
If you want to operate on a different set of lines, you can write :1,4 instead of :%, or write :'<,'> to operate on the visual selection.

Visual select and break multiple lines in VIM up to textwidth

If I have a few long lines like so
# I am Quater. Read my words, and be my friend.
# Father commands me to record the truth of history, so that readers will learn from those who went before.
# Therefore, I give each of my seven sons one of these self-engraving, history-recording klay walls.
I want to select the lines and issue a command and have it wrap to the text width while preserving the comment symbol (hashtag) to get the following
# I am Quater. Read my words, and be my friend.
# Father commands me to record the truth of
# history, so that readers will learn from
# those who went before. Therefore, I give each
# of my seven sons one of these self-engraving,
# history-recording klay walls.
I know there is a way to do this because I have done it before in VIM but I erased my .vimrc file and I can't find the solution anywhere on google
You can have Vim format the lines using the gq command. Either select the lines visually then use gq, or use gq followed by a motion (such as gq} to format until the next line), or use gqq with a count (3gqq to format the current line and the two that follow it).
For Vim to preserve comments, it needs two options to be set correctly. One is 'formatoptions', which needs to include q (to preserve comments over a gq operation), and the 'comments' option, which needs to include b:# to indicate that lines starting with a # are comments.
You can set both for the current buffer using:
setl fo+=q com+=b:#
After those settings, using gq will keep the comment characters at the start of lines when auto-formatting a block of comments.

Highlight empty lines at beginning and end of file and more than one empty lines

In vim, I would like to highlight empty lines at beginning and end of file, and more than one consecutive line. Example:
--- start of file
.
an empty line just before this line (at the beginning of the file)
more than one empty line will follow
.
.
empty lines at the end of file will follow
.
.
--- end of file
In the example above, the lines with a dot should be highlighted.
I've tried to match the lines with the following expression, unfortunately without luck:
call matchadd('EmptyLines', '\n\s\*\n\s\*\n')
How can I match all of these lines and highlight them (preferably the whole line)?
The special regular expression atoms \%^ (:help start-of-file) and \%$ (:help end-of-file) will help here. With them, you can match empty lines at the boundaries of the buffer, like this:
call matchadd('EmptyLines', '\%^\n\+')
call matchadd('EmptyLines', '^\n\+\%$')
Unfortunately, there are some limitations:
You can only match what's there, which is not much in empty lines. Vim will just highlight a single cell width (that represents the newline character).
In the very last line, nothing is highlighted at all. If you want to see any indication of a single empty final line, you could drop the ^ from the pattern. Then, the empty trailing line would be indicated by highlighting before that line.
Implementation alternatives
Using :help signs, you can highlight the full width of empty lines, and have an additional indication in the sign column. The downside is that you can't simply define a pattern for signs. You have to explicitly place them on certain lines, and adapt this position whenever the buffer contents change. That would mean defining some :autocmds, and living with either poor performance or accepting short delays until the signs update. (They are meant to be used for things like marking build errors that don't change so often and only on demand.)
Instead of a visual indication, if your goal is to avoid having those empty lines, you could also hook into the BufWrite event and either print a warning or completely abort the :write if such lines are found. My DeleteTrailingWhitespace plugin does this (but for whitespace at the end of individual lines).

How to move to the end of the continuous line by pressing keys in command line in vim editor

My file contains some content.
This file is just for testing and it may contain no about about any related data.
But used for copy of move the content to one file to another.
The given line is continuation of the same line. How to move to the cursor to end of the first line by pressing single key.
My _cursor is waiting here
_This file is just for testing and it may contain no about about any related data.
But used for copy of move the content to one file to another.
By pressing singe command or key I need to move to end of first line of my cursor_.
This file is just for testing and it may contain no about about any related data._
But used for copy of move the content to one file to another.
If there is any way, let me know.
You can move to the end of lines in vim using $ sign.
You may use a number in front $ to move n ends down. For instance, 2$ moves to the end of the second line from your cursor.
When "wrap" is turned "on", then g$ moves to the end of SCREEN line. So,
it differs in functionality from a $. For example: If you wanted to move 2 screen lines down use 2g$, if you wanted to move 2 lines down (when wrap is on) use 2$.
For more details you can do :help $ and :help g$.

Vim: delete empty lines around cursor

Suppose I'm editing the following document (* = cursor):
Lions
Tigers
Kittens
Puppies
*
Humans
What sequence can I use to delete the surrounding white space so that I'm left with:
Lions
Tigers
Kittens
Puppies
*
Humans
Note: I'm looking for an answer that handles any number of empty lines, not just this exact case.
EDIT 1: Line numbers are unknown and I only want to effect the span my cursor is in.
EDIT 2: Edited example to show I need to preserve leading whitespace on edges
Thanks
Easy. In normal mode, dipO<Esc> should do it.
Explanation:
dip on a blank line deletes it and all adjacent blank lines.
O<Esc> opens a new empty line, then goes back to normal mode.
Even more concise, cip<Esc> would roll these two steps into one, as suggested by #Lorkenpeist.
A possible solution is to use the :join command with a range:
:?.?+1,/./-1join!
Explanation:
[range]join! will join together a [range] of lines. The ! means with out inserting any extra space.
The starting point is to search backwards to the first character then down 1 line, ?.?+1
As the 1 in +1 can be assumed this can be abbreviated ?.?+
The ending point is to search forwards to the next character then up 1 line, /./-1
Same as before the 1 can be assumed so, /./-
As we are using the same pattern only searching forward the pattern can be omitted. //-
The command :join can be shorted to just :j
Final shortened command:
:?.?+,//-j!
Here are some related commands that might be handy:
1) to delete all empty lines:
:g/^$/d
:v/./d
2) Squeeze all empty lines into just 1 empty line:
:v/./,//-j
For more help see:
:h :j
:h [range]
:h :g
:h :v
Short Answer: ()V)kc<esc>
In normal mode, if you type () your cursor will move to the first blank line. ( moves the cursor to the beginning of the previous block of non-blank lines, and ) moves the cursor to the end (specifically, to the first blank line after said block). Then a simple d) will delete all text until the beginning of the next non-blank line. So the complete sequence is ()d).
EDIT: You're right, that deletes the whitespace at the beginning of the next non-blank line. Instead of d) try V)kd. V puts you in visual line mode, ) jumps to the first non-blank line (skipping the whitespace at the beginning of the line), k moves the cursor up one line. At this point you've selected all the blank lines, so d deletes the selection.
Finally, type O (capital O) followed by escape to crate a new blank line to replace the ones you deleted. Alternatively, replacing dO<Escape> with c<Escape> does the same thing with one less keystroke, so the entire sequence would be ()V)kc<Esc>.
These answers are irrelevant after the updated question:
This may not be the answer you want to hear, but I would make use of ranges. Take a look at the line number for the first empty line (let's say 55 for example) and the second to last empty line (perhaps 67). Then just do :55,67d.
Or, perhaps you only want there to ever be one empty line in your whole file. In that case you can match any occurrence of one or more empty lines and replace them with one empty line.
:%s/\(^$\n\)\+/\r/
This answer works:
If you just want to use normal mode you could search for the last line with something on it. For instance,
/.<Enter>kkVNjd
I didn't test so much, but it should work for your examples. There maybe more elegant solutions.
function! DelWrapLines()
while match(getline('.'),'^\s*$')>=0
exe 'normal kJ'
endwhile
exe 'silent +|+,/./-1d|noh'
exe 'normal k'
endfunction
source it and try :call DelWrapLines()
I know this question has already been resolved, but I just found a great solution in "sed & awk, 2nd Ed." (O'Reilly) that I thought was worth sharing. It does not use vim at all, but instead uses sed. This script will replace all instances of one or more blank lines (assuming there is no whitespace in those lines) with a single blank line. On the command line:
sed '/ˆ$/{
N
/ˆ\n$/D
}' myfile
Keep in mind that sed does not actually edit the file, but instead prints the edited lines to standard output. You can redirect this input to a file:
sed '/ˆ$/{
N
/ˆ\n$/D
}' myfile > tempfile
Be careful though, if you try to write it directly to myfile, it will just delete the entire contents of the file, which is clearly not what you want! After you write the output to tempfile, you can just mv tempfile myfile and tada! All instances of multiple blank lines are replaced by a single blank line.
Even better:
cat -s myfile > temp
mv temp myfile
cat is awesome, yes?
Bestest:
If you want to do it inside vim, you can replace all instances of multiple blank lines with a single blank line by using vim's handy feature of executing shell commands on a range of lines within vim.
:%!cat -s
That's all it takes, and your entire file is reformatted all nice!

Resources