Skip to content

Commit

Permalink
feat: add proper support for advanced typescript syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
kkoomen committed Jul 25, 2020
1 parent d1e1f02 commit 761d2a3
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 3 deletions.
14 changes: 14 additions & 0 deletions autoload/doge/preprocessors/javascript.vim
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,19 @@ function! doge#preprocessors#javascript#tokens(tokens) abort
endif
endfunction

" A callback function being called after the parameter tokens have been
" extracted. This function will adjust the input if needed.
function! doge#preprocessors#javascript#parameter_tokens(tokens) abort
for l:token in a:tokens
let l:token_idx = index(a:tokens, l:token)
if has_key(l:token, 'type') && !empty(l:token['type']) && match(l:token['type'], '\m^\s*{') >= 0
let a:tokens[l:token_idx]['type'] = '!type'
endif
if has_key(l:token, 'name') && !empty(l:token['name']) && match(l:token['name'], '\m^\s*{') >= 0
let a:tokens[l:token_idx]['name'] = '!name'
endif
endfor
endfunction

let &cpoptions = s:save_cpo
unlet s:save_cpo
4 changes: 4 additions & 0 deletions autoload/doge/token.vim
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ function! s:token_replace(tokens, text) abort
endfor
let l:text = join(l:multiline_replacement, "\n")
else
" A return type in some languages mightbe definedas 'TypeA & TypeB'.
" For the sake of the substitution process, we need to escape the '&'.
let l:token_value = substitute(l:token_value, '&', '\\&', 'g')

if empty(l:token_value) && l:has_token_default_value
let l:text = substitute(l:text, l:formatted_token, l:token_default_value, 'g')
elseif !empty(l:token_value)
Expand Down
5 changes: 2 additions & 3 deletions ftplugin/javascript.vim
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ let b:doge_patterns = doge#buffer#get_patterns()

let s:pattern_base = {
\ 'parameters': {
\ 'match': '\m\%(\%(public\|private\|protected\)\?\s*\)\?\%(@[[:alnum:]_]\+(.\{-})\s\+\)\?\([[:alnum:]_$]\+\)?\?\%(\s*:\s*\([[:alnum:]._| ]\+\%(\[[[:alnum:][:space:]_[\],]*\]\)\?\|([[:alnum:].:_| ]\+)\s*=>\s*[[:alnum:]_.]\+\)\)\?\%(\s*=\s*\([[:alnum:]_.]\+(.\{-})\|[^,]\+\)\+\)\?',
\ 'match': '\m\%(\%(@[[:alnum:]_]\+(.\{-})\)\s\+\)\?\%(\%(\%(private\|protected\|public\|readonly\)\s\+\)\+\)\?\({\s*\%(\%([[:alnum:]_]\+\)\s*,\?\s*\)*\s*}\|[[:alnum:]_$]\+\)?\?\%(\s*:\s*\(\%(\%({\s*\%(\%([[:alnum:]_.]\+\s*:\s*[[:alnum:]_]\+\)\s*,\?\s*\)*\s*}\|[[:alnum:]|.]\+\[[[:alnum:]_[\]<>, ]*\]\|[[:alnum:]_.]\+\%(<[[:alnum:]_,<> ]\+>\)\?\|(.\{-})\s*=>\s*[[:alnum:]_]\+\)\%(\s*[&|]\s*\)\?\)\+\)\)\?\%(\s*=\s*\([^,]\+\)\)\?',
\ 'tokens': ['name', 'type', 'default'],
\ 'format': '@param {{type|!type}} %(default|[)%{name}%(default|])% - !description',
\ },
Expand Down Expand Up @@ -74,7 +74,7 @@ unlet s:class_pattern['parameters']
" function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {}
" ------------------------------------------------------------------------------
let s:function_pattern = doge#helpers#deepextend(s:pattern_base, {
\ 'match': '\m^\%(\%(export\|export\s\+default\|public\|private\|protected\)\s\+\)*\(static\s\+\)\?\(async\s\+\)\?\%(function\(\*\)\?\s*\)\?\%([[:alnum:]_$]\+\)\?\s*\%(<[[:alnum:][:space:]_,=]*>\)\?\s*(\(.\{-}\))\%(\s*:\s*(\?\([[:alnum:][:space:]_[\].,|<>]\+\))\?\)\?\s*[{(]',
\ 'match': '\m^\%(\%(export\|export\s\+default\|public\|private\|protected\)\s\+\)*\(static\s\+\)\?\(async\s\+\)\?\%(function\(\*\)\?\s*\)\?\%([[:alnum:]_$]\+\)\?\s*\%(<[[:alnum:][:space:]_,=]*>\)\?\s*(\(\%(\%(@[[:alnum:]_]\+(.\{-})\)\s\+\)\?.\{-}\))\%(\s*:\s*(\?\([[:alnum:][:space:]_[\].,|<>&]\+\))\?\)\?\s*[{(]',
\ 'tokens': ['static', 'async', 'generator', 'parameters', 'returnType'],
\})

Expand All @@ -98,7 +98,6 @@ let s:prototype_pattern = doge#helpers#deepextend(s:pattern_base, {
" var myFunc = async function*($p1 = 'value', p2 = [], p3, p4) {}
" var myFunc = async ($p1 = 'value', p2 = [], p3, p4) => {}
" (p1: array = []) => (p2: string) => { console.log(5); }
" (p1: array = []) => (p2: string) => { console.log(5); }
" static myMethod({ b: number }): number {}
" static async myMethod({ b: number }): number {}
" const user = (p1 = 'default') => (subp1, subp2 = 'default') => 5;
Expand Down
53 changes: 53 additions & 0 deletions playground/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -631,3 +631,56 @@ class Child extends Parent {
return this.add(b);
}
}

/**
* [TODO:description]
*
* @param {[TODO:type]} [TODO:name] - [TODO:description]
* @return {[TODO:type]} [TODO:description]
*/
test({ idUser, userModel }: { idUser: ObjectId, userModel: string }) {}

/**
* [TODO:description]
*
* @param {[TODO:type]} [TODO:name] - [TODO:description]
* @return {[TODO:type]} [TODO:description]
*/
test(@Body() { idUser, userModel }: { idUser: ObjectId, userModel: string }) {}

/**
* [TODO:description]
*
* @param {Model<T>} $model - [TODO:description]
* @return {[TODO:type]} [TODO:description]
*/
test(@InjectModel(User.name) private readonly $model: Model<T>) {}

/**
* [TODO:description]
*
* @param {Model<T>} model - [TODO:description]
* @return {[TODO:type]} [TODO:description]
*/
test(@InjectModel('User') private readonly model: Model<T>) {}

class Test {
/**
* [TODO:description]
*
* @async
* @param {[TODO:type]} [TODO:name] - [TODO:description]
* @return {UserA & UserB} [TODO:description]
*/
@Test()
async test(@Body() { idUser, userModel }: { error: string } & { idUser: ObjectId, userModel: string }): UserA & UserB {}

/**
* [TODO:description]
*
* @async
* @param {[TODO:type]} [TODO:name] - [TODO:description]
* @return {Promise<UserA|UserB>} [TODO:description]
*/
async test(@Body() { idUser, userModel }: { idUser: ObjectId, userModel: string }): Promise<UserA | UserB> {}
}
71 changes: 71 additions & 0 deletions test/filetypes/typescript/class-methods.vader
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# ==============================================================================
# Functions with destructured parameters.
# ==============================================================================
Given typescript (functions with destructured parameters):
class Test {
@Test()
@Test()
test({ idUser, userModel }: { idUser: ObjectId, userModel: string }) {}

@Test()
async test(@Body() { idUser, userModel }: { error: string } & { idUser: ObjectId, userModel: string }): UserA & UserB {}
}

Do (trigger doge):
:4\<CR>
\<C-d>
:13\<CR>
\<C-d>

Expect typescript (generated comment with a description, @param and @return tags):
class Test {
/**
* [TODO:description]
*
* @param {[TODO:type]} [TODO:name] - [TODO:description]
* @return {[TODO:type]} [TODO:description]
*/
@Test()
@Test()
test({ idUser, userModel }: { idUser: ObjectId, userModel: string }) {}

/**
* [TODO:description]
*
* @async
* @param {[TODO:type]} [TODO:name] - [TODO:description]
* @return {UserA & UserB} [TODO:description]
*/
@Test()
async test(@Body() { idUser, userModel }: { error: string } & { idUser: ObjectId, userModel: string }): UserA & UserB {}
}

# ==============================================================================
# Functions with destructured parameters.
# ==============================================================================
Given typescript (constructor with decorator and scoped parameters):
class Test {
constructor(
@InjectModel(User.name) private readonly $model: Model<T>,
@InjectModel('User') private readonly model: Model<T>
) {}
}

Do (trigger doge):
:2\<CR>
\<C-d>

Expect typescript (generated comment with a description, @param and @return tags):
class Test {
/**
* [TODO:description]
*
* @param {Model<T>} $model - [TODO:description]
* @param {Model<T>} model - [TODO:description]
* @return {[TODO:type]} [TODO:description]
*/
constructor(
@InjectModel(User.name) private readonly $model: Model<T>,
@InjectModel('User') private readonly model: Model<T>
) {}
}

0 comments on commit 761d2a3

Please sign in to comment.