VIM: Replace line based of context on the next line - vim

I have a bunch of files and each mostly look like this:
...
apiVersion: v1
kind: List
...
apiVersion: v1
kind: ConfigMap
...
apiVersion: v1
kind: DeploymentConfig
...
apiVersion: "v1"
kind: "PersistentVolumeClaim"
...
apiVersion: v1
kind: Service
...
apiVersion: v1
kind: DeploymentConfig
...
I would like to replace apiVersion: v1 by apiVersion: apps.openshift.io/v1 but only if the line below matches kind: DeploymentConfig.
Is there any way to do it with native vim functionality?
For security reasons I am not able to install any vim-plugins on that specific server.

"Do something on each line matching XXX" is the ideal use case for :help :global:
:g/kind: DeploymentConfig/-s/v1/apps.openshift.io/&/c
where:
:g/<pattern>/<command> executes <command> on each line matching <pattern>,
kind: DeploymentConfig is our <pattern> and the rest is our <command>,
- is the address for :help :s, meaning "the line above",
s/v1/apps.openshift.io/& substitutes v1 with apps.openshift.io\/v1 (see :h :s/\&),
the /c at the end is a flag that tells Vim to prompt for confirmation for each substitution, it is facultative.

One solution is to use macro.
# start recording macro a
qa
# start searching
/kind: ConfigMap
# move up
k
# do replace
:.s/apiVersion: v1/apiVersion: apps.openshift.io\/v1/
# move down
j
# finish record
q
Then you can run n#a with number n to replace n times.

There are already two good suggestions, but here's a third method: You can match the next line in the :substitute pattern. The basic search-and-replace might look like this:
:%s/apiVersion: v1/apiVersion: v2/g
If you want to say "and the next line matches something particular", you can end the targeted pattern with \ze and then add \_s\+ to indicate "any whitespace, including a newline":
:%s/apiVersion: v1\ze\_s\+kind: DeploymentConfig/apiVersion: v2/g
The \zs and \ze patterns are great for ad-hoc matches like this. You can also substitute only v1 in this context by v2 by anchoring the beginning with \zs:
:%s/apiVersion: \zsv1\ze\_s\+kind: DeploymentConfig/v2/g

Related

The meaning of the quotation marks in "image"-keyword entries in GitLab CI/CD

The .gitlab-ci.yml file can accept the keyword: image, "to specify a Docker image that the job runs in."
I have encountered that the input entries of this keyword are sometimes enclosed with quotation marks (" ").
For example:
image: alpine
vs
image: "alpine"
GitLab Docs also contains both instances (sort of): quoted entry vs unquoted entry.
Could you please tell me whether there is any meaning of these quotation marks? If so, what is its meaning, and when should we use it?
Quotation marks are only significant with respect to YAML syntax and nothing else. GitLab always reads YAML according to the YAML specification. In the case you mention, alpine and "alpine" are parsed identically, there is no functional difference.
See also: YAML: Do I need quotes for strings in YAML?
In some cases, you need quotes to avoid YAML interpreting your strings as other kinds of YAML types. For instance: key: 123 is not the same as key: "123" for example.
As another example, if your string happens to start with a * you will need quotes to avoid it being interpreted as a YAML anchor:
# good:
artifacts:
paths:
- "*package.json"
# bad
artifacts:
paths:
- *package.json

Multi-line edit starting at the same character (= sign) [no macros?]

I'm working on an ansible playbook, where VIm seems to prove to be an extremely useful tool for (lots of similar patterns in style/formatting and such), and I'm hoping to take my current situation (since been written) to turn it into a Vim lesson.
I've made extensive use of code blocks to make multi-line edits, but I think I've reached their limit and wanted to reach out to figure out how I might approach making line edits more dynamically. In this scenario, I have a block of code that I'm trying to transform
from:
rcon.port=25575
rcon.password=strong-password
enable-rcon=true
into:
- { regexp: '^rcon.port', line: 'rcon.port=25575' }
- { regexp: '^rcon.password', line: 'rcon.password=strong-password' }
- { regexp: '^enable-rcon', line: 'enable-rcon=true' }
To do that, the first part is fairly simple. Shift-I, then ctrl-V for block, traverse lines to edit, type - { regexp: '^" to get to the following:
- { regexp: '^rcon.port=25575
- { regexp: '^rcon.password=strong-password
- { regexp: '^enable-rcon=true
Unfortunately, from there I'm a bit lost as the macros (and whether or not that's overkill or not) are still a bit unclear to me. Are there any possible approaches to solve this problem other than macros?
I'm not looking for a full solution, but simply a hint for the best (or only approach) here, and if there are any tricks to thinking about this in the Vim way.
Any links to good documentation/learning resources for macros would be AWESOME as well! I'm still new to Vim, so bear with me... thanks!
Building off #Stephen Warren's answer the following works as required:
:%s/^\(\(.\+\)=.\+\)/- regex: '\2', line: '\1' }/
The match section is:
From the beginning of the line ^
It has two groups rather than one \(\)
The first group is the outer one that matches the entire line
The second group is the inner group that matches up to the =
Match one or more characters \+ is used rather than the greedy * for matching either side of the =
The replace section:
Basically your expected output calling on the previously matched groups \1 and \2
Perhaps something like the following regular expression substitutions:
:%s/^\(.*\)$/- { regexp: '^\1' }/
Or with all relevant lines visually selected:
CTRL-o
:s/^\(.*\)$/- { regexp: '^\1' }/
I guess I should explain that a bit more:
: Enter command mode.
% Apply to all lines.
s Substitute.
/xxx/yyy/ Replace xxx with yyy.
^ Anchor at start of input string
(xxx) (in match string) capture whatever matches xxx.
\1 (in replacement string) replace with whatever matched (xxx).
.* Match any amount of any characters.
$ Anchor at end of input string.
Replacement string is emitted into the result literally without any interpretation, except for stuff like \1.

YAML: How can I create a multi-line string of comma delimited values?

I am using YAML to pass in a string of comma delimited values for a new relic config file to ignore errors. I need the output to look like this:
"NotFoundError,LocationError,InvalidParamsError"
I tried using folded style and stripping the last newline. My yaml file looks like this:
ignore_errors: >-
NotFoundError,
LocationError,
InvalidParamsError
However, it parses the other newlines as a space in the end giving me something like this:
"NotFoundError, LocationError, InvalidParamsError"
I need it to parse the folded string and not add spaces. Please help.
The only way to do this is to quote your string and escape the end of each line with \:
ignore_errors: "\
NotFoundError,\
LocationError,\
InvalidParamsError"
I don't think the YAML spec will enable you to do what you want, unfortunately (this great SO answer shows the myriad of ways to write multi-line strings in YAML... but doesn't cover your use case). I think your best bet if possible would be to store your error strings as a list, and then use your programming language to format the list. An example in Ruby would be:
require 'yaml'
yaml = <<-YAML
ignore_errors:
- NotFoundError
- LocationError
- InvalidParamsError
YAML
hash = YAML.load(yaml)
puts hash["ignore_errors"].join(',')
which gives you "NotFoundError,LocationError,InvalidParamsError"

Adding YAML syntax highlighting throughout an entire markdown file (in vim)

I currently have the plugin vim-markdown installed. Among other things, it adds syntax highlighting to markdown files. But frequently in markdown files there are yaml headers:
---
yaml: contents
more: yaml
---
# Usual markdown
Etc.
Using vim-markdown, the part of this file enclosed in --- is correctly rendered as yaml (from a syntax highlighting point of a view). The rest of the file is rendered as markdown.
Question: How do I make it so that, no matter where the enclosing --- are located -- yaml is rendered in between?
Attempt: I found in the ~/.vim/bundle/vim-markdown/syntax/markdown.vim the following snippet:
syn include #yamlTop syntax/yaml.vim
syn region Comment matchgroup=mkdDelimiter start="\%^---$" end="^---$" contains=#yamlTop
I removed the \% in the start= field and tried again. But it didn't work :(
In case you have not yet found the solution: Add the following to your .vimrc:
let g:vim_markdown_frontmatter = 1
It is documented here:
https://github.com/plasticboy/vim-markdown

How can I paste the content just after multiple lines

I always face this problem in my real life.
I want this:
My cursor is at the first line of multiple lines of codes. The content should be placed just after multiple line of codes.
[cursor]xxxx
xxxxxxxxxxxx
xxxxxxxxxxxx
xxxxxxxxxxxx
xxxxxxxxxxxx
[this is where i want to place the code]
The problem now:
Since the p in vim just paste the code after the cursor, I have to go to the end of the lines and p.
xxxxxxxxxxx
xxxxxxxxxxxx
xxxxxxxxxxxx
xxxxxxxxxxxx
[cursor]xxxxxx
[this is where i want to place the code]
put is the command you are looking for
:[lineNo]put x
e.g.
:200pu
will paste the value of register " after the lineNo 200.
:200pu n
will paste the value of register n (before you may have done: "ny) after the lineNo 200.
for more detail, pls check :h :pu
EDIT for the nu
I feel it is nice to use number or relative number in different cases. Not stick to one all the time. at least I am doing so. In my vimrc I wrote a little function to switch relative number and normal line number:
function! ToggleRelativeNumber()
let &relativenumber = &relativenumber?0:1
let &number = &relativenumber? 0:1
endfunction
"map <leader> rn to the function
nnoremap <silent> <Leader>nu :call ToggleRelativeNumber()<cr>
so I can type ,nu to switch between them. (my leader is ,)
If you want to paste after a block of code, you can use }P to navigate to the end of the block, then paste at that line.
Assuming you want to paste from the default register,
paste after next occurrence of foo:
/foo<cr>p
or Kent's suggestion:
:+4put

Resources