├── .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 |
5 |
6 |
7 |
8 |
9 |
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 |
--------------------------------------------------------------------------------