This sounds like a job for Awk
:%!awk '!a[$0]{a[$0]=1;print}'
However you asking a two questions:
- What does
:g/^/kl |if search('^'.escape(getline('.'),'\.*[]^$/').'$','bW') |'ld
do?
- How can I make a mapping to this?
The Mapping
Let's start with "How can I make a mapping to this?":
:nnoremap <f2> :g/^/kl<bar>if search('^'.escape(getline('.'),'\.*[]^$/').'$','bW')<bar>'ld<cr>
The trick is to use <bar>
instead of |
and to actually execute the command with <cr>
. See :h keycodes
.
What does this do?
It goes over every line of the buffer with :g/^/
and deletes lines that are the same as a line from above, if search('^'.escape(getline('.'),'\.*[]^$/').'$','bW')
and d
. The confusing parts to me are the following:
- Using marks needlessly i.e.
:k
and range with the :d
command.
- A complicated building of the regex for the
search()
function. By using the \V
(very nomagic
) we can reduce the line noise in the regex: '\V\^'.escape(getline('.'),'\').'\$'
- Why are you doing an
O(N^2)
operation when you can do an O(N)
?
Simplify the command
g/^/if search('\V\^'.escape(getline('.'),'\').'\$','bWn') | d | endif
We remove the needless marks and simplify the escaping. I also added the endif
to show the end of the if statement (this can be optionally left off because it will be assumed).
:g/{pat}/{cmd}
The :global
command runs {cmd}
on ever line matching {pat}
:g/^/
is a common idiom to run a command on every line, since all lins have a beginning, ^
.
if {expr} | {cmds} | endif
. Execute {cmds}
if the expression, {expr}
, evaluates to true
search({pat}, {flags})
search for {pat}
in the buffer. {flag}
alter the search behavior
search()
returns the line number of a match or 0 for no match found
b
flag for search()
means search backwards
W
flag means do not wrap around the buffer when searching
n
do not move the cursor
escape({str}, {chars})
escape {chars}
with \
\V
pattern uses very no magic meaning all regex meta characters need to be escaped
\^
and \$
are escaped for start and end of line because of \V
option
:delete
or :d
for short delete the current line
I suggest you use the awk solution at the start of this answer.
:nnoremap <f2> :%!awk '!a[$0]{a[$0]=1;print}'<cr>
For more help see the following:
:h :range!
:h :g
:h :d
:h :l
:h :if
:h search(
:h escape(
:h /\V