├── .gitignore ├── LICENSE ├── README.md ├── autoload └── plist.vim └── plugin └── plist.vim /.gitignore: -------------------------------------------------------------------------------- 1 | .*.sw[a-z] 2 | .*.un~ 3 | doc/tags 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010-2014 Elliott Linder 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim-plist 2 | 3 | ![Screenshot of vim-plist in action](http://i.imgur.com/ezBKTK7.png) 4 | 5 | This vim bundle adds complete support for [property lists](plist) (*plist*) 6 | files. 7 | 8 | The plugin uses the underlying **plutil** (or **plistutil**) tool for 9 | manipulating property lists. It supports reading and writing in *binary*, 10 | *xml* and *json* formats. 11 | 12 | ## Requirements 13 | 14 | - Vim 7.4 or later 15 | - plutil (bundled with macOS) or [plistutil](libplist) 16 | 17 | ## Installation 18 | 19 | Use your favorite plugin manager. 20 | 21 | - [NeoBundle][neobundle] 22 | 23 | ```vim 24 | NeoBundle 'darfink/vim-plist' 25 | ``` 26 | 27 | - [Vundle][vundle] 28 | 29 | ```vim 30 | Bundle 'darfink/vim-plist' 31 | ``` 32 | 33 | - [Pathogen][pathogen] 34 | 35 | ```sh 36 | git clone git://github.com/darfink/vim-plist.git ~/.vim/bundle/vim-plist 37 | ``` 38 | 39 | - [vim-plug][vim-plug] 40 | 41 | ```vim 42 | Plug 'darfink/vim-plist' 43 | ``` 44 | 45 | ## Usage 46 | 47 | None! Just go ahead and edit plist files. Although there are some customization 48 | options availabe. 49 | 50 | - Change the display format used when editing property lists: 51 | 52 | ```vim 53 | let g:plist_display_format = 'xml' 54 | ``` 55 | 56 | Available options for this command; *json* or *xml*. This does not only 57 | control the display format of binary property lists, but also for *json* 58 | and *xml* files. If the option is set to *json*, property lists in *xml* 59 | format will be displayed as *json* as well (but the format used when saving 60 | will be preserved). 61 | 62 | 63 | - Change the plist format used when saving property lists: 64 | 65 | ```vim 66 | let g:plist_save_format = '' 67 | ``` 68 | 69 | By default, property lists are saved in the same format as they had when 70 | opened. If you want to override this and always save property lists in a 71 | specific format, you can use *json*, *xml* or *binary* format. 72 | 73 | ## Notes 74 | 75 | If you want syntax checking I highly recommend [Syntastic][syntastic] since it 76 | has integrated support for property lists. 77 | 78 | In case you use the `sudo tee` trick for writing to root owned files when using 79 | Vim, it will **not** work with plist files. This is because the *tee* trick 80 | uses the underlying Vim *write* function which bypasses the plugins 81 | `BufWriteCmd` and `FileWriteCmd` hooks. 82 | 83 | This does not add *plist* as a new filetype, but merely conversion 84 | functionality between the different representable formats. 85 | 86 | ## Todo 87 | 88 | - Add saving format options while editing (e.g. `:PlistSaveAs json`) 89 | 90 | - Change display format while editing (e.g. `:PlistFormat xml`) 91 | 92 | ## License 93 | 94 | MIT: [License][license] 95 | 96 | [neobundle]: https://github.com/Shougo/neobundle.vim 97 | [vundle]: https://github.com/gmarik/vundle 98 | [pathogen]: https://github.com/tpope/vim-pathogen 99 | [vim-plug]: https://github.com/junegunn/vim-plug 100 | [vim-json]: https://github.com/elzr/vim-json 101 | [syntastic]: https://github.com/scrooloose/syntastic 102 | [license]: https://github.com/darfink/vim-plist/blob/master/LICENSE 103 | [libplist]: https://github.com/libimobiledevice/libplist 104 | [plist]: http://en.wikipedia.org/wiki/Property_list 105 | -------------------------------------------------------------------------------- /autoload/plist.vim: -------------------------------------------------------------------------------- 1 | " Maintainer: Elliott Linder 2 | " URL: http://github.com/darfink/vim-plist 3 | " License: MIT 4 | 5 | " Defines the mapping between the vim-plist options and plutil arguments 6 | let s:mapping = { 'json': 'json', 'binary': 'binary1', 'xml': 'xml1' } 7 | let s:mapping_util = { 'binary': 'bin', 'xml': 'xml' } 8 | let s:has_plist = executable('plutil') 9 | let s:has_plistutil = executable('plistutil') 10 | 11 | function! s:warn(message) abort 12 | echohl WarningMsg 13 | unsilent echomsg a:message 14 | call inputsave() | call input('(Press ENTER)') | call inputrestore() 15 | echohl None 16 | endfunction 17 | 18 | function! plist#BufReadCmd() abort 19 | if s:ReadCmd(1) 20 | call plist#BufReadPost() 21 | doautocmd BufReadPost 22 | return 1 23 | endif 24 | return 0 25 | endfunction 26 | function! plist#FileReadCmd() abort 27 | if s:ReadCmd(0) 28 | call plist#FileReadPost() 29 | execute 'doautocmd FileReadPost ' .. fnameescape(expand('')) 30 | return 1 31 | endif 32 | return 0 33 | endfunction 34 | function! s:ReadCmd(buf_read) abort 35 | " Get the filename of the current argument 36 | let plist_filename = expand('') 37 | 38 | " If the file does not exist, there is nothing to convert 39 | if !filereadable(plist_filename) 40 | execute 'doautocmd BufNewFile ' .. fnameescape(plist_filename) 41 | return 0 42 | endif 43 | 44 | if !s:has_plist && !s:has_plistutil 45 | echoerr 'plutil is not found in $PATH' 46 | silent execute 'read ' . fnameescape(plist_filename) 47 | return 0 48 | endif 49 | 50 | " Determine which format should be used when saving 51 | let b:plist_original_format = plist#DetectFormat(plist_filename) 52 | 53 | " Convert the file's content and read it into the current buffer 54 | if s:has_plist 55 | execute 'silent read !plutil -convert ' . s:mapping[g:plist_display_format] 56 | \ . ' -r ' . shellescape(plist_filename, 1) . ' -o -' 57 | else 58 | if g:plist_display_format == 'json' 59 | call s:warn('Plistutil does not support json display format') 60 | endif 61 | 62 | execute 'silent read !plistutil -f xml -i ' . shellescape(plist_filename, 1) . ' -o -' 63 | endif 64 | 65 | let b:read_error = v:shell_error != 0 66 | 67 | if (v:shell_error) 68 | call s:warn('Plist could not be converted!') 69 | 70 | " Only wipeout the buffer if one was being created to start with. 71 | " FileReadCmd just reads the content into the existing buffer 72 | if a:buf_read 73 | let plist_bufnr = bufnr() 74 | silent execute 'buffer! #' 75 | silent execute 'bwipeout! ' . plist_bufnr 76 | endif 77 | 78 | return 0 79 | endif 80 | 81 | " Tell the user about any information parsed 82 | call plist#DisplayInfo(plist_filename, b:plist_original_format) 83 | 84 | return 1 85 | endfunction 86 | 87 | function! plist#BufWriteCmd() abort 88 | return s:WriteCmd() 89 | endfunction 90 | function! plist#FileWriteCmd() abort 91 | return s:WriteCmd() 92 | endfunction 93 | function! s:WriteCmd() abort 94 | " Cache the argument filename destination 95 | let filename = resolve(expand('')) 96 | 97 | " If the user has not specified his preferred format when saving, use the 98 | " same format as the file had originally. Otherwise the user option takes 99 | " precedence. 100 | let save_format = !len(g:plist_save_format) ? b:plist_original_format : g:plist_save_format 101 | 102 | if s:has_plist 103 | " Use plutil even when the current format is the same as the target 104 | " format, since it will give the user additional error checking (they will 105 | " be notified if there is any error upon saving). 106 | execute "silent '[,']write !plutil -convert " . s:mapping[save_format] . 107 | \ ' - -o ' . shellescape(filename, 1) 108 | else 109 | if b:plist_original_format == 'json' 110 | call s:warn('Plistutil cannot process json, saving buffer contents directly') 111 | else 112 | execute "silent '[,']write !plistutil -f " . s:mapping_util[save_format] . 113 | \ ' -i - -o ' . shellescape(filename, 1) 114 | endif 115 | endif 116 | 117 | if (v:shell_error) 118 | call s:warn('Plist could not be saved!') 119 | return 0 120 | else 121 | " Give the user visual feedback about the write 122 | call plist#DisplayInfo(filename, save_format) 123 | 124 | " This indicates a successful write 125 | setlocal nomodified 126 | endif 127 | 128 | return 1 129 | endfunction 130 | 131 | function! plist#BufReadPost() abort 132 | " This needs to be validated... 133 | let levels = &undolevels 134 | set undolevels=-1 135 | silent 1delete 136 | let &undolevels = levels 137 | 138 | " Update the file content type 139 | call plist#SetFiletype() 140 | endfunction 141 | 142 | function! plist#FileReadPost() abort 143 | " Update the file content type 144 | call plist#SetFiletype() 145 | endfunction 146 | 147 | function! plist#SetFiletype() abort 148 | if g:plist_display_format == 'json' && s:has_plist || b:plist_original_format == 'json' && !s:has_plist 149 | let filetype = len(getcompletion('json', 'filetype')) ? 'json' : 'javascript' 150 | else 151 | let filetype = 'xml' 152 | endif 153 | if &filetype !=# filetype 154 | execute 'set filetype=' . filetype 155 | endif 156 | endfunction 157 | 158 | function! plist#DetectFormat(filename) abort 159 | let content = readfile(a:filename, 1, 2) 160 | 161 | if len(content) > 0 && content[0] =~ "^bplist" 162 | return 'binary' 163 | elseif len(content) > 1 && content[1] =~ '^ 2 | " URL: http://github.com/darfink/vim-plist 3 | " License: MIT 4 | 5 | if exists('g:loaded_plist') || &cp || !executable('plutil') 6 | finish 7 | endif 8 | 9 | let g:loaded_plist = '0.1' 10 | 11 | augroup plist 12 | " Remove all plist autocommands 13 | autocmd! 14 | 15 | " Output operations 16 | autocmd BufWriteCmd ++nested *.plist call plist#BufWriteCmd() 17 | autocmd FileWriteCmd ++nested *.plist call plist#FileWriteCmd() 18 | 19 | " Input operations 20 | autocmd BufReadCmd *.plist call plist#BufReadCmd() 21 | autocmd FileReadCmd *.plist call plist#FileReadCmd() 22 | 23 | " TODO: Add support for extensionless plists 24 | augroup END 25 | 26 | if !exists('g:plist_display_format') 27 | " Specifies the display format for all plists 28 | let g:plist_display_format = 'xml' 29 | end 30 | 31 | if !exists('g:plist_save_format') 32 | " Saves in the same format as the plist was originally opened as by default. 33 | " Available options are: xml, json & binary 34 | let g:plist_save_format = '' 35 | end 36 | --------------------------------------------------------------------------------