-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhelpers.py
More file actions
159 lines (125 loc) · 5.07 KB
/
helpers.py
File metadata and controls
159 lines (125 loc) · 5.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
from __future__ import annotations
from enum import Enum
from typing import Any
from typing import TYPE_CHECKING
from typing import TypedDict
import keyword
if TYPE_CHECKING:
from lsp_schema import BaseType
from lsp_schema import Enumeration
from lsp_schema import EnumerationType
from lsp_schema import EveryType
from lsp_schema import MapKeyType
from lsp_schema import Property
indentation = ' '
def capitalize(text: str) -> str:
return text[0].upper() + text[1:]
def format_comment(text: str | None, indent: str = '') -> str:
if not text:
return ''
# Replace zero-width spaces.
text = text.replace('\u200b', '')
# Replace DIVISION SLASH that triggers RUF001.
text = text.replace('\u2215', '/')
is_multiline = '\n' in text
contains_backslashes = '\\' in text
raw = 'r' if contains_backslashes else ''
if is_multiline:
lines = text.splitlines(keepends=True)
text = indent.join(lines)
text = f'\n{indent}{text}\n{indent}'
return f'{indent}{raw}"""{text}"""'
new_literal_structures: set[str] = set()
def get_new_literal_structures() -> list[str]:
return sorted(new_literal_structures)
def reset_new_literal_structures() -> None:
new_literal_structures.clear()
class StructureKind(Enum):
Class = 1
Function = 2
class FormatTypeContext(TypedDict):
enumerations: dict[str, Enumeration]
def format_type(typ: EveryType, context: FormatTypeContext) -> str:
result = 'Any'
if typ['kind'] == 'base':
return format_base_types(typ)
if typ['kind'] == 'reference':
literal_symbol_name = typ['name']
if (
(enum := context['enumerations'].get(literal_symbol_name))
and enum.get('supportsCustomValues')
and enum['type']['name'] == 'string' # only for string enums, number enums are handled fine by IntEnum
):
return f'Union[{format_type(enum["type"], context)}, {literal_symbol_name}]'
return f"'{literal_symbol_name}'"
if typ['kind'] == 'array':
literal_symbol_name = format_type(typ['element'], context)
return f'List[{literal_symbol_name}]'
if typ['kind'] == 'map':
key = format_base_types(typ['key'])
value = format_type(typ['value'], {'enumerations': context['enumerations']})
return f'Dict[{key}, {value}]'
if typ['kind'] == 'and':
pass
elif typ['kind'] == 'or':
union = [format_type(item, context) for item in typ['items']]
return f'Union[{", ".join(union)}]'
elif typ['kind'] == 'tuple':
union = [format_type(item, context) for item in typ['items']]
return f'list[{" | ".join(set(union))}]'
elif typ['kind'] == 'literal':
if not typ['value']['properties']:
return 'Dict[str, LSPAny]'
msg = 'Unsupported case, none of the cases in LSP schema need this currently!'
raise Exception(msg)
elif typ['kind'] == 'stringLiteral':
return f"Literal['{typ['value']}']"
elif typ['kind'] == 'integerLiteral' or typ['kind'] == 'booleanLiteral':
return f'Literal[{typ["value"]}]'
return result
def format_base_types(base_type: BaseType | MapKeyType | EnumerationType) -> str:
mapping: dict[str, str] = {
'integer': 'int',
'uinteger': 'Uint',
'decimal': 'float',
'string': 'str',
'boolean': 'bool',
'null': 'None',
}
name = base_type['name']
return mapping.get(name, name)
class FormattedProperty(TypedDict):
name: str
value: Any
documentation: str
def get_formatted_properties(properties: list[Property], context: FormatTypeContext) -> list[FormattedProperty]:
result: list[FormattedProperty] = []
for p in properties:
key = p['name']
value = format_type(p['type'], context)
if p.get('optional'):
value = f'NotRequired[{value}]'
documentation = p.get('documentation') or ''
result.append({'name': key, 'value': value, 'documentation': documentation}) # f"{key}: {value}{documentation}"
return result # "\n\t".join(result)
def has_invalid_property_name(properties: list[Property]) -> bool:
return any(keyword.iskeyword(p['name']) for p in properties)
def format_class_properties(properties: list[FormattedProperty]) -> str:
result: list[str] = []
for p in properties:
line = f'{p["name"]}: {p["value"]}'
comment = format_comment(p['documentation'], indentation)
if comment:
line += f'\n{comment}'
result.append(line)
return f'\n{indentation}'.join(result)
def format_dict_properties(properties: list[FormattedProperty]) -> str:
result: list[str] = []
for p in properties:
documentation = p.get('documentation')
formatted_documentation = ''
if documentation:
formatted_documentation = documentation.replace('\n', f'\n{indentation}# ')
formatted_documentation = f'# {formatted_documentation}\n{indentation}'
result.append(f"{formatted_documentation}'{p['name']}': {p['value']},")
return f'\n{indentation}'.join(result)