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 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
|
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import re
from typing import List, Optional, Union
from generator import model
BASIC_LINK_RE = re.compile(r"{@link +(\w+) ([\w ]+)}")
BASIC_LINK_RE2 = re.compile(r"{@link +(\w+)\.(\w+) ([\w \.`]+)}")
BASIC_LINK_RE3 = re.compile(r"{@link +(\w+)}")
BASIC_LINK_RE4 = re.compile(r"{@link +(\w+)\.(\w+)}")
PARTS_RE = re.compile(r"(([a-z0-9])([A-Z]))")
def _fix_links(line: str) -> str:
line = BASIC_LINK_RE.sub(r'<see cref="\1">\2</see>', line)
line = BASIC_LINK_RE2.sub(r'<see cref="\1.\2">\3</see>', line)
line = BASIC_LINK_RE3.sub(r'<see cref="\1" />', line)
line = BASIC_LINK_RE4.sub(r'<see cref="\1.\2" />', line)
return line
def lines_to_doc_comments(lines: List[str]) -> List[str]:
if not lines:
return []
return (
["/// <summary>"]
+ [f"/// {_fix_links(line)}" for line in lines if not line.startswith("@")]
+ ["/// </summary>"]
)
def get_parts(name: str) -> List[str]:
name = name.replace("_", " ")
return PARTS_RE.sub(r"\2 \3", name).split()
def to_camel_case(name: str) -> str:
parts = get_parts(name)
return parts[0] + "".join([p.capitalize() for p in parts[1:]])
def to_upper_camel_case(name: str) -> str:
return "".join([c.capitalize() for c in get_parts(name)])
def lsp_method_to_name(method: str) -> str:
if method.startswith("$"):
method = method[1:]
method = method.replace("/", "_")
return to_upper_camel_case(method)
def get_name(obj: Union[model.Request, model.Notification]) -> str:
if obj.typeName:
return obj.typeName
if hasattr(obj, "name"):
return obj.name
return lsp_method_to_name(obj.method)
def file_header() -> List[str]:
return [
"// Copyright (c) Microsoft Corporation. All rights reserved.",
"// Licensed under the MIT License.",
"// ",
"// THIS FILE IS AUTOGENERATED, DO NOT MODIFY IT",
"",
]
def namespace_wrapper(
namespace: str, imports: List[str], lines: List[str]
) -> List[str]:
indent = " " * 4
return (
file_header()
+ imports
+ [""]
+ ["namespace " + namespace + " {"]
+ [(f"{indent}{line}" if line else line) for line in lines]
+ ["}", ""]
)
def get_doc(doc: Optional[str]) -> str:
if doc:
return lines_to_doc_comments(doc.splitlines(keepends=False))
return []
def get_special_case_class_name(name: str) -> str:
# This is because C# does not allow class name and property name to be the same.
# public class Command{ public string Command { get; set; }} is not valid.
if name == "Command":
return "CommandAction"
return name
def get_special_case_property_name(name: str) -> str:
if name == "string":
return "stringValue"
if name == "int":
return "intValue"
if name == "event":
return "eventArgs"
if name == "params":
return "paramsValue"
return name
def class_wrapper(
type_def: Union[model.Structure, model.Notification, model.Request],
inner: List[str],
derived: Optional[str] = None,
class_attributes: Optional[List[str]] = None,
is_record=True,
) -> List[str]:
if hasattr(type_def, "name"):
name = get_special_case_class_name(type_def.name)
else:
raise ValueError(f"Unknown type: {type_def}")
rec_or_cls = "record" if is_record else "class"
lines = (
get_doc(type_def.documentation)
+ generate_extras(type_def)
+ (class_attributes if class_attributes else [])
+ [
"[DataContract]",
f"public {rec_or_cls} {name}: {derived}"
if derived
else f"public {rec_or_cls} {name}",
"{",
]
)
lines += indent_lines(inner)
lines += ["}", ""]
return lines
def property_wrapper(prop_def: model.Property, content: List[str]) -> List[str]:
lines = (get_doc(prop_def.documentation) + generate_extras(prop_def) + content,)
lines += indent_lines(content)
return lines
def indent_lines(lines: List[str], indent: str = " " * 4) -> List[str]:
return [(f"{indent}{line}" if line else line) for line in lines]
def cleanup_str(text: str) -> str:
return text.replace("\r", "").replace("\n", "")
def get_deprecated(text: Optional[str]) -> Optional[str]:
if not text:
return None
lines = text.splitlines(keepends=False)
for line in lines:
if line.startswith("@deprecated"):
return line.replace("@deprecated", "").strip()
return None
def generate_extras(
type_def: Union[
model.Enum,
model.EnumItem,
model.Property,
model.TypeAlias,
model.Structure,
model.Request,
model.Notification,
],
) -> List[str]:
deprecated = get_deprecated(type_def.documentation)
extras = []
if type_def.deprecated:
extras += [f'[Obsolete("{cleanup_str(type_def.deprecated)}")]']
elif deprecated:
extras += [f'[Obsolete("{cleanup_str(deprecated)}")]']
if type_def.proposed:
extras += ["[Proposed]"]
if type_def.since:
extras += [f'[Since("{cleanup_str(type_def.since)}")]']
if hasattr(type_def, "messageDirection"):
if type_def.since:
extras += [
f"[Direction(MessageDirection.{to_upper_camel_case(type_def.messageDirection)})]"
]
return extras
def get_usings(types: List[str]) -> List[str]:
usings = []
for t in ["DataMember", "DataContract"]:
if t in types:
usings.append("using System.Runtime.Serialization;")
for t in ["JsonConverter", "JsonConstructor", "JsonProperty", "NullValueHandling"]:
if t in types:
usings.append("using Newtonsoft.Json;")
for t in ["JToken", "JObject", "JArray"]:
if t in types:
usings.append("using Newtonsoft.Json.Linq;")
for t in ["List", "Dictionary"]:
if t in types:
usings.append("using System.Collections.Generic;")
for t in ["ImmutableArray", "ImmutableDictionary"]:
if t in types:
usings.append("using System.Collections.Immutable;")
return sorted(list(set(usings)))
|