├── .gitignore ├── LICENSE ├── README.md ├── autoload ├── swiftdocstring.vim └── swiftdocstring │ ├── docstring.vim │ ├── keywords.vim │ ├── options.vim │ ├── parser.vim │ ├── regex.vim │ ├── template.vim │ └── utils.vim ├── doc └── swiftdocstring.txt ├── ftdetect └── swift.vim └── plugin └── swiftdocstring.vim /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/vim,macos 2 | 3 | ### macOS ### 4 | # General 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | 9 | # Icon must end with two \r 10 | Icon 11 | 12 | # Thumbnails 13 | ._* 14 | 15 | # Files that might appear in the root of a volume 16 | .DocumentRevisions-V100 17 | .fseventsd 18 | .Spotlight-V100 19 | .TemporaryItems 20 | .Trashes 21 | .VolumeIcon.icns 22 | .com.apple.timemachine.donotpresent 23 | 24 | # Directories potentially created on remote AFP share 25 | .AppleDB 26 | .AppleDesktop 27 | Network Trash Folder 28 | Temporary Items 29 | .apdisk 30 | 31 | ### Vim ### 32 | # Swap 33 | [._]*.s[a-v][a-z] 34 | [._]*.sw[a-p] 35 | [._]s[a-rt-v][a-z] 36 | [._]ss[a-gi-z] 37 | [._]sw[a-p] 38 | 39 | # Session 40 | Session.vim 41 | 42 | # Temporary 43 | .netrwhist 44 | *~ 45 | # Auto-generated tag files 46 | tags 47 | # Persistent undo 48 | [._]*.un~ 49 | 50 | 51 | # End of https://www.gitignore.io/api/vim,macos 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Witek Bobrowski 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim-swiftdocstring 2 |

3 | 4 | screenshot 5 | 6 |

7 |

8 | Version 9 | Contact 10 |

11 |

12 | A simple vim/neovim plugin that provides Xcode-like docstring templates for Swift 13 |

14 | 15 | ## Features 16 | 17 | - 📑 Generate Swift docstrings for types, functions and properties 18 | - 🎛 Support for `///` and `/** ... */` delimiters 19 | - 🎨 Even more customizable output 20 | - 🔨 Xcode-like style of docstrings 21 | - 🧠 Better context awarness than in Xcode 22 | - 🔥 Fast parsing using regular expressions 23 | - 👌 Zero dependency (**100%** vim-script) 24 | 25 | ## Installation 26 | 27 | ##### vim-plug 28 | ```vim 29 | Plug 'witekbobrowski/vim-swiftdocstring' 30 | ``` 31 | 32 | ##### Vundle 33 | ```vim 34 | Plugin 'witekbobrowski/vim-swiftdocstring' 35 | ``` 36 | 37 | ##### Dein 38 | ```vim 39 | call dein#add('witekbobrowski/vim-swiftdocstring') 40 | ``` 41 | 42 | ## Usage 43 | 44 | ##### Commands 45 | - `:SwiftDocstringCurrent` 46 | 47 | > Generates docstring for the context in the current line of the cursor 48 | 49 | - `:SwiftDocstringTypes` 50 | 51 | > Generates docstrings for every type declaration in current buffer 52 | 53 | - `:SwiftDocstringFunctions` 54 | 55 | > Generates docstrings for every function declaration in current buffer 56 | 57 | ##### Mappings 58 | 59 | For your own convenience add the following to your `.vimrc` 60 | ```vim 61 | " Generate docstring for current context on 'Tab' + '/' 62 | autocmd Filetype swift nnoremap / :SwiftDocstringCurrent 63 | ``` 64 | 65 | ##### Options 66 | 67 | For customized behaviour, change values of these properties. Values presented 68 | below are the defaults. 69 | ```vim 70 | " Use Multi-line delimiter '/** ... */' instead of single-line '///' 71 | " Boolen value (0 or 1) 72 | let g:swiftdocstring#use_multi_line_delimiter = 0 73 | ``` 74 | ```vim 75 | " Set indentation for dosctring between the text and delimiters 76 | " Integer value (>=0) 77 | let g:swiftdocstring#text_indentation_level = 1 78 | ``` 79 | ```vim 80 | " Use placeholders for some components of the docstring 81 | " Boolen value (0 or 1) 82 | let g:swiftdocstring#use_placeholders = 1 83 | ``` 84 | ```vim 85 | " Placeholders to be used if setting above is set as true 86 | " Two element string array 87 | let g:swiftdocstring#placeholder_template = ['<#', '#>'] 88 | ``` 89 | 90 | ## Documentation 91 | 92 | Everything you need to know about the usage of this plugin is basically here 93 | in this readme. Feel free to dive into the codebase for more answers. Very 94 | basic documentation is available from inside of vim itself with `:h` command. 95 | 96 | ```vim 97 | :help swiftdocstring 98 | ``` 99 | 100 | ## Why 101 | 102 | This plugin was made under the desire to have a powerful Swift development 103 | environment in vim. Although the plugin brings only a small feature ported 104 | from Xcode, I want to add at least one brick to the wall. This is as far as 105 | I can go at the moment with the amount of time it took to create this piece 106 | of code. I intend it to remain simple, yet useful. Contributions are welcomed. 107 | 108 | ## Resources 109 | 110 | To learn more about Swift docstrings read this awesome article at NSHipster: 111 | [Swift Documentation](https://nshipster.com/swift-documentation/) 112 | > Swift Documentation 113 | > 114 | > Written by Nate Cook & Mattt — July 11th, 2018 (revised) 115 | 116 | 117 | ## License 118 | 119 | The plugin in licensed under MIT license so do what ever you want with it. 120 | -------------------------------------------------------------------------------- /autoload/swiftdocstring.vim: -------------------------------------------------------------------------------- 1 | " 2 | " swiftdocstring.vim 3 | " witekbobrowski/vim-swiftdocstring 4 | " 5 | " Created by Witek Bobrowski (witek@bobrowski.co). 6 | " Published under MIT license. 7 | " 8 | 9 | " Public 10 | 11 | " Generate docstring for context at current line. 12 | function! g:swiftdocstring#docstring_current() 13 | call s:docstring(line('.')) 14 | endfunction 15 | 16 | " Generate docstring for all functions in current file. 17 | function! g:swiftdocstring#docstring_functions() 18 | let l:keywords = swiftdocstring#keywords#factory() 19 | call s:docstrings_for(l:keywords.functions()) 20 | endfunction 21 | 22 | " Generate docstring for all types in current file. 23 | function! g:swiftdocstring#docstring_types() 24 | let l:keywords = swiftdocstring#keywords#factory() 25 | call s:docstrings_for(l:keywords.types()) 26 | endfunction 27 | 28 | " Private 29 | 30 | " Main function that parses context to Intermediate Representation for given 31 | " line to generate docstring that is being outputted to the file. 32 | function! s:docstring(line) 33 | " Prepare dependencies 34 | let l:options = swiftdocstring#options#build() 35 | let l:template = swiftdocstring#template#factory(l:options) 36 | 37 | " Begin flow 38 | let l:parsed = swiftdocstring#parser#parse(a:line, l:options) 39 | " Return if there is no context detected and parsed 40 | if empty(l:parsed) 41 | echom 'Nothing found to document at the current location' 42 | return 43 | endif 44 | let l:docstring = swiftdocstring#docstring#build(l:parsed, l:template, l:options) 45 | 46 | " Output generated docstring 47 | let l:line = l:options['context-start-line-number'] - 1 48 | call swiftdocstring#utils#output(l:docstring, l:line) 49 | endfunction 50 | 51 | " Wraper function for generating docstring in multiple lines 52 | function! s:docstrings(lines) 53 | " Iterate in reversed order so pasting docstrings will not mess with correct 54 | " placement 55 | for line in reverse(a:lines) 56 | call s:docstring(line) 57 | endfor 58 | endfunction 59 | 60 | " Generate docstring for contexts with passed keywords 61 | function! s:docstrings_for(keywords) 62 | let l:lines = g:swiftdocstring#parser#get_lines_of_keywords(a:keywords) 63 | call s:docstrings(l:lines) 64 | endfunction 65 | -------------------------------------------------------------------------------- /autoload/swiftdocstring/docstring.vim: -------------------------------------------------------------------------------- 1 | " 2 | " docstring.vim 3 | " witekbobrowski/vim-swiftdocstring 4 | " 5 | " Created by Witek Bobrowski (witek@bobrowski.co). 6 | " Published under MIT license. 7 | " 8 | 9 | " Builder for generating docstring from intermediate representation. 10 | " 11 | " Parameters 12 | " - intermediate_representaiton: Dictionary containing all the parsed 13 | " info about the context to which the docstring should be generated. 14 | " - template: Object with methods that returns appropriate docstring parts. 15 | " - options: Dictionary with user defined or contex related options that are 16 | " used during generation process. 17 | function! g:swiftdocstring#docstring#build(intermediate_representaiton, template, options) 18 | let l:lines = s:generate(a:intermediate_representaiton, a:template) 19 | let l:updated = s:update_with_options(l:lines, a:options, a:template) 20 | return l:updated 21 | endfunction 22 | 23 | " Helper function that delegates other, more specified functions to perform 24 | " docstring generation from Intermediate Representation with template. 25 | function! s:generate(ir, template) 26 | let l:lines = [] 27 | if has_key(a:ir, 'property') 28 | let l:lines = [a:template.simple()] 29 | elseif has_key(a:ir, 'function') 30 | let l:lines = s:generate_from_function(a:ir['function'], a:template) 31 | elseif has_key(a:ir, 'type') 32 | let l:lines = s:generate_from_type(a:ir['type'], a:template) 33 | endif 34 | return l:lines 35 | endfunction 36 | 37 | " Generating docstring for Swift funcitons from Intermediate Representation. 38 | function! s:generate_from_function(function_ir, template) 39 | let l:lines = [] 40 | if has_key(a:function_ir, 'parameters') 41 | let l:params = a:function_ir['parameters'] 42 | let l:lines = s:generate_from_parameters(l:params, a:template) 43 | endif 44 | if has_key(a:function_ir, 'throws') 45 | call add(l:lines, a:template.throws()) 46 | endif 47 | if has_key(a:function_ir, 'returns') 48 | call add(l:lines, a:template.returns()) 49 | endif 50 | if !empty(l:lines) 51 | let l:lines = [a:template.empty()] + l:lines 52 | endif 53 | return [a:template.simple()] + l:lines 54 | endfunction 55 | 56 | " Generate docstring for parameters in function 57 | function! s:generate_from_parameters(parameters, template) 58 | " Early exit if there is a only one parameter 59 | if len(a:parameters) ==# 1 60 | return [a:template.parameter_single(a:parameters[0])] 61 | endif 62 | " Otherwise proceed normally with iterating over parameters 63 | let l:lines = [] 64 | call add(l:lines, a:template.parameters()) 65 | for parameter in a:parameters 66 | call add(l:lines, a:template.parameter(parameter)) 67 | endfor 68 | return l:lines 69 | endfunction 70 | 71 | " Generating docstring for Swift types from Intermediate Representation. 72 | function! s:generate_from_type(type_ir, template) 73 | if has_key(a:type_ir, 'enum') 74 | return s:generate_from_enum(a:type_ir['enum'], a:template) 75 | else 76 | return [a:template.simple()] 77 | endif 78 | endfunction 79 | 80 | " Generating docstring for Swift enumerations from Intermediate Representation. 81 | function! s:generate_from_enum(enum_ir, template) 82 | let l:lines = [a:template.simple()] 83 | call add(l:lines, a:template.empty()) 84 | for case in a:enum_ir['cases'] 85 | call add(l:lines, a:template.enumCase(case)) 86 | endfor 87 | return l:lines 88 | endfunction 89 | 90 | " Evaluating options dictionary to adjust the generated docstring to context 91 | " related and user-defined options. 92 | function! s:update_with_options(lines, options, template) 93 | let l:updated = a:lines 94 | 95 | if has_key(a:options, 'indentation-level') 96 | let l:indentation_level = a:options['indentation-level'] 97 | let l:updated = g:swiftdocstring#utils#indented_strings(l:updated, l:indentation_level) 98 | endif 99 | 100 | if has_key(a:options, 'delimiter-type') 101 | let l:delimiter = a:options['delimiter-type'] 102 | let l:updated = s:update_with_delimiter(l:updated, l:delimiter, a:template) 103 | endif 104 | 105 | if has_key(a:options, 'context-start-line-number') 106 | let l:line_number = a:options['context-start-line-number'] 107 | let l:updated = s:update_with_indentation(l:updated, l:line_number) 108 | endif 109 | 110 | return l:updated 111 | endfunction 112 | 113 | " Append appropriate Swift docstring delimiter to generated docstring, either 114 | " single-line `/** ... */` or multi-line `///`. 115 | function! s:update_with_delimiter(lines, delimiter_type, template) 116 | let l:updated = [] 117 | let l:prefix = '' 118 | if a:delimiter_type ==# 'multi-line' 119 | let l:prefix = ' ' 120 | call add(l:updated, a:template.multi_line_begin()) 121 | elseif a:delimiter_type ==# 'single-line' 122 | let l:prefix = a:template.single_line() 123 | endif 124 | let l:updated += g:swiftdocstring#utils#prefixed_strings(a:lines, l:prefix) 125 | if a:delimiter_type ==# 'multi-line' 126 | call add(l:updated, a:template.multi_line_end()) 127 | endif 128 | return l:updated 129 | endfunction 130 | 131 | " Prefix all lines with indentation from given line number. 132 | function! s:update_with_indentation(lines, line_n) 133 | let l:indentation = indent(a:line_n) 134 | return g:swiftdocstring#utils#indented_strings(a:lines, l:indentation) 135 | endfunction 136 | -------------------------------------------------------------------------------- /autoload/swiftdocstring/keywords.vim: -------------------------------------------------------------------------------- 1 | " 2 | " keywords.vim 3 | " witekbobrowski/vim-swiftdocstring 4 | " 5 | " Created by Witek Bobrowski (witek@bobrowski.co). 6 | " Published under MIT license. 7 | " 8 | 9 | " Factory for different types of Swift keyword 10 | function! g:swiftdocstring#keywords#factory() 11 | let l:self = {} 12 | 13 | function! self.properties() 14 | return ['let', 'var', 'case'] 15 | endfunction 16 | 17 | function! self.functions() 18 | return ['func', 'init'] 19 | endfunction 20 | 21 | function! self.types() 22 | return ['protocol', 'struct', 'class', 'enum', 'typealias', 'associatedtype'] 23 | endfunction 24 | 25 | function! self.all() 26 | return l:self.properties() + l:self.functions() + l:self.types() 27 | endfunction 28 | 29 | return l:self 30 | endfunction 31 | -------------------------------------------------------------------------------- /autoload/swiftdocstring/options.vim: -------------------------------------------------------------------------------- 1 | " 2 | " options.vim 3 | " witekbobrowski/vim-swiftdocstring 4 | " 5 | " Created by Witek Bobrowski (witek@bobrowski.co). 6 | " Published under MIT license. 7 | " 8 | 9 | " Public builder of dictionary containing user-defined and context related 10 | " options that will be used to generate docstring. 11 | function! g:swiftdocstring#options#build() 12 | let l:options = {} 13 | 14 | call s:retrive_delimiter_option(l:options) 15 | call s:retrive_indentation_option(l:options) 16 | call s:retrive_placeholder_options(l:options) 17 | 18 | return l:options 19 | endfunction 20 | 21 | " Get Swift docstring delimiter type from global option. 22 | function! s:retrive_delimiter_option(options) 23 | if g:swiftdocstring#use_multi_line_delimiter 24 | let a:options['delimiter-type'] = 'multi-line' 25 | else 26 | let a:options['delimiter-type'] = 'single-line' 27 | endif 28 | endfunction 29 | 30 | " Get indentation from global option. 31 | function! s:retrive_indentation_option(options) 32 | let l:indentation_level = g:swiftdocstring#text_indentation_level 33 | let a:options['indentation-level'] = l:indentation_level 34 | endfunction 35 | 36 | " Get placeholder settings from global option. 37 | function! s:retrive_placeholder_options(options) 38 | let l:use_placeholders = g:swiftdocstring#use_placeholders 39 | let a:options['use-placeholder'] = l:use_placeholders 40 | if l:use_placeholders 41 | let l:placeholder_template = g:swiftdocstring#placeholder_template 42 | let a:options['placeholder-open'] = l:placeholder_template[0] 43 | let a:options['placeholder-close'] = l:placeholder_template[-1] 44 | endif 45 | endfunction 46 | 47 | 48 | -------------------------------------------------------------------------------- /autoload/swiftdocstring/parser.vim: -------------------------------------------------------------------------------- 1 | " 2 | " parser.vim 3 | " witekbobrowski/vim-swiftdocstring 4 | " 5 | " Created by Witek Bobrowski (witek@bobrowski.co). 6 | " Published under MIT license. 7 | " 8 | 9 | " Main function to trigger parsing process, which consists of retrievied the 10 | " context from passed line and finally parsing it to the intermediate 11 | " representation. 12 | " 13 | " Parameters 14 | " - line_n: An intiger value referencinf the line number in current file 15 | " - options: Dictionary with user defined or contex related options that could 16 | " be updated during the parsing procedure. 17 | " Returns: Internal representation that got parsed 18 | function! g:swiftdocstring#parser#parse(line_n, options) 19 | let l:context = s:get_context(a:line_n, a:options) 20 | if empty(l:context) 21 | return {} 22 | endif 23 | return s:parse(l:context, a:options) 24 | endfunction 25 | 26 | " Retrive a list of numbers that represent lines that match any of the passed 27 | " keywords 28 | " 29 | " Parameter keywords: a list of Swift keywords that shoudl be matched 30 | " Returns: A list of line numbers 31 | function! g:swiftdocstring#parser#get_lines_of_keywords(keywords) 32 | let l:matched = [] 33 | for line_number in range(0, line('$')) 34 | let l:keyword = s:get_keyword(line_number) 35 | " Skip if not a single requested keyword was matched 36 | if index(a:keywords, l:keyword) == -1 37 | continue 38 | endif 39 | " Skip iteration if the docstring already exists for the context 40 | if g:swiftdocstring#regex#is_docstring(getline(line_number - 1)) 41 | continue 42 | endif 43 | " Add matched line number to the list 44 | call add(l:matched, line_number) 45 | endfor 46 | return l:matched 47 | endfunction 48 | 49 | " Get keyword if present at line with passed keyword. This function rejects 50 | " lines that are comments or docstrings. 51 | " 52 | " Parameter line_n: number of line in burrent buffer 53 | " Returns: Keyword if gets matched, else empty string 54 | function! s:get_keyword(line_n) 55 | let l:line = getline(a:line_n) 56 | " Return empty string if line is a comment 57 | if g:swiftdocstring#regex#is_comment(l:line) 58 | return '' 59 | endif 60 | " Return empty string if line is docstring 61 | if g:swiftdocstring#regex#is_docstring(l:line) 62 | return '' 63 | endif 64 | return g:swiftdocstring#regex#match_keyword(l:line) 65 | endfunction 66 | 67 | " Traverse up looking for a keyword in burrent buffer. This funciton will 68 | " return negative value if beginning of the file was reached or it stumbled 69 | " upon an empty line. 70 | " 71 | " Parameter line_n: number of line in burrent buffer 72 | " Returns: Keyword if gets matched, else empty string 73 | function! s:traverse_up_for_keyword(line_n) 74 | let l:line_number = a:line_n 75 | " While there are no keywords matched iterate over 76 | while empty(s:get_keyword(l:line_number)) 77 | " If line is empty return with -1 78 | if empty(getline(l:line_number)) 79 | return -1 80 | endif 81 | " Break loop if reached beginning of file 82 | if l:line_number <= 0 83 | return -1 84 | endif 85 | " Traverse one line above current 86 | let l:line_number -= 1 87 | endwhile 88 | return l:line_number 89 | endfunction 90 | 91 | " Retrive context for passed line that will be used for parsing to 92 | " intermediate representation. 93 | " 94 | " Parameters 95 | " - line_n: An intiger value referencinf the line number in current file 96 | " - options: Dictionary with user defined or contex related options that could 97 | " be updated during the parsing procedure. 98 | " Returns: List of lines relative to the context 99 | function! s:get_context(line_n, options) 100 | " Skip iteration if line is a comment 101 | if g:swiftdocstring#regex#is_comment(getline(a:line_n)) 102 | return [] 103 | endif 104 | " Skip iteration if line is docstring 105 | if g:swiftdocstring#regex#is_docstring(getline(a:line_n)) 106 | return [] 107 | endif 108 | let l:current_line_n = s:traverse_up_for_keyword(a:line_n) 109 | " Return if could not get line for keyword 110 | if l:current_line_n ==# -1 111 | return [] 112 | endif 113 | 114 | let l:lines = [getline(l:current_line_n)] 115 | let l:keyword = s:get_keyword(l:current_line_n) 116 | let a:options['context-start-line-number'] = l:current_line_n 117 | 118 | " Traverse down as long as the context is not full 119 | while !s:is_full_context(l:lines, l:keyword) 120 | let l:current_line_n += 1 121 | " Break loop if reached end of file 122 | if l:current_line_n > line('$') 123 | return [] 124 | endif 125 | " Special check for function declarations in protocols 126 | " Return previous lines if current contains other keyword 127 | if l:keyword ==# 'func' && !empty(s:get_keyword(l:current_line_n)) 128 | return l:lines 129 | endif 130 | " Proceed normally 131 | call add(l:lines, getline(l:current_line_n)) 132 | endwhile 133 | return l:lines 134 | endfunction 135 | 136 | " Check if full context is present in lines for give keyword 137 | " 138 | " Parameters: 139 | " - lines: List of lines 140 | " - keyword: Swift keyword that defines how the context should look like 141 | " Returns: A boolean (0 or 1) if requirements are met 142 | function! s:is_full_context(lines, keyword) 143 | let l:keywords = g:swiftdocstring#keywords#factory() 144 | let l:context = g:swiftdocstring#utils#merge(a:lines) 145 | if index(l:keywords.functions(), a:keyword) >= 0 146 | return g:swiftdocstring#regex#is_full_function_context(l:context) 147 | elseif index(['enum'], a:keyword) >= 0 148 | return g:swiftdocstring#regex#is_full_enum_context(l:context) 149 | else 150 | return 1 151 | endif 152 | endfunction 153 | 154 | " An actual parsing method that takes the lines with the context and converts 155 | " them to an intermediate representation. 156 | " 157 | " Parameters: 158 | " - lines: List of lines 159 | " - options: Dictionary with options for parsing process 160 | " Returns: Internal representation 161 | function! s:parse(lines, options) 162 | let l:keywords = g:swiftdocstring#keywords#factory() 163 | let l:keyword = s:get_keyword(a:options['context-start-line-number']) 164 | if index(l:keywords.properties(), l:keyword) >= 0 165 | return {'property': {}} 166 | elseif index(l:keywords.types(), l:keyword) >= 0 167 | return s:parse_type(l:keyword, a:lines) 168 | elseif index(l:keywords.functions(), l:keyword) >= 0 169 | return s:parse_function(a:lines) 170 | else 171 | return {} 172 | endif 173 | endfunction 174 | 175 | " Helper function for parsing function context to intermediate representation 176 | function! s:parse_function(lines) 177 | let l:context = swiftdocstring#utils#merge(a:lines) 178 | let l:function_info = {} 179 | let l:parameters = s:parse_function_parameters(l:context) 180 | if !empty(l:parameters) 181 | let l:function_info['parameters'] = l:parameters 182 | endif 183 | if swiftdocstring#regex#function_throws(l:context) 184 | let l:function_info['throws'] = 1 185 | endif 186 | if swiftdocstring#regex#function_returns(l:context) 187 | let l:function_info['returns'] = 1 188 | endif 189 | return {'function': l:function_info} 190 | endfunction 191 | 192 | " Helper function for parsing function parameters 193 | function! s:parse_function_parameters(context) 194 | let l:raw = g:swiftdocstring#regex#match_function_parameters(a:context) 195 | let l:parameters = [] 196 | for raw_param in split(l:raw, ',') 197 | let l:components = split(raw_param, ':') 198 | call add(l:parameters, split(l:components[0], ' ')[-1]) 199 | endfor 200 | return l:parameters 201 | endfunction 202 | 203 | " Helper function for parsing type context to intermediate representation 204 | function! s:parse_type(type, lines) 205 | let l:type_info = {} 206 | if 'enum' ==# a:type 207 | let l:type_info[a:type] = s:parse_enum(a:lines) 208 | else 209 | let l:type_info[a:type] = {} 210 | endif 211 | return {'type': l:type_info} 212 | endfunction 213 | 214 | " Helper function for parsing enums 215 | function! s:parse_enum(lines) 216 | let l:context = g:swiftdocstring#utils#merge(a:lines) 217 | let l:declarations = [] 218 | for line in a:lines 219 | if g:swiftdocstring#regex#is_enum_case_declaration(line) 220 | call add(l:declarations, line) 221 | endif 222 | endfor 223 | let l:cases = [] 224 | for line in l:declarations 225 | let l:cases += s:parse_enum_cases(line) 226 | endfor 227 | return {'cases': l:cases} 228 | endfunction 229 | 230 | " Parse enum cases from singe line of enum context 231 | function! s:parse_enum_cases(line) 232 | let l:cases = [] 233 | let l:context = g:swiftdocstring#regex#match_cases_context(a:line) 234 | for word in split(l:context, ',') 235 | let l:stripped = g:swiftdocstring#regex#strip_enum_case_value(word) 236 | call add(l:cases, l:stripped) 237 | endfor 238 | return l:cases 239 | endfunction 240 | -------------------------------------------------------------------------------- /autoload/swiftdocstring/regex.vim: -------------------------------------------------------------------------------- 1 | " 2 | " regex.vim 3 | " witekbobrowski/vim-swiftdocstring 4 | " 5 | " Created by Witek Bobrowski (witek@bobrowski.co). 6 | " Published under MIT license. 7 | " 8 | 9 | " Keyword matching 10 | 11 | " Retrive keyword if present in string 12 | function! g:swiftdocstring#regex#match_keyword(context) 13 | let l:keywords = join(g:swiftdocstring#keywords#factory().all(), '|') 14 | let l:pattern = '\v(' . l:keywords . ')' 15 | return matchstr(a:context, l:pattern) 16 | endfunction 17 | 18 | " Enum matching 19 | 20 | " Check if given string contains full function context 21 | function! g:swiftdocstring#regex#is_full_enum_context(context) 22 | let l:pattern = '' 23 | let l:count = 0 24 | while g:swiftdocstring#utils#match_times(a:context, '\v\{', l:count + 1) 25 | let l:count += 1 26 | endwhile 27 | if l:count < 1 28 | return 0 29 | endif 30 | return g:swiftdocstring#utils#match_times(a:context, '\v\}', l:count) 31 | endfunction 32 | 33 | " Check if line contains proper case declaration, ignore cases in switch 34 | function! g:swiftdocstring#regex#is_enum_case_declaration(context) 35 | let l:pattern = '\v^(\s)*(\s)+\.@!' 36 | return g:swiftdocstring#utils#match(a:context, l:pattern) 37 | endfunction 38 | 39 | " Get content with actual cases in single line ignoring case keyword 40 | function! g:swiftdocstring#regex#match_cases_context(line) 41 | let l:pattern = '\v((case\s)@<=(.)*)\s*' 42 | return matchstr(a:line, l:pattern) 43 | endfunction 44 | 45 | " Strip enum case identifier from spaces, associated values and assigned values 46 | function! g:swiftdocstring#regex#strip_enum_case_value(line) 47 | let l:pattern = '\v<\w*>' 48 | return matchstr(a:line, l:pattern) 49 | endfunction 50 | 51 | " Function matching 52 | 53 | " Check if given string contains full function context 54 | function! g:swiftdocstring#regex#is_full_function_context(context) 55 | let l:pattern = '\v(func|init)@<=(.|\s)*\(@<=(.|\s)*\)@=(.|\s)*(\{|\})+' 56 | return g:swiftdocstring#utils#match(a:context, l:pattern) 57 | endfunction 58 | 59 | " Retrive functions parameters from declaration 60 | function! g:swiftdocstring#regex#match_function_parameters(context) 61 | let l:pattern = '\v\(@<=(.|\s)*\)@=' 62 | return matchstr(a:context, l:pattern) 63 | endfunction 64 | 65 | " Check if function throws 66 | function! g:swiftdocstring#regex#function_throws(context) 67 | let l:pattern = '\v\(@<=(.|\s)*\)@=(.|\s)*(throws)+' 68 | return g:swiftdocstring#utils#match(a:context, l:pattern) 69 | endfunction 70 | 71 | " Check if function returns 72 | function! g:swiftdocstring#regex#function_returns(context) 73 | let l:pattern = '\v\(@<=(.|\s)*\)@=(.|\s)*[->]+' 74 | return g:swiftdocstring#utils#match(a:context, l:pattern) 75 | endfunction 76 | 77 | " Comment/Docstring matching 78 | 79 | " Check in context is begins with Swift comment delimiters 80 | function! g:swiftdocstring#regex#is_comment(context) 81 | let l:pattern = '\v^(\s)*(/{2}|\*/|/\*)' 82 | return g:swiftdocstring#utils#match(a:context, l:pattern) 83 | endfunction 84 | 85 | " Check in context is begins with Swift comment delimiters 86 | function! g:swiftdocstring#regex#is_docstring(context) 87 | let l:pattern = '\v^(\s)*(/{3}|\*/|/\*\*)' 88 | return g:swiftdocstring#utils#match(a:context, l:pattern) 89 | endfunction 90 | -------------------------------------------------------------------------------- /autoload/swiftdocstring/template.vim: -------------------------------------------------------------------------------- 1 | " 2 | " template.vim 3 | " witekbobrowski/vim-swiftdocstring 4 | " 5 | " Created by Witek Bobrowski (witek@bobrowski.co). 6 | " Published under MIT license. 7 | " 8 | 9 | " Factory of single line templates designated to use in docstring builder 10 | function! g:swiftdocstring#template#factory(options) 11 | let self = {'options': a:options} 12 | 13 | function! self.placeholder(text) 14 | let l:options = self['options'] 15 | if !l:options['use-placeholder'] 16 | return a:text 17 | endif 18 | let l:open = l:options['placeholder-open'] 19 | let l:close = l:options['placeholder-close'] 20 | return l:open . a:text . l:close 21 | endfunction 22 | 23 | function! self.single_line() 24 | return '///' 25 | endfunction 26 | 27 | function! self.multi_line_begin() 28 | return '/**' 29 | endfunction 30 | 31 | function! self.multi_line_end() 32 | return '*/' 33 | endfunction 34 | 35 | function! self.empty() 36 | return '' 37 | endfunction 38 | 39 | function! self.simple() 40 | return self.placeholder('Description') 41 | endfunction 42 | 43 | function! self.parameters() 44 | return '- Parameters:' 45 | endfunction 46 | 47 | function! self.returns() 48 | let l:placeholder = self.placeholder('return value description') 49 | return '- Returns: ' . l:placeholder 50 | endfunction 51 | 52 | function! self.throws() 53 | let l:placeholder = self.placeholder('throws value description') 54 | return '- Throws: ' . l:placeholder 55 | endfunction 56 | 57 | function! self.parameter_single(parameter) 58 | let l:placeholder = self.placeholder(a:parameter . ' description') 59 | return '- Parameter ' . a:parameter . ': ' . l:placeholder 60 | endfunction 61 | 62 | function! self.parameter(parameter) 63 | let l:placeholder = self.placeholder(a:parameter . ' description') 64 | return ' - '. a:parameter . ': ' . l:placeholder 65 | endfunction 66 | 67 | function! self.enumCase(case) 68 | let l:placeholder = self.placeholder(a:case . ' description') 69 | return '- ' . a:case . ': ' . l:placeholder 70 | endfunction 71 | 72 | return self 73 | endfunction 74 | -------------------------------------------------------------------------------- /autoload/swiftdocstring/utils.vim: -------------------------------------------------------------------------------- 1 | " 2 | " utils.vim 3 | " witekbobrowski/vim-swiftdocstring 4 | " 5 | " Created by Witek Bobrowski (witek@bobrowski.co). 6 | " Published under MIT license. 7 | " 8 | 9 | " Merge all lines from list of string to a single string 10 | function! g:swiftdocstring#utils#merge(lines) 11 | let l:text = '' 12 | for line in a:lines 13 | let l:text .= line 14 | endfor 15 | return l:text 16 | endfunction 17 | 18 | " Simple wrapper funciton that outputs text to line in current file 19 | function! g:swiftdocstring#utils#output(text, line) 20 | call append(a:line, a:text) 21 | endfunction 22 | 23 | " Concatenate string with a prefix 24 | function! g:swiftdocstring#utils#prefixed(string, prefix) 25 | return a:prefix . a:string 26 | endfunction 27 | 28 | " Contatenate a list of strings with a prefix 29 | function! g:swiftdocstring#utils#prefixed_strings(strings, prefix) 30 | let l:prefixed = [] 31 | for string in a:strings 32 | call add(l:prefixed, swiftdocstring#utils#prefixed(string, a:prefix)) 33 | endfor 34 | return prefixed 35 | endfunction 36 | 37 | " Contatenate string with given number of spaces 38 | function! g:swiftdocstring#utils#indented(string, n_spaces) 39 | let l:prefix = repeat(' ', a:n_spaces) 40 | return swiftdocstring#utils#prefixed(a:string, l:prefix) 41 | endfunction 42 | 43 | " Contatenate a list of strings with given number of spaces 44 | function! g:swiftdocstring#utils#indented_strings(strings, n_spaces) 45 | let l:prefix = repeat(' ', a:n_spaces) 46 | return swiftdocstring#utils#prefixed_strings(a:strings, l:prefix) 47 | endfunction 48 | 49 | " Warpper funciton for mapping result from match function to 0 or 1 50 | function! g:swiftdocstring#utils#match(text, pattern) 51 | return match(a:text, a:pattern) != -1 52 | endfunction 53 | 54 | " Match n-times pattern in string 55 | function! g:swiftdocstring#utils#match_times(text, pattern, times) 56 | return match(a:text, a:pattern, 0, a:times) != -1 57 | endfunction 58 | -------------------------------------------------------------------------------- /doc/swiftdocstring.txt: -------------------------------------------------------------------------------- 1 | *swiftdocstring.txt* Last change: 2018 November 14 2 | 3 | _ __ _ _ _ _ 4 | (_)/ _| | | | | | (_) 5 | _____ ___| |_| |_ __| | ___ ___ ___| |_ _ __ _ _ __ __ _ 6 | / __\ \ /\ / / | _| __/ _` |/ _ \ / __/ __| __| '__| | '_ \ / _` | 7 | \__ \\ V V /| | | | || (_| | (_) | (__\__ \ |_| | | | | | | (_| | 8 | |___/ \_/\_/ |_|_| \__\__,_|\___/ \___|___/\__|_| |_|_| |_|\__, | 9 | __/ | 10 | |___/ 11 | 12 | *Author*: Witek Bobrowski 13 | 14 | =============================================================================== 15 | CONTENTS *swiftdocstring-contents* 16 | 17 | 18 | 1.Intro...................................|swiftdocstring| 19 | 2.Installation............................|swiftdocstring-install| 20 | 3.Features................................|swiftdocstring-features| 21 | 4.Commands................................|swiftdocstrin-commands| 22 | 5.Mappings................................|swiftdocstring-mappings| 23 | 6.Options.................................|swiftdocstrin-options| 24 | 7.Why.....................................|swiftdocstring-why| 25 | 8.Recources...............................|swiftdocstring-resources| 26 | 9.License.................................|swiftdocstring-license| 27 | 28 | =============================================================================== 29 | 1. Intro *vim-swiftdocstring* 30 | 31 | A simple vim/neovim plugin that provides Xcode-like docstring templates for 32 | Swift. 33 | 34 | =============================================================================== 35 | 2. Installation *swiftdocstring-install* 36 | 37 | Install the plugin using any of the popular vim plugin managers. 38 | 39 | Examples: 40 | 41 | - vim-plug 42 | Plug 'witekbobrowski/vim-swiftdocstring' 43 | 44 | - Vundle 45 | Plugin 'witekbobrowski/vim-swiftdocstring' 46 | 47 | - Dein 48 | call dein#add('witekbobrowski/vim-swiftdocstring') 49 | 50 | You could always import the plugin files manually by cloning the repository and 51 | pasting files in vim directories. 52 | 53 | =============================================================================== 54 | 3. Features *swiftdocstring-features* 55 | 56 | - Generate Swift docstrings for types, functions and properties 57 | - Support for `///` and `/** ... */` delimiters 58 | - Even more customizable output 59 | - Xcode-like style of docstrings 60 | - Better context awarness than in Xcode 61 | - Fast parsing using regular expressions 62 | - Zero dependency (100% vim-script) 63 | 64 | =============================================================================== 65 | 4. Commands *swiftdocstring-commands* 66 | 67 | 4.1 *:SwiftDocstringCurrent* 68 | 69 | Generates docstring for the context in the current line of the cursor 70 | 71 | 4.2 *:SwiftDocstringTypes* 72 | 73 | Generates docstrings for every type declaration in current buffer 74 | 75 | 4.3 *:SwiftDocstringFunctions* 76 | 77 | Generates docstrings for every function declaration in current buffer 78 | 79 | =============================================================================== 80 | 5. Mappings *swiftdocstring-mappings* 81 | 82 | Generate docstring for current context on 'Tab' + '/' 83 | 84 | autocmd Filetype swift nnoremap / :SwiftDocstringCurrent 85 | 86 | =============================================================================== 87 | 6. Options *swiftdocstring-options* 88 | 89 | For customized behaviour, change values of these properties. Values presented 90 | below are the defaults. 91 | 92 | 6.1 *g:swiftdocstring#use_multi_line_delimiter* 93 | 94 | Use Multi-line delimiter '/** ... */' instead of single-line '///'. 95 | Expects: Boolen value (0 or 1) 96 | 97 | let g:swiftdocstring#use_multi_line_delimiter = 0 98 | 99 | 6.2 *g:swiftdocstring#text_indentation_level* 100 | 101 | Set indentation for dosctring between the text and delimiters. 102 | Expects: Integer value (>=0) 103 | 104 | let g:swiftdocstring#text_indentation_level = 1 105 | 106 | 6.3 *g:swiftdocstring#use_placeholders* 107 | 108 | Use placeholders for some components of the docstring. 109 | Expects: Boolen value (0 or 1) 110 | 111 | let g:swiftdocstring#use_placeholders = 1 112 | 113 | 6.4 *g:swiftdocstring#placeholder_template* 114 | 115 | Placeholders to be used if setting above is set as true. 116 | Expects: Two element string array 117 | 118 | let g:swiftdocstring#placeholder_template = ['<#', '#>'] 119 | 120 | =============================================================================== 121 | 7. About *swiftdocstring-why* 122 | 123 | This plugin was made under the desire to have a powerfull Swift development 124 | envirionment in vim. Although the plugin brings only a small feature ported 125 | from Xcode, I want to add at least one brick to the wall. This is as far as 126 | I can go at the moment with the amount of time it took to create this peace 127 | of code. I intend it to remain simple, yet useful. Contributions are welcomed. 128 | 129 | Feel free to contact me using any of the options below and consider leaving it 130 | a star on GitHub. 131 | 132 | e-mail - witek@bobrowski.co 133 | twitter - @witekbobrowski 134 | github - @witekbobrowski 135 | url - https://github.com/witekbobrowski/vim-swiftdocstring 136 | 137 | =============================================================================== 138 | 8. Resources *swiftdocstring-resources* 139 | 140 | 141 | =============================================================================== 142 | 9. License *swiftdocstring-license* 143 | 144 | The plugin in licensed under MIT license so do what ever you want with it. 145 | ------------------------------------------------------------------------------- 146 | MIT License 147 | 148 | Copyright (c) 2018 Witek Bobrowski 149 | 150 | Permission is hereby granted, free of charge, to any person obtaining a copy 151 | of this software and associated documentation files (the "Software"), to deal 152 | in the Software without restriction, including without limitation the rights 153 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 154 | copies of the Software, and to permit persons to whom the Software is 155 | furnished to do so, subject to the following conditions: 156 | 157 | The above copyright notice and this permission notice shall be included in all 158 | copies or substantial portions of the Software. 159 | 160 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 161 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 162 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 163 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 164 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 165 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 166 | SOFTWARE. 167 | ------------------------------------------------------------------------------- 168 | -------------------------------------------------------------------------------- /ftdetect/swift.vim: -------------------------------------------------------------------------------- 1 | " 2 | " swift.vim 3 | " witekbobrowski/vim-swiftdocstring 4 | " 5 | " Created by Witek Bobrowski (witek@bobrowski.co). 6 | " Published under MIT license. 7 | " 8 | 9 | autocmd BufNewFile,BufRead *.swift set filetype=swift 10 | -------------------------------------------------------------------------------- /plugin/swiftdocstring.vim: -------------------------------------------------------------------------------- 1 | " 2 | " swiftdocstring.vim 3 | " witekbobrowski/vim-swiftdocstring 4 | " 5 | " Created by Witek Bobrowski (witek@bobrowski.co). 6 | " Published under MIT license. 7 | " 8 | 9 | if exists('g:loaded_swiftdocstring') 10 | finish 11 | endif 12 | let g:loaded_swiftdocstring = 1 13 | 14 | " Public commands 15 | command! -nargs=0 SwiftDocstringCurrent call g:swiftdocstring#docstring_current() 16 | command! -nargs=0 SwiftDocstringFunctions call g:swiftdocstring#docstring_functions() 17 | command! -nargs=0 SwiftDocstringTypes call g:swiftdocstring#docstring_types() 18 | 19 | " User defined options 20 | let g:swiftdocstring#use_multi_line_delimiter = 0 21 | let g:swiftdocstring#text_indentation_level = 1 22 | let g:swiftdocstring#use_placeholders = 1 23 | let g:swiftdocstring#placeholder_template = ['<#', '#>'] 24 | --------------------------------------------------------------------------------