├── README.md ├── test ├── file ├── testgcd.py ├── autodocparameters.pyc ├── gcd.py ├── testclass.py ├── test1.py ├── testanneal.py ├── parameters.py └── anneal.py ├── ftplugin └── python │ ├── file │ ├── testgcd.py │ ├── testutil.py │ ├── longtest.py │ ├── test1.py │ ├── parameters.log │ ├── testpep8.py │ ├── testlog.log │ ├── test.py │ ├── gcd.py │ ├── parameters.py │ ├── vimbufferutil.py │ ├── vim-autodoc.vim │ ├── countparentheses.py │ ├── autodoc.py │ └── typestyle.py └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | # vim-autodoc 2 | -------------------------------------------------------------------------------- /test/file: -------------------------------------------------------------------------------- 1 | import gcd 2 | 3 | gcd.gcd(1, 2) 4 | -------------------------------------------------------------------------------- /ftplugin/python/file: -------------------------------------------------------------------------------- 1 | import gcd 2 | 3 | gcd.gcd(1, 2) 4 | -------------------------------------------------------------------------------- /ftplugin/python/testgcd.py: -------------------------------------------------------------------------------- 1 | import gcd 2 | 3 | gcd.gcd(1, 2) 4 | -------------------------------------------------------------------------------- /ftplugin/python/testutil.py: -------------------------------------------------------------------------------- 1 | # this is just a test 2 | # line 2 3 | # line 3 4 | # line 4 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /ftplugin/python/__pycache__ 2 | /test/__pycache__ 3 | .mypy_cache/ 4 | __pycache__/ 5 | -------------------------------------------------------------------------------- /test/testgcd.py: -------------------------------------------------------------------------------- 1 | import gcd 2 | 3 | gcd.gcd(1, 2) 4 | gcd.autodocparameters.logfunctionparameters() 5 | -------------------------------------------------------------------------------- /test/autodocparameters.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sillybun/vim-autodoc/HEAD/test/autodocparameters.pyc -------------------------------------------------------------------------------- /ftplugin/python/longtest.py: -------------------------------------------------------------------------------- 1 | def f(a, 2 | b, 3 | c): 4 | return a + b + c 5 | 6 | 7 | f(1, 2, 3) 8 | -------------------------------------------------------------------------------- /ftplugin/python/test1.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | def f(a: Union[float, int], b: int): 4 | return a + b 5 | 6 | f(1, 2) 7 | f(1.0, 2) 8 | -------------------------------------------------------------------------------- /ftplugin/python/parameters.log: -------------------------------------------------------------------------------- 1 | A.f>>0:A;1:int;2:int; 2 | A.g>>0:A;1:int;2:int; 3 | A.g..f>>0:int;1:int; 4 | g>>0:int;1:int; 5 | g..f>>0:int;1:int; 6 | f>>0:int,float;1:int,float; 7 | -------------------------------------------------------------------------------- /test/gcd.py: -------------------------------------------------------------------------------- 1 | def main(): 2 | print(gcd(15, 10)) 3 | print(gcd(45, 12)) 4 | 5 | def gcd(a, b): 6 | while b: 7 | a, b = b, a%b 8 | return a 9 | 10 | if __name__ == "__main__": 11 | main() 12 | -------------------------------------------------------------------------------- /ftplugin/python/testpep8.py: -------------------------------------------------------------------------------- 1 | from typing import Iterable, List, Iterator 2 | 3 | def f(a: Iterable[int]) -> str: 4 | for i in a: 5 | print(i) 6 | return "str" 7 | 8 | b: Iterable[int] = [1, 2, 3] 9 | b[f(range(3))] 10 | -------------------------------------------------------------------------------- /ftplugin/python/testlog.log: -------------------------------------------------------------------------------- 1 | {"A.f": {"parameters": {"0": ["__main__.A"], "1": ["int"], "2": ["float", "int"]}, "analysis": {"callnumber": 2, "collapsetime": 2.1457672119140625e-06}, "return": ["float", "int"]}, "g": {"parameters": {"0": ["list[int]", "range"]}, "analysis": {"callnumber": 2, "collapsetime": 9.5367431640625e-07}, "return": ["int"]}} -------------------------------------------------------------------------------- /ftplugin/python/test.py: -------------------------------------------------------------------------------- 1 | from typing import List, Callable, 2 | 3 | def testFun() -> List[Callable[..., Any]]: 4 | """ 5 | called number: 1 6 | total time: 3.814697265625e-06s 7 | """ 8 | temp = [lambda x : i*x for i in range(4)] 9 | return temp 10 | 11 | for everyLambda in testFun(): 12 | print(everyLambda(2)) 13 | -------------------------------------------------------------------------------- /test/testclass.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | import numpy as np 3 | 4 | class A: 5 | def copy(self) -> "A": 6 | return self 7 | 8 | def f(self, a: int, b: int) -> int: 9 | """ 10 | called number: 1 11 | total time: 1.1920928955078125e-06s 12 | """ 13 | return a + b 14 | 15 | 16 | a = A() 17 | a.f(1, 2) 18 | -------------------------------------------------------------------------------- /ftplugin/python/gcd.py: -------------------------------------------------------------------------------- 1 | def main() -> None: 2 | """ 3 | called number: 1 4 | total time: 0.00025391578674316406s 5 | """ 6 | print(gcd(15, 10)) 7 | print(gcd(45, 12)) 8 | print(gcd(10, 3)) 9 | 10 | def gcd(a: int, b: int) -> int: 11 | """ 12 | called number: 3 13 | total time: 4.0531158447265625e-06s 14 | """ 15 | while b: 16 | a, b = b, a%b 17 | return a 18 | 19 | if __name__ == "__main__": 20 | main() 21 | -------------------------------------------------------------------------------- /test/test1.py: -------------------------------------------------------------------------------- 1 | def f(a: Union[float, int], b: Union[float, int]=10) -> Union[float, int]: 2 | return a + b 3 | 4 | 5 | class A: 6 | def f(self, a: int, b: int) -> int: 7 | return a + b 8 | 9 | def g(self, a: int, b: int) -> int: 10 | def f(a: int, b: int) -> int: 11 | return a + b 12 | return f(a, b) 13 | 14 | def g(a: int, b: int) -> int: 15 | def f(a: int, b: int) -> int: 16 | return a + b 17 | return f(a, b) 18 | 19 | addunit = A() 20 | addunit.f(1, 2) 21 | addunit.g(1, 2) 22 | 23 | g(1, 2) 24 | f(1, 2) 25 | f(1.0, 2.0) 26 | -------------------------------------------------------------------------------- /test/testanneal.py: -------------------------------------------------------------------------------- 1 | import math 2 | import random 3 | from anneal import Annealer 4 | import anneal 5 | 6 | 7 | def distance(a, b): 8 | """Calculates distance between two latitude-longitude coordinates.""" 9 | R = 3963 # radius of Earth (miles) 10 | lat1, lon1 = math.radians(a[0]), math.radians(a[1]) 11 | lat2, lon2 = math.radians(b[0]), math.radians(b[1]) 12 | return math.acos(math.sin(lat1) * math.sin(lat2) + 13 | math.cos(lat1) * math.cos(lat2) * math.cos(lon1 - lon2)) * R 14 | 15 | 16 | class TravellingSalesmanProblem(Annealer): 17 | 18 | """Test annealer with a travelling salesman problem. 19 | """ 20 | 21 | # pass extra data (the distance matrix) into the constructor 22 | def __init__(self, state, distance_matrix): 23 | self.distance_matrix = distance_matrix 24 | super(TravellingSalesmanProblem, self).__init__(state) # important! 25 | 26 | def move(self): 27 | """Swaps two cities in the route.""" 28 | a = random.randint(0, len(self.state) - 1) 29 | b = random.randint(0, len(self.state) - 1) 30 | self.state[a], self.state[b] = self.state[b], self.state[a] 31 | 32 | def energy(self): 33 | """Calculates the length of the route.""" 34 | e = 0 35 | for i in range(len(self.state)): 36 | e += self.distance_matrix[self.state[i-1]][self.state[i]] 37 | return e 38 | 39 | 40 | 41 | if __name__ == '__main__': 42 | 43 | # latitude and longitude for the twenty largest U.S. cities 44 | cities = { 45 | 'New York City': (40.72, 74.00), 46 | 'Los Angeles': (34.05, 118.25), 47 | 'Chicago': (41.88, 87.63), 48 | 'Houston': (29.77, 95.38), 49 | 'Phoenix': (33.45, 112.07), 50 | 'Philadelphia': (39.95, 75.17), 51 | 'San Antonio': (29.53, 98.47), 52 | 'Dallas': (32.78, 96.80), 53 | 'San Diego': (32.78, 117.15), 54 | 'San Jose': (37.30, 121.87), 55 | 'Detroit': (42.33, 83.05), 56 | 'San Francisco': (37.78, 122.42), 57 | 'Jacksonville': (30.32, 81.70), 58 | 'Indianapolis': (39.78, 86.15), 59 | 'Austin': (30.27, 97.77), 60 | 'Columbus': (39.98, 82.98), 61 | 'Fort Worth': (32.75, 97.33), 62 | 'Charlotte': (35.23, 80.85), 63 | 'Memphis': (35.12, 89.97), 64 | 'Baltimore': (39.28, 76.62) 65 | } 66 | 67 | # initial state, a randomly-ordered itinerary 68 | init_state = list(cities.keys()) 69 | random.shuffle(init_state) 70 | 71 | # create a distance matrix 72 | distance_matrix = {} 73 | for ka, va in cities.items(): 74 | distance_matrix[ka] = {} 75 | for kb, vb in cities.items(): 76 | if kb == ka: 77 | distance_matrix[ka][kb] = 0.0 78 | else: 79 | distance_matrix[ka][kb] = distance(va, vb) 80 | 81 | tsp = TravellingSalesmanProblem(init_state, distance_matrix) 82 | tsp.steps = 100000 83 | # since our state is just a list, slice is the fastest way to copy 84 | tsp.copy_strategy = "slice" 85 | state, e = tsp.anneal() 86 | 87 | while state[0] != 'New York City': 88 | state = state[1:] + state[:1] # rotate NYC to start 89 | 90 | print() 91 | print("%i mile route:" % e) 92 | for city in state: 93 | print("\t", city) 94 | -------------------------------------------------------------------------------- /test/parameters.py: -------------------------------------------------------------------------------- 1 | import json 2 | import time 3 | from functools import wraps 4 | 5 | FUNCTION = dict() 6 | 7 | 8 | def recordparametertype(func): 9 | 10 | def gettypename(sth): 11 | _type = type(sth) 12 | if isinstance(sth, list) and len(sth) > 0: 13 | return "list[" + gettypename(sth[0]) + "]" 14 | elif isinstance(sth, tuple) and len(sth) > 0: 15 | if len(sth) <= 5: 16 | return "tuple[" + ",".join( 17 | [gettypename(sth[i]) for i in range(len(sth))] 18 | ) + "]" 19 | else: 20 | flag = True 21 | for i in range(1, len(sth)): 22 | if gettypename(sth[i]) != gettypename(sth[0]): 23 | flag = False 24 | break 25 | if flag: 26 | return "tuple[" + gettypename(sth[0]) + ",...]" 27 | else: 28 | return "tuple[" + ",".join( 29 | [gettypename(sth[i]) for i in range(5)] 30 | ) + ",...]" 31 | elif isinstance(sth, set) and len(sth) > 0: 32 | return "set[" + gettypename(list(sth)[0]) + "]" 33 | elif isinstance(sth, dict) and len(sth) > 0: 34 | key = list(sth.keys())[0] 35 | return "dict[" + gettypename(key) +\ 36 | "," + gettypename(sth[key]) + "]" 37 | else: 38 | typestr = str(_type) 39 | return typestr[(typestr.find("'") + 1):typestr.rfind("'")] 40 | 41 | global FUNCTION 42 | 43 | @wraps(func) 44 | def wrapper(*args, **kwargs): 45 | if func.__qualname__ not in FUNCTION: 46 | FUNCTION[func.__qualname__] = dict() 47 | FUNCTION[func.__qualname__]["parameters"] = dict() 48 | FUNCTION[func.__qualname__]["analysis"] = dict() 49 | for i, arg in enumerate(args): 50 | FUNCTION[func.__qualname__]["parameters"][i] = set( 51 | [gettypename(arg)] 52 | ) 53 | FUNCTION[func.__qualname__]["return"] = set() 54 | FUNCTION[func.__qualname__]["analysis"]["callnumber"] = 1 55 | FUNCTION[func.__qualname__]["analysis"]["collapsetime"] = 0 56 | else: 57 | for i, arg in enumerate(args): 58 | FUNCTION[func.__qualname__]["parameters"][i].add( 59 | gettypename(arg) 60 | ) 61 | FUNCTION[func.__qualname__]["analysis"]["callnumber"] += 1 62 | starttime = time.time() 63 | result = func(*args, **kwargs) 64 | endtime = time.time() 65 | FUNCTION[func.__qualname__]["analysis"]["collapsetime" 66 | ] += endtime - starttime 67 | FUNCTION[func.__qualname__]["return"].add(gettypename(result)) 68 | return result 69 | 70 | return wrapper 71 | 72 | 73 | class SetEncoder(json.JSONEncoder): 74 | 75 | def default(self, obj): 76 | if isinstance(obj, set): 77 | return sorted(list(obj)) 78 | return json.JSONEncoder.default(self, obj) 79 | 80 | 81 | def logfunctionparameters(): 82 | logfile = open(".autodocparameters.log", "w") 83 | logfile.write(json.dumps(FUNCTION, cls=SetEncoder)) 84 | 85 | 86 | if __name__ == "__main__": 87 | 88 | class A: 89 | 90 | @recordparametertype 91 | def f(self, a, b): 92 | return a + b 93 | 94 | a = A() 95 | a.f(1, 2) 96 | a.f(1, 2.0) 97 | 98 | @recordparametertype 99 | def g(b): 100 | return 101 | 102 | g(range(10)) 103 | g([1, 2, 3]) 104 | print(FUNCTION) 105 | -------------------------------------------------------------------------------- /ftplugin/python/parameters.py: -------------------------------------------------------------------------------- 1 | import json 2 | import time 3 | from functools import wraps 4 | import types 5 | import itertools 6 | from typing import Dict 7 | 8 | 9 | FUNCTION: Dict[str, Dict] = dict() 10 | 11 | def gettypename(sth): 12 | _type = type(sth) 13 | if isinstance(sth, list) and len(sth) > 0: 14 | return "list[" + gettypename(sth[0]) + "]" 15 | elif isinstance(sth, tuple) and len(sth) > 0: 16 | if len(sth) <= 5: 17 | return "tuple[" + ",".join( 18 | [gettypename(sth[i]) for i in range(len(sth))] 19 | ) + "]" 20 | else: 21 | flag = True 22 | for i in range(1, len(sth)): 23 | if gettypename(sth[i]) != gettypename(sth[0]): 24 | flag = False 25 | break 26 | if flag: 27 | return "tuple[" + gettypename(sth[0]) + ",...]" 28 | else: 29 | return "tuple[" + ",".join( 30 | [gettypename(sth[i]) for i in range(5)] 31 | ) + ",...]" 32 | elif isinstance(sth, set) and len(sth) > 0: 33 | return "set[" + gettypename(list(sth)[0]) + "]" 34 | elif isinstance(sth, dict) and len(sth) > 0: 35 | try: 36 | key = list(sth.keys())[0] 37 | return "dict[" + gettypename(key) +\ 38 | "," + gettypename(sth[key]) + "]" 39 | except IndexError: 40 | return "dict" 41 | elif isinstance(sth, types.GeneratorType): 42 | return "generator" 43 | elif isinstance(sth, range): 44 | return "range" 45 | else: 46 | typestr = str(_type) 47 | return typestr[(typestr.find("'") + 1):typestr.rfind("'")] 48 | 49 | def recordparametertype(func): 50 | 51 | 52 | global FUNCTION 53 | 54 | @wraps(func) 55 | def wrapper(*args, **kwargs): 56 | if func.__qualname__ not in FUNCTION: 57 | FUNCTION[func.__qualname__] = dict() 58 | FUNCTION[func.__qualname__]["parameters"] = dict() 59 | FUNCTION[func.__qualname__]["analysis"] = dict() 60 | for i, arg in enumerate(args): 61 | FUNCTION[func.__qualname__]["parameters"][i] = set( 62 | [gettypename(arg)] 63 | ) 64 | FUNCTION[func.__qualname__]["return"] = set() 65 | FUNCTION[func.__qualname__]["analysis"]["callnumber"] = 1 66 | FUNCTION[func.__qualname__]["analysis"]["collapsetime"] = 0 67 | else: 68 | for i, arg in enumerate(args): 69 | FUNCTION[func.__qualname__]["parameters"][i].add( 70 | gettypename(arg) 71 | ) 72 | FUNCTION[func.__qualname__]["analysis"]["callnumber"] += 1 73 | starttime = time.time() 74 | result = func(*args, **kwargs) 75 | endtime = time.time() 76 | FUNCTION[func.__qualname__]["analysis"]["collapsetime" 77 | ] += endtime - starttime 78 | FUNCTION[func.__qualname__]["return"].add(gettypename(result)) 79 | return result 80 | 81 | return wrapper 82 | 83 | 84 | class SetEncoder(json.JSONEncoder): 85 | def default(self, obj): 86 | if isinstance(obj, set): 87 | return sorted(list(obj)) 88 | return json.JSONEncoder.default(self, obj) 89 | 90 | 91 | def logfunctionparameters(): 92 | logfile = open(".autodocparameters.log", "w") 93 | logfile.write(json.dumps(FUNCTION, cls=SetEncoder)) 94 | 95 | 96 | if __name__ == "__main__": 97 | 98 | class A: 99 | 100 | @recordparametertype 101 | def f(self, a, b): 102 | return a + b 103 | 104 | a = A() 105 | a.f(1, 2) 106 | a.f(1, 2.0) 107 | 108 | @recordparametertype 109 | def g(b): 110 | return 111 | 112 | g(range(10)) 113 | g([1, 2, 3]) 114 | print(FUNCTION) 115 | -------------------------------------------------------------------------------- /ftplugin/python/vimbufferutil.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Set 2 | 3 | import countparentheses 4 | 5 | 6 | class AddBufferContent: 7 | 8 | def __init__(self): 9 | self.adddict: Dict[int, List[str]] = dict() 10 | self.removeset: Set[int] = set() 11 | self.insertdict: Dict[int, Dict[int, List[str]]] = dict() 12 | 13 | def addandwait(self, content: str, linenumber: int): 14 | if linenumber in self.adddict: 15 | self.adddict[linenumber].append(content) 16 | else: 17 | self.adddict[linenumber] = [content] 18 | 19 | def insertandwait( 20 | self, content: str, linenumber: int, column: int 21 | ) -> None: 22 | if linenumber in self.insertdict: 23 | if column in self.insertdict[linenumber]: 24 | self.insertdict[linenumber][column].append(content) 25 | else: 26 | self.insertdict[linenumber][column] = [content] 27 | else: 28 | self.insertdict[linenumber] = dict() 29 | self.insertdict[linenumber][column] = [content] 30 | 31 | def insert(self, buffer, content: str, linenumber: int, column: int) -> None: 32 | tline: str = buffer[linenumber - 1] 33 | buffer[linenumber - 1] = tline[0:column] + content + tline[column:] 34 | 35 | def removeandwait(self, linenumber: int): 36 | self.removeset.add(linenumber) 37 | 38 | def remove(self, buffer, linenumber: int): 39 | del buffer[linenumber - 1] 40 | 41 | def add(self, buffer, content: str, linenumber: int): 42 | buffer.append(content, linenumber) 43 | 44 | def conduct(self, buffer): 45 | alreadyadd = 0 46 | alreadyremove = 0 47 | for i in sorted(set(self.adddict.keys()).union(self.removeset).\ 48 | union(set(self.insertdict.keys()))): 49 | if i in self.removeset: 50 | self.remove(buffer, i + alreadyadd - alreadyremove) 51 | alreadyremove += 1 52 | elif i in self.insertdict: 53 | alreadyinsert = 0 54 | for j in sorted(self.insertdict[i].keys()): 55 | for content in self.insertdict[i][j]: 56 | self.insert(buffer, content, i + alreadyadd - alreadyremove, j + alreadyinsert) 57 | alreadyinsert += len(content) 58 | if i in self.adddict: 59 | for content in self.adddict[i]: 60 | self.add(buffer, content, i + alreadyadd - alreadyremove) 61 | alreadyadd += 1 62 | self.removeset = set() 63 | self.adddict = dict() 64 | 65 | 66 | class FunctionCode: 67 | 68 | def __init__(self) -> None: 69 | return 70 | 71 | def regist(self, buffer, startline: int) -> None: 72 | self.startline = startline 73 | self.endline = self.startline 74 | self.functiondefstatement: List[str] = list() 75 | self.containdocstring = False 76 | tline: str = buffer[startline] 77 | textra = tline.lstrip() 78 | self.functionname = textra[4:textra.find('(')] 79 | self.indentlevel = (len(tline) - len(textra)) // 4 80 | if self.indentlevel != 0: 81 | indentlevel = self.indentlevel 82 | for i in range(self.startline - 1, -1, -1): 83 | cline: str = buffer[i] 84 | if len(cline.strip()) == 0: 85 | continue 86 | cextra: str = cline.lstrip() 87 | cindentlevel = (len(cline) - len(cextra)) // 4 88 | if cindentlevel == indentlevel - 1: 89 | if cextra.startswith("def "): 90 | addname = cextra[4:cextra.find('(')] 91 | self.functionname = addname + ".." + self.functionname 92 | elif cextra.startswith("class "): 93 | addname = cextra[6:cextra.find("(")] 94 | self.functionname = addname + "." + self.functionname 95 | indentlevel -= 1 96 | if indentlevel == 0: 97 | break 98 | prev = (0, 0, None) 99 | while True: 100 | prev = countparentheses.count(buffer[self.endline], prev) 101 | self.functiondefstatement.append(buffer[self.endline]) 102 | if prev[0] != prev[1]: 103 | self.endline += 1 104 | else: 105 | break 106 | self.functionargsdict = countparentheses.getargument( 107 | self.functiondefstatement 108 | ) 109 | if self.endline < len(buffer) - 1 and\ 110 | buffer[self.endline + 1].strip().startswith('"""'): 111 | self.containdocstring = True 112 | self.docstartline = self.endline + 1 113 | if '"""' in buffer[self.docstartline].lstrip()[3:]: 114 | self.docendline = self.docstartline 115 | else: 116 | self.docendline = self.docstartline + 1 117 | while self.docendline < len(buffer) - 1 and '"""' not in buffer[ 118 | self.docendline 119 | ]: 120 | self.docendline += 1 121 | self.docstring: List[str] = list() 122 | if len(buffer[self.docstartline].strip()) > 3: 123 | tline = buffer[self.docstartline].strip() 124 | if self.docendline == self.docstartline: 125 | if len(tline) > 6: 126 | self.docstring.append( 127 | tline[(tline.find('"""') + 3):tline.rfind('"""')] 128 | ) 129 | else: 130 | self.docstring.append(tline[(tline.find('"""') + 3):]) 131 | if self.docendline > self.docstartline: 132 | for row in range(self.docstartline + 1, self.docendline): 133 | self.docstring.append( 134 | buffer[row][4 * (self.indentlevel + 1):] 135 | ) 136 | if buffer[self.docendline].strip() != '"""': 137 | self.docstring.append( 138 | buffer[self.docendline][4 * ( 139 | self.indentlevel + 1 140 | ):buffer[self.docendline].rfind('"""')] 141 | ) 142 | 143 | 144 | class AllFunctions: 145 | 146 | def __init__(self): 147 | self.functions = list() 148 | 149 | def regist(self, buffer): 150 | for row, line in enumerate(buffer): 151 | extra = line.lstrip() 152 | if extra.startswith("def ") and row < len(buffer) - 1: 153 | fc = FunctionCode() 154 | fc.regist(buffer, row) 155 | self.functions.append(fc) 156 | -------------------------------------------------------------------------------- /ftplugin/python/vim-autodoc.vim: -------------------------------------------------------------------------------- 1 | " -------------------------------- 2 | " Add our plugin to the path 3 | " -------------------------------- 4 | python3 import sys 5 | python3 import vim 6 | python3 sys.path.append(vim.eval('expand(":h")')) 7 | 8 | " -------------------------------- 9 | " Function(s) 10 | " -------------------------------- 11 | function! s:TestUtil(...) abort 12 | python3 << endOfPython 13 | 14 | import vim 15 | import vimbufferutil 16 | 17 | #abc = vimbufferutil.AddBufferContent() 18 | # 19 | #abc.removeandwait(1) 20 | #abc.addandwait("hello from line2", 2) 21 | #abc.removeandwait(3) 22 | # 23 | #abc.conduct(vim.current.buffer) 24 | 25 | endOfPython 26 | endfunction 27 | 28 | 29 | function! s:RecordAllFunctions(...) abort 30 | python3 << endOfPython 31 | 32 | import vim 33 | import countparentheses 34 | import vimbufferutil 35 | import autodoc 36 | import time 37 | from shutil import copyfile 38 | import sys 39 | 40 | start = 0 41 | peek = 0 42 | 43 | if int(vim.eval("a:0")) > 0 and ("python3" in vim.eval("a:1") or "py3" in vim.eval("a:1")): 44 | py = "py3" 45 | else: 46 | py = "py3" if sys.version[0] == "3" else "py2" 47 | 48 | print("python mode: {}".format(py)) 49 | 50 | while True: 51 | if vim.current.buffer[peek].startswith("#"): 52 | peek += 1 53 | elif vim.current.buffer[peek].startswith(" "): 54 | peek += 1 55 | elif vim.current.buffer[peek].startswith("from"): 56 | start = peek + 1 57 | peek += 1 58 | elif vim.current.buffer[peek].startswith("import"): 59 | start = peek + 1 60 | peek += 1 61 | else: 62 | break 63 | 64 | vim.current.buffer.append("import autodocparameters", start) 65 | vim.current.buffer.append("from autodocparameters import recordparametertype", start) 66 | 67 | path = vim.eval("s:path") 68 | flag_return_type = (vim.eval("g:autodoc_display_return_type") == "1") 69 | 70 | copyfile("{}/parameters.py".format(path), "./autodocparameters.py") 71 | 72 | 73 | for row, line in enumerate(vim.current.buffer): 74 | extra = line.lstrip() 75 | if extra.startswith("def ") and (row==0 or not vim.current.buffer[row-1].lstrip().startswith("@")): 76 | space = " " * (len(line) - len(extra)) 77 | vim.current.buffer.append("", row) 78 | vim.current.buffer[row] = space + "@recordparametertype" 79 | 80 | if vim.eval("g:autodoc_debug_mode") == "0": 81 | vim.command("call s:RunScript({})".format(", ".join(["'" + t + "'" for t in vim.eval("a:000")]))) 82 | 83 | if vim.eval("g:autodoc_typehint_style_" + py) == "pep484": 84 | autodoc.addpep484hint(vim.current.buffer, flag_return_type) 85 | else: 86 | autodoc.adddocstring_paramtype(vim.current.buffer, flag_return_type) 87 | if vim.eval("g:autodoc_display_runtime_info") == "1": 88 | autodoc.adddocstring_runtime_info(vim.current.buffer) 89 | 90 | vim.command("w") 91 | vim.command('call delete("autodocparameters.py")') 92 | vim.command('call delete(".autodocparameters.log")') 93 | 94 | endOfPython 95 | endfunction 96 | 97 | 98 | function! s:RecordCurrentFunction(...) abort 99 | python3 << endOfPython 100 | 101 | import vim 102 | import countparentheses 103 | import autodoc 104 | 105 | if int(vim.eval("a:0")) > 0 and ("python3" in vim.eval("a:1") or "py3" in vim.eval("a:1")): 106 | py = "py3" 107 | else: 108 | py = "py3" if sys.version[0] == "3" else "py2" 109 | 110 | print("python mode: {}".format(py)) 111 | 112 | currentindent = 999 113 | 114 | for row in range(vim.current.window.cursor[0]-1, -1, -1): 115 | line = vim.current.buffer[row] 116 | extra = line.lstrip() 117 | if len(extra) == 0: 118 | continue 119 | elif (len(line) - len(extra)) // 4 >= currentindent: 120 | continue 121 | elif extra.startswith("def ") and (row==0 or not vim.current.buffer[row-1].lstrip().startswith("@")): 122 | space = " " * (len(line) - len(extra)) 123 | vim.current.buffer.append("", row) 124 | vim.current.buffer[row] = space + "@recordparametertype" 125 | break 126 | else: 127 | currentindent = (len(line) - len(extra)) // 4 128 | 129 | 130 | vim.current.buffer.append("import autodocparameters", 0) 131 | vim.current.buffer.append("from autodocparameters import recordparametertype", 0) 132 | 133 | path = vim.eval("s:path") 134 | flag_return_type = (vim.eval("g:autodoc_display_return_type") == "1") 135 | 136 | vim.command("!cp {}/parameters.py ./autodocparameters.py".format(path)) 137 | 138 | if vim.eval("g:autodoc_debug_mode") == "0": 139 | vim.command("call s:RunScript({})".format(", ".join(["'" + t + "'" for t in vim.eval("a:000")]))) 140 | 141 | if vim.eval("g:autodoc_typehint_style_" + py) == "pep484": 142 | autodoc.addpep484hint(vim.current.buffer, flag_return_type) 143 | else: 144 | autodoc.adddocstring_paramtype(vim.current.buffer, flag_return_type) 145 | if vim.eval("g:autodoc_display_runtime_info") == "1": 146 | autodoc.adddocstring_runtime_info(vim.current.buffer) 147 | 148 | vim.command("w") 149 | vim.command('call delete("autodocparameters.py")') 150 | vim.command('call delete(".autodocparameters.log")') 151 | 152 | endOfPython 153 | endfunction 154 | 155 | 156 | function! s:RunScript(...) abort 157 | python3 << endOfPython 158 | 159 | import vim 160 | import countparentheses 161 | import autodoc 162 | 163 | try: 164 | otherfile = "" 165 | if int(vim.eval("a:0")) == 0: 166 | vim.current.buffer.append("autodocparameters.logfunctionparameters()") 167 | vim.command("w") 168 | vim.command("!python %") 169 | elif vim.eval("a:1").startswith("python"): 170 | otherfile = vim.eval("a:2") 171 | if otherfile != vim.eval("expand('%')"): 172 | with open(otherfile, 'a+') as f: 173 | f.write(vim.eval("expand('%:t:r')") + ".autodocparameters.logfunctionparameters()"+'\n') 174 | vim.command("w") 175 | vim.command("!" + " ".join(vim.eval("a:000"))) 176 | else: 177 | otherfile = "" 178 | vim.current.buffer.append("autodocparameters.logfunctionparameters()") 179 | vim.command("w") 180 | vim.command("!" + " ".join(vim.eval("a:000"))) 181 | elif vim.eval("a:1").endswith(".py"): 182 | otherfile = vim.eval("a:1") 183 | if otherfile != vim.eval("expand('%')"): 184 | with open(otherfile, 'a+') as f: 185 | f.write(vim.eval("expand('%:t:r')") + ".autodocparameters.logfunctionparameters()"+'\n') 186 | vim.command("w") 187 | vim.command("!python " + " ".join(vim.eval("a:000"))) 188 | else: 189 | otherfile = "" 190 | vim.current.buffer.append("autodocparameters.logfunctionparameters()") 191 | vim.command("w") 192 | vim.command("!python " + " ".join(vim.eval("a:000"))) 193 | else: 194 | vim.current.buffer.append("autodocparameters.logfunctionparameters()") 195 | vim.command("w") 196 | vim.command("!python % " + " ".join(vim.eval("a:000"))) 197 | except: 198 | pass 199 | 200 | vim.command("g/import autodocparameters/d") 201 | vim.command("g/from autodocparameters import recordparametertype/d") 202 | vim.command("g/@recordparametertype/d") 203 | vim.command("g/autodocparameters.logfunctionparameters/d") 204 | 205 | if otherfile != "": 206 | readFile = open(otherfile) 207 | lines = readFile.readlines() 208 | readFile.close() 209 | w = open(otherfile,'w') 210 | w.writelines([item for item in lines[:-1]]) 211 | w.close() 212 | 213 | endOfPython 214 | endfunction 215 | " -------------------------------- 216 | " Expose our commands to the user 217 | " -------------------------------- 218 | 219 | let s:path = expand(':p:h') 220 | 221 | if !exists('g:autodoc_display_return_type') 222 | let g:autodoc_display_return_type = 1 223 | endif 224 | 225 | if !exists('g:autodoc_display_runtime_info') 226 | let g:autodoc_display_runtime_info = 0 227 | endif 228 | 229 | if !exists('g:autodoc_typehint_style_py3') 230 | let g:autodoc_typehint_style_py3 = 'pep484' 231 | endif 232 | 233 | if !exists('g:autodoc_typehint_style_py2') 234 | let g:autodoc_typehint_style_py2 = '' 235 | endif 236 | 237 | if !exists('g:autodoc_debug_mode') 238 | let g:autodoc_debug_mode = 0 239 | endif 240 | 241 | command! -nargs=* RecordParameter :call s:RecordAllFunctions() 242 | command! -nargs=* RecordCurrentFunction :call s:RecordCurrentFunction() 243 | command! -nargs=* TestUtil :call s:TestUtil() 244 | -------------------------------------------------------------------------------- /ftplugin/python/countparentheses.py: -------------------------------------------------------------------------------- 1 | def count(line, prev): 2 | """ 3 | @type line: str 4 | @type prev: tuple(int) 5 | """ 6 | line = line.strip() 7 | leftnum = prev[0] 8 | rightnum = prev[1] 9 | condition = prev[2] 10 | i = 0 11 | while i < len(line): 12 | if condition == '"': 13 | if line[i] == "\\": 14 | i += 2 15 | elif line[i] == '"': 16 | condition = None 17 | i += 1 18 | else: 19 | i += 1 20 | elif condition == "'": 21 | if line[i] == "\\": 22 | i += 2 23 | elif line[i] == "'": 24 | condition = None 25 | i += 1 26 | else: 27 | i += 1 28 | else: 29 | if line[i] == "(": 30 | leftnum += 1 31 | elif line[i] == ")": 32 | rightnum += 1 33 | elif line[i] == '"': 34 | condition = '"' 35 | elif line[i] == "'": 36 | condition = "'" 37 | i += 1 38 | return (leftnum, rightnum, condition) 39 | 40 | 41 | def getargument(lines): 42 | argdict = dict() 43 | conditionstack = list() 44 | temparg = "" 45 | tempargtype = "" 46 | argnum = -1 47 | for row, line in enumerate(lines): 48 | i = 0 49 | while i < len(line): 50 | if len(conditionstack) == 0: 51 | if line[i] == "(": 52 | conditionstack.append("()") 53 | elif line[i:].startswith("->"): 54 | conditionstack.append("->") 55 | i += 1 56 | elif line[i] == ":" or (row == (len(lines) - 1) and i == (len(line) - 1)): 57 | if tempargtype != "": 58 | argdict[-1]["type"] = tempargtype 59 | 60 | i += 1 61 | elif conditionstack[-1] == "()": 62 | if line[i].isalpha(): 63 | tempargpos = (row, i) 64 | argnum += 1 65 | conditionstack.append("arg") 66 | elif line[i] == "=": 67 | conditionstack.append("=") 68 | i += 1 69 | elif line[i] == ",": 70 | argdict[argnum] = dict() 71 | argdict[argnum]["pos"] = tempargpos 72 | argdict[argnum]["arg"] = temparg 73 | if tempargtype != "": 74 | argdict[argnum]["type"] = tempargtype 75 | temparg = "" 76 | tempargtype = "" 77 | i += 1 78 | elif line[i] == ")": 79 | if argnum >= 0: 80 | argdict[argnum] = dict() 81 | argdict[argnum]["pos"] = tempargpos 82 | argdict[argnum]["arg"] = temparg 83 | if tempargtype != "": 84 | argdict[argnum]["type"] = tempargtype 85 | conditionstack.pop() 86 | argdict[-1] = dict() 87 | argdict[-1]["pos"] = (row, i) 88 | tempargtype = "" 89 | temparg = "" 90 | elif line[i] == ":": 91 | conditionstack.append(":") 92 | i += 1 93 | else: 94 | i += 1 95 | elif conditionstack[-1] == "arg": 96 | if line[i].isalpha() or line[i].isdigit() or line[i] == "_": 97 | temparg += line[i] 98 | i += 1 99 | elif line[i] == "=" or line[i] == " " or line[i] == "," or line[i] == ")": 100 | conditionstack.pop() 101 | elif line[i] == ":": 102 | conditionstack.append(":") 103 | i += 1 104 | elif line[i] == ",": 105 | conditionstack.pop() 106 | else: 107 | i += 1 108 | elif conditionstack[-1] == ":": 109 | if line[i] != "," and line[i] != ")" and line[i] != " ": 110 | tempargtype += line[i] 111 | if line[i] == '"': 112 | conditionstack.append('"') 113 | elif line[i] == "'": 114 | conditionstack.append("'") 115 | elif line[i] == "[": 116 | conditionstack.append("[") 117 | elif line[i] == "{": 118 | conditionstack.append("{") 119 | elif line[i] == "(": 120 | conditionstack.append("(") 121 | elif line[i] == ")": 122 | conditionstack.pop() 123 | continue 124 | elif line[i] == ",": 125 | conditionstack.pop() 126 | continue 127 | i += 1 128 | elif conditionstack[-1] == "->": 129 | if line[i] != "," and\ 130 | line[i] != ")" and\ 131 | line[i] != " " and\ 132 | line[i] != ":": 133 | tempargtype += line[i] 134 | if line[i] == '"': 135 | conditionstack.append('"') 136 | elif line[i] == "'": 137 | conditionstack.append("'") 138 | elif line[i] == "[": 139 | conditionstack.append("[") 140 | elif line[i] == "{": 141 | conditionstack.append("{") 142 | elif line[i] == "(": 143 | conditionstack.append("(") 144 | elif line[i] == ":": 145 | conditionstack.pop() 146 | continue 147 | i += 1 148 | elif conditionstack[-1] == "=": 149 | if line[i] == '"': 150 | conditionstack.append('"') 151 | elif line[i] == "'": 152 | conditionstack.append("'") 153 | elif line[i] == "[": 154 | conditionstack.append("[") 155 | elif line[i] == "{": 156 | conditionstack.append("{") 157 | elif line[i] == "(": 158 | conditionstack.append("(") 159 | elif line[i] == ")": 160 | conditionstack.pop() 161 | continue 162 | elif line[i] == ",": 163 | conditionstack.pop() 164 | continue 165 | i += 1 166 | elif conditionstack[-1] == '"' or conditionstack[-1] == "'": 167 | if line[i] == conditionstack[-1]: 168 | conditionstack.pop() 169 | i += 1 170 | elif line[i] == "\\": 171 | i += 2 172 | else: 173 | i += 1 174 | elif conditionstack[-1] == "[": 175 | if ":" in conditionstack or "->" in conditionstack: 176 | tempargtype += line[i] 177 | if line[i] == '"': 178 | conditionstack.append('"') 179 | elif line[i] == "'": 180 | conditionstack.append("'") 181 | elif line[i] == "[": 182 | conditionstack.append("[") 183 | elif line[i] == "{": 184 | conditionstack.append("{") 185 | elif line[i] == "(": 186 | conditionstack.append("(") 187 | elif line[i] == "]": 188 | conditionstack.pop() 189 | i += 1 190 | elif conditionstack[-1] == "(": 191 | if line[i] == '"': 192 | conditionstack.append('"') 193 | elif line[i] == "'": 194 | conditionstack.append("'") 195 | elif line[i] == "[": 196 | conditionstack.append("[") 197 | elif line[i] == "{": 198 | conditionstack.append("{") 199 | elif line[i] == "(": 200 | conditionstack.append("(") 201 | elif line[i] == ")": 202 | conditionstack.pop() 203 | i += 1 204 | elif conditionstack[-1] == "{": 205 | if line[i] == '"': 206 | conditionstack.append('"') 207 | elif line[i] == "'": 208 | conditionstack.append("'") 209 | elif line[i] == "[": 210 | conditionstack.append("[") 211 | elif line[i] == "{": 212 | conditionstack.append("{") 213 | elif line[i] == "(": 214 | conditionstack.append("(") 215 | elif line[i] == "}": 216 | conditionstack.pop() 217 | i += 1 218 | return argdict 219 | 220 | 221 | 222 | 223 | 224 | 225 | if __name__ == "__main__": 226 | print(count("def f(a=\"(\"", (0, 0, None))) 227 | print(getargument(["def f(a =1) -> int:"])) 228 | print(getargument(["def f() -> int:"])) 229 | print(getargument(["def f(a, b_t) -> int:"])) 230 | print(getargument(["def f(a: int, b) -> int:"])) 231 | print(getargument(["def f(a = \"hello\", b)"])) 232 | print(getargument(["def f(a = (1, 2, 3), b = [4, 5, 6])"])) 233 | print(getargument(["def f(a = \"()))\")"])) 234 | print(getargument(["def f(a=10, ", " b: int)"])) 235 | print(getargument([" def __init__(self, varname='?', freqs=None):"])) 236 | -------------------------------------------------------------------------------- /ftplugin/python/autodoc.py: -------------------------------------------------------------------------------- 1 | import json 2 | from typing import Dict, Optional, Set 3 | 4 | import vimbufferutil 5 | import typestyle 6 | import time 7 | import re 8 | 9 | debug = False 10 | 11 | class FunctionAna: 12 | 13 | def __init__(self, path: Optional[str] = None) -> None: 14 | if path is None: 15 | return 16 | else: 17 | self.readfromfile(path) 18 | 19 | def readfromfile(self, path: str) -> None: 20 | with open(path) as file: 21 | self.FUNCTION: Dict[str, Dict] = json.loads(file.read()) 22 | 23 | def getparamtype(self, func: str, parnum: int) -> list: 24 | return self.FUNCTION[func]["parameters"][parnum] 25 | 26 | def getparamnumber(self, func: str) -> int: 27 | return len(self.FUNCTION[func]["parameters"]) 28 | 29 | def getreturntype(self, func: str) -> list: 30 | return self.FUNCTION[func]["return"] 31 | 32 | def getcallnumber(self, func: str) -> int: 33 | return self.FUNCTION[func]["analysis"]["callnumber"] 34 | 35 | def getcollapsetime(self, func: str) -> float: 36 | return self.FUNCTION[func]["analysis"]["collapse"] 37 | 38 | 39 | def adddocstring_paramtype( 40 | buffer, returnflag=False, doctype: str = "Epydoc" 41 | ) -> None: 42 | functionlog = FunctionAna(".autodocparameters.log") 43 | 44 | functioncode = vimbufferutil.AllFunctions() 45 | functioncode.regist(buffer) 46 | 47 | abc = vimbufferutil.AddBufferContent() 48 | for func in functionlog.FUNCTION: 49 | funcdict = functionlog.FUNCTION[func] 50 | for _fc in functioncode.functions: 51 | fc: vimbufferutil.FunctionCode = _fc 52 | if fc.functionname == func: 53 | tfc: vimbufferutil.FunctionCode = fc 54 | commentspace = " " * (4 * (fc.indentlevel + 1)) 55 | abc.addandwait(commentspace + '"""', fc.endline + 1) 56 | for index in fc.functionargsdict: 57 | param = fc.functionargsdict[index]["arg"] 58 | if str(index) in funcdict["parameters"]: 59 | abc.addandwait( 60 | commentspace + "@type " + param + ": " + 61 | ", ".join(funcdict["parameters"][str(index)]), 62 | fc.endline + 1 63 | ) 64 | if returnflag: 65 | abc.addandwait( 66 | commentspace + "@rtype: " + 67 | ", ".join(funcdict["return"]), fc.endline + 1 68 | ) 69 | if tfc.containdocstring: 70 | for i in range(tfc.docstartline, tfc.docendline + 1): 71 | abc.removeandwait(i + 1) 72 | for docline in tfc.docstring: 73 | if "@type" not in docline and "@rtype" not in docline: 74 | abc.addandwait( 75 | commentspace + docline, fc.endline + 1 76 | ) 77 | abc.addandwait(commentspace + '"""', fc.endline + 1) 78 | abc.conduct(buffer) 79 | 80 | 81 | def adddocstring_runtime_info(buffer) -> None: 82 | functionlog = FunctionAna(".autodocparameters.log") 83 | 84 | functioncode = vimbufferutil.AllFunctions() 85 | functioncode.regist(buffer) 86 | 87 | abc = vimbufferutil.AddBufferContent() 88 | for func in functionlog.FUNCTION: 89 | funcdict = functionlog.FUNCTION[func] 90 | for _fc in functioncode.functions: 91 | fc: vimbufferutil.FunctionCode = _fc 92 | if fc.functionname == func: 93 | tfc: vimbufferutil.FunctionCode = fc 94 | commentspace = " " * (4 * (fc.indentlevel + 1)) 95 | abc.addandwait(commentspace + '"""', tfc.endline + 1) 96 | if tfc.containdocstring: 97 | for i in range(tfc.docstartline, tfc.docendline + 1): 98 | abc.removeandwait(i + 1) 99 | for docline in tfc.docstring: 100 | if "called number: " not in docline and "total time: " not in docline: 101 | abc.addandwait( 102 | commentspace + docline, fc.endline + 1 103 | ) 104 | abc.addandwait( 105 | commentspace + "called number: " + 106 | str(funcdict["analysis"]["callnumber"]), 107 | tfc.endline + 1 108 | ) 109 | abc.addandwait( 110 | commentspace + "total time: " + 111 | str(funcdict["analysis"]["collapsetime"]) + "s", 112 | tfc.endline + 1 113 | ) 114 | abc.addandwait(commentspace + '"""', tfc.endline + 1) 115 | abc.conduct(buffer) 116 | 117 | 118 | def addpep484hint(buffer, returnflag) -> None: 119 | # starttime = time.time() 120 | logfile = FunctionAna(".autodocparameters.log") 121 | 122 | functioncode = vimbufferutil.AllFunctions() 123 | functioncode.regist(buffer) 124 | 125 | styledecoder = typestyle.ZYTType() 126 | 127 | abc = vimbufferutil.AddBufferContent() 128 | 129 | importset: Set[str] = set() 130 | 131 | if debug: 132 | print(logfile.FUNCTION) 133 | print([t.functionname for t in functioncode.functions]) 134 | for loggedfunc in logfile.FUNCTION: 135 | loggedfuncinfo = logfile.FUNCTION[loggedfunc] 136 | for _fc in functioncode.functions: 137 | fc: vimbufferutil.FunctionCode = _fc 138 | if fc.functionname == loggedfunc: 139 | tfc: vimbufferutil.FunctionCode = fc 140 | # if -1 not in tfc.functionargsdict: 141 | # print(tfc.functiondefstatement) 142 | methodflag = False 143 | if "." in loggedfunc and\ 144 | loggedfunc.split(".")[-2] != "": 145 | methodflag = True 146 | if debug: 147 | print(tfc.functionargsdict) 148 | print(loggedfuncinfo) 149 | for index in sorted(tfc.functionargsdict): 150 | if methodflag and index == 0: 151 | continue 152 | if index == -1: 153 | continue 154 | arginfo = tfc.functionargsdict[index] 155 | argpos = arginfo["pos"] 156 | if str(index) in loggedfuncinfo["parameters"]: 157 | if "type" in tfc.functionargsdict[index]: 158 | continue 159 | print(loggedfuncinfo["parameters"][str(index)]) 160 | types = [typestyle.ZYTType().fromdoc(adtype) 161 | for adtype in loggedfuncinfo["parameters"][str(index)]] 162 | for t in types: 163 | importset = importset.union(t.getpep484import()) 164 | if len(types) == 1: 165 | abc.insertandwait(": " + types[0].generatepep484(), tfc.startline + 166 | argpos[0] + 1, argpos[1] + len(arginfo["arg"])) 167 | else: 168 | tline = typestyle.unionpep484(types) 169 | abc.insertandwait(": " + tline, tfc.startline + 170 | argpos[0] + 1, argpos[1] + len(arginfo["arg"])) 171 | if "Optional" in tline: 172 | importset.add("Optional") 173 | importset.add("Union") 174 | if returnflag and "type" not in tfc.functionargsdict[-1]: 175 | types = [typestyle.ZYTType().fromdoc(adtype) 176 | for adtype in loggedfuncinfo["return"]] 177 | for t in types: 178 | importset = importset.union(t.getpep484import()) 179 | arginfo = tfc.functionargsdict[-1] 180 | argpos = arginfo["pos"] 181 | if len(types) == 1: 182 | abc.insertandwait(" -> " + types[0].generatepep484(), tfc.startline + 183 | argpos[0] + 1, argpos[1] + 1) 184 | else: 185 | abc.insertandwait(" -> " + typestyle.unionpep484(types), tfc.startline + 186 | argpos[0] + 1, argpos[1] + 1) 187 | 188 | 189 | start = 0 190 | peek = 0 191 | 192 | while True: 193 | if buffer[peek].startswith("#"): 194 | peek += 1 195 | elif buffer[peek].startswith(" "): 196 | peek += 1 197 | elif buffer[peek].startswith("from"): 198 | start = peek + 1 199 | peek += 1 200 | elif buffer[peek].startswith("import"): 201 | start = peek + 1 202 | peek += 1 203 | else: 204 | break 205 | 206 | for i in range(0, start): 207 | if buffer[i].startswith("from typing import"): 208 | extra = buffer[i][19:] 209 | print(extra) 210 | extra = re.sub("\s", "", extra) 211 | already = set(extra.split(",")) 212 | importset = importset - already 213 | print(already) 214 | print(importset) 215 | abc.insertandwait(", " + ", ".join(list(importset)), i+1, len(buffer[i])) 216 | importset = set() 217 | break 218 | if len(importset) != 0: 219 | abc.addandwait("from typing import " + ", ".join(list(importset)), start) 220 | abc.addandwait("", start) 221 | 222 | abc.conduct(buffer) 223 | # endtime = time.time() 224 | # print(endtime - starttime) 225 | 226 | def main() -> None: 227 | fa = FunctionAna() 228 | fa.readfromfile("testlog.log") 229 | print(fa.FUNCTION) 230 | 231 | 232 | if __name__ == "__main__": 233 | main() 234 | 235 | -------------------------------------------------------------------------------- /ftplugin/python/typestyle.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional, Type, Dict, Iterable, Set 2 | import types 3 | import parameters 4 | 5 | 6 | class ZYTType: 7 | 8 | def __init__(self) -> None: 9 | self.type: Optional[Type] = None 10 | self.any = False 11 | self.subtype: Optional[List[ZYTType]] = None 12 | self.basicflag = False 13 | self.repeatflag = False 14 | self.classname: Optional[str] = None 15 | 16 | BasicType = { 17 | "NoneType": type(None), 18 | "int": int, 19 | "float": float, 20 | "str": str, 21 | "bool": bool, 22 | "bytes": bytes, 23 | "function": types.FunctionType, 24 | "builtin_function_or_method": types.BuiltinFunctionType, 25 | "type": type, 26 | } 27 | CompondType = { 28 | "list": list, 29 | "set": set, 30 | "dict": dict, 31 | "tuple": tuple, 32 | "generator": types.GeneratorType, 33 | "range": range 34 | } 35 | BasicTypePEP484 = { 36 | "None": type(None), 37 | "int": int, 38 | "float": float, 39 | "str": str, 40 | "bool": bool, 41 | "bytes": bytes, 42 | "Type": type, 43 | "Callable": types.FunctionType, 44 | } 45 | CompondTypePEP484 = { 46 | "List": list, 47 | "Set": set, 48 | "Dict": type, 49 | "Tuple": tuple, 50 | "Iterable": types.GeneratorType, 51 | } 52 | BasicPEP484: Dict[Type, str] = { 53 | type(None): "None", 54 | int: "int", 55 | float: "float", 56 | str: "str", 57 | bool: "bool", 58 | bytes: "bytes", 59 | types.FunctionType: "Callable[..., Any]", 60 | types.BuiltinMethodType: "Callable[..., Any]", 61 | types.BuiltinFunctionType: "Callable[..., Any]", 62 | type: "Type" 63 | } 64 | CompondPEP484: Dict[Type, str] = { 65 | list: "List", 66 | set: "Set", 67 | dict: "Dict", 68 | tuple: "Tuple", 69 | types.GeneratorType: "Iterable", 70 | range: "Iterable[int]" 71 | } 72 | def gettypename(self, sth): 73 | return parameters.gettypename(sth) 74 | 75 | def fromvar(self, sth): 76 | self.fromAutoDoc(self.gettypename(sth)) 77 | return self 78 | 79 | def fromdoc(self, typestr: str): 80 | self.fromAutoDoc(typestr) 81 | return self 82 | 83 | def fromAutoDoc(self, typestr: str) -> str: 84 | self.type = None 85 | self.subtype = None 86 | self.basicflag = False 87 | self.repeatflag = False 88 | self.classname = None 89 | self.any = False 90 | if typestr == "": 91 | return "" 92 | for bt in self.BasicType: 93 | if typestr.startswith(bt): 94 | self.type = self.BasicType[bt] 95 | self.basicflag = True 96 | return typestr[len(bt):] 97 | for ct in self.CompondType: 98 | if typestr.startswith(ct): 99 | self.type = self.CompondType[ct] 100 | typestr = typestr[len(ct):] 101 | if typestr.startswith("["): 102 | temptype = ZYTType() 103 | typestr = temptype.fromAutoDoc(typestr[1:]) 104 | self.subtype = [temptype] 105 | while(typestr.startswith(",")): 106 | if typestr.startswith(",..."): 107 | self.repeatflag = True 108 | typestr = typestr[4:] 109 | break 110 | temptype = ZYTType() 111 | typestr = temptype.fromAutoDoc(typestr[1:]) 112 | self.subtype.append(temptype) 113 | return typestr[1:] 114 | else: 115 | return typestr 116 | if typestr.startswith("__main__."): 117 | typestr = typestr[9:] 118 | self.classname = "" 119 | for t in typestr: 120 | if t == "]" or t == ",": 121 | break 122 | self.classname += t 123 | return typestr[len(self.classname):] 124 | 125 | def fromPEP484(self, typestr: str) -> str: 126 | self.type = None 127 | self.subtype = None 128 | self.basicflag = False 129 | self.repeatflag = False 130 | self.classname = None 131 | self.any = False 132 | if " " in typestr: 133 | typestr = "".join([t for t in typestr if t != 0]) 134 | if typestr == "": 135 | return "" 136 | if typestr.startswith("Any"): 137 | self.any = True 138 | return typestr[3:] 139 | for bt in self.BasicTypePEP484: 140 | if typestr.startswith(bt): 141 | self.type = self.BasicTypePEP484[bt] 142 | self.basicflag = True 143 | typestr = typestr[len(bt)] 144 | if bt == "Callable": 145 | left = 1 146 | for i in range(1, len(typestr)): 147 | if typestr == "[": 148 | left += 1 149 | elif typestr == "]": 150 | left -= 1 151 | if left == 0: 152 | typestr = typestr[i + 1] 153 | return typestr 154 | return typestr 155 | for ct in self.CompondTypePEP484: 156 | if typestr.startswith(ct): 157 | self.type = self.CompondTypePEP484[ct] 158 | typestr = typestr[len(ct):] 159 | if typestr.startswith("["): 160 | if typestr.startswith("[()]"): 161 | return typestr[4:] 162 | temptype = ZYTType() 163 | typestr = temptype.fromPEP484(typestr[1:]) 164 | self.subtype = [temptype] 165 | while(typestr.startswith(",")): 166 | if typestr.startswith(",..."): 167 | self.repeatflag = True 168 | typestr = typestr[4:] 169 | break 170 | temptype = ZYTType() 171 | typestr = temptype.fromPEP484(typestr[1:]) 172 | self.subtype.append(temptype) 173 | return typestr[1:] 174 | else: 175 | return typestr 176 | self.classname = "" 177 | for t in typestr: 178 | if t == "]" or t == ",": 179 | break 180 | self.classname += t 181 | return typestr[len(self.classname):] 182 | 183 | 184 | def subset(self, othertype) -> bool: 185 | if othertype.any: 186 | return True 187 | if self.type == othertype.type: 188 | if self.type is None: 189 | return self.classname == othertype.classname 190 | else: 191 | if self.basicflag: 192 | return True 193 | else: 194 | if self.type != tuple: 195 | if othertype.subtype is None: 196 | return True 197 | elif self.subtype is None: 198 | return False 199 | elif len(self.subtype) != len(othertype.subtype): 200 | return False 201 | if self.repeatflag != othertype.repeatflag: 202 | return False 203 | for i in range(len(self.subtype)): 204 | if not self.subtype[i].subset(othertype.subtype[i]): 205 | return False 206 | return True 207 | else: 208 | if othertype.subtype is None or self.subtype is None: 209 | return self.subtype is None and self.subtype is None 210 | elif len(self.subtype) != len(othertype.subtype): 211 | return False 212 | if self.repeatflag != othertype.repeatflag: 213 | return False 214 | for i in range(len(self.subtype)): 215 | if not self.subtype[i].subset(othertype.subtype[i]): 216 | return False 217 | return True 218 | return False 219 | 220 | 221 | def getpep484import(self) -> Set[str]: 222 | if self.basicflag: 223 | if self.type == types.FunctionType: 224 | return set(["Callable"]) 225 | elif self.type == types.BuiltinMethodType: 226 | return set(["Callable"]) 227 | elif self.type == types.BuiltinMethodType: 228 | return set(["Callable"]) 229 | elif self.type == type: 230 | return set(["Type"]) 231 | else: 232 | return set([]) 233 | elif self.type is None: 234 | return set() 235 | else: 236 | rimport = set() 237 | if self.CompondPEP484[self.type].endswith("[int]"): 238 | rimport.add("Iterable") 239 | else: 240 | rimport.add(self.CompondPEP484[self.type]) 241 | if self.subtype is not None: 242 | for t in self.subtype: 243 | rimport = rimport.union(t.getpep484import()) 244 | return rimport 245 | 246 | 247 | def generatepep484(self) -> str: 248 | if self.basicflag: 249 | if self.type is not None: 250 | return self.BasicPEP484[self.type] 251 | else: 252 | return "" 253 | elif self.type is not None: 254 | rstr: str = self.CompondPEP484[self.type] 255 | if self.subtype is None: 256 | if self.type == tuple: 257 | return rstr + "[()]" 258 | else: 259 | return rstr 260 | else: 261 | rstr += "[" 262 | rstr += ", ".join(t.generatepep484() for t in self.subtype) 263 | if self.repeatflag: 264 | rstr += ", ..." 265 | rstr += "]" 266 | return rstr 267 | elif self.classname is not None: 268 | return '"' + self.classname.split(".")[-1] + '"' 269 | else: 270 | return "Any" 271 | 272 | def autodoctopep484(self, doc: str) -> str: 273 | self.fromAutoDoc(doc) 274 | return self.generatepep484() 275 | 276 | def unionpep484(types: List[ZYTType]) -> str: 277 | for t in types: 278 | if t.type == type(None): 279 | newtypes = [t for t in types if t.type != type(None)] 280 | return "Optional[{}]".format(unionpep484(newtypes)) 281 | shorttypes = list() 282 | for i in range(len(types)): 283 | flag = True 284 | for j in range(len(types)): 285 | if j != i and types[j].subset(types[i]): 286 | if j > i or not types[i].subset(types[j]): 287 | flag = False 288 | break 289 | if flag: 290 | shorttypes.append(types[i]) 291 | if len(shorttypes) == 1: 292 | return shorttypes[0].generatepep484() 293 | else: 294 | return "Union[" + ", ".join(t.generatepep484() for t in shorttypes) + "]" 295 | 296 | 297 | def main(): 298 | print(ZYTType().fromvar(sum).generatepep484()) 299 | print(ZYTType().fromvar(range(10)).generatepep484()) 300 | print(ZYTType().fromvar((t for t in range(2))).generatepep484()) 301 | print(ZYTType().fromvar(sum).getpep484import()) 302 | print(ZYTType().fromvar(range(10)).getpep484import()) 303 | print(ZYTType().fromvar((t for t in range(2))).getpep484import()) 304 | 305 | if __name__ == "__main__": 306 | main() 307 | -------------------------------------------------------------------------------- /test/anneal.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | from __future__ import unicode_literals 5 | import abc 6 | import copy 7 | import datetime 8 | import math 9 | import pickle 10 | import random 11 | import signal 12 | import sys 13 | import time 14 | 15 | 16 | def round_figures(x, n): 17 | """Returns x rounded to n significant figures.""" 18 | return round(x, int(n - math.ceil(math.log10(abs(x))))) 19 | 20 | 21 | def time_string(seconds: float) -> str: 22 | """Returns time in seconds as a string formatted HHHH:MM:SS.""" 23 | s = int(round(seconds)) # round to nearest second 24 | h, s = divmod(s, 3600) # get hours and remainder 25 | m, s = divmod(s, 60) # split remainder into minutes and seconds 26 | return '%4i:%02i:%02i' % (h, m, s) 27 | 28 | 29 | class Annealer(object): 30 | 31 | """Performs simulated annealing by calling functions to calculate 32 | energy and make moves on a state. The temperature schedule for 33 | annealing may be provided manually or estimated automatically. 34 | """ 35 | 36 | __metaclass__ = abc.ABCMeta 37 | 38 | # defaults 39 | Tmax = 25000.0 40 | Tmin = 2.5 41 | steps = 50000 42 | updates = 100 43 | copy_strategy = 'deepcopy' 44 | user_exit = False 45 | save_state_on_exit = False 46 | 47 | # placeholders 48 | best_state = None 49 | best_energy = None 50 | start = None 51 | 52 | def __init__(self, initial_state: List[str]=None, load_state=None) -> None: 53 | if initial_state is not None: 54 | self.state = self.copy_state(initial_state) 55 | elif load_state: 56 | self.load_state(load_state) 57 | else: 58 | raise ValueError('No valid values supplied for neither \ 59 | initial_state nor load_state') 60 | 61 | signal.signal(signal.SIGINT, self.set_user_exit) 62 | 63 | def save_state(self, fname=None): 64 | """Saves state to pickle""" 65 | if not fname: 66 | date = datetime.datetime.now().strftime("%Y-%m-%dT%Hh%Mm%Ss") 67 | fname = date + "_energy_" + str(self.energy()) + ".state" 68 | with open(fname, "wb") as fh: 69 | pickle.dump(self.state, fh) 70 | 71 | def load_state(self, fname=None): 72 | """Loads state from pickle""" 73 | with open(fname, 'rb') as fh: 74 | self.state = pickle.load(fh) 75 | 76 | @abc.abstractmethod 77 | def move(self): 78 | """Create a state change""" 79 | pass 80 | 81 | @abc.abstractmethod 82 | def energy(self): 83 | """Calculate state's energy""" 84 | pass 85 | 86 | def set_user_exit(self, signum, frame): 87 | """Raises the user_exit flag, further iterations are stopped 88 | """ 89 | self.user_exit = True 90 | 91 | def set_schedule(self, schedule): 92 | """Takes the output from `auto` and sets the attributes 93 | """ 94 | self.Tmax = schedule['tmax'] 95 | self.Tmin = schedule['tmin'] 96 | self.steps = int(schedule['steps']) 97 | self.updates = int(schedule['updates']) 98 | 99 | def copy_state(self, state: List[str]) -> List[str]: 100 | """Returns an exact copy of the provided state 101 | Implemented according to self.copy_strategy, one of 102 | 103 | * deepcopy : use copy.deepcopy (slow but reliable) 104 | * slice: use list slices (faster but only works if state is list-like) 105 | * method: use the state's copy() method 106 | """ 107 | if self.copy_strategy == 'deepcopy': 108 | return copy.deepcopy(state) 109 | elif self.copy_strategy == 'slice': 110 | return state[:] 111 | elif self.copy_strategy == 'method': 112 | return state.copy() 113 | else: 114 | raise RuntimeError('No implementation found for ' + 115 | 'the self.copy_strategy "%s"' % 116 | self.copy_strategy) 117 | 118 | def update(self, *args: int, **kwargs: float) -> None: 119 | """Wrapper for internal update. 120 | 121 | If you override the self.update method, 122 | you can chose to call the self.default_update method 123 | from your own Annealer. 124 | """ 125 | self.default_update(*args, **kwargs) 126 | 127 | def default_update(self, step: int, T: float, E: float, acceptance: Union[None, float], improvement: Union[None, float]) -> None: 128 | """Default update, outputs to stderr. 129 | 130 | Prints the current temperature, energy, acceptance rate, 131 | improvement rate, elapsed time, and remaining time. 132 | 133 | The acceptance rate indicates the percentage of moves since the last 134 | update that were accepted by the Metropolis algorithm. It includes 135 | moves that decreased the energy, moves that left the energy 136 | unchanged, and moves that increased the energy yet were reached by 137 | thermal excitation. 138 | 139 | The improvement rate indicates the percentage of moves since the 140 | last update that strictly decreased the energy. At high 141 | temperatures it will include both moves that improved the overall 142 | state and moves that simply undid previously accepted moves that 143 | increased the energy by thermal excititation. At low temperatures 144 | it will tend toward zero as the moves that can decrease the energy 145 | are exhausted and moves that would increase the energy are no longer 146 | thermally accessible.""" 147 | 148 | elapsed = time.time() - self.start 149 | if step == 0: 150 | print(' Temperature Energy Accept Improve Elapsed Remaining', 151 | file=sys.stderr) 152 | print('\r%12.5f %12.2f %s ' % 153 | (T, E, time_string(elapsed)), file=sys.stderr, end="\r") 154 | sys.stderr.flush() 155 | else: 156 | remain = (self.steps - step) * (elapsed / step) 157 | print('\r%12.5f %12.2f %7.2f%% %7.2f%% %s %s\r' % 158 | (T, E, 100.0 * acceptance, 100.0 * improvement, 159 | time_string(elapsed), time_string(remain)), file=sys.stderr, end="\r") 160 | sys.stderr.flush() 161 | 162 | def anneal(self) -> Tuple[List[str], float]: 163 | """Minimizes the energy of a system by simulated annealing. 164 | 165 | Parameters 166 | state : an initial arrangement of the system 167 | 168 | Returns 169 | (state, energy): the best state and energy found. 170 | """ 171 | step = 0 172 | self.start = time.time() 173 | 174 | # Precompute factor for exponential cooling from Tmax to Tmin 175 | if self.Tmin <= 0.0: 176 | raise Exception('Exponential cooling requires a minimum "\ 177 | "temperature greater than zero.') 178 | Tfactor = -math.log(self.Tmax / self.Tmin) 179 | 180 | # Note initial state 181 | T = self.Tmax 182 | E = self.energy() 183 | prevState = self.copy_state(self.state) 184 | prevEnergy = E 185 | self.best_state = self.copy_state(self.state) 186 | self.best_energy = E 187 | trials, accepts, improves = 0, 0, 0 188 | if self.updates > 0: 189 | updateWavelength = self.steps / self.updates 190 | self.update(step, T, E, None, None) 191 | 192 | # Attempt moves to new states 193 | while step < self.steps and not self.user_exit: 194 | step += 1 195 | T = self.Tmax * math.exp(Tfactor * step / self.steps) 196 | self.move() 197 | E = self.energy() 198 | dE = E - prevEnergy 199 | trials += 1 200 | if dE > 0.0 and math.exp(-dE / T) < random.random(): 201 | # Restore previous state 202 | self.state = self.copy_state(prevState) 203 | E = prevEnergy 204 | else: 205 | # Accept new state and compare to best state 206 | accepts += 1 207 | if dE < 0.0: 208 | improves += 1 209 | prevState = self.copy_state(self.state) 210 | prevEnergy = E 211 | if E < self.best_energy: 212 | self.best_state = self.copy_state(self.state) 213 | self.best_energy = E 214 | if self.updates > 1: 215 | if (step // updateWavelength) > ((step - 1) // updateWavelength): 216 | self.update( 217 | step, T, E, accepts / trials, improves / trials) 218 | trials, accepts, improves = 0, 0, 0 219 | 220 | self.state = self.copy_state(self.best_state) 221 | if self.save_state_on_exit: 222 | self.save_state() 223 | 224 | # Return best state and energy 225 | return self.best_state, self.best_energy 226 | 227 | def auto(self, minutes, steps=2000): 228 | """Explores the annealing landscape and 229 | estimates optimal temperature settings. 230 | 231 | Returns a dictionary suitable for the `set_schedule` method. 232 | """ 233 | 234 | def run(T, steps): 235 | """Anneals a system at constant temperature and returns the state, 236 | energy, rate of acceptance, and rate of improvement.""" 237 | E = self.energy() 238 | prevState = self.copy_state(self.state) 239 | prevEnergy = E 240 | accepts, improves = 0, 0 241 | for _ in range(steps): 242 | self.move() 243 | E = self.energy() 244 | dE = E - prevEnergy 245 | if dE > 0.0 and math.exp(-dE / T) < random.random(): 246 | self.state = self.copy_state(prevState) 247 | E = prevEnergy 248 | else: 249 | accepts += 1 250 | if dE < 0.0: 251 | improves += 1 252 | prevState = self.copy_state(self.state) 253 | prevEnergy = E 254 | return E, float(accepts) / steps, float(improves) / steps 255 | 256 | step = 0 257 | self.start = time.time() 258 | 259 | # Attempting automatic simulated anneal... 260 | # Find an initial guess for temperature 261 | T = 0.0 262 | E = self.energy() 263 | self.update(step, T, E, None, None) 264 | while T == 0.0: 265 | step += 1 266 | self.move() 267 | T = abs(self.energy() - E) 268 | 269 | # Search for Tmax - a temperature that gives 98% acceptance 270 | E, acceptance, improvement = run(T, steps) 271 | 272 | step += steps 273 | while acceptance > 0.98: 274 | T = round_figures(T / 1.5, 2) 275 | E, acceptance, improvement = run(T, steps) 276 | step += steps 277 | self.update(step, T, E, acceptance, improvement) 278 | while acceptance < 0.98: 279 | T = round_figures(T * 1.5, 2) 280 | E, acceptance, improvement = run(T, steps) 281 | step += steps 282 | self.update(step, T, E, acceptance, improvement) 283 | Tmax = T 284 | 285 | # Search for Tmin - a temperature that gives 0% improvement 286 | while improvement > 0.0: 287 | T = round_figures(T / 1.5, 2) 288 | E, acceptance, improvement = run(T, steps) 289 | step += steps 290 | self.update(step, T, E, acceptance, improvement) 291 | Tmin = T 292 | 293 | # Calculate anneal duration 294 | elapsed = time.time() - self.start 295 | duration = round_figures(int(60.0 * minutes * step / elapsed), 2) 296 | 297 | # Don't perform anneal, just return params 298 | return {'tmax': Tmax, 'tmin': Tmin, 'steps': duration, 'updates': self.updates} 299 | --------------------------------------------------------------------------------