Skip to content

Commit

Permalink
feat: Implement support for python docs; improved ftplugin documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
kkoomen committed May 11, 2019
1 parent e50dd63 commit 6588e53
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 79 deletions.
70 changes: 55 additions & 15 deletions autoload/doge/generate.vim
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,35 @@ function! doge#generate#pattern(pattern) abort
return 0
endif

let l:tokens = doge#token#extract(l:curr_line, a:pattern['match'], a:pattern['match_group_names'])
let l:tokens = get(doge#token#extract(l:curr_line, a:pattern['match'], a:pattern['match_group_names']), 0)

" Split the 'parameters' token value into a list.
let l:params_dict = a:pattern['parameters']
let l:parameter_match_group_name = l:params_dict['parent_match_group_name']
let l:params = map(split(l:tokens[l:parameter_match_group_name], ','), {k, v -> trim(v)})
let l:params = l:tokens['parameters']

" Go through each parameter, match the regex, extract the token values and
" replace the 'parameters' key with the formatted version.
let l:param_tokens = doge#token#extract(l:params, l:params_dict['match'], l:params_dict['match_group_names'])
let l:formatted_params = []
for l:param in l:params
let l:param_tokens = doge#token#extract(l:param, l:params_dict['match'], l:params_dict['match_group_names'])
let l:format = doge#token#replace(l:param_tokens, l:params_dict['format'])
for l:param_token in l:param_tokens
let l:format = doge#token#replace(l:param_token, l:params_dict['format'])
let l:format = join(filter(l:format, 'v:val !=# ""'), ' ')
call add(l:formatted_params, l:format)
endfor
let l:tokens[l:parameter_match_group_name] = l:formatted_params
let l:tokens['parameters'] = l:formatted_params

" Create the comment by replacing the tokens in the template with their
" corresponding values.
let l:comment = []
for l:line in a:pattern['comment']['template']
" If empty lines are present, just append them to ensure a whiteline is
" inserted rather then completely removed. This allows us to insert some
" whitelines in the comment template.
if empty(l:line)
call add(l:comment, l:line)
continue
endif

let l:line_replaced = split(doge#token#replace(l:tokens, l:line), "\n")
for l:replaced in l:line_replaced
call add(l:comment, l:replaced)
Expand All @@ -52,29 +59,62 @@ function! doge#generate#pattern(pattern) abort

" If an existing comment exists, remove it before we insert a new one.
let l:old_comment_pattern = fnameescape(a:pattern['comment']['opener']) . '\_.\{-}' . fnameescape(a:pattern['comment']['closer']) . '$'
let l:old_comment_start_lnum = search(l:old_comment_pattern, 'bn')
let l:old_comment_end_lnum = search(l:old_comment_pattern, 'bne')

if a:pattern['comment']['insert'] ==# 'below'
let l:old_comment_start_lnum = search(l:old_comment_pattern, 'n')
let l:old_comment_end_lnum = search(l:old_comment_pattern, 'ne')
let l:has_old_comment = l:old_comment_start_lnum == line('.') + 1
else
let l:old_comment_start_lnum = search(l:old_comment_pattern, 'bn')
let l:old_comment_end_lnum = search(l:old_comment_pattern, 'bne')
let l:has_old_comment = l:old_comment_end_lnum == line('.') - 1
endif
let l:old_comment_lines_amount = l:old_comment_end_lnum - l:old_comment_start_lnum + 1


" When deleting the old comment and inserting the new one, it might be that
" some things have been added or deleted.
" For example: a new parameter might have been added.
" If so, we have to increment the line number as well.
" The same goes for deleting but then we subtract the line number.
let l:adjusted_cursor_lnum = line('.') + (len(l:comment) - l:old_comment_lines_amount)
if a:pattern['comment']['insert'] ==# 'below'
let l:adjusted_cursor_lnum = line('.') + (1 + len(l:comment) - l:old_comment_lines_amount)
else
let l:adjusted_cursor_lnum = line('.') + (len(l:comment) - l:old_comment_lines_amount)
endif
let l:cursor_pos = [0, l:adjusted_cursor_lnum, col('.'), 0]

" If the old comment ends at the line above our cursor, then remove it.
" If it returns any other number then the prev line, it belongs to another code block.
let l:has_old_comment = l:old_comment_end_lnum == line('.') - 1
if l:has_old_comment
let l:cursor_pos = getpos('.')
execute(l:old_comment_start_lnum . 'd' . l:old_comment_lines_amount)

" If we have deleted a comment that is 'below' the function expression then
" our cursor moved a line too much, so revert its position. This is the
" same for 'above' the function expression, but we land on our old position
" automatically, hence that we don't have to take that situation into
" account.
if a:pattern['comment']['insert'] ==# 'below'
call setpos('.', l:cursor_pos)
endif
endif

" If the old comment ends at the line above or below our cursor, then remove
" it. If it returns any other number then the prev or next line, it belongs to
" another code block.
if a:pattern['comment']['insert'] ==# 'below'
let l:comment_lnum_insert_position = line('.')
let l:comment_lnum_inherited_indent = line('.') + 1
else
let l:comment_lnum_insert_position = line('.') - 1
let l:comment_lnum_inherited_indent = line('.')
endif

" Write the comment.
call append(line('.') - 1, map(l:comment, {k, line -> doge#indent#line(line('.'), line)}))
call append(
\ l:comment_lnum_insert_position,
\ map(l:comment, {k, line -> doge#indent#line(l:comment_lnum_inherited_indent, line)})
\ )

" Set the cursor position back at where we started.
if l:has_old_comment
call setpos('.', l:cursor_pos)
endif
Expand Down
22 changes: 16 additions & 6 deletions autoload/doge/token.vim
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,11 @@ function! doge#token#replace(tokens, text) abort
endfunction

function! doge#token#extract(line, regex, regex_group_names) abort
let l:matches = map(matchlist(a:line, a:regex), {key, val -> trim(val)})
let l:tokens = {}
let l:submatches = []
call substitute(a:line, a:regex, '\=add(l:submatches, submatch(0))', 'g')

let l:matches = map(l:submatches, {key, val -> trim(val)})
let l:tokens = []

" We can expect a list of matches like:
" ['val1', 'val2', '', 'val3']
Expand All @@ -108,10 +111,17 @@ function! doge#token#extract(line, regex, regex_group_names) abort
" 'type': 'val1',
" }
if len(l:matches) > 0 && len(a:regex_group_names) > 0
for l:token in a:regex_group_names
let l:index = index(a:regex_group_names, l:token)
let l:match = l:matches[l:index + 1]
let l:tokens[l:token] = l:match
for l:match in l:matches
let l:values = matchlist(l:match, a:regex)
let l:group = {}

for l:token in a:regex_group_names
let l:group_idx = index(a:regex_group_names, l:token)
let l:token_value = l:values[l:group_idx + 1]
let l:group[l:token] = trim(l:token_value)
endfor

call add(l:tokens, l:group)
endfor
endif

Expand Down
43 changes: 21 additions & 22 deletions ftplugin/javascript.vim
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
" ==============================================================================
"
" The javascript documentation should follow the 'jsdoc' conventions.
" @see https://jsdoc.app
" see https://jsdoc.app

let s:save_cpo = &cpoptions
set cpoptions&vim
Expand Down Expand Up @@ -40,51 +40,50 @@ let b:doge_patterns = []
" denoted as '\s*{'.
"
" {parameters.match}: Should match at least the following scenarios:
" - arg1
" - arg1 = 5
" - arg1 = 'string'
" - arg1: bool
" - arg1: bool = 5
" - arg1: Person = false
" - arg1: string = 'string'
" - param1
" - param1 = 5
" - param1 = 'string'
" - param1: bool
" - param1: bool = 5
" - param1: Person = false
" - param1: string = 'string'
"
" Regex explanation
" \m
" Use magic notation.
"
" ^
" Matches the position before the first character in the string.
"
" \([^,:]\+\)
" Matches a group which may contain every character besides ',' or ':'.
" This group should match the parameter name.
" ------------------------------------------------------------------------
" Matches a group which may contain every character besides ',' or ':'.
"
" \%(\%(\s*:\s*\([a-zA-Z_]\+\)\)\)\?
" This group
" ------------------------------------------------------------------------
" Matches an optional non-capturing group containing 1 sub-group which may
" contain 1 or more of the following characters: [a-zA-Z_].
"
" \%(\s*=\s*.\+\)\?
" Matches an optional and non-capturing group
" where it should match the format ' = VALUE'.
" This group should match the parameter default value.
"
" $
" Matches right after the last character in the string.
" ------------------------------------------------------------------------
" Matches an optional and non-capturing group where it should match
" the format ' = VALUE'.
call add(b:doge_patterns, {
\ 'match': '\m^function \([^(]\+\)\s*(\(.\{-}\))\s*{',
\ 'match_group_names': ['funcName', 'params'],
\ 'match': '\m^function \([^(]\+\)\s*(\(.\{-}\))\%(\s*:\s*\(.\{-}\)\)\?{',
\ 'match_group_names': ['funcName', 'parameters', 'returnType'],
\ 'parameters': {
\ 'parent_match_group_name': 'params',
\ 'match': '\m^\([^,:]\+\)\%(\%(\s*:\s*\([a-zA-Z_]\+\)\)\)\?\%(\s*=\s*.\+\)\?$',
\ 'match': '\m\([^,:]\+\)\%(\%(\s*:\s*\([a-zA-Z_]\+\)\)\)\?\%(\s*=\s*[^,]\+\)\?',
\ 'match_group_names': ['name', 'type'],
\ 'format': ['@param', '!{{type|*}}', '{name}', 'TODO'],
\ },
\ 'comment': {
\ 'insert': 'above',
\ 'opener': '/**',
\ 'closer': '*/',
\ 'template': [
\ '/**',
\ ' * {params}',
\ ' * {parameters}',
\ '! * @return {{returnType}}: TODO',
\ ' */',
\ ],
\ },
Expand Down
73 changes: 41 additions & 32 deletions ftplugin/php.vim
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@
" ==============================================================================
"
" The PHP documentation should follow the 'phpdoc' conventions.
" @see https://www.phpdoc.org
" see https://www.phpdoc.org

let s:save_cpo = &cpoptions
set cpoptions&vim

let b:doge_patterns = []

""
" ==============================================================================
" Matches regular function expressions and class methods.
" ==============================================================================
"
" {match}: Should match at least the following scenarios:
" - function myFunction(...) {
Expand All @@ -29,17 +31,20 @@ let b:doge_patterns = []
" ^
" Matches the position before the first character in the string.
"
" \%(\%(public\|private\|protected\)\s\)\?
" Match an optional and non-captured group that may
" contain the keywords: 'public', 'private' or 'protected'.
" \%(\%(public\|private\|protected\)\s*\)\?
" Match an optional and non-captured group that may contain the keywords:
" 'public', 'private' or 'protected', followed by 0 or more whitespaces,
" denoted as '\s*'.
"
" \%(static\s\)\?
" \%(static\s*\)\?
" Match an optional and non-captured group that may
" contain the keyword: 'static'.
" contain the keyword: 'static', followed by 0 or more whitespaces,
" denoted as '\s*'.
"
" \%(final\s\)\?
" \%(final\s*\)\?
" Match an optional and non-captured group that may
" contain the keyword: 'final'.
" contain the keyword: 'final', followed by 0 or more whitespaces, denoted
" as '\s*'.
"
" function \([^(]\+\)\s*(\(.\{-}\))\s*{
" Match two groups where group #1 is the function name,
Expand All @@ -56,51 +61,55 @@ let b:doge_patterns = []
" denoted as '\s*{'.
"
" {parameters.match}: Should match at least the following scenarios:
" - $arg1
" - $arg1 = FALSE
" - string $arg1
" - string $arg1 = TRUE
" - \Lorem\Ipsum\Dor\Sit\Amet $arg1 = NULL
"
" - $param1
" - $param1 = FALSE
" - string $param1
" - string $param1 = TRUE
" - array $param1 = []
" - array $param1 = array()
" - \Lorem\Ipsum\Dor\Sit\Amet $param1 = NULL
"
" \m\([a-zA-Z0-9_\\]\+\s*\)\?
" \($[a-zA-Z0-9_]\+\)
"
" \%(\s*=\s*[^,]\+\)\?
" Regex explanation
" \m
" Use magic notation.
"
" ^
" Matches the position before the first character in the string.
"
" \([a-zA-Z0-9_\\]\+\s*\)\?
" This group should match the parameter type.
" ------------------------------------------------------------------------
" Matches an optional group containing 1 or more of the following
" characters: [a-zA-Z0-9_\\] followed by 0 or more spaces.
" This group should match the typing in a parameter.
" characters: '[a-zA-Z0-9_\\]' followed by 0 or more spaces.
"
" \($[a-zA-Z0-9_]\+\)
" Matches a group containing the character '$' followed by 1 or more
" characters of the following: [a-zA-Z0-9_].
" This group should match the parameter name.
" ------------------------------------------------------------------------
" Matches a group containing the character '$' followed by 1 or more
" characters of the following: '[a-zA-Z0-9_]'.
"
" \%(\s*=\s*[a-zA-Z0-9_']\+\)\?
" Matches an optional and non-capturing group
" where it should match the format ' = VALUE'.
" \%(\s*=\s*[^,]\+\)\?
" This group should match the parameter default value.
"
" $
" Matches right after the last character in the string.
" ------------------------------------------------------------------------
" Matches an optional and non-capturing group where it should match the
" format ' = VALUE'. The 'VALUE' should contain 1 or more of the following
" characters: '[^,]'.
call add(b:doge_patterns, {
\ 'match': '\m^\%(\%(public\|private\|protected\)\s\)\?\%(static\s\)\?\%(final\s\)\?function \([^(]\+\)\s*(\(.\{-}\))\s*{',
\ 'match_group_names': ['funcName', 'params'],
\ 'match': '\m^\%(\%(public\|private\|protected\)\s*\)\?\%(static\s*\)\?\%(final\s*\)\?function \([^(]\+\)\s*(\(.\{-}\))\s*{',
\ 'match_group_names': ['funcName', 'parameters'],
\ 'parameters': {
\ 'parent_match_group_name': 'params',
\ 'match': '\m^\([a-zA-Z0-9_\\]\+\s*\)\?\($[a-zA-Z0-9_]\+\)\%(\s*=\s*.\+\)\?$',
\ 'match': '\m\([a-zA-Z0-9_\\]\+\s*\)\?\($[a-zA-Z0-9_]\+\)\%(\s*=\s*[^,]\+\)\?',
\ 'match_group_names': ['type', 'name'],
\ 'format': ['@param', '{type|mixed}', '{name}', 'TODO'],
\ },
\ 'comment': {
\ 'insert': 'above',
\ 'opener': '/**',
\ 'closer': '*/',
\ 'template': [
\ '/**',
\ ' * {params}',
\ ' * {parameters}',
\ ' */',
\ ],
\ },
Expand Down
Loading

0 comments on commit 6588e53

Please sign in to comment.