123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- #!/usr/bin/env python
- #
- # Copyright 2013 The Closure Library Authors. All Rights Reserved.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS-IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- """Tool to insert JsDoc before a function.
- This script attempts to find the first function passed in to stdin, generate
- JSDoc for it (with argument names and possibly return value), and inject
- it in the string. This is intended to be used as a subprocess by editors
- such as emacs and vi.
- """
- import re
- import sys
- # Matches a typical Closure-style function definition.
- _FUNCTION_REGEX = re.compile(r"""
- # Start of line
- ^
- # Indentation
- (?P<indentation>[ ]*)
- # Identifier (handling split across line)
- (?P<identifier>\w+(\s*\.\s*\w+)*)
- # "= function"
- \s* = \s* function \s*
- # opening paren
- \(
- # Function arguments
- (?P<arguments>(?:\s|\w+|,)*)
- # closing paren
- \)
- # opening bracket
- \s* {
- """, re.MULTILINE | re.VERBOSE)
- def _MatchFirstFunction(script):
- """Match the first function seen in the script."""
- return _FUNCTION_REGEX.search(script)
- def _ParseArgString(arg_string):
- """Parse an argument string (inside parens) into parameter names."""
- for arg in arg_string.split(','):
- arg = arg.strip()
- if arg:
- yield arg
- def _ExtractFunctionBody(script, indentation=0):
- """Attempt to return the function body."""
- # Real extraction would require a token parser and state machines.
- # We look for first bracket at the same level of indentation.
- regex_str = r'{(.*?)^[ ]{%d}}' % indentation
- function_regex = re.compile(regex_str, re.MULTILINE | re.DOTALL)
- match = function_regex.search(script)
- if match:
- return match.group(1)
- def _ContainsReturnValue(function_body):
- """Attempt to determine if the function body returns a value."""
- return_regex = re.compile(r'\breturn\b[^;]')
- # If this matches, we assume they're returning something.
- return bool(return_regex.search(function_body))
- def _InsertString(original_string, inserted_string, index):
- """Insert a string into another string at a given index."""
- return original_string[0:index] + inserted_string + original_string[index:]
- def _GenerateJsDoc(args, return_val=False):
- """Generate JSDoc for a function.
- Args:
- args: A list of names of the argument.
- return_val: Whether the function has a return value.
- Returns:
- The JSDoc as a string.
- """
- lines = []
- lines.append('/**')
- lines += [' * @param {} %s' % arg for arg in args]
- if return_val:
- lines.append(' * @return')
- lines.append(' */')
- return '\n'.join(lines) + '\n'
- def _IndentString(source_string, indentation):
- """Indent string some number of characters."""
- lines = [(indentation * ' ') + line
- for line in source_string.splitlines(True)]
- return ''.join(lines)
- def InsertJsDoc(script):
- """Attempt to insert JSDoc for the first seen function in the script.
- Args:
- script: The script, as a string.
- Returns:
- Returns the new string if function was found and JSDoc inserted. Otherwise
- returns None.
- """
- match = _MatchFirstFunction(script)
- if not match:
- return
- # Add argument flags.
- args_string = match.group('arguments')
- args = _ParseArgString(args_string)
- start_index = match.start(0)
- function_to_end = script[start_index:]
- lvalue_indentation = len(match.group('indentation'))
- return_val = False
- function_body = _ExtractFunctionBody(function_to_end, lvalue_indentation)
- if function_body:
- return_val = _ContainsReturnValue(function_body)
- jsdoc = _GenerateJsDoc(args, return_val)
- if lvalue_indentation:
- jsdoc = _IndentString(jsdoc, lvalue_indentation)
- return _InsertString(script, jsdoc, start_index)
- if __name__ == '__main__':
- stdin_script = sys.stdin.read()
- result = InsertJsDoc(stdin_script)
- if result:
- sys.stdout.write(result)
- else:
- sys.stdout.write(stdin_script)
|