1# SPDX-FileCopyrightText: 2021 GNOME Foundation 2# SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later 3 4import argparse 5import json 6import os 7import sys 8 9from . import gir, log, utils 10 11 12HELP_MSG = "Search terms in the symbol indices" 13 14 15def _gen_alias_result(symbol, namespace): 16 alias = namespace.find_alias(symbol["name"]) 17 if alias.doc is not None: 18 summary = utils.preprocess_docs(alias.doc.content, namespace, summary=True, plain=True) 19 else: 20 summary = "Missing documentation" 21 22 return { 23 "type": "alias", 24 "name": alias.name, 25 "ctype": alias.base_ctype, 26 "summary": summary, 27 "link": f"alias.{alias.name}.html", 28 } 29 30 31def _gen_bitfield_result(symbol, namespace): 32 enum = namespace.find_bitfield(symbol["name"]) 33 if enum.doc is not None: 34 summary = utils.preprocess_docs(enum.doc.content, namespace, summary=True, plain=True) 35 else: 36 summary = "Missing documentation" 37 38 return { 39 "type": "flags", 40 "name": enum.name, 41 "ctype": enum.base_ctype, 42 "summary": summary.split("\n"), 43 "link": f"flags.{enum.name}.html", 44 } 45 46 47def _gen_callback_result(symbol, namespace): 48 pass 49 50 51def _gen_class_result(symbol, namespace): 52 cls = namespace.find_class(symbol["name"]) 53 if cls.doc is not None: 54 summary = utils.preprocess_docs(cls.doc.content, namespace, summary=True, plain=True) 55 else: 56 summary = "Missing documentation" 57 58 return { 59 "type": "class", 60 "name": cls.name, 61 "ctype": cls.base_ctype, 62 "summary": summary.split("\n"), 63 "link": f"class.{cls.name}.html", 64 } 65 66 67def _gen_class_method_result(symbol, namespace): 68 t = namespace.find_real_type(symbol["type_name"]) 69 cls_name = t.struct_for 70 if cls_name is None: 71 return None 72 73 methods = getattr(t, "methods", []) 74 for m in methods: 75 if m.name == symbol["name"]: 76 if m.doc is not None: 77 summary = utils.preprocess_docs(m.doc.content, namespace, summary=True, plain=True) 78 else: 79 summary = "Missing documentation" 80 81 return { 82 "type": "class method", 83 "name": f"{t.name}.{m.name}", 84 "ctype": m.identifier, 85 "summary": summary.split("\n"), 86 "link": f"class_method.{cls_name}.{m.name}.html", 87 } 88 89 return None 90 91 92def _gen_ctor_result(symbol, namespace): 93 t = namespace.find_real_type(symbol["type_name"]) 94 95 methods = getattr(t, "constructors", []) 96 for m in methods: 97 if m.name == symbol["name"]: 98 if m.doc is not None: 99 summary = utils.preprocess_docs(m.doc.content, namespace, summary=True, plain=True) 100 else: 101 summary = "Missing documentation" 102 103 return { 104 "type": "constructor", 105 "name": f"{t.name}.{m.name}", 106 "ctype": m.identifier, 107 "summary": summary.split("\n"), 108 "link": f"ctor.{t.name}.{m.name}.html", 109 } 110 111 return None 112 113 114def _gen_domain_result(symbol, namespace): 115 enum = namespace.find_error_domain(symbol["name"]) 116 if enum.doc is not None: 117 summary = utils.preprocess_docs(enum.doc.content, namespace, summary=True, plain=True) 118 else: 119 summary = "Missing documentation" 120 121 return { 122 "type": "error", 123 "name": enum.name, 124 "ctype": enum.base_ctype, 125 "summary": summary.split("\n"), 126 "link": f"error.{enum.name}.html", 127 } 128 129 130def _gen_enum_result(symbol, namespace): 131 enum = namespace.find_enumeration(symbol["name"]) 132 if enum.doc is not None: 133 summary = utils.preprocess_docs(enum.doc.content, namespace, summary=True, plain=True) 134 else: 135 summary = "Missing documentation" 136 137 return { 138 "type": "enum", 139 "name": enum.name, 140 "ctype": enum.base_ctype, 141 "summary": summary.split("\n"), 142 "link": f"enum.{enum.name}.html", 143 } 144 145 146def _gen_func_result(symbol, namespace): 147 functions = namespace.get_functions() 148 for func in functions: 149 if func.name == symbol["name"]: 150 if func.doc is not None: 151 summary = utils.preprocess_docs(func.doc.content, namespace, summary=True, plain=True) 152 else: 153 summary = "Missing documentation" 154 return { 155 "type": "function", 156 "name": func.name, 157 "ctype": func.identifier, 158 "summary": summary.split("\n"), 159 "link": f"func.{func.name}.html", 160 } 161 162 163def _gen_func_macro_result(symbol, namespace): 164 macros = namespace.get_effective_function_macros() 165 for func in macros: 166 if func.name == symbol["name"]: 167 if func.doc is not None: 168 summary = utils.preprocess_docs(func.doc.content, namespace, summary=True, plain=True) 169 else: 170 summary = "Missing documentation" 171 return { 172 "type": "function macro", 173 "name": func.name, 174 "ctype": func.identifier, 175 "summary": summary.split("\n"), 176 "link": f"func.{func.name}.html", 177 } 178 179 180def _gen_interface_result(symbol, namespace): 181 iface = namespace.find_interface(symbol["name"]) 182 if iface.doc is not None: 183 summary = utils.preprocess_docs(iface.doc.content, namespace, summary=True, plain=True) 184 else: 185 summary = "Missing documentation" 186 187 return { 188 "type": "interface", 189 "name": iface.name, 190 "ctype": iface.base_ctype, 191 "summary": summary.split("\n"), 192 "link": f"iface.{iface.name}.html", 193 } 194 195 196def _gen_method_result(symbol, namespace): 197 t = namespace.find_real_type(symbol["type_name"]) 198 199 methods = getattr(t, "methods", []) 200 for m in methods: 201 if m.name == symbol["name"]: 202 if m.doc is not None: 203 summary = utils.preprocess_docs(m.doc.content, namespace, summary=True, plain=True) 204 else: 205 summary = "Missing documentation" 206 207 return { 208 "type": "method", 209 "name": f"{t.name}.{m.name}", 210 "ctype": m.identifier, 211 "summary": summary.split("\n"), 212 "link": f"method.{t.name}.{m.name}.html", 213 } 214 215 return None 216 217 218def _gen_property_result(symbol, namespace): 219 t = namespace.find_real_type(symbol["type_name"]) 220 221 properties = getattr(t, "properties", {}) 222 prop = properties.get(symbol["name"], None) 223 if prop is None: 224 return None 225 226 if prop.doc is not None: 227 summary = utils.preprocess_docs(prop.doc.content, namespace, summary=True, plain=True) 228 else: 229 summary = "Missing documentation" 230 231 return { 232 "type": "property", 233 "name": f"{t.name}:{prop.name}", 234 "ctype": t.base_ctype, 235 "summary": summary.split("\n"), 236 "link": f"property.{t.name}.{prop.name}.html", 237 } 238 239 240def _gen_record_result(symbol, namespace): 241 record = namespace.find_record(symbol["name"]) 242 if record.doc is not None: 243 summary = utils.preprocess_docs(record.doc.content, namespace, summary=True, plain=True) 244 else: 245 summary = "Missing documentation" 246 247 return { 248 "type": "struct", 249 "name": record.name, 250 "ctype": record.base_ctype, 251 "summary": summary.split("\n"), 252 "link": f"struct.{record.name}.html", 253 } 254 255 256def _gen_signal_result(symbol, namespace): 257 t = namespace.find_real_type(symbol["type_name"]) 258 259 signals = getattr(t, "signals", {}) 260 signal = signals.get(symbol["name"], None) 261 if signal is None: 262 return None 263 264 if signal.doc is not None: 265 summary = utils.preprocess_docs(signal.doc.content, namespace, summary=True, plain=True) 266 else: 267 summary = "Missing documentation" 268 269 return { 270 "type": "signal", 271 "name": f"{t.name}::{signal.name}", 272 "ctype": t.base_ctype, 273 "summary": summary.split("\n"), 274 "link": f"signal.{t.name}.{signal.name}.html", 275 } 276 277 278def _gen_type_func_result(symbol, namespace): 279 t = namespace.find_real_type(symbol["type_name"]) 280 281 functions = getattr(t, "functions", []) 282 for f in functions: 283 if f.name == symbol["name"]: 284 if f.doc is not None: 285 summary = utils.preprocess_docs(f.doc.content, namespace, summary=True, plain=True) 286 else: 287 summary = "Missing documentation" 288 289 return { 290 "type": "func", 291 "name": f"{t.name}.{f.name}", 292 "ctype": f.identifier, 293 "summary": summary.split("\n"), 294 "link": f"type_func.{t.name}.{f.name}.html", 295 } 296 297 return None 298 299 300def _gen_union_result(symbol, namespace): 301 union = namespace.find_union(symbol["name"]) 302 if union.doc is not None: 303 summary = utils.preprocess_docs(union.doc.content, namespace, summary=True, plain=True) 304 else: 305 summary = "Missing documentation" 306 307 return { 308 "type": "union", 309 "name": union.name, 310 "ctype": union.base_ctype, 311 "summary": summary.split("\n"), 312 "link": f"union.{union.name}.html", 313 } 314 315 316def _gen_vfunc_result(symbol, namespace): 317 t = namespace.find_real_type(symbol["type_name"]) 318 319 methods = getattr(t, "virtual_methods", []) 320 for m in methods: 321 if m.name == symbol["name"]: 322 if m.doc is not None: 323 summary = utils.preprocess_docs(m.doc.content, namespace, summary=True, plain=True) 324 else: 325 summary = "Missing documentation" 326 327 return { 328 "type": "vfunc", 329 "name": f"{t.name}.{m.name}", 330 "ctype": t.base_ctype, 331 "summary": summary.split("\n"), 332 "link": f"vfunc.{t.name}.{m.name}.html", 333 } 334 335 return None 336 337 338def query(repository, terms, index_file): 339 if index_file is None: 340 index_file = os.path.join(os.getcwd(), "index.json") 341 342 with open(index_file, "r") as f: 343 index = json.load(f) 344 345 namespace = repository.namespace 346 347 index_meta = index["meta"] 348 index_symbols = index["symbols"] 349 index_terms = index["terms"] 350 351 if index_meta["ns"] != namespace.name or index_meta["version"] != namespace.version: 352 log.error("Index file does not match the GIR namespace") 353 354 result_types = { 355 "alias": _gen_alias_result, 356 "bitfield": _gen_bitfield_result, 357 "callback": _gen_callback_result, 358 "class": _gen_class_result, 359 "class_method": _gen_class_method_result, 360 "ctor": _gen_ctor_result, 361 "domain": _gen_domain_result, 362 "enum": _gen_enum_result, 363 "function": _gen_func_result, 364 "function_macro": _gen_func_macro_result, 365 "interface": _gen_interface_result, 366 "method": _gen_method_result, 367 "property": _gen_property_result, 368 "record": _gen_record_result, 369 "signal": _gen_signal_result, 370 "type_func": _gen_type_func_result, 371 "union": _gen_union_result, 372 "vfunc": _gen_vfunc_result, 373 } 374 375 results = [] 376 377 for term in terms: 378 docs = index_terms.get(term, []) 379 for doc in docs: 380 symbol = index_symbols[doc] 381 382 gen_result = result_types.get(symbol["type"]) 383 if gen_result is None: 384 log.warning(f"Unhandled symbol type {symbol['type']} for '{term}'") 385 continue 386 387 res = gen_result(symbol, namespace) 388 results.append(res) 389 390 prefix = "result:" 391 indent = ''.join([' ' for x in range(len(prefix))]) 392 393 n_results = len(results) 394 terms_str = ", ".join(terms) 395 print(f"Found {n_results} results matching '{terms_str}'") 396 397 for res in results: 398 lines = [f"{prefix} [{res['type']}] {res['name']} - {res['ctype']}"] 399 for chunk in res["summary"]: 400 lines.append(f"{indent} {chunk}") 401 lines.append(f"{indent} link: {res['link']}") 402 print("\n".join(lines)) 403 404 405def add_args(parser): 406 parser.add_argument("--add-include-path", action="append", dest="include_paths", default=[], 407 help="include paths for other GIR files") 408 parser.add_argument("--index", help="the index file") 409 parser.add_argument("--term", action="append", dest="terms", default=[], 410 help="a search term") 411 parser.add_argument("infile", metavar="GIRFILE", type=argparse.FileType('r', encoding='UTF-8'), 412 default=sys.stdin, help="the GIR file to parse") 413 414 415def run(options): 416 paths = [] 417 paths.extend(options.include_paths) 418 paths.append(utils.default_search_paths()) 419 log.debug(f"Search paths: {paths}") 420 421 log.info("Parsing GIR file") 422 parser = gir.GirParser(search_paths=paths) 423 parser.parse(options.infile) 424 425 log.checkpoint() 426 query(parser.get_repository(), options.terms, options.index) 427 428 return 0 429