minimal adjustments
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-python / pythonFiles / symbolProvider.py
1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License.
3
4 import ast
5 import json
6 import sys
7
8
9 class Visitor(ast.NodeVisitor):
10     def __init__(self):
11         self.symbols = {"classes": [], "methods": [], "functions": []}
12
13     def visit_Module(self, node):
14         self.visitChildren(node)
15
16     def visitChildren(self, node, namespace=""):
17         for child in node.body:
18             if isinstance(child, ast.FunctionDef):
19                 self.visitDef(child, namespace)
20             if isinstance(child, ast.ClassDef):
21                 self.visitClassDef(child, namespace)
22             try:
23                 if isinstance(child, ast.AsyncFunctionDef):
24                     self.visitDef(child, namespace)
25             except Exception:
26                 pass
27
28     def visitDef(self, node, namespace=""):
29         end_position = self.getEndPosition(node)
30         symbol = "functions" if namespace == "" else "methods"
31         self.symbols[symbol].append(self.getDataObject(node, namespace))
32
33     def visitClassDef(self, node, namespace=""):
34         end_position = self.getEndPosition(node)
35         self.symbols['classes'].append(self.getDataObject(node, namespace))
36
37         if len(namespace) > 0:
38             namespace = "{0}::{1}".format(namespace, node.name)
39         else:
40             namespace = node.name
41         self.visitChildren(node, namespace)
42
43     def getDataObject(self, node, namespace=""):
44         end_position = self.getEndPosition(node)
45         return {
46             "namespace": namespace,
47             "name": node.name,
48             "range": {
49                 "start": {
50                     "line": node.lineno - 1,
51                     "character": node.col_offset
52                 },
53                 "end": {
54                     "line": end_position[0],
55                     "character": end_position[1]
56                 }
57             }
58         }
59
60     def getEndPosition(self, node):
61         if not hasattr(node, 'body') or len(node.body) == 0:
62             return (node.lineno - 1, node.col_offset)
63         return self.getEndPosition(node.body[-1])
64
65
66 def provide_symbols(source):
67     """Provides a list of all symbols in provided code.
68
69     The list comprises of 3-item tuples that contain the starting line number,
70     ending line number and whether the statement is a single line.
71
72     """
73     tree = ast.parse(source)
74     visitor = Visitor()
75     visitor.visit(tree)
76     sys.stdout.write(json.dumps(visitor.symbols))
77     sys.stdout.flush()
78
79
80 if __name__ == "__main__":
81     if len(sys.argv) == 3:
82         contents = sys.argv[2]
83     else:
84         with open(sys.argv[1], "r") as source:
85             contents = source.read()
86
87     try:
88         default_encoding = sys.getdefaultencoding()
89         encoded_contents = contents.encode(default_encoding, 'surrogateescape')
90         contents = encoded_contents.decode(default_encoding, 'replace')
91     except (UnicodeError, LookupError):
92         pass
93     if isinstance(contents, bytes):
94         contents = contents.decode('utf8')
95     provide_symbols(contents)