├── LICENSE ├── README.md └── ftplugin └── yaml.vim /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Yann Rabiller 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yaml Revealer 2 | 3 | **Yaml Revealer** is a vim plugin which allows you to handle the full tree structure of a Yaml key. 4 | 5 | If you often use Yaml files, you know they are very readable at the beginning, but also that they can become a bit harder to read when becoming longer and longer… **Yaml Revealer** is here to guide you when you're lost. 6 | 7 | ![demo gif](https://user-images.githubusercontent.com/5675200/40068961-32d58f2a-586a-11e8-8db4-4da212f2f3b1.gif) 8 | 9 | ## Features 10 | 11 | ### Reveal the full tree structure of a key 12 | 13 | myRoot: 14 | firstChild: 15 | myVar: "foo" 16 | secondChild: 17 | myVar: 18 | foo: "foo" 19 | myVar2: 20 | foo: "foo" 21 | bar: "bar" 22 | > specialKey: "Hi" 23 | 24 | Moving to the indicated line will automatically make vim echo 25 | 26 | myRoot > secondChild > myVar2 27 | 28 | ### Search for a specific key 29 | 30 | Typing `:call SearchYamlKey()` will display a prompt to search a specific key. 31 | 32 | Search for a Yaml key: 33 | 34 | Searching for 35 | 36 | myVar2>specialKey 37 | 38 | or 39 | 40 | myvar2 > specialKey 41 | 42 | will find the concerned line. 43 | 44 | ## Requirements 45 | 46 | This plugin requires vim version superior to `8.0.1630` or neovim. 47 | 48 | ## Configuration 49 | 50 | - You can configure the separator between the keys by setting the variable 51 | `g:yaml_revealer_separator`. Default is `>` 52 | 53 | - By default, a list indicator (`[]`) is added to the path. This can be removed 54 | by setting `g:yaml_revealer_list_indicator` to 0 55 | 56 | - You can configure how the YAML path is displayed by setting 57 | `g:yaml_revealer_display_mode`. Available options: 58 | - `'echo'` (default): Display path in the command line 59 | - `'virtual'`: Show path as virtual text at the end of the current line (Neovim only) 60 | - `'statusline'`: Store path in a variable for status line integration 61 | 62 | - You can limit the maximum display width of YAML paths to prevent the "Press ENTER" 63 | prompt in Vim by setting `g:yaml_revealer_max_width`. Set to 0 (default) for no 64 | truncation, or any positive number to limit path width. Paths exceeding this 65 | width will be truncated with "..." appended. 66 | 67 | ### Display Mode Examples 68 | 69 | **Virtual text mode (recommended for Neovim):** 70 | ```vim 71 | let g:yaml_revealer_display_mode = 'virtual' 72 | ``` 73 | 74 | (or `g:yaml_revealer_display_mode = 'virtual'` in lua config) 75 | 76 | Shows the YAML path as subtle text at the end of the current line. 77 | 78 | **Status line integration:** 79 | ```vim 80 | let g:yaml_revealer_display_mode = 'statusline' 81 | ``` 82 | 83 | (or `g:yaml_revealer_display_mode = 'statusline'` in lua config) 84 | 85 | Then add `%{YamlRevealerStatusLine()}` to your status line configuration. 86 | 87 | If you use [lualine](https://github.com/nvim-lualine/lualine.nvim), you can add this to your config: 88 | 89 | ```lua 90 | { 91 | function() 92 | return vim.fn.YamlRevealerStatusLine() 93 | end, 94 | cond = function() 95 | return vim.bo.filetype == "yaml" 96 | end, 97 | }, 98 | ``` 99 | 100 | **Enhanced echo mode:** 101 | ```vim 102 | let g:yaml_revealer_display_mode = 'echo' 103 | ``` 104 | 105 | (or `g:yaml_revealer_display_mode = 'echo'` in lua config) 106 | 107 | Uses highlighted echo messages that are more visible than the default. 108 | 109 | **Truncation configuration:** 110 | ```vim 111 | " Limit path display to 80 characters to prevent "Press ENTER" prompts 112 | let g:yaml_revealer_max_width = 80 113 | ``` 114 | 115 | (or `g:yaml_revealer_max_width = 80` in lua config) 116 | 117 | Prevents long YAML paths from wrapping and causing Vim's "Press ENTER" prompt. 118 | 119 | ## Installation 120 | 121 | ### Vundle Installation 122 | 123 | Add `Plugin 'Einenlum/yaml-revealer'` to your `.vimrc`, reload your config and run a `Plugin:Install`. 124 | 125 | ### vim-plug Installation 126 | 127 | Add `Plug 'Einenlum/yaml-revealer'` to your `.vimrc`, reload your config and run a `:PlugInstall`. 128 | 129 | ### Lazy.nvim Installation 130 | 131 | ```lua 132 | { 133 | 'Einenlum/yaml-revealer', 134 | config = function() 135 | -- Recommended for Neovim users 136 | vim.g.yaml_revealer_display_mode = 'virtual' 137 | end, 138 | ft = 'yaml', 139 | } 140 | ``` 141 | 142 | ## Credits 143 | 144 | - Thanks to [@PedroTroller](https://github.com/PedroTroller) for his useful help :). 145 | - Thanks to [@ezpuzz](https://github.com/ezpuzz) for improving performance. 146 | - Thanks to [@mosheavni](https://github.com/mosheavni) for adding lists support. 147 | -------------------------------------------------------------------------------- /ftplugin/yaml.vim: -------------------------------------------------------------------------------- 1 | " Reveals the full tree structure of a Yaml key 2 | " and allows to find a Yaml key 3 | " 4 | " Maintainer: Einenlum 5 | " URL: https://github.com/Einenlum/yaml-revealer 6 | 7 | " Global variables 8 | let g:yaml_revealer_separator = get(g:, 'yaml_revealer_separator', '>') 9 | let g:yaml_revealer_list_indicator = get(g:, 'yaml_revealer_list_indicator', 1) 10 | let g:yaml_revealer_display_mode = get(g:, 'yaml_revealer_display_mode', 'echo') 11 | let g:yaml_revealer_max_width = get(g:, 'yaml_revealer_max_width', 0) 12 | 13 | function! TrimList(l) 14 | let trimWords = [] 15 | for word in a:l 16 | call add(trimWords, trim(word)) 17 | endfor 18 | 19 | return trimWords 20 | endfunction 21 | 22 | function! TruncatePath(path) 23 | if g:yaml_revealer_max_width <= 0 24 | return a:path 25 | endif 26 | 27 | let max_width = g:yaml_revealer_max_width 28 | 29 | if len(a:path) <= max_width 30 | return a:path 31 | endif 32 | 33 | " Truncate and add ellipsis 34 | let truncated = a:path[0:(max_width - 4)] 35 | return truncated . '...' 36 | endfunction 37 | 38 | function! GetIndentationStep() 39 | call cursor(1,1) 40 | call search('^\s') 41 | return indent(".") 42 | endfunction 43 | 44 | function! KeyNotFound(keyName) 45 | echo "\n\"".a:keyName."\" not found." 46 | echo "\nThe correct syntax is: firstVar>secondVar>thirdVar" 47 | endfunction 48 | 49 | function! SearchYamlKey() 50 | let userInput = input('Search for a Yaml key: ') 51 | let indentationStep = GetIndentationStep() 52 | 53 | " reset cursor 54 | call cursor(1,1) 55 | let inputList = split(userInput, g:yaml_revealer_separator) 56 | let inputList = TrimList(inputList) 57 | 58 | " We look for the first match at 0, then at 2 or 4, and so on… 59 | " If not found at more that 10 the script stops 60 | let found = 0 61 | let indentationSearch = 0 62 | let loopCount = 0 63 | while !found 64 | let loopCount += 1 65 | if indentationSearch == 0 66 | let found = search('^'.inputList[0].':') 67 | else 68 | let found = search('^\s\{'.indentationSearch.'}'.inputList[0].':') 69 | endif 70 | let indentationSearch += indentationStep 71 | if loopCount > 10 72 | :call KeyNotFound(inputList[0]) 73 | return 0 74 | endif 75 | endwhile 76 | 77 | " When we have found the first match 78 | " If there are other keys… 79 | if len(inputList) > 1 80 | let range = range(1, (len(inputList) - 1)) 81 | for i in range 82 | let currentIndent = indent(".") 83 | let indentationSearch = currentIndent + indentationStep 84 | let found = search('^\s\{'.indentationSearch.'}'.inputList[i].':') 85 | if found == 0 86 | :call KeyNotFound(inputList[i]) 87 | return 0 88 | endif 89 | endfor 90 | endif 91 | endfunction 92 | 93 | function! GetAncestors(line) 94 | " Handle edge cases for empty files or invalid lines 95 | if a:line <= 0 || a:line > line('$') 96 | return '' 97 | endif 98 | 99 | " For root level keys (indent 0), return the key name itself 100 | if(indent(a:line) == 0) 101 | if(getline(a:line) !~ '^\s*$' && getline(a:line) =~ ':') " not empty and has colon 102 | return matchstr(getline(a:line), '\s*[\-]\?\s*\zs.\+\ze:') 103 | elseif(getline(a:line) =~ '^\s*$') " empty line 104 | " sometimes there are newlines within a multiline key 105 | if a:line > 1 106 | return GetAncestors(a:line-1) " return ancestors of previous line 107 | else 108 | return '' " first line is empty 109 | endif 110 | else 111 | return '' " not a key line 112 | endif 113 | endif 114 | 115 | let currentIndent = indent(a:line) 116 | let lowerIndent = max([0, currentIndent - 1]) 117 | 118 | " check if line is part of a list 119 | let isList = '' 120 | if(getline(a:line) =~# '^\s*-') 121 | let isList = '[]' 122 | " find the first key above this in the file that has is not a list member 123 | let lastKeyLine = search('^\s\{0,'.currentIndent.'}[^-]\S\+:', 'bnW') 124 | else 125 | " find the first key above this in the file that has a lower indent or a 126 | " containing list member 127 | let lastKeyLine = search('^\s\{0,'.lowerIndent.'}\(\S\+\|-\s\S\+\):', 'bnW') 128 | let lastKeyLineContent = getline(lastKeyLine) 129 | " check if the containing key is not a member of the same object 130 | if(lastKeyLineContent =~# ':\s.\+$' && lastKeyLineContent =~# '^\s*-') 131 | let lastKeyLine = search('^\s\{0,'.lowerIndent.'}\(\S\+\|-\s\S\+\):\s*$', 'zbnW') 132 | let isList = '[]' 133 | endif 134 | endif 135 | 136 | " If no parent key found, return empty 137 | if lastKeyLine == 0 138 | return '' 139 | endif 140 | 141 | if(!g:yaml_revealer_list_indicator) 142 | let isList = '' 143 | endif 144 | 145 | let key = matchstr(getline(lastKeyLine), '\s*[\-]\?\s*\zs.\+\ze:').isList 146 | 147 | if(indent(lastKeyLine) > 0) 148 | return GetAncestors(lastKeyLine).' '.g:yaml_revealer_separator.' '.key 149 | endif 150 | 151 | return key 152 | endfunction 153 | 154 | " Initialize namespace ID globally 155 | if !exists('g:yaml_revealer_ns_id') 156 | let g:yaml_revealer_ns_id = has('nvim') ? nvim_create_namespace('yaml_revealer') : 0 157 | endif 158 | 159 | function! DisplayYamlPath() 160 | let path = GetAncestors(line('.')) 161 | 162 | if g:yaml_revealer_display_mode == 'virtual' 163 | " Clear existing virtual text for this buffer and namespace 164 | if has('nvim') && exists('*nvim_buf_clear_namespace') 165 | call nvim_buf_clear_namespace(0, g:yaml_revealer_ns_id, 0, -1) 166 | endif 167 | 168 | " Add virtual text (Neovim only) 169 | if has('nvim') && exists('*nvim_buf_set_extmark') 170 | if path != '' 171 | try 172 | call nvim_buf_set_extmark(0, g:yaml_revealer_ns_id, line('.') - 1, -1, { 173 | \ 'virt_text': [[' → ' . path, 'Comment']], 174 | \ 'virt_text_pos': 'eol' 175 | \ }) 176 | catch 177 | " Fallback to echo if virtual text fails 178 | redraw | echo "VirtText Error: " . v:exception . " | Path: " . path 179 | endtry 180 | endif 181 | else 182 | " API not available, fallback to echo 183 | if path != '' 184 | redraw | echo "No VirtText API | Path: " . path 185 | endif 186 | endif 187 | elseif g:yaml_revealer_display_mode == 'statusline' 188 | " Store path in a variable that can be used in statusline 189 | let b:yaml_path = path 190 | else 191 | " Default echo behavior with better visibility and truncation 192 | if path != '' 193 | let truncated_path = TruncatePath(path) 194 | redraw 195 | echohl MoreMsg 196 | echo truncated_path 197 | echohl None 198 | endif 199 | endif 200 | endfunction 201 | 202 | function! YamlRevealerStatusLine() 203 | return get(b:, 'yaml_path', '') 204 | endfunction 205 | 206 | augroup YamlRevealer 207 | au! 208 | if(&filetype =~# 'yaml') 209 | autocmd CursorMoved call DisplayYamlPath() 210 | endif 211 | aug END 212 | --------------------------------------------------------------------------------