1# SPDX-FileCopyrightText: 2020 GNOME Foundation
2# SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later
3
4import argparse
5import sys
6
7from . import gir, log, utils
8
9
10HELP_MSG = "Generates an index of all the symbols"
11
12
13def add_args(parser):
14    parser.add_argument("--add-include-path", action="append", dest="include_paths", default=[],
15                        help="include paths for other GIR files")
16    parser.add_argument("infile", metavar="GIRFILE", type=argparse.FileType('r', encoding='UTF-8'),
17                        default=sys.stdin, help="the GIR file to parse")
18
19
20def _print_function(function):
21    return_val = f" -> {log.color(function.return_value.target.ctype, 40)}"
22    func_name = f"{log.color(function.name, 220)}"
23
24    params = []
25    for param in function.parameters:
26        params += [f"{param.name}: {log.color(param.target.ctype, 40)}"]
27
28    params = ', '.join(params)
29
30    return f"{func_name}({params}){return_val}"
31
32
33def _print_method(method):
34    return_val = f" -> {log.color(method.return_value.target.ctype, 40)}"
35    method_name = f"{log.color(method.name, 220)}"
36
37    params = ['self']
38    for param in method.parameters:
39        params += [f"{param.name}: {log.color(param.target.ctype, 40)}"]
40
41    params = ', '.join(params)
42
43    return f"{method_name}({params}){return_val}"
44
45
46def _print_property(prop):
47    flags = []
48    if prop.readable:
49        flags += [log.color('readable', 196)]
50    if prop.writable:
51        flags += [log.color('writable', 196)]
52    if prop.construct:
53        flags += [log.color('construct', 196)]
54    if prop.construct_only:
55        flags += [log.color('construct-only', 196)]
56
57    flags = ', '.join(flags)
58
59    return f"{log.color(prop.name, 220)}: {flags} -> {log.color(prop.target.name, 40)}"
60
61
62def _print_signal(signal):
63    return_val = f" -> {log.color(signal.return_value.target.ctype, 40)}"
64    signal_name = f"{log.color(signal.name, 220)}"
65
66    params = ['self']
67    for param in signal.parameters:
68        params += [f"{param.name}: {log.color(param.target.ctype, 40)}"]
69
70    params = ', '.join(params)
71
72    return f"{signal_name}({params}){return_val}"
73
74
75def _print_enum_member(member):
76    return f"{log.color(member.name.upper(), 220)} = {member.value} ({member.nick})"
77
78
79def _print_enum_members(enum, sections=[], is_last_enum=False, is_last_branch=False):
80    if is_last_branch:
81        root_branch = ' '
82    else:
83        root_branch = '│'
84
85    if is_last_enum:
86        enum_branch = ' '
87    else:
88        enum_branch = '│'
89
90    if not sections:
91        sect_branch = '└──'
92        member_branch = ' '
93    else:
94        sect_branch = '├──'
95        member_branch = '│'
96
97    title = str(log.color('Members', 36))
98    log.log(f'    {root_branch}   {enum_branch}   {sect_branch} {title}')
99
100    for i, member in enumerate(enum.members):
101        is_last_member = i == len(enum.members) - 1
102        enum_str = _print_enum_member(member)
103
104        if is_last_member:
105            leaf = '└──'
106        else:
107            leaf = '├──'
108
109        log.log(f'    {root_branch}   {enum_branch}   {member_branch}   {leaf} {enum_str}')
110
111
112def _print_enum_functions(enum, sections=[], is_last_enum=False, is_last_branch=False):
113    if not enum.functions:
114        return
115
116    if is_last_branch:
117        root_branch = ' '
118    else:
119        root_branch = '│'
120
121    if is_last_enum:
122        enum_branch = ' '
123    else:
124        enum_branch = '│'
125
126    if not sections:
127        sect_branch = '└──'
128        member_branch = ' '
129    else:
130        sect_branch = '├──'
131        member_branch = '│'
132
133    title = str(log.color('Functions', 36))
134    log.log(f'    {root_branch}   {enum_branch}   {sect_branch} {title}')
135
136    for i, function in enumerate(enum.functions):
137        is_last_function = i == len(enum.functions) - 1
138        func_str = _print_function(function)
139
140        if is_last_function:
141            leaf = '└──'
142        else:
143            leaf = '├──'
144
145        log.log(f'    {root_branch}   {enum_branch}   {member_branch}   {leaf} {func_str}')
146
147
148def _print_class_implements(cls, sections=[], is_last_class=False):
149    if not cls.implements:
150        return
151
152    root_branch = '│'
153
154    if is_last_class:
155        ifaces_branch = ' '
156    else:
157        ifaces_branch = '│'
158
159    if not sections:
160        sect_branch = '└──'
161        iface_branch = ' '
162    else:
163        sect_branch = '├──'
164        iface_branch = '│'
165
166    title = str(log.color('Implements', 36))
167    log.log(f'    {root_branch}   {ifaces_branch}   {sect_branch} {title}')
168
169    for i, iface in enumerate(cls.implements):
170        is_last_iface = i == len(cls.implements) - 1
171        if is_last_iface:
172            leaf = '└──'
173        else:
174            leaf = '├──'
175
176        log.log(f'    {root_branch}   {ifaces_branch}   {iface_branch}   {leaf} {iface}')
177
178
179def _print_class_properties(cls, sections=[], is_last_class=False):
180    if not cls.properties:
181        return
182
183    root_branch = '│'
184
185    if is_last_class:
186        props_branch = ' '
187    else:
188        props_branch = '│'
189
190    if not sections:
191        sect_branch = '└──'
192        prop_branch = ' '
193    else:
194        sect_branch = '├──'
195        prop_branch = '│'
196
197    title = str(log.color('Properties', 36))
198    log.log(f'    {root_branch}   {props_branch}   {sect_branch} {title}')
199
200    for i, prop in enumerate(cls.properties.values()):
201        is_last_prop = i == len(cls.properties) - 1
202        prop_str = _print_property(prop)
203        if is_last_prop:
204            leaf = '└──'
205        else:
206            leaf = '├──'
207
208        log.log(f'    {root_branch}   {props_branch}   {prop_branch}   {leaf} {prop_str}')
209
210
211def _print_class_signals(cls, sections=[], is_last_class=False):
212    if not cls.signals:
213        return
214
215    root_branch = '│'
216
217    if is_last_class:
218        signals_branch = ' '
219    else:
220        signals_branch = '│'
221
222    if not sections:
223        sect_branch = '└──'
224        signal_branch = ' '
225    else:
226        sect_branch = '├──'
227        signal_branch = '│'
228
229    title = str(log.color('Signals', 36))
230    log.log(f'    {root_branch}   {signals_branch}   {sect_branch} {title}')
231
232    for i, signal in enumerate(cls.signals.values()):
233        is_last_signal = i == len(cls.signals) - 1
234        signal_str = _print_signal(signal)
235        if is_last_signal:
236            leaf = '└──'
237        else:
238            leaf = '├──'
239
240        log.log(f'    {root_branch}   {signals_branch}   {signal_branch}   {leaf} {signal_str}')
241
242
243def _print_class_constructors(cls, sections=[], is_last_class=False):
244    if not cls.constructors:
245        return
246
247    root_branch = '│'
248
249    if is_last_class:
250        ctors_branch = ' '
251    else:
252        ctors_branch = '│'
253
254    if not sections:
255        sect_branch = '└──'
256        ctor_branch = ' '
257    else:
258        sect_branch = '├──'
259        ctor_branch = '│'
260
261    title = str(log.color('Constructors', 36))
262    log.log(f'    {root_branch}   {ctors_branch}   {sect_branch} {title}')
263
264    for i, ctor in enumerate(cls.constructors):
265        is_last_ctor = i == len(cls.constructors) - 1
266        ctor_str = _print_function(ctor)
267        if is_last_ctor:
268            leaf = '└──'
269        else:
270            leaf = '├──'
271
272        log.log(f'    {root_branch}   {ctors_branch}   {ctor_branch}   {leaf} {ctor_str}')
273
274
275def _print_class_methods(cls, sections=[], is_last_class=False):
276    if not cls.methods:
277        return
278
279    root_branch = '│'
280
281    if is_last_class:
282        methods_branch = ' '
283    else:
284        methods_branch = '│'
285
286    if not sections:
287        sect_branch = '└──'
288        method_branch = ' '
289    else:
290        sect_branch = '├──'
291        method_branch = '│'
292
293    title = str(log.color('Methods', 36))
294    log.log(f'    {root_branch}   {methods_branch}   {sect_branch} {title}')
295
296    for i, method in enumerate(cls.methods):
297        is_last_method = i == len(cls.methods) - 1
298        method_str = _print_method(method)
299        if is_last_method:
300            leaf = '└──'
301        else:
302            leaf = '├──'
303
304        log.log(f'    {root_branch}   {methods_branch}   {method_branch}   {leaf} {method_str}')
305
306
307def _print_class_functions(cls, sections=[], is_last_class=False):
308    if not cls.functions:
309        return
310
311    root_branch = '│'
312
313    if is_last_class:
314        functions_branch = ' '
315    else:
316        functions_branch = '│'
317
318    if not sections:
319        sect_branch = '└──'
320        function_branch = ' '
321    else:
322        sect_branch = '├──'
323        function_branch = '│'
324
325    title = str(log.color('Functions', 36))
326    log.log(f'    {root_branch}   {functions_branch}   {sect_branch} {title}')
327
328    for i, function in enumerate(cls.functions):
329        is_last_func = i == len(cls.functions) - 1
330        func_str = _print_function(function)
331        if is_last_func:
332            leaf = '└──'
333        else:
334            leaf = '├──'
335
336        log.log(f'    {root_branch}   {functions_branch}   {function_branch}   {leaf} {func_str}')
337
338
339def gen_tree(repository):
340    includes = ', '.join([str(repository.includes[r]) for r in repository.includes])
341    c_includes = ', '.join(repository.c_includes)
342    packages = ', '.join(repository.packages)
343
344    title = str(log.color('Repository', 12))
345    log.log(f'{title}')
346    log.log(f'├── Includes:  {includes}')
347    log.log(f'├── C headers: {c_includes}')
348    log.log(f'├── Packages:  {packages}')
349
350    namespace = repository.namespace
351    shlibs = ', '.join(namespace.get_shared_libraries())
352
353    aliases = sorted(namespace.get_aliases(), key=lambda alias: alias.name.lower())
354    classes = sorted(namespace.get_classes(), key=lambda cls: cls.name.lower())
355    constants = sorted(namespace.get_constants(), key=lambda const: const.name.lower())
356    domains = sorted(namespace.get_error_domains(), key=lambda domain: domain.name.lower())
357    enums = sorted(namespace.get_enumerations(), key=lambda enum: enum.name.lower())
358    functions = sorted(namespace.get_functions(), key=lambda func: func.name.lower())
359    interfaces = sorted(namespace.get_interfaces(), key=lambda interface: interface.name.lower())
360    records = sorted(namespace.get_records(), key=lambda record: record.name.lower())
361    unions = sorted(namespace.get_unions(), key=lambda union: union.name.lower())
362
363    title = str(log.color('Namespace', 36))
364    log.log(f'└── {title}: {namespace.name}, version: {namespace.version}')
365    log.log(f'    ├── Shared libraries: {shlibs}')
366
367    title = str(log.color('Classes', 36))
368    log.log(f'    ├── {title}')
369
370    if len(classes) == 0:
371        log.log('    │   └── None')
372    else:
373        for i, cls in enumerate(classes):
374            is_last_class = i == len(classes) - 1
375            if is_last_class:
376                log.log(f'    │   └── {cls.name} - parent: {cls.parent}, abstract: {log.color(cls.abstract, 196)}')
377            else:
378                log.log(f'    │   ├── {cls.name} - parent: {cls.parent}, abstract: {log.color(cls.abstract, 196)}')
379
380            sections = []
381            if cls.implements:
382                sections += ['implements']
383            if cls.properties:
384                sections += ['properties']
385            if cls.signals:
386                sections += ['signals']
387            if cls.constructors:
388                sections += ['constructors']
389            if cls.methods:
390                sections += ['methods']
391            if cls.functions:
392                sections += ['functions']
393
394            if 'implements' in sections:
395                sections.remove('implements')
396                _print_class_implements(cls, sections, is_last_class)
397            if 'properties' in sections:
398                sections.remove('properties')
399                _print_class_properties(cls, sections, is_last_class)
400            if 'signals' in sections:
401                sections.remove('signals')
402                _print_class_signals(cls, sections, is_last_class)
403            if 'constructors' in sections:
404                sections.remove('constructors')
405                _print_class_constructors(cls, sections, is_last_class)
406            if 'methods' in sections:
407                sections.remove('methods')
408                _print_class_methods(cls, sections, is_last_class)
409            if 'functions' in sections:
410                sections.remove('functions')
411                _print_class_functions(cls, sections, is_last_class)
412
413    title = str(log.color('Interfaces', 36))
414    log.log(f'    ├── {title}')
415
416    if len(interfaces) == 0:
417        log.log('    │   └── None')
418    else:
419        for i, iface in enumerate(interfaces):
420            is_last_iface = i == len(interfaces) - 1
421            if is_last_iface:
422                log.log(f'    │   └── {iface.name}, prerequisite: {iface.prerequisite}')
423            else:
424                log.log(f'    │   ├── {iface.name}, prerequisite: {iface.prerequisite}')
425
426            sections = []
427            if iface.properties:
428                sections += ['properties']
429            if iface.signals:
430                sections += ['signals']
431            if iface.methods:
432                sections += ['methods']
433            if iface.functions:
434                sections += ['functions']
435
436            if 'properties' in sections:
437                sections.remove('properties')
438                _print_class_properties(iface, sections, is_last_iface)
439            if 'signals' in sections:
440                sections.remove('signals')
441                _print_class_signals(iface, sections, is_last_iface)
442            if 'methods' in sections:
443                sections.remove('methods')
444                _print_class_methods(iface, sections, is_last_iface)
445            if 'functions' in sections:
446                sections.remove('functions')
447                _print_class_functions(iface, sections, is_last_iface)
448
449    title = str(log.color('Records', 36))
450    log.log(f'    ├── {title}')
451
452    if len(records) == 0:
453        log.log('    │   └── None')
454    else:
455        for i, record in enumerate(records):
456            is_last_record = i == len(records) - 1
457            if is_last_record:
458                log.log(f'    │   └── {record.name}')
459            else:
460                log.log(f'    │   ├── {record.name}')
461
462            sections = []
463            if record.constructors:
464                sections += ['constructors']
465            if record.methods:
466                sections += ['methods']
467            if record.functions:
468                sections += ['functions']
469
470            if 'constructors' in sections:
471                sections.remove('constructors')
472                _print_class_constructors(record, sections, is_last_record)
473            if 'methods' in sections:
474                sections.remove('methods')
475                _print_class_methods(record, sections, is_last_record)
476            if 'functions' in sections:
477                sections.remove('functions')
478                _print_class_functions(record, sections, is_last_record)
479
480    title = str(log.color('Unions', 36))
481    log.log(f'    ├── {title}')
482
483    if len(records) == 0:
484        log.log('    │   └── None')
485    else:
486        for i, union in enumerate(unions):
487            is_last_union = i == len(unions) - 1
488            if is_last_union:
489                log.log(f'    │   └── {union.name}')
490            else:
491                log.log(f'    │   ├── {union.name}')
492
493            sections = []
494            if union.constructors:
495                sections += ['constructors']
496            if union.methods:
497                sections += ['methods']
498            if union.functions:
499                sections += ['functions']
500
501            if 'constructors' in sections:
502                sections.remove('constructors')
503                _print_class_constructors(union, sections, is_last_union)
504            if 'methods' in sections:
505                sections.remove('methods')
506                _print_class_methods(union, sections, is_last_union)
507            if 'functions' in sections:
508                sections.remove('functions')
509                _print_class_functions(union, sections, is_last_union)
510
511    title = str(log.color('Functions', 36))
512    log.log(f'    ├── {title}')
513
514    if len(aliases) == 0:
515        log.log('    │   └── None')
516    else:
517        for i, func in enumerate(functions):
518            is_last_func = i == len(functions) - 1
519            func_str = _print_function(func)
520            if is_last_func:
521                log.log(f'    │   ├── {func_str}')
522            else:
523                log.log(f'    │   └── {func_str}')
524
525    title = str(log.color('Aliases', 36))
526    log.log(f'    ├── {title}')
527
528    if len(aliases) == 0:
529        log.log('    │   └── None')
530    else:
531        for i in range(len(aliases)):
532            alias = aliases[i]
533            if i < len(aliases) - 1:
534                log.log(f'    │   ├── {log.color(alias.name, 220)} → {alias.target.name} ({log.color(alias.target.ctype, 40)})')
535            else:
536                log.log(f'    │   └── {log.color(alias.name, 220)} → {alias.target.name} ({log.color(alias.target.ctype, 40)})')
537
538    title = str(log.color('Constants', 36))
539    log.log(f'    ├── {title}')
540
541    if len(constants) == 0:
542        log.log('    │   └── None')
543    else:
544        for i in range(len(constants)):
545            const = constants[i]
546            if i < len(constants) - 1:
547                log.log(f'    │   ├── {log.color(const.name, 220)} = {const.value} ({log.color(const.target.ctype, 40)})')
548            else:
549                log.log(f'    │   └── {log.color(const.name, 220)} = {const.value} ({log.color(const.target.ctype, 40)})')
550
551    title = str(log.color('Enumerations', 36))
552    log.log(f'    ├── {title}')
553
554    if len(enums) == 0:
555        log.log('    │   └── None')
556    else:
557        for i, enum in enumerate(enums):
558            is_last_enum = i == len(enums) - 1
559            if is_last_enum:
560                log.log(f'    │   └── {enum.name}')
561            else:
562                log.log(f'    │   ├── {enum.name}')
563
564            sections = []
565            if enum.members:
566                sections += ['members']
567            if enum.functions:
568                sections += ['functions']
569
570            if 'members' in sections:
571                sections.remove('members')
572                _print_enum_members(enum, sections, is_last_enum)
573            if 'functions' in sections:
574                sections.remove('functions')
575                _print_enum_functions(enum, sections, is_last_enum)
576
577    title = str(log.color('Error domains', 36))
578    log.log(f'    └── {title}')
579
580    if len(domains) == 0:
581        log.log('        └── None')
582    else:
583        for i, domain in enumerate(domains):
584            is_last_domain = i == len(domains) - 1
585            if is_last_domain:
586                log.log(f'        └── {domain.name} - domain: {domain.domain}')
587            else:
588                log.log(f'        ├── {domain.name} - domain: {domain.domain}')
589
590            sections = []
591            if domain.members:
592                sections += ['members']
593            if domain.functions:
594                sections += ['functions']
595
596            if 'members' in sections:
597                sections.remove('members')
598                _print_enum_members(domain, sections, is_last_domain, True)
599            if 'functions' in sections:
600                sections.remove('functions')
601                _print_enum_functions(domain, sections, is_last_domain, True)
602
603
604def run(options):
605    paths = []
606    paths.extend(options.include_paths)
607    paths.extend(utils.default_search_paths())
608    log.info(f"Search paths: {paths}")
609
610    parser = gir.GirParser(search_paths=paths)
611    parser.parse(options.infile)
612
613    log.checkpoint()
614    gen_tree(parser.get_repository())
615
616    return 0
617