git subrepo clone https://github.com/pearofducks/ansible-vim.git ./dotfiles/.vim/plugged/ansible-vim

subrepo:
  subdir:   "dotfiles/.vim/plugged/ansible-vim"
  merged:   "93798e8c8"
upstream:
  origin:   "https://github.com/pearofducks/ansible-vim.git"
  branch:   "master"
  commit:   "93798e8c8"
git-subrepo:
  version:  "0.4.3"
  origin:   "???"
  commit:   "???"
This commit is contained in:
Git E2E Dev Test Username 2022-10-18 10:37:50 -04:00
parent 1dcbe335e1
commit 8a6af30c98
15 changed files with 1035 additions and 0 deletions

View file

@ -0,0 +1,12 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = https://github.com/pearofducks/ansible-vim.git
branch = master
commit = 93798e8c89c441d29d4678da0c0d5e1429eb43b0
parent = 1dcbe335e115f9620b426aa8a0ff2c6c31e29f69
method = merge
cmdver = 0.4.3

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Dave Honneffer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,161 @@
## introduction
This is a vim syntax plugin for Ansible 2.x, it supports YAML playbooks, Jinja2 templates, and Ansible's `hosts` files.
- YAML playbooks are detected if:
- they are in the `group_vars` or `host_vars` folder
- they are in the `tasks`, `roles`, or `handlers` folder and have either a *.yml* or *.yaml* suffix
- they are named `playbook.y(a)ml`, `site.y(a)ml`, or `main.y(a)ml`
- Jinja2 templates are detected if they have a *.j2* suffix
- Files named `hosts` will be treated as Ansible hosts files
You can also set the filetype to `yaml.ansible`, `*.jinja2`, or `ansible_hosts` if auto-detection does not work (e.g. `:set ft=yaml.ansible` or `:set ft=ruby.jinja2`). **Note**: If you want to detect a custom pattern of your own, you can easily add this in your `.vimrc` using something like this:
```vim
au BufRead,BufNewFile */playbooks/*.yml set filetype=yaml.ansible
```
If you want to override the default file type detection you can easily do this in your `.vimrc`. For example if you use YAML syntax for `hosts` include something like this:
```vim
augroup ansible_vim_fthosts
autocmd!
autocmd BufNewFile,BufRead hosts setfiletype yaml.ansible
augroup END
```
This plugin should be quite reliable, as it sources the original formats and simply modifies the highlights as appropriate. This also enables a focus on simplicity and configurability instead of patching bad syntax detection.
##### examples (with [solarized](https://github.com/altercation/vim-colors-solarized) colorscheme)
Bright (and selective highlight) | Dim
:-----------------------------------:|:-------------------------:
![](http://i.imgur.com/whBOZZK.png) | ![](http://i.imgur.com/XS0T00e.png)
##### installation
Use your favorite plugin manager, or try [vim-plug](https://github.com/junegunn/vim-plug) if you're looking for a really nice one!
**vim-plug:** `Plug 'pearofducks/ansible-vim'`
**vim-plug with post-update hook:** `Plug 'pearofducks/ansible-vim', { 'do':
'./UltiSnips/generate.sh' }`
*Note: Because of Ansible API changes, `generate.sh` may require the latest (or near-latest) version of Ansible.*
*Note2: `generate.sh` can receive some parameters, for more info see its [Readme](https://github.com/pearofducks/ansible-vim/tree/master/UltiSnips#script-parameters)*
**vundle:** `Plugin 'pearofducks/ansible-vim'`
**pathogen:** `git clone https://github.com/pearofducks/ansible-vim ~/.vim/bundle/ansible-vim`
**Arch Linux:** Package [vim-ansible](https://www.archlinux.org/packages/community/any/vim-ansible/) is available in the *community* repository.
**Fedora:** The [vim-ansible](https://src.fedoraproject.org/rpms/vim-ansible) package is available in the default repository.
**RHEL/CentOS:** The [vim-ansible](https://src.fedoraproject.org/rpms/vim-ansible) package is available in the [EPEL](https://fedoraproject.org/wiki/EPEL) repository.
## options
##### g:ansible_unindent_after_newline
`let g:ansible_unindent_after_newline = 1`
When this variable is set, indentation will completely reset (unindent to column 0) after two newlines in insert-mode. The normal behavior of YAML is to always keep the previous indentation, even across multiple newlines with no content.
##### g:ansible_yamlKeyName
`let g:ansible_yamlKeyName = 'yamlKey'`
This option exists to provide additional compatibility with [stephpy/vim-yaml](https://github.com/stephpy/vim-yaml).
##### g:ansible_attribute_highlight
`let g:ansible_attribute_highlight = "ob"`
Ansible modules use a `key=value` format for specifying module-attributes in playbooks. This highlights those as specified. This highlight option is also used when highlighting key/value pairs in `hosts` files.
Available flags (bold are defaults):
- **a**: highlight *all* instances of `key=`
- o: highlight *only* instances of `key=` found on newlines
- **d**: *dim* the instances of `key=` found
- b: *brighten* the instances of `key=` found
- n: turn this highlight off completely
##### g:ansible_name_highlight
`let g:ansible_name_highlight = 'd'`
Ansible modules commonly start with a `name:` key for self-documentation of playbooks. This option enables/changes highlight of this.
Available flags (this feature is off by default):
- d: *dim* the instances of `name:` found
- b: *brighten* the instances of `name:` found
##### g:ansible_extra_keywords_highlight
`let g:ansible_extra_keywords_highlight = 1`
*Note:* This option is enabled when set, and disabled when not set.
Highlight the following additional keywords: `become become_exe become_flags become_method become_user become_pass prompt_l10n debugger always_run check_mode diff no_log args tags force_handlers vars vars_files vars_prompt delegate_facts delegate_to any_errors_fatal ignore_errors ignore_unreachable max_fail_percentage connection hosts port remote_user module_defaults environment fact_path gather_facts gather_subset gather_timeout async poll throttle timeout order run_once serial strategy`.
By default we only highlight: `include include_role include_tasks include_vars import_role import_playbook import_tasks when changed_when failed_when block rescue always notify listen register action local_action post_tasks pre_tasks tasks handlers roles collections` and loop keywords `with_.+`, `loop`, `loop_control`, `until`, `retries`, `delay`.
##### g:ansible_extra_keywords_highlight_group
`let g:ansible_extra_keywords_highlight_group = 'Statement'`
Accepts any syntax group name from `:help E669` - e.g. _Comment_, _Constant_, _Identifier_
*Note:* Defaults to 'Structure' when not set.
##### g:ansible_normal_keywords_highlight
`let g:ansible_normal_keywords_highlight = 'Constant'`
Accepts any syntax group name from `:help E669` - e.g. _Comment_, _Constant_, _Identifier_
*Note:* Defaults to 'Statement' when not set.
This option change the highlight of the following common keywords: `include include_role include_tasks include_vars import_role import_playbook import_tasks when changed_when failed_when block rescue always notify listen register action local_action post_tasks pre_tasks tasks handlers roles collections`.
##### g:ansible_loop_keywords_highlight
`let g:ansible_loop_keywords_highlight = 'Constant'`
Accepts any syntax group-name from `:help E669` - e.g. _Comment_, _Constant_, _Identifier_
*Note:* Defaults to 'Statement' when not set.
This option changes the highlight of all `with_.+`, `loop`, `loop_control`, `until`, `retries` and `delay` keywords.
##### g:ansible_template_syntaxes
`let g:ansible_template_syntaxes = { '*.rb.j2': 'ruby' }`
Accepts a dictionary in the form of `'regex-for-file': 'filetype'`.
- _regex-for-file_ will receive the full filepath, so directory matching can be done.
- _filetype_ is the root filetype to be applied, `jinja2` will be automatically appended
All files ending in `*.j2` that aren't matched will simply get the `jinja2` filetype.
##### g:ansible_ftdetect_filename_regex
`let g:ansible_ftdetect_filename_regex = '\v(playbook|site|main|local|requirements)\.ya?ml$'`
Accepts a regex string that is used to match the filename to determine if the file should use the Ansible filetype
Can be used to avoid clashes with other files that are named the same - e.g. main.yaml used in github workflows by removing `main` from the regex
## goto role under cursor (similar to gf)
This behavior is not supported out of the box, but you can use [this snippet](https://gist.github.com/mtyurt/3529a999af675a0aff00eb14ab1fdde3) in your vimrc.
You'll then be able to go to a role's definition with `<leader>gr`.
## bugs, suggestions/requests, & contributions
##### bugs
It's unlikely that there will be bugs in highlighting that don't exist in the core format. Where appropriate these will be fixed in this plugin, but if the problem is with the original syntax we should probably focus on fixing that instead.
Indenting a full document - e.g with `gg=G` - will not be supported and is not a goal of this plugin (unless someone else develops it!). Please do not file a bug report on this.
##### suggestions/requests
Suggestions for improvements are welcome, pull-requests with completed features even more so. :)

View file

@ -0,0 +1 @@
ansible.snippets

View file

@ -0,0 +1,35 @@
Generate Snippets Based on Ansible Modules
==========================================
A script to generate `UltiSnips` based on ansible code on the fly.
**Note:** Requires Ansible 2.4 or later.
Script parameters
-----------------
There are a couple of optional arguments for the script.
* --output: Output filename (Default: ansible.snippets)
* --style: YAML formatting style for snippets
Choices: multiline (default), dictionary
* --sort: Whether to sort module arguments (default: no)
For Users
---------
We display option description somewhere, however, there are some special formatters in it.
For your reference, we list them here and you can find them under `/ansible/repo/hacking/module_formatter.py`:
* I: Italic
* B: Bold
* M: Module
* U: URL
* C: Const
For Developers
--------------
* `pip install ansible` first
Thanks
------
* Based on (ansible)[https://github.com/ansible/ansible] Awesome Documentation
* Inspired by [bleader/ansible_snippet_generator](https://github.com/bleader/ansible_snippet_generator)

View file

@ -0,0 +1,389 @@
#!/usr/bin/env python3
import argparse
import os
import os.path
import ansible
from packaging import version
import ansible.modules
from ansible.utils.plugin_docs import get_docstring
from ansible.plugins.loader import fragment_loader
from typing import Any, List
OUTPUT_FILENAME = "ansible.snippets"
OUTPUT_STYLE = ["multiline", "dictionary"]
HEADER = [
"# NOTE: This file is auto-generated. Modifications may be overwritten.",
"priority -50",
]
MAX_DESCRIPTION_LENGTH = 512
ANSIBLE_VERSION = ansible.release.__version__
def get_files_builtin() -> List[str]:
"""Return the sorted list of all module files that ansible provides with the ansible package
Returns
-------
List[str]
A list of strings representing the Python module files provided by
Ansible
"""
file_names: List[str] = []
for root, dirs, files in os.walk(os.path.dirname(ansible.modules.__file__)):
files_without_symlinks = []
for f in files:
if not os.path.islink(os.path.join(root, f)):
files_without_symlinks.append(f)
file_names += [
f"{root}/{file_name}"
for file_name in files_without_symlinks
if file_name.endswith(".py") and not file_name.startswith("__init__")
]
return sorted(file_names)
def get_files_collections(user: bool = False) -> List[str]:
"""Return the sorted list of all module files provided by collections installed in either
the system folder /usr/share/ansible/collections/ or user folder ~/.ansible/collections/
Parameters
----------
user: bool (default: False)
A boolean indicating whether to get collections installed in the user folder
Returns
-------
List[str]
A list of strings representing the Python module files provided by collections
"""
if user:
collection_path = '~/.ansible/collections/ansible_collections/'
else:
collection_path = '/usr/share/ansible/collections/ansible_collections/'
file_names: List[str] = []
for root, dirs, files in os.walk(os.path.expanduser(collection_path)):
files_without_symlinks = []
for f in files:
if not os.path.islink(os.path.join(root, f)):
files_without_symlinks.append(f)
file_names += [
f"{root}/{file_name}"
for file_name in files_without_symlinks
if file_name.endswith(".py") and not file_name.startswith("__init__") and "plugins/modules" in root
]
return sorted(file_names)
def get_module_docstring(file_path: str) -> Any:
"""Extract and return docstring information from a module file
Parameters
----------
file_names: file_path[str]
string representing module file
Returns
-------
Any
An AnsibleMapping object, representing docstring information
(in dict form), excluding those that are marked as deprecated.
"""
docstring = get_docstring(file_path, fragment_loader)[0]
if docstring and not docstring.get("deprecated"):
return docstring
def escape_strings(escapist: str) -> str:
"""Escapes strings as required for ultisnips snippets
Escapes instances of \\, `, {, }, $
Parameters
----------
escapist: str
A string to apply string replacement on
Returns
-------
str
The input string with all defined replacements applied
"""
return (
escapist.replace("\\", "\\\\")
.replace("`", r"\`")
.replace("{", r"\{")
.replace("}", r"\}")
.replace("$", r"\$")
.replace("\"", "'")
)
def option_data_to_snippet_completion(option_data: Any) -> str:
"""Convert Ansible option info into a string used for ultisnip completion
Converts data about an Ansible module option (retrieved from an
AnsibleMapping object) into a formatted string that can be used within an
UltiSnip macro.
Parameters
----------
option_data: Any
The option parameters
Returns
-------
str
A string representing one formatted option parameter
"""
# join descriptions that are provided as lists and crop them
description = escape_strings(
"".join(option_data.get("description"))[0:MAX_DESCRIPTION_LENGTH]
)
default = option_data.get("default")
choices = option_data.get("choices")
option_type = option_data.get("type")
# if the option is of type "bool" return "yes" or "no"
if option_type and "bool" in option_type:
if default in [True, "True", "true", "yes"]:
return "true"
if default in [False, "False", "false", "no"]:
return "false"
# if there is no default and no choices, return the description
if not choices and default is None and not args.no_description:
return f"# {description}"
# if there is a default but no choices return the default as string
if default is not None and not choices:
if len(str(default)) == 0:
return '""'
else:
if isinstance(default, str) and "\\" in default:
return f'"{escape_strings(str(default))}"'
elif isinstance(default, str):
return escape_strings(str(default))
else:
return default
# if there is a default and there are choices return the list of choices
# with the default prefixed with #
if default is not None and choices:
if isinstance(default, list):
# prefix default choice(s)
prefixed_choices = [
f"#{choice}" if choice in default else f"{choice}" for choice in choices
]
return str(prefixed_choices)
else:
# prefix default choice
prefixed_choices = [
f"#{choice}" if str(choice) == str(default) else f"{choice}"
for choice in choices
]
return "|".join(prefixed_choices)
# if there are choices but no default, return the choices as pipe separated
# list
if choices and default is None:
return "|".join([str(choice) for choice in choices])
# as fallback return empty string
return ""
def module_options_to_snippet_options(module_options: Any) -> List[str]:
"""Convert module options to UltiSnips snippet options
Parameters
----------
module_options: Any
The "options" attribute of an AnsibleMapping object
Returns
-------
List[str]
A list of strings representing converted options
"""
options: List[str] = []
delimiter = ": " if args.style == "dictionary" else "="
if not module_options:
return options
# order by option name
module_options = sorted(module_options.items(), key=lambda x: x[0])
# order by "required" attribute
module_options = sorted(
module_options, key=lambda x: x[1].get("required", False), reverse=True
)
# insert an empty option above the list of non-required options
for index, (_, option) in enumerate(module_options):
if not option.get("required") and not args.comment_non_required:
if index != 0:
module_options.insert(index, (None, None))
break
for index, (name, option_data) in enumerate(module_options, start=1):
# insert a line to separate required/non-required options
if not name and not option_data:
options += [""]
else:
# set comment character for non-required options
if not option_data.get("required") and args.comment_non_required:
comment = "#"
else:
comment = ""
# the free_form option in some modules are special
if name == "free_form":
options += [
f"\t{comment}${{{index}:{name}{delimiter}{option_data_to_snippet_completion(option_data)}}}"
]
else:
options += [
f"\t{comment}{name}{delimiter}${{{index}:{option_data_to_snippet_completion(option_data)}}}"
]
return options
def convert_docstring_to_snippet(convert_docstring: Any, collection_name) -> List[str]:
"""Converts data about an Ansible module into an UltiSnips snippet string
Parameters
----------
convert_docstring: Any
An AnsibleMapping object representing the docstring for an Ansible
module
Returns
-------
str
A string representing an ultisnips compatible snippet of an Ansible
module
"""
snippet: List[str] = []
snippet_options = "b"
if "module" in convert_docstring.keys():
module_name = convert_docstring["module"]
module_short_description = convert_docstring["short_description"]
# use only the module name if ansible version < 2.10
if version.parse(ANSIBLE_VERSION) < version.parse("2.10"):
snippet_module_name = f"{module_name}:"
# use FQCN if ansible version is 2.10 or higher
else:
snippet_module_name = f"{collection_name}.{module_name}:"
snippet += [f'snippet {module_name} "{escape_strings(module_short_description)}" {snippet_options}']
if args.style == "dictionary":
snippet += [f"{snippet_module_name}"]
else:
snippet += [f"{snippet_module_name}:{' >' if convert_docstring.get('options') else ''}"]
module_options = module_options_to_snippet_options(convert_docstring.get("options"))
snippet += module_options
snippet += ["endsnippet"]
return snippet
def get_collection_name(filepath:str) -> str:
""" Returns the collection name for a full file path """
path_splitted = filepath.split('/')
collection_top_folder_index = path_splitted.index('ansible_collections')
collection_namespace = path_splitted[collection_top_folder_index + 1]
collection_name = path_splitted[collection_top_folder_index + 2]
# print(f"{collection_namespace}.{collection_name}")
return f"{collection_namespace}.{collection_name}"
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--output",
help=f"Output filename (default: {OUTPUT_FILENAME})",
default=OUTPUT_FILENAME,
)
parser.add_argument(
"--style",
help=f"YAML format used for snippets (default: {OUTPUT_STYLE[0]})",
choices=OUTPUT_STYLE,
default=OUTPUT_STYLE[0],
)
parser.add_argument(
'--user',
help="Include user modules",
action="store_true",
default=False
)
parser.add_argument(
'--no-description',
help="Remove options description",
action="store_true",
default=False
)
parser.add_argument(
'--comment-non-required',
help="Comment non-required options",
action="store_true",
default=False
)
args = parser.parse_args()
if version.parse(ANSIBLE_VERSION) < version.parse("2.10"):
print(f"ansible version {ANSIBLE_VERSION} doesn't support FQCN")
print("generated snippets will only use the module name e.g. 'yum' instead of 'ansible.builtin.yum'")
else:
print(f"ansible version {ANSIBLE_VERSION} supports using FQCN")
print("Generated snippets will use FQCN e.g. 'ansible.builtin.yum' instead of 'yum'")
print("Still, you only need to type 'yum' to trigger the snippet")
modules_docstrings = []
builtin_modules_paths = get_files_builtin()
for f in builtin_modules_paths:
docstring_builtin = get_module_docstring(f)
if docstring_builtin and docstring_builtin not in modules_docstrings:
docstring_builtin['collection_name'] = "ansible.builtin"
modules_docstrings.append(docstring_builtin)
system_modules_paths = get_files_collections()
for f in system_modules_paths:
docstring_system = get_module_docstring(f)
if docstring_system and docstring_system not in modules_docstrings:
collection_name = get_collection_name(f)
docstring_system['collection_name'] = collection_name
modules_docstrings.append(docstring_system)
if args.user:
user_modules_paths = get_files_collections(user=True)
for f in user_modules_paths:
docstring_user = get_module_docstring(f)
if docstring_user and docstring_user not in modules_docstrings:
collection_name = get_collection_name(f)
docstring_user['collection_name'] = collection_name
modules_docstrings.append(docstring_user)
with open(args.output, "w") as f:
f.writelines(f"{header}\n" for header in HEADER)
for docstring in modules_docstrings:
f.writelines(
f"{line}\n" for line in convert_docstring_to_snippet(docstring, docstring.get("collection_name"))
)

View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
cd "$(dirname "$0")"
ANSIBLE_MODULE_LOCATION="$(ansible --version | grep "ansible python module location" | sed -E 's/ +ansible python module location += +//g')"
PYTHONPATH="$(dirname "$ANSIBLE_MODULE_LOCATION")"
PYTHON_VERSION=$(basename "$(dirname "$PYTHONPATH")")
export PYTHONPATH
exec "$PYTHON_VERSION" generate.py $@

View file

@ -0,0 +1,45 @@
function! s:isAnsible()
let filepath = expand("%:p")
let filename = expand("%:t")
if filepath =~ '\v/(tasks|roles|handlers)/.*\.ya?ml$' | return 1 | en
if filepath =~ '\v/(group|host)_vars/' | return 1 | en
let s:ftdetect_filename_regex = '\v(playbook|site|main|local|requirements)\.ya?ml$'
if exists("g:ansible_ftdetect_filename_regex")
let s:ftdetect_filename_regex = g:ansible_ftdetect_filename_regex
endif
if filename =~ s:ftdetect_filename_regex | return 1 | en
let shebang = getline(1)
if shebang =~# '^#!.*/bin/env\s\+ansible-playbook\>' | return 1 | en
if shebang =~# '^#!.*/bin/ansible-playbook\>' | return 1 | en
return 0
endfunction
function! s:setupTemplate()
if exists("g:ansible_template_syntaxes")
let filepath = expand("%:p")
for syntax_name in items(g:ansible_template_syntaxes)
let s:syntax_string = '\v/'.syntax_name[0]
if filepath =~ s:syntax_string
execute 'set ft='.syntax_name[1].'.jinja2'
return
endif
endfor
endif
set ft=jinja2
endfunction
augroup ansible_vim_ftyaml_ansible
au!
au BufNewFile,BufRead * if s:isAnsible() | set ft=yaml.ansible | en
augroup END
augroup ansible_vim_ftjinja2
au!
au BufNewFile,BufRead *.j2 call s:setupTemplate()
augroup END
augroup ansible_vim_fthosts
au!
au BufNewFile,BufRead hosts set ft=ansible_hosts
augroup END

View file

@ -0,0 +1,2 @@
set isfname+=@-@
set path+=./../templates,./../files,templates,files

View file

@ -0,0 +1,9 @@
if exists("b:did_ftplugin")
finish
else
let b:did_ftplugin = 1
endif
setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions-=c
let b:undo_ftplugin = "setl comments< commentstring< formatoptions<"

View file

@ -0,0 +1,63 @@
let s:save_cpo = &cpo
set cpo&vim
setlocal indentexpr=GetAnsibleIndent(v:lnum)
setlocal indentkeys=!^F,o,O,0#,0},0],<:>,-,*<Return>
setlocal nosmartindent
setlocal expandtab
setlocal softtabstop=2
setlocal shiftwidth=2
setlocal commentstring=#%s
setlocal formatoptions+=cl
" c -> wrap long comments, including #
" l -> do not wrap long lines
let s:comment = '\v^\s*#' " # comment
let s:array_entry = '\v^\s*-\s' " - foo
let s:named_module_entry = '\v^\s*-\s*(name|hosts|role):\s*\S' " - name: 'do stuff'
let s:dictionary_entry = '\v^\s*[^:-]+:\s*$' " with_items:
let s:key_value = '\v^\s*[^:-]+:\s*\S' " apt: name=package
let s:scalar_value = '\v:\s*[>|\|]\s*$' " shell: >
let s:blank = '\v^\s*$' " line with only spaces
if exists('*GetAnsibleIndent')
finish
endif
function GetAnsibleIndent(lnum)
if a:lnum == 1 || !prevnonblank(a:lnum-1)
return 0
endif
if exists("g:ansible_unindent_after_newline")
if (a:lnum -1) != prevnonblank(a:lnum - 1)
return 0
endif
endif
let prevlnum = prevnonblank(a:lnum - 1)
let default = GetYAMLIndent(a:lnum)
let increase = indent(prevlnum) + &sw
let prevline = getline(prevlnum)
let line = getline(a:lnum)
if line !~ s:blank
return default " we only special case blank lines
elseif prevline =~ s:array_entry
if prevline =~ s:named_module_entry
return increase
else
return default
endif
elseif prevline =~ s:dictionary_entry
return increase
elseif prevline =~ s:key_value
if prevline =~ s:scalar_value
return increase
else
return default
endif
else
return default
endif
endfunction
let &cpo = s:save_cpo

View file

@ -0,0 +1,126 @@
" Vim syntax file
" Language: Ansible YAML/Jinja templates
" Maintainer: Dave Honneffer <pearofducks@gmail.com>
" Last Change: 2018.02.08
if !exists("main_syntax")
let main_syntax = 'yaml'
endif
if exists('b:current_syntax')
let s:current_syntax=b:current_syntax
unlet b:current_syntax
endif
syntax include @Jinja syntax/jinja2.vim
if exists('s:current_syntax')
let b:current_syntax=s:current_syntax
endif
" Jinja
" ================================
syn cluster jinjaSLSBlocks add=jinjaTagBlock,jinjaVarBlock,jinjaComment
" https://github.com/mitsuhiko/jinja2/blob/6b7c0c23/ext/Vim/jinja.vim
syn region jinjaTagBlock matchgroup=jinjaTagDelim start=/{%-\?/ end=/-\?%}/ containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaRaw,jinjaString,jinjaNested,jinjaComment,@jinjaSLSBlocks
syn region jinjaVarBlock matchgroup=jinjaVarDelim start=/{{-\?/ end=/-\?}}/ containedin=ALLBUT,yamlComment,jinjaTagBlock,jinjaVarBlock,jinjaRaw,jinjaString,jinjaNested,jinjaComment,@jinjaSLSBlocks
syn region jinjaComment matchgroup=jinjaCommentDelim start="{#" end="#}" containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaString,@jinjaSLSBlocks
highlight link jinjaVariable Constant
highlight link jinjaVarDelim Delimiter
" YAML
" ================================
if exists("g:ansible_yamlKeyName")
let s:yamlKey = g:ansible_yamlKeyName
else
let s:yamlKey = "yamlBlockMappingKey"
endif
" Reset some YAML to plain styling
" the number 80 in Ansible isn't any more important than the word root
highlight link yamlInteger NONE
highlight link yamlBool NONE
highlight link yamlFlowString NONE
" but it does make sense we visualize quotes easily
highlight link yamlFlowStringDelimiter Delimiter
" This is only found in stephypy/vim-yaml, since it's one line it isn't worth
" making conditional
highlight link yamlConstant NONE
fun! s:attribute_highlight(attributes)
if a:attributes =~ 'a'
syn match ansible_attributes "\v\w+\=" containedin=yamlPlainScalar
else
syn match ansible_attributes "\v^\s*\w+\=" containedin=yamlPlainScalar
endif
if a:attributes =~ 'n'
highlight link ansible_attributes NONE
elseif a:attributes =~ 'd'
highlight link ansible_attributes Comment
else
highlight default link ansible_attributes Structure
endif
endfun
if exists("g:ansible_attribute_highlight")
call s:attribute_highlight(g:ansible_attribute_highlight)
else
call s:attribute_highlight('ad')
endif
if exists("g:ansible_name_highlight")
execute 'syn keyword ansible_name name containedin='.s:yamlKey.' contained'
if g:ansible_name_highlight =~ 'd'
highlight link ansible_name Comment
else
highlight default link ansible_name Underlined
endif
endif
execute 'syn keyword ansible_debug_keywords debug containedin='.s:yamlKey.' contained'
highlight default link ansible_debug_keywords Debug
if exists("g:ansible_extra_keywords_highlight")
execute 'syn keyword ansible_extra_special_keywords
\ become become_exe become_flags become_method become_user become_pass prompt_l10n
\ debugger always_run check_mode diff no_log args tags force_handlers
\ vars vars_files vars_prompt delegate_facts delegate_to
\ any_errors_fatal ignore_errors ignore_unreachable max_fail_percentage
\ connection children hosts port remote_user module_defaults
\ environment fact_path gather_facts gather_subset gather_timeout
\ async poll throttle timeout order run_once serial strategy
\ containedin='.s:yamlKey.' contained'
if exists("g:ansible_extra_keywords_highlight_group")
execute 'highlight link ansible_extra_special_keywords '.g:ansible_extra_keywords_highlight_group
else
highlight link ansible_extra_special_keywords Structure
endif
endif
execute 'syn keyword ansible_normal_keywords
\ include include_role include_tasks include_vars import_role import_playbook import_tasks
\ when changed_when failed_when block rescue always notify listen register
\ action local_action post_tasks pre_tasks tasks handlers roles collections
\ containedin='.s:yamlKey.' contained'
if exists("g:ansible_normal_keywords_highlight")
execute 'highlight link ansible_normal_keywords '.g:ansible_normal_keywords_highlight
else
highlight default link ansible_normal_keywords Statement
endif
execute 'syn keyword ansible_loop_keywords
\ loop loop_control until retries delay
\ containedin='.s:yamlKey.' contained'
execute 'syn match ansible_loop_keywords "\vwith_.+" containedin='.s:yamlKey.' contained'
if exists("g:ansible_loop_keywords_highlight")
execute 'highlight link ansible_loop_keywords '.g:ansible_loop_keywords_highlight
" backward compatibility: ansible_with_keywords_highlight replaced by ansible_loop_keywords_highlight
elseif exists("g:ansible_with_keywords_highlight")
execute 'highlight link ansible_loop_keywords '.g:ansible_with_keywords_highlight
else
highlight default link ansible_loop_keywords Statement
endif
let b:current_syntax = "ansible"

View file

@ -0,0 +1,31 @@
" Vim syntax file
" Language: Ansible hosts files
" Maintainer: Dave Honneffer <pearofducks@gmail.com>
" Last Change: 2015.09.23
if exists("b:current_syntax")
finish
endif
syn case ignore
syn match hostsFirstWord "\v^\S+"
syn match hostsAttributes "\v\S*\="
syn region hostsHeader start="\v^\s*\[" end="\v\]"
syn keyword hostsHeaderSpecials children vars containedin=hostsHeader contained
syn match hostsComment "\v^[#;].*$"
highlight default link hostsFirstWord Label
highlight default link hostsHeader Define
highlight default link hostsComment Comment
highlight default link hostsHeaderSpecials Identifier
highlight default link hostsAttributes Structure
if exists("g:ansible_attribute_highlight")
if g:ansible_attribute_highlight =~ 'n'
highlight link hostsAttributes NONE
elseif g:ansible_attribute_highlight =~ 'd'
highlight link hostsAttributes Comment
endif
endif
let b:current_syntax = "ansible_hosts"

View file

@ -0,0 +1,97 @@
" Vim syntax file
" Language: Jinja2 - with special modifications for compound-filetype
" compatibility
" Maintainer: Dave Honneffer <pearofducks@gmail.com>
" Last Change: 2018.02.11
if !exists("main_syntax")
let main_syntax = 'jinja2'
endif
let b:current_syntax = ''
unlet b:current_syntax
syntax case match
" Jinja template built-in tags and parameters (without filter, macro, is and raw, they
" have special threatment)
syn keyword jinjaStatement containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained and if else in not or recursive as import
syn keyword jinjaStatement containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained is filter skipwhite nextgroup=jinjaFilter
syn keyword jinjaStatement containedin=jinjaTagBlock contained macro skipwhite nextgroup=jinjaFunction
syn keyword jinjaStatement containedin=jinjaTagBlock contained block skipwhite nextgroup=jinjaBlockName
" Variable Names
syn match jinjaVariable containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[a-zA-Z_][a-zA-Z0-9_]*/
syn keyword jinjaSpecial containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained false true none False True None loop super caller varargs kwargs
" Filters
syn match jinjaOperator "|" containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained skipwhite nextgroup=jinjaFilter
syn match jinjaFilter contained /[a-zA-Z_][a-zA-Z0-9_]*/
syn match jinjaFunction contained /[a-zA-Z_][a-zA-Z0-9_]*/
syn match jinjaBlockName contained /[a-zA-Z_][a-zA-Z0-9_]*/
" Jinja template constants
syn region jinjaString containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained start=/"/ skip=/\(\\\)\@<!\(\(\\\\\)\@>\)*\\"/ end=/"/
syn region jinjaString containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained start=/'/ skip=/\(\\\)\@<!\(\(\\\\\)\@>\)*\\'/ end=/'/
syn match jinjaNumber containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[0-9]\+\(\.[0-9]\+\)\?/
" Operators
syn match jinjaOperator containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[+\-*\/<>=!,:]/
syn match jinjaPunctuation containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /[()\[\]]/
syn match jinjaOperator containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained /\./ nextgroup=jinjaAttribute
syn match jinjaAttribute contained /[a-zA-Z_][a-zA-Z0-9_]*/
" Jinja template tag and variable blocks
syn region jinjaNested matchgroup=jinjaOperator start="(" end=")" transparent display containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained
syn region jinjaNested matchgroup=jinjaOperator start="\[" end="\]" transparent display containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained
syn region jinjaNested matchgroup=jinjaOperator start="{" end="}" transparent display containedin=jinjaVarBlock,jinjaTagBlock,jinjaNested contained
syn region jinjaTagBlock matchgroup=jinjaTagDelim start=/{%-\?/ end=/-\?%}/ containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaRaw,jinjaString,jinjaNested,jinjaComment
syn region jinjaVarBlock matchgroup=jinjaVarDelim start=/{{-\?/ end=/-\?}}/ containedin=ALLBUT,yamlComment,jinjaTagBlock,jinjaVarBlock,jinjaRaw,jinjaString,jinjaNested,jinjaComment
" Jinja template 'raw' tag
syn region jinjaRaw matchgroup=jinjaRawDelim start="{%\s*raw\s*%}" end="{%\s*endraw\s*%}" containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaString,jinjaComment
" Jinja comments
syn region jinjaComment matchgroup=jinjaCommentDelim start="{#" end="#}" containedin=ALLBUT,jinjaTagBlock,jinjaVarBlock,jinjaString
" Block start keywords. A bit tricker. We only highlight at the start of a
" tag block and only if the name is not followed by a comma or equals sign
" which usually means that we have to deal with an assignment.
syn match jinjaStatement containedin=jinjaTagBlock contained /\({%-\?\s*\)\@<=\<[a-zA-Z_][a-zA-Z0-9_]*\>\(\s*[,=]\)\@!/
" and context modifiers
syn match jinjaStatement containedin=jinjaTagBlock contained /\<with\(out\)\?\s\+context\>/
" Define the default highlighting.
if !exists("did_jinja_syn_inits")
command -nargs=+ HiLink hi def link <args>
HiLink jinjaPunctuation jinjaOperator
HiLink jinjaAttribute jinjaVariable
HiLink jinjaFunction jinjaFilter
HiLink jinjaTagDelim jinjaTagBlock
HiLink jinjaVarDelim jinjaVarBlock
HiLink jinjaCommentDelim jinjaComment
HiLink jinjaRawDelim jinja
HiLink jinjaSpecial Special
HiLink jinjaOperator Normal
HiLink jinjaRaw Normal
HiLink jinjaTagBlock PreProc
HiLink jinjaVarBlock PreProc
HiLink jinjaStatement Statement
HiLink jinjaFilter Function
HiLink jinjaBlockName Function
HiLink jinjaVariable Identifier
HiLink jinjaString Constant
HiLink jinjaNumber Constant
HiLink jinjaComment Comment
delcommand HiLink
endif
let b:current_syntax = "jinja2"

View file

@ -0,0 +1,31 @@
Copyright (c) 2009 by the Jinja Team, see AUTHORS for more details.
Some rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.