View source with raw comments or as raw
    1/*  Part of XPCE --- The SWI-Prolog GUI toolkit
    2
    3    Author:        Jan Wielemaker and Anjo Anjewierden
    4    E-mail:        wielemak@science.uva.nl
    5    WWW:           http://www.swi-prolog.org/packages/xpce/
    6    Copyright (c)  2006-2020, University of Amsterdam
    7                              SWI-Prolog Solutions b.v.
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module(pce_xref_gui,
   37          [ gxref/0,
   38            xref_file_imports/2,        % +File, -Imports
   39            xref_file_exports/2         % +File, -Exports
   40          ]).   41:- use_module(pce).   42:- use_module(persistent_frame).   43:- use_module(tabbed_window).   44:- use_module(toolbar).   45:- use_module(pce_report).   46:- use_module(pce_util).   47:- use_module(pce_toc).   48:- use_module(pce_arm).   49:- use_module(pce_tagged_connection).   50:- use_module(dragdrop).   51:- use_module(pce_prolog_xref).   52:- use_module(print_graphics).   53:- use_module(tabular).   54:- use_module(library(lists)).   55:- use_module(library(autowin)).   56:- use_module(library(broadcast)).   57:- use_module(library(prolog_source)).   58:- require([ auto_call/1,
   59	     edit/1,
   60	     exists_file/1,
   61	     (\=)/2,
   62	     call_cleanup/2,
   63	     file_base_name/2,
   64	     file_directory_name/2,
   65	     portray_clause/2,
   66	     term_to_atom/2,
   67	     time_file/2,
   68	     absolute_file_name/3,
   69	     atomic_list_concat/3,
   70	     file_name_extension/3,
   71	     format_time/3,
   72	     maplist/3,
   73	     strip_module/3,
   74	     xref_called/4,
   75             head_name_arity/3
   76	   ]).   77
   78gxref_version('0.1.1').
   79
   80:- dynamic
   81    setting/2.   82
   83setting_menu([ warn_autoload,
   84               warn_not_called
   85             ]).
   86
   87setting(warn_autoload,      false).
   88setting(warn_not_called,    true).
   89setting(hide_system_files,  true).
   90setting(hide_profile_files, true).

Cross-referencer front-end

XPCE based font-end of the Prolog cross-referencer. Tasks:

See also
- library(prolog_xref) holds the actual data-collection. */
bug
- Tool produces an error if a file that has been xref'ed is deleted. Paulo Moura.
 gxref
Start graphical cross-referencer on loaded program. The GUI is started in the XPCE thread.
  124gxref :-
  125    in_pce_thread(xref_gui).
  126
  127xref_gui :-
  128    send(new(XREF, xref_frame), open),
  129    send(XREF, wait),
  130    send(XREF, update).
  131
  132
  133:- pce_begin_class(xref_frame, persistent_frame,
  134                   "GUI for the Prolog cross-referencer").
  135
  136initialise(F) :->
  137    send_super(F, initialise, 'Prolog XREF'),
  138    new(FilterDialog, xref_filter_dialog),
  139    send(new(BrowserTabs, tabbed_window), below, FilterDialog),
  140    send(BrowserTabs, left, new(WSTabs, tabbed_window)),
  141    send(BrowserTabs, name, browsers),
  142    send(BrowserTabs, hor_shrink, 10),
  143    send(BrowserTabs, hor_stretch, 10),
  144    send(WSTabs, name, workspaces),
  145    send_list([BrowserTabs, WSTabs], label_popup, F?tab_popup),
  146    send(new(TD, tool_dialog(F)), above, BrowserTabs),
  147    send(new(report_dialog), below, BrowserTabs),
  148    send(F, append, BrowserTabs),
  149    send_list(BrowserTabs,
  150              [ append(new(xref_file_tree), files),
  151                append(new(xref_predicate_browser), predicates)
  152              ]),
  153    send_list(WSTabs,
  154              [ append(new(xref_depgraph), dependencies)
  155              ]),
  156    send(F, fill_toolbar, TD).
  157
  158tab_popup(_F, P:popup) :<-
  159    "Popup for tab labels"::
  160    new(P, popup),
  161    send_list(P, append,
  162              [ menu_item(close, message(@arg1, destroy)),
  163                menu_item(detach, message(@arg1, untab))
  164              ]).
  165
  166fill_toolbar(F, TD:tool_dialog) :->
  167    send(TD, append, new(File, popup(file))),
  168    send(TD, append,
  169         new(Settings, popup(settings,
  170                             message(F, setting, @arg1, @arg2)))),
  171    send(TD, append, new(View, popup(view))),
  172    send(TD, append, new(Help, popup(help))),
  173    send_list(File, append,
  174              [ menu_item(exit, message(F, destroy))
  175              ]),
  176    send_list(View, append,
  177              [ menu_item(refresh, message(F, update))
  178              ]),
  179    send_list(Help, append,
  180              [ menu_item(about, message(F, about))
  181              ]),
  182    send(Settings, show_current, @on),
  183    send(Settings, multiple_selection, @on),
  184    send(F, update_setting_menu).
  185
  186about(_F) :->
  187    gxref_version(Version),
  188    send(@display, inform,
  189         string('SWI-Prolog cross-referencer version %s\n\c
  190                    By Jan Wielemaker', Version)).
  191
  192:- pce_group(parts).
  193
  194workspace(F, Which:name, Create:[bool], Expose:bool, WS:window) :<-
  195    "Find named workspace"::
  196    get(F, member, workspaces, Tabs),
  197    (   get(Tabs, member, Which, WS)
  198    ->  true
  199    ;   Create == @on
  200    ->  workspace_term(Which, New),
  201        new(WS, New),
  202        send(WS, name, Which),
  203        send(Tabs, append, WS)
  204    ),
  205    (   Expose == @on
  206    ->  send(Tabs, on_top, WS?name)
  207    ;   true
  208    ).
  209
  210workspace_term(file_info, prolog_file_info).
  211workspace_term(header,    xref_view).
  212
  213browser(F, Which:name, Browser:browser) :<-
  214    "Find named browser"::
  215    get(F, member, browsers, Tabs),
  216    get(Tabs, member, Which, Browser).
  217
  218update(F) :->
  219    "Update all windows"::
  220    send(F, xref_all),
  221    get(F, member, browsers, Tabs),
  222    send(Tabs?members, for_some,
  223         message(@arg1, update)),
  224    get(F, member, workspaces, WSs),
  225    send(WSs?members, for_some,
  226         message(@arg1, update)).
  227
  228xref_all(F) :->
  229    "Run X-referencer on all files"::
  230    forall(( source_file(File),
  231             exists_file(File)
  232           ),
  233           send(F, xref_file, File)).
  234
  235xref_file(F, File:name) :->
  236    "XREF a single file if not already done"::
  237    (   xref_done(File, Time),
  238        catch(time_file(File, Modified), _, fail),
  239        Modified == Time
  240    ->  true
  241    ;   send(F, report, progress, 'XREF %s', File),
  242        xref_source(File, [silent(true)]),
  243        send(F, report, done)
  244    ).
  245
  246:- pce_group(actions).
  247
  248
  249file_info(F, File:name) :->
  250    "Show summary info on File"::
  251    get(F, workspace, file_info, @on, @on, Window),
  252    send(Window, file, File),
  253    broadcast(xref_refresh_file(File)).
  254
  255file_header(F, File:name) :->
  256    "Create import/export header"::
  257    get(F, workspace, header, @on, @on, View),
  258    send(View, file_header, File).
  259
  260:- pce_group(settings).
  261
  262update_setting_menu(F) :->
  263    "Update the menu for the settings with the current values"::
  264    get(F, member, tool_dialog, TD),
  265    get(TD, member, menu_bar, MB),
  266    get(MB, member, settings, Popup),
  267    send(Popup, clear),
  268    setting_menu(Entries),
  269    (   member(Name, Entries),
  270        setting(Name, Value),
  271        send(Popup, append, new(MI, menu_item(Name))),
  272        (   Value == true
  273        ->  send(MI, selected, @on)
  274        ;   true
  275        ),
  276        fail ; true
  277    ).
  278
  279setting(F, S:name, PceVal:bool) :->
  280    "Update setting and redo analysis"::
  281    pce_to_prolog_bool(PceVal, Val),
  282    retractall(setting(S, _)),
  283    assert(setting(S, Val)),
  284    send(F, update).
  285
  286pce_to_prolog_bool(@on, true).
  287pce_to_prolog_bool(@off, false).
  288
  289:- pce_end_class(xref_frame).
  290
  291
  292                 /*******************************
  293                 *            WORKSPACE         *
  294                 *******************************/
  295
  296:- pce_begin_class(xref_depgraph, picture,
  297                   "Workspace showing dependecies").
  298:- use_class_template(arm).
  299:- use_class_template(print_graphics).
  300
  301initialise(W) :->
  302    send_super(W, initialise),
  303    send(W, popup, new(P, popup)),
  304    send_list(P, append,
  305              [ menu_item(layout, message(W, layout)),
  306                gap,
  307                menu_item(view_whole_project, message(W, show_project)),
  308                gap,
  309                menu_item(clear, message(W, clear, destroy)),
  310                gap,
  311                menu_item(print, message(W, print))
  312              ]).
  313
  314update(P) :->
  315    "Initial screen"::
  316    send(P, display,
  317         new(T, text('Drag files or directories to dependency view\n\c
  318                          or use background menu to show the whole project')),
  319         point(10,10)),
  320    send(T, name, intro_text),
  321    send(T, colour, grey50).
  322
  323remove_intro_text(P) :->
  324    "Remove the introductionary text"::
  325    (   get(P, member, intro_text, Text)
  326    ->  send(Text, destroy)
  327    ;   true
  328    ).
  329
  330show_project(P) :->
  331    get(P, sources, Sources),
  332    send(P, clear, destroy),
  333    forall(member(Src, Sources),
  334           send(P, append, Src)),
  335    send(P, update_links),
  336    send(P, layout).
  337
  338sources(_, Sources:prolog) :<-
  339    findall(S, dep_source(S), Sources).
 dep_source(?Src)
Generate all sources for the dependecy graph one-by-one.
  345dep_source(Src) :-
  346    source_file(Src),
  347    (   setting(hide_system_files, true)
  348    ->  \+ library_file(Src)
  349    ;   true
  350    ),
  351    (   setting(hide_profile_files, true)
  352    ->  \+ profile_file(Src)
  353    ;   true
  354    ).
  355
  356append(P, File:name, Create:[bool|{always}]) :->
  357    "Append File.  If Create == always also if a system file"::
  358    default(Create, @on, C),
  359    get(P, node, File, C, _).
  360
  361node(G, File:name, Create:[bool|{always}], Pos:[point],
  362     Gr:xref_file_graph_node) :<-
  363    "Get the node representing File"::
  364    (   get(G, member, File, Gr)
  365    ->  true
  366    ;   (   Create == @on
  367        ->  dep_source(File)
  368        ;   Create == always
  369        ),
  370        (   Pos == @default
  371        ->  get(G?visible, center, At)
  372        ;   At = Pos
  373        ),
  374        send(G, display, new(Gr, xref_file_graph_node(File)), At),
  375        send(G, remove_intro_text)
  376    ).
  377
  378update_links(G) :->
  379    "Add all export links"::
  380    send(G?graphicals, for_all,
  381         if(message(@arg1, instance_of, xref_file_graph_node),
  382            message(@arg1, create_export_links))).
  383
  384layout(G, MoveOnly:[chain]) :->
  385    "Do graph layout"::
  386    get(G?graphicals, find_all,
  387        message(@arg1, instance_of, xref_file_graph_node), Nodes),
  388    get(Nodes, find_all, not(@arg1?connections), UnConnected),
  389    send(Nodes, subtract, UnConnected),
  390    new(Pos, point(10,10)),
  391    send(UnConnected, for_all,
  392         and(message(@arg1, position, Pos),
  393             message(Pos, offset, 0, 25))),
  394    get(Nodes, head, First),
  395    send(First, layout,
  396         nominal := 100,
  397         iterations := 1000,
  398         network := Nodes,
  399         move_only := MoveOnly).
  400
  401
  402:- pce_group(dragdrop).
  403
  404drop(G, Obj:object, Pos:point) :->
  405    "Drop a file on the graph"::
  406    (   send(Obj, instance_of, xref_file_text)
  407    ->  get(Obj, path, File),
  408        (   get(G, node, File, Node)
  409        ->  send(Node, flash)
  410        ;   get(G, node, File, always, Pos, _Node),
  411            send(G, update_links)
  412        )
  413    ;   send(Obj, instance_of, xref_directory_text)
  414    ->  get(Obj, files, Files),
  415        layout_new(G,
  416                   (   send(Files, for_all,
  417                            message(G, append, @arg1, always)),
  418                       send(G, update_links)
  419                   ))
  420    ).
  421
  422preview_drop(G, Obj:object*, Pos:point) :->
  423    "Show preview of drop"::
  424    (   Obj == @nil
  425    ->  send(G, report, status, '')
  426    ;   send(Obj, instance_of, xref_file_text)
  427    ->  (   get(Obj, device, G)
  428        ->  send(Obj, move, Pos)
  429        ;   get(Obj, path, File),
  430            get(Obj, string, Label),
  431            (   get(G, node, File, _Node)
  432            ->  send(G, report, status, '%s: already in graph', Label)
  433            ;   send(G, report, status, 'Add %s to graph', Label)
  434            )
  435        )
  436    ;   send(Obj, instance_of, xref_directory_text)
  437    ->  get(Obj, path, Path),
  438        send(G, report, status, 'Add files from directory %s', Path)
  439    ).
  440
  441:- pce_end_class(xref_depgraph).
  442
  443:- pce_begin_class(xref_file_graph_node, xref_file_text).
  444
  445:- send(@class, handle, handle(w/2, 0, link, north)).  446:- send(@class, handle, handle(w, h/2, link, west)).  447:- send(@class, handle, handle(w/2, h, link, south)).  448:- send(@class, handle, handle(0, h/2, link, east)).  449
  450initialise(N, File:name) :->
  451    send_super(N, initialise, File),
  452    send(N, font, bold),
  453    send(N, background, grey80).
  454
  455create_export_links(N, Add:[bool]) :->
  456    "Create the export links to other files"::
  457    get(N, path, Exporter),
  458    forall(export_link(Exporter, Importer, Callables),
  459           create_export_link(N, Add, Importer, Callables)).
  460
  461create_export_link(From, Add, Importer, Callables) :-
  462    (   get(From?device, node, Importer, Add, INode)
  463    ->  send(From, link, INode, Callables)
  464    ;   true
  465    ).
  466
  467create_import_links(N, Add:[bool]) :->
  468    "Create the import links from other files"::
  469    get(N, path, Importer),
  470    forall(export_link(Exporter, Importer, Callables),
  471           create_import_link(N, Add, Exporter, Callables)).
  472
  473create_import_link(From, Add, Importer, Callables) :-
  474    (   get(From?device, node, Importer, Add, INode)
  475    ->  send(INode, link, From, Callables)
  476    ;   true
  477    ).
  478
  479link(N, INode:xref_file_graph_node, Callables:prolog) :->
  480    "Create export link to INode"::
  481    (   get(N, connections, INode, CList),
  482        get(CList, find, @arg1?from == N, C)
  483    ->  send(C, callables, Callables)
  484    ;   new(L, xref_export_connection(N, INode, Callables)),
  485        send(L, hide)
  486    ).
  487
  488:- pce_global(@xref_file_graph_node_recogniser,
  489              make_xref_file_graph_node_recogniser).  490
  491make_xref_file_graph_node_recogniser(G) :-
  492    new(G, move_gesture(left, '')).
  493
  494event(N, Ev:event) :->
  495    "Add moving (overrule supreclass"::
  496    (   send(@xref_file_graph_node_recogniser, event, Ev)
  497    ->  true
  498    ;   send_super(N, event, Ev)
  499    ).
  500
  501popup(N, Popup:popup) :<-
  502    get_super(N, popup, Popup),
  503    send_list(Popup, append,
  504              [ gap,
  505                menu_item(show_exports,
  506                          message(@arg1, show_import_exports, export)),
  507                menu_item(show_imports,
  508                          message(@arg1, show_import_exports, import)),
  509                gap,
  510                menu_item(hide,
  511                          message(@arg1, destroy))
  512              ]).
  513
  514show_import_exports(N, Which:{import,export}) :->
  515    "Show who I'm exporting to"::
  516    get(N, device, G),
  517    layout_new(G,
  518               (   (   Which == export
  519                   ->  send(N, create_export_links, @on)
  520                   ;   send(N, create_import_links, @on)
  521                   ),
  522                   send(G, update_links)
  523               )).
  524
  525layout_new(G, Goal) :-
  526    get(G?graphicals, find_all,
  527        message(@arg1, instance_of, xref_file_graph_node), Nodes0),
  528    Goal,
  529    get(G?graphicals, find_all,
  530        message(@arg1, instance_of, xref_file_graph_node), Nodes),
  531    send(Nodes, subtract, Nodes0),
  532    (   send(Nodes, empty)
  533    ->  send(G, report, status, 'No nodes added')
  534    ;   send(G, layout, Nodes),
  535        get(Nodes, size, Size),
  536        send(G, report, status, '%d nodes added', Size)
  537    ).
  538
  539:- pce_end_class(xref_file_graph_node).
  540
  541:- pce_begin_class(xref_export_connection, tagged_connection).
  542
  543variable(callables, prolog, get, "Callables in Import/export link").
  544
  545initialise(C, From:xref_file_graph_node, To:xref_file_graph_node,
  546           Callables:prolog) :->
  547    send_super(C, initialise, From, To),
  548    send(C, arrows, second),
  549    send(C, slot, callables, Callables),
  550    length(Callables, N),
  551    send(C, tag, xref_export_connection_tag(C, N)).
  552
  553callables(C, Callables:prolog) :->
  554    send(C, slot, callables, Callables). % TBD: update tag?
  555
  556called_by_popup(Conn, P:popup) :<-
  557    "Create popup to show relating predicates"::
  558    new(P, popup(called_by, message(Conn, edit_callable, @arg1))),
  559    get(Conn, callables, Callables),
  560    get(Conn?from, path, ExportFile),
  561    get(Conn?to, path, ImportFile),
  562    sort_callables(Callables, Sorted),
  563    forall(member(C, Sorted),
  564           append_io_callable(P, ImportFile, ExportFile, C)).
 append_io_callable(+Popup, -ImportFile, +Callable)
  568append_io_callable(P, ImportFile, ExportFile, Callable) :-
  569    callable_to_label(Callable, Label),
  570    send(P, append, new(MI, menu_item(@nil, @default, Label))),
  571    send(MI, popup, new(P2, popup)),
  572    send(P2, append,
  573         menu_item(prolog('<definition>'(Callable)),
  574                   @default, definition?label_name)),
  575    send(P2, append, gap),
  576    qualify_from_file(Callable, ExportFile, QCall),
  577    findall(By, used_in(ImportFile, QCall, By), ByList0),
  578    sort_callables(ByList0, ByList),
  579    forall(member(C, ByList),
  580           ( callable_to_label(C, CLabel),
  581             send(P2, append, menu_item(prolog(C), @default, CLabel)))).
  582
  583edit_callable(C, Callable:prolog) :->
  584    "Edit definition or callers"::
  585    (   Callable = '<definition>'(Def)
  586    ->  get(C?from, path, ExportFile),
  587        edit_callable(Def, ExportFile)
  588    ;   get(C?to, path, ImportFile),
  589        edit_callable(Callable, ImportFile)
  590    ).
  591
  592:- pce_end_class(xref_export_connection).
  593
  594
  595:- pce_begin_class(xref_export_connection_tag, text,
  596                   "Text showing import/export count").
  597
  598variable(connection, xref_export_connection, get, "Related connection").
  599
  600initialise(Tag, C:xref_export_connection, N:int) :->
  601    send(Tag, slot, connection, C),
  602    send_super(Tag, initialise, string('(%d)', N)),
  603    send(Tag, colour, blue),
  604    send(Tag, underline, @on).
  605
  606:- pce_global(@xref_export_connection_tag_recogniser,
  607              new(popup_gesture(@receiver?connection?called_by_popup, left))).
  608
  609event(Tag, Ev:event) :->
  610    (   send_super(Tag, event, Ev)
  611    ->  true
  612    ;   send(@xref_export_connection_tag_recogniser, event, Ev)
  613    ).
  614
  615:- pce_end_class(xref_export_connection_tag).
 export_link(+ExportingFile, -ImportingFile, -Callables) is det
export_link(-ExportingFile, +ImportingFile, -Callables) is det
Callables are exported from ExportingFile to ImportingFile.
  624export_link(ExportFile, ImportingFile, Callables) :-
  625    setof(Callable,
  626          export_link_1(ExportFile, ImportingFile, Callable),
  627          Callables0),
  628    sort_callables(Callables0, Callables).
  629
  630
  631export_link_1(ExportFile, ImportFile, Callable) :-       % module export
  632    nonvar(ExportFile),
  633    xref_module(ExportFile, Module),
  634    !,
  635    (   xref_exported(ExportFile, Callable),
  636        xref_defined(ImportFile, Callable, imported(ExportFile)),
  637        xref_called(ImportFile, Callable)
  638    ;   defined(ExportFile, Callable),
  639        single_qualify(Module:Callable, QCall),
  640        xref_called(ImportFile, QCall)
  641    ),
  642    ImportFile \== ExportFile,
  643    atom(ImportFile).
  644export_link_1(ExportFile, ImportFile, Callable) :-      % Non-module export
  645    nonvar(ExportFile),
  646    !,
  647    defined(ExportFile, Callable),
  648    xref_called(ImportFile, Callable),
  649    atom(ImportFile),
  650    ExportFile \== ImportFile.
  651export_link_1(ExportFile, ImportFile, Callable) :-      % module import
  652    nonvar(ImportFile),
  653    xref_module(ImportFile, Module),
  654    !,
  655    xref_called(ImportFile, Callable),
  656    (   xref_defined(ImportFile, Callable, imported(ExportFile))
  657    ;   single_qualify(Module:Callable, QCall),
  658        QCall = M:G,
  659        (   defined(ExportFile, G),
  660            xref_module(ExportFile, M)
  661        ;   defined(ExportFile, QCall)
  662        )
  663    ),
  664    ImportFile \== ExportFile,
  665    atom(ExportFile).
  666export_link_1(ExportFile, ImportFile, Callable) :-      % Non-module import
  667    xref_called(ImportFile, Callable),
  668    \+ (  xref_defined(ImportFile, Callable, How),
  669          How \= imported(_)
  670       ),
  671                                    % see also undefined/2
  672    (   xref_defined(ImportFile, Callable, imported(ExportFile))
  673    ;   defined(ExportFile, Callable),
  674        \+ xref_module(ExportFile, _)
  675    ;   Callable = _:_,
  676        defined(ExportFile, Callable)
  677    ;   Callable = M:G,
  678        defined(ExportFile, G),
  679        xref_module(ExportFile, M)
  680    ).
  681
  682
  683                 /*******************************
  684                 *             FILTER           *
  685                 *******************************/
  686
  687:- pce_begin_class(xref_filter_dialog, dialog,
  688                   "Show filter options").
  689
  690class_variable(border, size, size(0,0)).
  691
  692initialise(D) :->
  693    send_super(D, initialise),
  694    send(D, hor_stretch, 100),
  695    send(D, hor_shrink, 100),
  696    send(D, name, filter_dialog),
  697    send(D, append, xref_file_filter_item(filter_on_filename)).
  698
  699resize(D) :->
  700    send(D, layout, D?visible?size).
  701
  702:- pce_end_class(xref_filter_dialog).
  703
  704
  705:- pce_begin_class(xref_file_filter_item, text_item,
  706                   "Filter files as you type").
  707
  708typed(FFI, Id) :->
  709    "Activate filter"::
  710    send_super(FFI, typed, Id),
  711    get(FFI, displayed_value, Current),
  712    get(FFI?frame, browser, files, Tree),
  713    (   send(Current, equal, '')
  714    ->  send(Tree, filter_file_name, @nil)
  715    ;   (   text_to_regex(Current, Filter)
  716        ->  send(Tree, filter_file_name, Filter)
  717        ;   send(FFI, report, status, 'Incomplete expression')
  718        )
  719    ).
 text_to_regex(+Pattern, -Regex) is semidet
Convert text to a regular expression. Fail if the text does not represent a valid regular expression.
  726text_to_regex(Pattern, Regex) :-
  727    send(@pce, last_error, @nil),
  728    new(Regex, regex(Pattern)),
  729    ignore(pce_catch_error(_, send(Regex, search, ''))),
  730    get(@pce, last_error, @nil).
  731
  732:- pce_end_class(xref_file_filter_item).
  733
  734
  735
  736                 /*******************************
  737                 *           FILE TREE          *
  738                 *******************************/
  739
  740:- pce_begin_class(xref_file_tree, toc_window,
  741                   "Show loaded files as a tree").
  742:- use_class_template(arm).
  743
  744initialise(Tree) :->
  745    send_super(Tree, initialise),
  746    send(Tree, clear),
  747    listen(Tree, xref_refresh_file(File),
  748           send(Tree, refresh_file, File)).
  749
  750unlink(Tree) :->
  751    unlisten(Tree),
  752    send_super(Tree, unlink).
  753
  754refresh_file(Tree, File:name) :->
  755    "Update given file"::
  756    (   get(Tree, node, File, Node)
  757    ->  send(Node, set_flags)
  758    ;   true
  759    ).
  760
  761collapse_node(_, _:any) :->
  762    true.
  763
  764expand_node(_, _:any) :->
  765    true.
  766
  767update(FL) :->
  768    get(FL, expanded_ids, Chain),
  769    send(FL, clear),
  770    send(FL, report, progress, 'Building source tree ...'),
  771    send(FL, append_all_sourcefiles),
  772    send(FL, expand_ids, Chain),
  773    send(@display, synchronise),
  774    send(FL, report, progress, 'Flagging files ...'),
  775    send(FL, set_flags),
  776    send(FL, report, done).
  777
  778append_all_sourcefiles(FL) :->
  779    "Append all files loaded into Prolog"::
  780    forall(source_file(File),
  781           send(FL, append, File)),
  782    send(FL, sort).
  783
  784clear(Tree) :->
  785    "Remove all nodes, recreate the toplevel"::
  786    send_super(Tree, clear),
  787    send(Tree, root, new(Root, toc_folder(project, project))),
  788    forall(top_node(Name, Class),
  789           (   New =.. [Class, Name, Name],
  790               send(Tree, son, project, New))),
  791    send(Root, for_all, message(@arg1, collapsed, @off)).
  792
  793append(Tree, File:name) :->
  794    "Add Prolog source file"::
  795    send(Tree, append_node, new(prolog_file_node(File))).
  796
  797append_node(Tree, Node:toc_node) :->
  798    "Append a given node to the tree"::
  799    get(Node, parent_id, ParentId),
  800    (   get(Tree, node, ParentId, Parent)
  801    ->  true
  802    ;   send(Tree, append_node,
  803             new(Parent, prolog_directory_node(ParentId)))
  804    ),
  805    send(Parent, son, Node).
  806
  807sort(Tree) :->
  808    forall(top_node(Name, _),
  809           (   get(Tree, node, Name, Node),
  810               send(Node, sort_sons, ?(@arg1, compare, @arg2)),
  811               send(Node?sons, for_all, message(@arg1, sort))
  812           )).
  813
  814select_node(Tree, File:name) :->
  815    "User selected a node"::
  816    (   exists_file(File)
  817    ->  send(Tree?frame, file_info, File)
  818    ;   true
  819    ).
  820
  821set_flags(Tree) :->
  822    "Set alert-flags on all nodes"::
  823    forall(top_node(Name, _),
  824           (   get(Tree, node, Name, Node),
  825               (   send(Node, instance_of, prolog_directory_node)
  826               ->  send(Node, set_flags)
  827               ;   send(Node?sons, for_all, message(@arg1, set_flags))
  828               )
  829           )).
  830
  831top_node('.',           prolog_directory_node).
  832top_node('alias',       toc_folder).
  833top_node('/',           prolog_directory_node).
  834
  835
  836:- pce_group(filter).
  837
  838filter_file_name(Tree, Regex:regex*) :->
  839    "Only show files that match Regex"::
  840    (   Regex == @nil
  841    ->  send(Tree, filter_files, @nil)
  842    ;   send(Tree, filter_files,
  843             message(Regex, search, @arg1?base_name))
  844    ).
  845
  846filter_files(Tree, Filter:code*) :->
  847    "Highlight files that match Filter"::
  848    send(Tree, collapse_all),
  849    send(Tree, selection, @nil),
  850    (   Filter == @nil
  851    ->  send(Tree, expand_id, '.'),
  852        send(Tree, expand_id, project)
  853    ;   new(Count, number(0)),
  854        get(Tree?tree, root, Root),
  855        send(Root, for_all,
  856             if(and(message(@arg1, instance_of, prolog_file_node),
  857                    message(Filter, forward, @arg1)),
  858                and(message(Tree, show_node_path, @arg1),
  859                    message(Count, plus, 1)))),
  860        send(Tree, report, status, 'Filter on file name: %d hits', Count)
  861    ),
  862    send(Tree, scroll_to, point(0,0)).
  863
  864show_node_path(Tree, Node:node) :->
  865    "Select Node and make sure all parents are expanded"::
  866    send(Node, selected, @on),
  867    send(Tree, expand_parents, Node).
  868
  869expand_parents(Tree, Node:node) :->
  870    (   get(Node, collapsed, @nil)
  871    ->  true
  872    ;   send(Node, collapsed, @off)
  873    ),
  874    send(Node?parents, for_all, message(Tree, expand_parents, @arg1)).
  875
  876collapse_all(Tree) :->
  877    "Collapse all nodes"::
  878    get(Tree?tree, root, Root),
  879    send(Root, for_all,
  880         if(@arg1?collapsed == @off,
  881            message(@arg1, collapsed, @on))).
  882
  883:- pce_end_class(xref_file_tree).
  884
  885
  886:- pce_begin_class(prolog_directory_node, toc_folder,
  887                   "Represent a directory").
  888
  889variable(flags, name*, get, "Warning status").
  890
  891initialise(DN, Dir:name, Label:[name]) :->
  892    "Create a directory node"::
  893    (   Label \== @default
  894    ->  Name = Label
  895    ;   file_alias_path(Name, Dir)
  896    ->  true
  897    ;   file_base_name(Dir, Name)
  898    ),
  899    send_super(DN, initialise, xref_directory_text(Dir, Name), Dir).
  900
  901parent_id(FN, ParentId:name) :<-
  902    "Get id for the parent"::
  903    get(FN, identifier, Path),
  904    (   file_alias_path(_, Path)
  905    ->  ParentId = alias
  906    ;   file_directory_name(Path, ParentId)
  907    ).
  908
  909sort(DN) :->
  910    "Sort my sons"::
  911    send(DN, sort_sons, ?(@arg1, compare, @arg2)),
  912    send(DN?sons, for_all, message(@arg1, sort)).
  913
  914compare(DN, Node:toc_node, Diff:{smaller,equal,larger}) :<-
  915    "Compare for sorting children"::
  916    (   send(Node, instance_of, prolog_file_node)
  917    ->  Diff = smaller
  918    ;   get(DN, label, L1),
  919        get(Node, label, L2),
  920        get(L1, compare, L2, Diff)
  921    ).
  922
  923set_flags(DN) :->
  924    "Set alert images"::
  925    send(DN?sons, for_all, message(@arg1, set_flags)),
  926    (   get(DN?sons, find, @arg1?flags \== ok, _Node)
  927    ->  send(DN, collapsed_image, @xref_alert_closedir),
  928        send(DN, expanded_image, @xref_alert_opendir),
  929        send(DN, slot, flags, alert)
  930    ;   send(DN, collapsed_image, @xref_ok_closedir),
  931        send(DN, expanded_image, @xref_ok_opendir),
  932        send(DN, slot, flags, ok)
  933    ),
  934    send(@display, synchronise).
  935
  936:- pce_end_class(prolog_directory_node).
  937
  938
  939:- pce_begin_class(prolog_file_node, toc_file,
  940                   "Represent a file").
  941
  942variable(flags,         name*, get, "Warning status").
  943variable(base_name,     name,  get, "Base-name of file").
  944
  945initialise(FN, File:name) :->
  946    "Create from a file"::
  947    absolute_file_name(File, Path),
  948    send_super(FN, initialise, new(T, xref_file_text(Path)), Path),
  949    file_base_name(File, Base),
  950    send(FN, slot, base_name, Base),
  951    send(T, default_action, info).
  952
  953basename(FN, BaseName:name) :<-
  954    "Get basename of the file for sorting"::
  955    get(FN, identifier, File),
  956    file_base_name(File, BaseName).
  957
  958parent_id(FN, ParentId:name) :<-
  959    "Get id for the parent"::
  960    get(FN, identifier, Path),
  961    file_directory_name(Path, Dir),
  962    (   file_alias_path('.', Dir)
  963    ->  ParentId = '.'
  964    ;   ParentId = Dir
  965    ).
  966
  967sort(_) :->
  968    true.
  969
  970compare(FN, Node:toc_node, Diff:{smaller,equal,larger}) :<-
  971    "Compare for sorting children"::
  972    (   send(Node, instance_of, prolog_directory_node)
  973    ->  Diff = larger
  974    ;   get(FN, basename, L1),
  975        get(Node, basename, L2),
  976        get(L1, compare, L2, Diff)
  977    ).
  978
  979set_flags(FN) :->
  980    "Set alert images"::
  981    get(FN, identifier, File),
  982    (   file_warnings(File, _)
  983    ->  send(FN, image, @xref_alert_file),
  984        send(FN, slot, flags, alert)
  985    ;   send(FN, image, @xref_ok_file),
  986        send(FN, slot, flags, ok)
  987    ),
  988    send(@display, synchronise).
  989
  990:- pce_global(@xref_ok_file,
  991              make_xref_image([ image('16x16/doc.xpm'),
  992                                image('16x16/ok.xpm')
  993                              ])).  994:- pce_global(@xref_alert_file,
  995              make_xref_image([ image('16x16/doc.xpm'),
  996                                image('16x16/alert.xpm')
  997                              ])).  998
  999:- pce_global(@xref_ok_opendir,
 1000              make_xref_image([ image('16x16/opendir.xpm'),
 1001                                image('16x16/ok.xpm')
 1002                              ])). 1003:- pce_global(@xref_alert_opendir,
 1004              make_xref_image([ image('16x16/opendir.xpm'),
 1005                                image('16x16/alert.xpm')
 1006                              ])). 1007
 1008:- pce_global(@xref_ok_closedir,
 1009              make_xref_image([ image('16x16/closedir.xpm'),
 1010                                image('16x16/ok.xpm')
 1011                              ])). 1012:- pce_global(@xref_alert_closedir,
 1013              make_xref_image([ image('16x16/closedir.xpm'),
 1014                                image('16x16/alert.xpm')
 1015                              ])). 1016
 1017make_xref_image([First|More], Image) :-
 1018    new(Image, image(@nil, 0, 0, pixmap)),
 1019    send(Image, copy, First),
 1020    forall(member(I2, More),
 1021           send(Image, draw_in, bitmap(I2))).
 1022
 1023:- pce_end_class(prolog_file_node).
 1024
 1025
 1026
 1027
 1028                 /*******************************
 1029                 *           FILE INFO          *
 1030                 *******************************/
 1031
 1032
 1033:- pce_begin_class(prolog_file_info, window,
 1034                   "Show information on File").
 1035:- use_class_template(arm).
 1036
 1037variable(tabular,     tabular, get, "Displayed table").
 1038variable(prolog_file, name*,   get, "Displayed Prolog file").
 1039
 1040initialise(W, File:[name]*) :->
 1041    send_super(W, initialise),
 1042    send(W, pen, 0),
 1043    send(W, scrollbars, vertical),
 1044    send(W, display, new(T, tabular)),
 1045    send(T, rules, all),
 1046    send(T, cell_spacing, -1),
 1047    send(W, slot, tabular, T),
 1048    (   atom(File)
 1049    ->  send(W, prolog_file, File)
 1050    ;   true
 1051    ).
 1052
 1053resize(W) :->
 1054    send_super(W, resize),
 1055    get(W?visible, width, Width),
 1056    send(W?tabular, table_width, Width-3).
 1057
 1058
 1059file(V, File0:name*) :->
 1060    "Set vizualized file"::
 1061    (   File0 == @nil
 1062    ->  File = File0
 1063    ;   absolute_file_name(File0, File)
 1064    ),
 1065    (   get(V, prolog_file, File)
 1066    ->  true
 1067    ;   send(V, slot, prolog_file, File),
 1068        send(V, update)
 1069    ).
 1070
 1071
 1072clear(W) :->
 1073    send(W?tabular, clear).
 1074
 1075
 1076update(V) :->
 1077    "Show information on the current file"::
 1078    send(V, clear),
 1079    send(V, scroll_to, point(0,0)),
 1080    (   get(V, prolog_file, File),
 1081        File \== @nil
 1082    ->  send(V?frame, xref_file, File), % Make sure data is up-to-date
 1083        send(V, show_info)
 1084    ;   true
 1085    ).
 1086
 1087
 1088module(W, Module:name) :<-
 1089    "Module associated with this file"::
 1090    get(W, prolog_file, File),
 1091    (   xref_module(File, Module)
 1092    ->  true
 1093    ;   Module = user               % TBD: does not need to be true!
 1094    ).
 1095
 1096:- pce_group(info).
 1097
 1098show_info(W) :->
 1099    get(W, tabular, T),
 1100    BG = (background := khaki1),
 1101    get(W, prolog_file, File),
 1102    new(FG, xref_file_text(File)),
 1103    send(FG, font, huge),
 1104    send(T, append, FG, halign := center, colspan := 2, BG),
 1105    send(T, next_row),
 1106    send(W, show_module),
 1107    send(W, show_modified),
 1108    send(W, show_undefined),
 1109    send(W, show_not_called),
 1110    send(W, show_exports),
 1111    send(W, show_imports),
 1112    true.
 1113
 1114show_module(W) :->
 1115    "Show basic module info"::
 1116    get(W, prolog_file, File),
 1117    get(W, tabular, T),
 1118    (   xref_module(File, Module)
 1119    ->  send(T, append, 'Module:', bold, right),
 1120        send(T, append, Module),
 1121        send(T, next_row)
 1122    ;   true
 1123    ).
 1124
 1125show_modified(W) :->
 1126    get(W, prolog_file, File),
 1127    get(W, tabular, T),
 1128    time_file(File, Stamp),
 1129    format_time(string(Modified), '%+', Stamp),
 1130    send(T, append, 'Modified:', bold, right),
 1131    send(T, append, Modified),
 1132    send(T, next_row).
 1133
 1134show_exports(W) :->
 1135    get(W, prolog_file, File),
 1136    (   xref_module(File, Module),
 1137        findall(E, xref_exported(File, E), Exports),
 1138        Exports \== []
 1139    ->  send(W, show_export_header, export, imported_by),
 1140        sort_callables(Exports, Sorted),
 1141        forall(member(Callable, Sorted),
 1142               send(W, show_module_export, File, Module, Callable))
 1143    ;   true
 1144    ),
 1145    (   findall(C-Fs,
 1146                ( setof(F, export_link_1(File, F, C), Fs),
 1147                  \+ xref_exported(File, C)),
 1148                Pairs0),
 1149        Pairs0 \== []
 1150    ->  send(W, show_export_header, defined, used_by),
 1151        keysort(Pairs0, Pairs),     % TBD
 1152        forall(member(Callable-ImportFiles, Pairs),
 1153               send(W, show_file_export, Callable, ImportFiles))
 1154    ;   true
 1155    ).
 1156
 1157show_export_header(W, Left:name, Right:name) :->
 1158    get(W, tabular, T),
 1159    BG = (background := khaki1),
 1160    send(T, append, Left?label_name, bold, center, BG),
 1161    send(T, append, Right?label_name, bold, center, BG),
 1162    send(T, next_row).
 1163
 1164show_module_export(W, File:name, Module:name, Callable:prolog) :->
 1165    get(W, prolog_file, File),
 1166    get(W, tabular, T),
 1167    send(T, append, xref_predicate_text(Module:Callable, @default, File)),
 1168    findall(In, exported_to(File, Callable, In), InL),
 1169    send(T, append, new(XL, xref_graphical_list)),
 1170    (   InL == []
 1171    ->  true
 1172    ;   sort_files(InL, Sorted),
 1173        forall(member(F, Sorted),
 1174               send(XL, append, xref_imported_by(F, Callable)))
 1175    ),
 1176    send(T, next_row).
 1177
 1178show_file_export(W, Callable:prolog, ImportFiles:prolog) :->
 1179    get(W, prolog_file, File),
 1180    get(W, tabular, T),
 1181    send(T, append, xref_predicate_text(Callable, @default, File)),
 1182    send(T, append, new(XL, xref_graphical_list)),
 1183    sort_files(ImportFiles, Sorted),
 1184    qualify_from_file(Callable, File, QCall),
 1185    forall(member(F, Sorted),
 1186           send(XL, append, xref_imported_by(F, QCall))),
 1187    send(T, next_row).
 1188
 1189qualify_from_file(Callable, _, Callable) :-
 1190    Callable = _:_,
 1191    !.
 1192qualify_from_file(Callable, File, M:Callable) :-
 1193    xref_module(File, M),
 1194    !.
 1195qualify_from_file(Callable, _, Callable).
 exported_to(+ExportFile, +Callable, -ImportFile)
ImportFile imports Callable from ExportFile. The second clause deals with auto-import.

TBD: Make sure the autoload library is loaded before we begin.

 1205exported_to(ExportFile, Callable, ImportFile) :-
 1206    xref_defined(ImportFile, Callable, imported(ExportFile)),
 1207    atom(ImportFile).               % avoid XPCE buffers.
 1208exported_to(ExportFile, Callable, ImportFile) :-
 1209    '$autoload':library_index(Callable, _, ExportFileNoExt),
 1210    file_name_extension(ExportFileNoExt, _, ExportFile),
 1211    xref_called(ImportFile, Callable),
 1212    atom(ImportFile),
 1213    \+ xref_defined(ImportFile, Callable, _).
 1214
 1215show_imports(W) :->
 1216    "Show predicates we import"::
 1217    get(W, prolog_file, File),
 1218    findall(E-Cs,
 1219            setof(C, export_link_1(E, File, C), Cs),
 1220            Pairs),
 1221    (   Pairs \== []
 1222    ->  sort(Pairs, Sorted),        % TBD: use sort_files/2
 1223        (   xref_module(File, _)
 1224        ->  send(W, show_export_header, from, imports)
 1225        ;   send(W, show_export_header, from, uses)
 1226        ),
 1227        forall(member(E-Cs, Sorted),
 1228               send(W, show_import, E, Cs))
 1229    ;   true
 1230    ).
 1231
 1232show_import(W, File:name, Callables:prolog) :->
 1233    "Show imports from file"::
 1234    get(W, tabular, T),
 1235    send(T, append, xref_file_text(File)),
 1236    send(T, append, new(XL, xref_graphical_list)),
 1237    sort_callables(Callables, Sorted),
 1238    forall(member(C, Sorted),
 1239           send(XL, append, xref_predicate_text(C, @default, File))),
 1240    send(T, next_row).
 1241
 1242
 1243show_undefined(W) :->
 1244    "Add underfined predicates to table"::
 1245    get(W, prolog_file, File),
 1246    findall(Undef, undefined(File, Undef), UndefList),
 1247    (   UndefList == []
 1248    ->  true
 1249    ;   BG = (background := khaki1),
 1250        get(W, tabular, T),
 1251        (   setting(warn_autoload, true)
 1252        ->  Label = 'Undefined/autoload'
 1253        ;   Label = 'Undefined'
 1254        ),
 1255        send(T, append, Label, bold, center, BG),
 1256        send(T, append, 'Called by', bold, center, BG),
 1257        send(T, next_row),
 1258        sort_callables(UndefList, Sorted),
 1259        forall(member(Callable, Sorted),
 1260               send(W, show_undef, Callable))
 1261    ).
 1262
 1263show_undef(W, Callable:prolog) :->
 1264    "Show undefined predicate"::
 1265    get(W, prolog_file, File),
 1266    get(W, module, Module),
 1267    get(W, tabular, T),
 1268    send(T, append,
 1269         xref_predicate_text(Module:Callable, undefined, File)),
 1270    send(T, append, new(L, xref_graphical_list)),
 1271    findall(By, xref_called(File, Callable, By), By),
 1272    sort_callables(By, Sorted),
 1273    forall(member(P, Sorted),
 1274           send(L, append, xref_predicate_text(Module:P, called_by, File))),
 1275    send(T, next_row).
 1276
 1277
 1278show_not_called(W) :->
 1279    "Show predicates that are not called"::
 1280    get(W, prolog_file, File),
 1281    findall(NotCalled, not_called(File, NotCalled), NotCalledList),
 1282    (   NotCalledList == []
 1283    ->  true
 1284    ;   BG = (background := khaki1),
 1285        get(W, tabular, T),
 1286        send(T, append, 'Not called', bold, center, colspan := 2, BG),
 1287         send(T, next_row),
 1288        sort_callables(NotCalledList, Sorted),
 1289        forall(member(Callable, Sorted),
 1290               send(W, show_not_called_pred, Callable))
 1291    ).
 1292
 1293show_not_called_pred(W, Callable:prolog) :->
 1294    "Show a not-called predicate"::
 1295    get(W, prolog_file, File),
 1296    get(W, module, Module),
 1297    get(W, tabular, T),
 1298    send(T, append,
 1299         xref_predicate_text(Module:Callable, not_called, File),
 1300         colspan := 2),
 1301    send(T, next_row).
 1302
 1303:- pce_end_class(prolog_file_info).
 1304
 1305
 1306:- pce_begin_class(xref_predicate_text, text,
 1307                   "Text representing a predicate").
 1308
 1309class_variable(colour, colour, dark_green).
 1310
 1311variable(callable,       prolog, get, "Predicate indicator").
 1312variable(classification, [name], get, "Classification of the predicate").
 1313variable(file,           name*,  get, "File of predicate").
 1314
 1315initialise(T, Callable0:prolog,
 1316           Class:[{undefined,called_by,not_called}],
 1317           File:[name]) :->
 1318    "Create from callable or predicate indicator"::
 1319    single_qualify(Callable0, Callable),
 1320    send(T, slot, callable, Callable),
 1321    callable_to_label(Callable, File, Label),
 1322    send_super(T, initialise, Label),
 1323    (   File \== @default
 1324    ->  send(T, slot, file, File)
 1325    ;   true
 1326    ),
 1327    send(T, classification, Class).
 single_qualify(+Term, -Qualified)
Strip redundant M: from the term, leaving at most one qualifier.
 1333single_qualify(_:Q0, Q) :-
 1334    is_qualified(Q0),
 1335    !,
 1336    single_qualify(Q0, Q).
 1337single_qualify(Q, Q).
 1338
 1339is_qualified(M:_) :-
 1340    atom(M).
 1341
 1342pi(IT, PI:prolog) :<-
 1343    "Get predicate as predicate indicator (Name/Arity)"::
 1344    get(IT, callable, Callable),
 1345    to_predicate_indicator(Callable, PI).
 1346
 1347classification(T, Class:[name]) :->
 1348    send(T, slot, classification, Class),
 1349    (   Class == undefined
 1350    ->  get(T, callable, Callable),
 1351        strip_module(Callable, _, Plain),
 1352        (   autoload_predicate(Plain)
 1353        ->  send(T, colour, navy_blue),
 1354            send(T, slot, classification, autoload)
 1355        ;   global_predicate(Plain)
 1356        ->  send(T, colour, navy_blue),
 1357            send(T, slot, classification, global)
 1358        ;   send(T, colour, red)
 1359        )
 1360    ;   Class == not_called
 1361    ->  send(T, colour, red)
 1362    ;   true
 1363    ).
 1364
 1365:- pce_global(@xref_predicate_text_recogniser,
 1366              new(handler_group(@arm_recogniser,
 1367                                click_gesture(left, '', single,
 1368                                              message(@receiver, edit))))).
 1369
 1370event(T, Ev:event) :->
 1371    (   send_super(T, event, Ev)
 1372    ->  true
 1373    ;   send(@xref_predicate_text_recogniser, event, Ev)
 1374    ).
 1375
 1376
 1377arm(TF, Val:bool) :->
 1378    "Preview activiity"::
 1379    (   Val == @on
 1380    ->  send(TF, underline, @on),
 1381        (   get(TF, classification, Class),
 1382            Class \== @default
 1383        ->  send(TF, report, status,
 1384                 '%s predicate %s', Class?capitalise, TF?string)
 1385        ;   send(TF, report, status,
 1386                 'Predicate %s', TF?string)
 1387        )
 1388    ;   send(TF, underline, @off),
 1389        send(TF, report, status, '')
 1390    ).
 1391
 1392edit(T) :->
 1393    get(T, file, File),
 1394    get(T, callable, Callable),
 1395    edit_callable(Callable, File).
 1396
 1397:- pce_end_class(xref_predicate_text).
 1398
 1399
 1400:- pce_begin_class(xref_file_text, text,
 1401                   "Represent a file-name").
 1402
 1403variable(path,           name,         get, "Filename represented").
 1404variable(default_action, name := edit, both, "Default on click").
 1405
 1406initialise(TF, File:name) :->
 1407    absolute_file_name(File, Path),
 1408    file_name_on_path(Path, ShortId),
 1409    short_file_name_to_atom(ShortId, Label),
 1410    send_super(TF, initialise, Label),
 1411    send(TF, name, Path),
 1412    send(TF, slot, path, Path).
 1413
 1414:- pce_global(@xref_file_text_recogniser,
 1415              make_xref_file_text_recogniser). 1416
 1417make_xref_file_text_recogniser(G) :-
 1418    new(C, click_gesture(left, '', single,
 1419                         message(@receiver, run_default_action))),
 1420    new(P, popup_gesture(@arg1?popup)),
 1421    new(D, drag_and_drop_gesture(left)),
 1422    send(D, cursor, @default),
 1423    new(G, handler_group(C, D, P, @arm_recogniser)).
 1424
 1425popup(_, Popup:popup) :<-
 1426    new(Popup, popup),
 1427    send_list(Popup, append,
 1428              [ menu_item(edit, message(@arg1, edit)),
 1429                menu_item(info, message(@arg1, info)),
 1430                menu_item(header, message(@arg1, header))
 1431              ]).
 1432
 1433event(T, Ev:event) :->
 1434    (   send_super(T, event, Ev)
 1435    ->  true
 1436    ;   send(@xref_file_text_recogniser, event, Ev)
 1437    ).
 1438
 1439arm(TF, Val:bool) :->
 1440    "Preview activity"::
 1441    (   Val == @on
 1442    ->  send(TF, underline, @on),
 1443        send(TF, report, status, 'File %s', TF?path)
 1444    ;   send(TF, underline, @off),
 1445        send(TF, report, status, '')
 1446    ).
 1447
 1448run_default_action(T) :->
 1449    get(T, default_action, Def),
 1450    send(T, Def).
 1451
 1452edit(T) :->
 1453    get(T, path, Path),
 1454    auto_call(edit(file(Path))).
 1455
 1456info(T) :->
 1457    get(T, path, Path),
 1458    send(T?frame, file_info, Path).
 1459
 1460header(T) :->
 1461    get(T, path, Path),
 1462    send(T?frame, file_header, Path).
 1463
 1464prolog_source(T, Src:string) :<-
 1465    "Import declarations"::
 1466    get(T, path, File),
 1467    new(V, xref_view),
 1468    send(V, file_header, File),
 1469    get(V?text_buffer, contents, Src),
 1470    send(V, destroy).
 1471
 1472:- pce_end_class(xref_file_text).
 1473
 1474
 1475:- pce_begin_class(xref_directory_text, text,
 1476                   "Represent a directory-name").
 1477
 1478variable(path,           name,         get, "Filename represented").
 1479
 1480initialise(TF, Dir:name, Label:[name]) :->
 1481    absolute_file_name(Dir, Path),
 1482    (   Label == @default
 1483    ->  file_base_name(Path, TheLabel)
 1484    ;   TheLabel = Label
 1485    ),
 1486    send_super(TF, initialise, TheLabel),
 1487    send(TF, slot, path, Path).
 1488
 1489files(DT, Files:chain) :<-
 1490    "List of files that belong to this directory"::
 1491    new(Files, chain),
 1492    get(DT, path, Path),
 1493    (   source_file(File),
 1494        sub_atom(File, 0, _, _, Path),
 1495        send(Files, append, File),
 1496        fail ; true
 1497    ).
 1498
 1499:- pce_global(@xref_directory_text_recogniser,
 1500              make_xref_directory_text_recogniser). 1501
 1502make_xref_directory_text_recogniser(G) :-
 1503    new(D, drag_and_drop_gesture(left)),
 1504    send(D, cursor, @default),
 1505    new(G, handler_group(D, @arm_recogniser)).
 1506
 1507event(T, Ev:event) :->
 1508    (   send_super(T, event, Ev)
 1509    ->  true
 1510    ;   send(@xref_directory_text_recogniser, event, Ev)
 1511    ).
 1512
 1513arm(TF, Val:bool) :->
 1514    "Preview activiity"::
 1515    (   Val == @on
 1516    ->  send(TF, underline, @on),
 1517        send(TF, report, status, 'Directory %s', TF?path)
 1518    ;   send(TF, underline, @off),
 1519        send(TF, report, status, '')
 1520    ).
 1521
 1522:- pce_end_class(xref_directory_text).
 1523
 1524
 1525:- pce_begin_class(xref_imported_by, figure,
 1526                   "Indicate import of callable into file").
 1527
 1528variable(callable, prolog, get, "Callable term of imported predicate").
 1529
 1530:- pce_global(@xref_horizontal_format,
 1531              make_xref_horizontal_format). 1532
 1533make_xref_horizontal_format(F) :-
 1534    new(F, format(vertical, 1, @on)),
 1535    send(F, row_sep, 3),
 1536    send(F, column_sep, 0).
 1537
 1538initialise(IT, File:name, Imported:prolog) :->
 1539    send_super(IT, initialise),
 1540    send(IT, format, @xref_horizontal_format),
 1541    send(IT, display, new(F, xref_file_text(File))),
 1542    send(F, name, file_text),
 1543    send(IT, slot, callable, Imported),
 1544    send(IT, show_called_by).
 1545
 1546path(IT, Path:name) :<-
 1547    "Represented file"::
 1548    get(IT, member, file_text, Text),
 1549    get(Text, path, Path).
 1550
 1551show_called_by(IT) :->
 1552    "Add number indicating calls"::
 1553    get(IT, called_by, List),
 1554    length(List, N),
 1555    send(IT, display, new(T, text(string('(%d)', N)))),
 1556    send(T, name, called_count),
 1557    (   N > 0
 1558    ->  send(T, underline, @on),
 1559        send(T, colour, blue),
 1560        send(T, recogniser, @xref_called_by_recogniser)
 1561    ;   send(T, colour, grey60)
 1562    ).
 1563
 1564called_by(IT, ByList:prolog) :<-
 1565    "Return list of callables satisfied by the import"::
 1566    get(IT, path, Source),
 1567    get(IT, callable, Callable),
 1568    findall(By, used_in(Source, Callable, By), ByList).
 used_in(+Source, +QCallable, -CalledBy)
Determine which the callers for QCallable in Source. QCallable is qualified with the module of the exporting file (if any).
 1575used_in(Source, M:Callable, By) :-              % we are the same module
 1576    xref_module(Source, M),
 1577    !,
 1578    xref_called(Source, Callable, By).
 1579used_in(Source, _:Callable, By) :-              % we imported
 1580    xref_defined(Source, Callable, imported(_)),
 1581    !,
 1582    xref_called(Source, Callable, By).
 1583used_in(Source, Callable, By) :-
 1584    xref_called(Source, Callable, By).
 1585used_in(Source, Callable, '<export>') :-
 1586    xref_exported(Source, Callable).
 1587
 1588:- pce_group(event).
 1589
 1590:- pce_global(@xref_called_by_recogniser,
 1591              new(popup_gesture(@receiver?device?called_by_popup, left))).
 1592
 1593called_by_popup(IT, P:popup) :<-
 1594    "Show called where import is called"::
 1595    new(P, popup(called_by, message(IT, edit_called_by, @arg1))),
 1596    get(IT, called_by, ByList),
 1597    sort_callables(ByList, Sorted),
 1598    forall(member(C, Sorted),
 1599           ( callable_to_label(C, Label),
 1600             send(P, append, menu_item(prolog(C), @default, Label)))).
 1601
 1602edit_called_by(IT, Called:prolog) :->
 1603    "Edit file on the predicate Called"::
 1604    get(IT, path, Source),
 1605    edit_callable(Called, Source).
 1606
 1607:- pce_end_class(xref_imported_by).
 1608
 1609
 1610:- pce_begin_class(xref_graphical_list, figure,
 1611                   "Show list of exports to files").
 1612
 1613variable(wrap, {extend,wrap,wrap_fixed_width,clip} := extend, get,
 1614         "Wrapping mode").
 1615
 1616initialise(XL) :->
 1617    send_super(XL, initialise),
 1618    send(XL, margin, 500, wrap).
 1619
 1620append(XL, I:graphical) :->
 1621    (   send(XL?graphicals, empty)
 1622    ->  true
 1623    ;   send(XL, display, text(', '))
 1624    ),
 1625    send(XL, display, I).
 1626
 1627:- pce_group(layout).
 1628
 1629:- pce_global(@xref_graphical_list_format,
 1630              make_xref_graphical_list_format). 1631
 1632make_xref_graphical_list_format(F) :-
 1633    new(F, format(horizontal, 500, @off)),
 1634    send(F, column_sep, 0),
 1635    send(F, row_sep, 0).
 1636
 1637margin(T, Width:int*, How:[{wrap,wrap_fixed_width,clip}]) :->
 1638    "Wrap items to indicated width"::
 1639    (   Width == @nil
 1640    ->  send(T, slot, wrap, extend),
 1641        send(T, format, @rdf_composite_format)
 1642    ;   send(T, slot, wrap, How),
 1643        How == wrap
 1644    ->  FmtWidth is max(10, Width),
 1645        new(F, format(horizontal, FmtWidth, @off)),
 1646        send(F, column_sep, 0),
 1647        send(F, row_sep, 0),
 1648        send(T, format, F)
 1649    ;   throw(tbd)
 1650    ).
 1651
 1652:- pce_end_class(xref_graphical_list).
 1653
 1654
 1655
 1656                 /*******************************
 1657                 *          PREDICATES          *
 1658                 *******************************/
 1659
 1660:- pce_begin_class(xref_predicate_browser, browser,
 1661                 "Show loaded files").
 1662
 1663initialise(PL) :->
 1664    send_super(PL, initialise),
 1665    send(PL, popup, new(P, popup)),
 1666    send_list(P, append,
 1667              [ menu_item(edit, message(@arg1, edit))
 1668              ]).
 1669
 1670update(PL) :->
 1671    send(PL, clear),
 1672    forall((defined(File, Callable), atom(File), \+ library_file(File)),
 1673           send(PL, append, Callable, @default, File)),
 1674    forall((xref_current_source(File), atom(File), \+library_file(File)),
 1675           forall(undefined(File, Callable),
 1676                  send(PL, append, Callable, undefined, File))),
 1677    send(PL, sort).
 1678
 1679append(PL, Callable:prolog, Class:[name], File:[name]) :->
 1680    send_super(PL, append, xref_predicate_dict_item(Callable, Class, File)).
 1681
 1682:- pce_end_class(xref_predicate_browser).
 1683
 1684
 1685:- pce_begin_class(xref_predicate_dict_item, dict_item,
 1686                   "Represent a Prolog predicate").
 1687
 1688variable(callable, prolog, get, "Callable term").
 1689variable(file,     name*,  get, "Origin file").
 1690
 1691initialise(PI, Callable0:prolog, _Class:[name], File:[name]) :->
 1692    "Create from callable, class and file"::
 1693    single_qualify(Callable0, Callable),
 1694    send(PI, slot, callable, Callable),
 1695    callable_to_label(Callable, Label),
 1696    send_super(PI, initialise, Label),
 1697    (   File \== @default
 1698    ->  send(PI, slot, file, File)
 1699    ;   true
 1700    ).
 1701
 1702edit(PI) :->
 1703    "Edit Associated prediate"::
 1704    get(PI, file, File),
 1705    get(PI, callable, Callable),
 1706    edit_callable(Callable, File).
 1707
 1708:- pce_end_class(xref_predicate_dict_item).
 1709
 1710
 1711                 /*******************************
 1712                 *         UTIL CLASSES         *
 1713                 *******************************/
 1714
 1715:- pce_begin_class(xref_view, view,
 1716                   "View with additional facilities for formatting").
 1717
 1718initialise(V) :->
 1719    send_super(V, initialise),
 1720    send(V, font, fixed).
 1721
 1722update(_) :->
 1723    true.                           % or ->clear?  ->destroy?
 1724
 1725file_header(View, File:name) :->
 1726    "Create import/export fileheader for File"::
 1727    (   xref_module(File, _)
 1728    ->  Decls = Imports
 1729    ;   xref_file_exports(File, Export),
 1730        Decls = [Export|Imports]
 1731    ),
 1732    xref_file_imports(File, Imports),
 1733    send(View, clear),
 1734    send(View, declarations, Decls),
 1735    (   (   nonvar(Export)
 1736        ->  send(View, report, status,
 1737                 'Created module header for non-module file %s', File)
 1738        ;   send(View, report, status,
 1739                 'Created import header for module file %s', File)
 1740        )
 1741    ->  true
 1742    ;   true
 1743    ).
 1744
 1745declarations(V, Decls:prolog) :->
 1746    pce_open(V, append, Out),
 1747    call_cleanup(print_decls(Decls, Out), close(Out)).
 1748
 1749print_decls([], _) :- !.
 1750print_decls([H|T], Out) :-
 1751    !,
 1752    print_decls(H, Out),
 1753    print_decls(T, Out).
 1754print_decls(Term, Out) :-
 1755    portray_clause(Out, Term).
 1756
 1757:- pce_end_class(xref_view).
 1758
 1759
 1760                 /*******************************
 1761                 *        FILE-NAME LOGIC       *
 1762                 *******************************/
 short_file_name_to_atom(+ShortId, -Atom)
Convert a short filename into an atom
 1768short_file_name_to_atom(Atom, Atom) :-
 1769    atomic(Atom),
 1770    !.
 1771short_file_name_to_atom(Term, Atom) :-
 1772    term_to_atom(Term, Atom).
 library_file(+Path)
True if Path comes from the Prolog tree and must be considered a library.
 1780library_file(Path) :-
 1781    current_prolog_flag(home, Home),
 1782    sub_atom(Path, 0, _, _, Home).
 profile_file(+Path)
True if path is a personalisation file. This is a bit hairy.
 1788profile_file(Path) :-
 1789    file_name_on_path(Path, user_profile(File)),
 1790    known_profile_file(File).
 1791
 1792known_profile_file('.swiplrc').
 1793known_profile_file('swipl.ini').
 1794known_profile_file('.pceemacsrc').
 1795known_profile_file(File) :-
 1796    sub_atom(File, 0, _, _, 'lib/xpce/emacs').
 sort_files(+Files, -Sorted)
Sort files, keeping groups comming from the same alias together.
 1802sort_files(Files0, Sorted) :-
 1803    sort(Files0, Files),            % remove duplicates
 1804    maplist(key_file, Files, Keyed),
 1805    keysort(Keyed, KSorted),
 1806    unkey(KSorted, Sorted).
 1807
 1808key_file(File, Key-File) :-
 1809    file_name_on_path(File, Key).
 1810
 1811
 1812                 /*******************************
 1813                 *           PREDICATES         *
 1814                 *******************************/
 available(+File, +Callable, -HowDefined)
True if Callable is available in File.
 1820available(File, Called, How) :-
 1821    xref_defined(File, Called, How0),
 1822    !,
 1823    How = How0.
 1824available(_, Called, How) :-
 1825    built_in_predicate(Called),
 1826    !,
 1827    How = builtin.
 1828available(_, Called, How) :-
 1829    setting(warn_autoload, false),
 1830    autoload_predicate(Called),
 1831    !,
 1832    How = autoload.
 1833available(_, Called, How) :-
 1834    setting(warn_autoload, false),
 1835    global_predicate(Called),
 1836    !,
 1837    How = global.
 1838available(_, Called, How) :-
 1839    Called = _:_,
 1840    defined(_, Called),
 1841    !,
 1842    How = module_qualified.
 1843available(_, M:G, How) :-
 1844    defined(ExportFile, G),
 1845    xref_module(ExportFile, M),
 1846    !,
 1847    How = module_overruled.
 1848available(_, Called, How) :-
 1849    defined(ExportFile, Called),
 1850    \+ xref_module(ExportFile, _),
 1851    !,
 1852    How == plain_file.
 built_in_predicate(+Callable)
True if Callable is a built-in
 1859built_in_predicate(Goal) :-
 1860    strip_module(Goal, _, Plain),
 1861    xref_built_in(Plain).
 autoload_predicate(+Callable) is semidet
 autoload_predicate(+Callable, -File) is semidet
True if Callable can be autoloaded. TBD: make sure the autoload index is up-to-date.
 1869autoload_predicate(Goal) :-
 1870    '$autoload':library_index(Goal, _, _).
 1871
 1872
 1873autoload_predicate(Goal, File) :-
 1874    '$autoload':library_index(Goal, _, FileNoExt),
 1875    file_name_extension(FileNoExt, pl, File).
 global_predicate(+Callable)
True if Callable can be auto-imported from the global user module.
 1883global_predicate(Goal) :-
 1884    predicate_property(user:Goal, _),
 1885    !.
 to_predicate_indicator(+Term, -PI)
Convert to a predicate indicator.
 1891to_predicate_indicator(PI, PI) :-
 1892    is_predicate_indicator(PI),
 1893    !.
 1894to_predicate_indicator(Callable, PI) :-
 1895    callable(Callable),
 1896    predicate_indicator(Callable, PI).
 is_predicate_indicator(+PI) is semidet
True if PI is a predicate indicator.
 1902is_predicate_indicator(Name/Arity) :-
 1903    atom(Name),
 1904    integer(Arity).
 1905is_predicate_indicator(Module:Name/Arity) :-
 1906    atom(Module),
 1907    atom(Name),
 1908    integer(Arity).
 predicate_indicator(+Callable, -Name)
Generate a human-readable predicate indicator
 1914predicate_indicator(Module:Goal, PI) :-
 1915    atom(Module),
 1916    !,
 1917    predicate_indicator(Goal, PI0),
 1918    (   hidden_module(Module)
 1919    ->  PI = PI0
 1920    ;   PI = Module:PI0
 1921    ).
 1922predicate_indicator(Goal, Name/Arity) :-
 1923    callable(Goal),
 1924    !,
 1925    head_name_arity(Goal, Name, Arity).
 1926predicate_indicator(Goal, Goal).
 1927
 1928hidden_module(user) :- !.
 1929hidden_module(system) :- !.
 1930hidden_module(M) :-
 1931    sub_atom(M, 0, _, _, $).
 sort_callables(+List, -Sorted)
Sort list of callable terms.
 1937sort_callables(Callables, Sorted) :-
 1938    key_callables(Callables, Tagged),
 1939    keysort(Tagged, KeySorted),
 1940    unkey(KeySorted, SortedList),
 1941    ord_list_to_set(SortedList, Sorted).
 1942
 1943key_callables([], []).
 1944key_callables([H0|T0], [Key-H0|T]) :-
 1945    key_callable(H0, Key),
 1946    key_callables(T0, T).
 1947
 1948key_callable(Callable, k(Name, Arity, Module)) :-
 1949    predicate_indicator(Callable, PI),
 1950    (   PI = Name/Arity
 1951    ->  Module = user
 1952    ;   PI = Module:Name/Arity
 1953    ).
 1954
 1955unkey([], []).
 1956unkey([_-H|T0], [H|T]) :-
 1957    unkey(T0, T).
 ord_list_to_set(+OrdList, -OrdSet)
Removed duplicates (after unification) from an ordered list, creating a set.
 1964ord_list_to_set([], []).
 1965ord_list_to_set([H|T0], [H|T]) :-
 1966    ord_remove_same(H, T0, T1),
 1967    ord_list_to_set(T1, T).
 1968
 1969ord_remove_same(H, [H|T0], T) :-
 1970    !,
 1971    ord_remove_same(H, T0, T).
 1972ord_remove_same(_, L, L).
 callable_to_label(+Callable, +File, -Label:atom) is det
 callable_to_label(+Callable, -Label:atom) is det
Label is a textual label representing Callable in File.
 1980callable_to_label(Callable, Label) :-
 1981    callable_to_label(Callable, @nil, Label).
 1982
 1983callable_to_label(pce_principal:send_implementation(Id,_,_), _, Id) :-
 1984    atom(Id),
 1985    !.
 1986callable_to_label(pce_principal:get_implementation(Id,_,_,_), _, Id) :-
 1987    atom(Id),
 1988    !.
 1989callable_to_label('<export>', _, '<export>') :- !.
 1990callable_to_label('<directive>'(Line), _, Label) :-
 1991    !,
 1992    atom_concat('<directive>@', Line, Label).
 1993callable_to_label(_:'<directive>'(Line), _, Label) :-
 1994    !,
 1995    atom_concat('<directive>@', Line, Label).
 1996callable_to_label(Callable, File, Label) :-
 1997    to_predicate_indicator(Callable, PI0),
 1998    (   PI0 = M:PI1
 1999    ->  (   atom(File),
 2000            xref_module(File, M)
 2001        ->  PI = PI1
 2002        ;   PI = PI0
 2003        )
 2004    ;   PI = PI0
 2005    ),
 2006    term_to_atom(PI, Label).
 edit_callable(+Callable, +File)
 2010edit_callable('<export>', File) :-
 2011    !,
 2012    edit(file(File)).
 2013edit_callable(Callable, File) :-
 2014    local_callable(Callable, File, Local),
 2015    (   xref_defined(File, Local, How),
 2016        xref_definition_line(How, Line)
 2017    ->  edit(file(File, line(Line)))
 2018    ;   autoload_predicate(Local)
 2019    ->  functor(Local, Name, Arity),
 2020        edit(Name/Arity)
 2021    ).
 2022edit_callable(pce_principal:send_implementation(Id,_,_), _) :-
 2023    atom(Id),
 2024    atomic_list_concat([Class,Method], ->, Id),
 2025    !,
 2026    edit(send(Class, Method)).
 2027edit_callable(pce_principal:get_implementation(Id,_,_,_), _) :-
 2028    atom(Id),
 2029    atomic_list_concat([Class,Method], <-, Id),
 2030    !,
 2031    edit(get(Class, Method)).
 2032edit_callable('<directive>'(Line), File) :-
 2033    File \== @nil,
 2034    !,
 2035    edit(file(File, line(Line))).
 2036edit_callable(_:'<directive>'(Line), File) :-
 2037    File \== @nil,
 2038    !,
 2039    edit(file(File, line(Line))).
 2040edit_callable(Callable, _) :-
 2041    to_predicate_indicator(Callable, PI),
 2042    edit(PI).
 2043
 2044local_callable(M:Callable, File, Callable) :-
 2045    xref_module(File, M),
 2046    !.
 2047local_callable(Callable, _, Callable).
 2048
 2049
 2050                 /*******************************
 2051                 *            WARNINGS          *
 2052                 *******************************/
 file_warnings(+File:atom, -Warnings:list(atom))
Unify Warnings with a list of dubious things found in File. Intended to create icons. Fails if the file is totally ok.
 2059file_warnings(File, Warnings) :-
 2060    setof(W, file_warning(File, W), Warnings).
 2061
 2062file_warning(File, undefined) :-
 2063    undefined(File, _) -> true.
 2064file_warning(File, not_called) :-
 2065    setting(warn_not_called, true),
 2066    not_called(File, _) -> true.
 not_called(+File, -Callable)
Callable is a term defined in File, and for which no callers can be found.
 2074not_called(File, NotCalled) :-          % module version
 2075    xref_module(File, Module),
 2076    !,
 2077    defined(File, NotCalled),
 2078    \+ (   xref_called(File, NotCalled)
 2079       ;   xref_exported(File, NotCalled)
 2080       ;   xref_hook(NotCalled)
 2081       ;   xref_hook(Module:NotCalled)
 2082       ;   NotCalled = _:Goal,
 2083           xref_hook(Goal)
 2084       ;   xref_called(_, Module:NotCalled)
 2085       ;   NotCalled = _:_,
 2086           xref_called(_, NotCalled)
 2087       ;   NotCalled = M:G,
 2088           xref_called(ModFile, G),
 2089           xref_module(ModFile, M)
 2090       ;   generated_callable(Module:NotCalled)
 2091       ).
 2092not_called(File, NotCalled) :-          % non-module version
 2093    defined(File, NotCalled),
 2094    \+ (   xref_called(ImportFile, NotCalled),
 2095           \+ xref_module(ImportFile, _)
 2096       ;   NotCalled = _:_,
 2097           xref_called(_, NotCalled)
 2098       ;   NotCalled = M:G,
 2099           xref_called(ModFile, G),
 2100           xref_module(ModFile, M)
 2101       ;   xref_called(AutoImportFile, NotCalled),
 2102           \+ defined(AutoImportFile, NotCalled),
 2103           global_predicate(NotCalled)
 2104       ;   xref_hook(NotCalled)
 2105       ;   xref_hook(user:NotCalled)
 2106       ;   generated_callable(user:NotCalled)
 2107       ).
 2108
 2109generated_callable(M:Term) :-
 2110    head_name_arity(Term, Name, Arity),
 2111    prolog:generated_predicate(M:Name/Arity).
 xref_called(?Source, ?Callable) is nondet
True if Callable is called in Source, after removing recursive calls and calls made to predicates where the condition says that the predicate should not exist.
 2119xref_called(Source, Callable) :-
 2120    xref_called_cond(Source, Callable, _).
 2121
 2122xref_called_cond(Source, Callable, Cond) :-
 2123    xref_called(Source, Callable, By, Cond),
 2124    By \= Callable.                 % recursive calls
 defined(?File, ?Callable)
True if Callable is defined in File and not imported.
 2130defined(File, Callable) :-
 2131    xref_defined(File, Callable, How),
 2132    atom(File),
 2133    How \= imported(_),
 2134    How \= (multifile).
 undefined(+File, -Callable)
Callable is called in File, but no definition can be found. If File is not a module file we consider other files that are not module files.
 2142undefined(File, Undef) :-
 2143    xref_module(File, _),
 2144    !,
 2145    xref_called_cond(File, Undef, Cond),
 2146    \+ (   available(File, Undef, How),
 2147           How \== plain_file
 2148       ),
 2149    included_if_defined(Cond, Undef).
 2150undefined(File, Undef) :-
 2151    xref_called_cond(File, Undef, Cond),
 2152    \+ available(File, Undef, _),
 2153    included_if_defined(Cond, Undef).
 included_if_defined(+Condition, +Callable) is semidet
 2157included_if_defined(true, _)  :- !.
 2158included_if_defined(false, _) :- !, fail.
 2159included_if_defined(fail, _)  :- !, fail.
 2160included_if_defined(current_predicate(Name/Arity), Callable) :-
 2161    \+ functor(Callable, Name, Arity),
 2162    !.
 2163included_if_defined(\+ Cond, Callable) :-
 2164    !,
 2165    \+ included_if_defined(Cond, Callable).
 2166included_if_defined((A,B), Callable) :-
 2167    !,
 2168    included_if_defined(A, Callable),
 2169    included_if_defined(B, Callable).
 2170included_if_defined((A;B), Callable) :-
 2171    !,
 2172    (   included_if_defined(A, Callable)
 2173    ;   included_if_defined(B, Callable)
 2174    ).
 2175
 2176
 2177                 /*******************************
 2178                 *    IMPORT/EXPORT HEADERS     *
 2179                 *******************************/
 file_imports(+File, -Imports)
Determine which modules must be imported into this one. It considers all called predicates that are not covered by system predicates. Next, we have three sources to resolve the remaining predicates, which are tried in the order below. The latter two is dubious.

We first resolve all imports to absolute files. Localizing is done afterwards. Imports is a list of

! use_module(FileSpec, Callables)

 2198xref_file_imports(FileSpec, Imports) :-
 2199    canonical_filename(FileSpec, File),
 2200    findall(Called, called_no_builtin(File, Called), Resolve0),
 2201    resolve_old_imports(Resolve0, File, Resolve1, Imports0),
 2202    find_new_imports(Resolve1, File, Imports1),
 2203    disambiguate_imports(Imports1, File, Imports2),
 2204    flatten([Imports0, Imports2], ImportList),
 2205    keysort(ImportList, SortedByFile),
 2206    merge_by_key(SortedByFile, ImportsByFile),
 2207    maplist(make_import(File), ImportsByFile, Imports).
 2208
 2209canonical_filename(FileSpec, File) :-
 2210    absolute_file_name(FileSpec,
 2211                       [ file_type(prolog),
 2212                         access(read),
 2213                         file_errors(fail)
 2214                       ],
 2215                       File).
 2216
 2217called_no_builtin(File, Callable) :-
 2218    xref_called(File, Callable),
 2219    \+ defined(File, Callable),
 2220    \+ built_in_predicate(Callable).
 2221
 2222resolve_old_imports([], _, [], []).
 2223resolve_old_imports([H|T0], File, UnRes, [From-H|T]) :-
 2224    xref_defined(File, H, imported(From)),
 2225    !,
 2226    resolve_old_imports(T0, File, UnRes, T).
 2227resolve_old_imports([H|T0], File, [H|UnRes], Imports) :-
 2228    resolve_old_imports(T0, File, UnRes, Imports).
 2229
 2230find_new_imports([], _, []).
 2231find_new_imports([H|T0], File, [FL-H|T]) :-
 2232    findall(F, resolve(H, F), FL0),
 2233    sort(FL0, FL),
 2234    find_new_imports(T0, File, T).
 2235
 2236disambiguate_imports(Imports0, File, Imports) :-
 2237    ambiguous_imports(Imports0, Ambig, UnAmbig, _Undef),
 2238    (   Ambig == []
 2239    ->  Imports = UnAmbig
 2240    ;   new(D, xref_disambiguate_import_dialog(File, Ambig)),
 2241        get(D, confirm_centered, Result),
 2242        (   Result == ok
 2243        ->  get(D, result, List),
 2244            send(D, destroy),
 2245            append(UnAmbig, List, Imports)
 2246        )
 2247    ).
 2248
 2249ambiguous_imports([], [], [], []).
 2250ambiguous_imports([[]-C|T0], Ambig, UnAmbig, [C|T]) :-
 2251    !,
 2252    ambiguous_imports(T0, Ambig, UnAmbig, T).
 2253ambiguous_imports([[F]-C|T0], Ambig, [F-C|T], Undef) :-
 2254    !,
 2255    ambiguous_imports(T0, Ambig, T, Undef).
 2256ambiguous_imports([A-C|T0], [A-C|T], UnAmbig, Undef) :-
 2257    is_list(A),
 2258    !,
 2259    ambiguous_imports(T0, T, UnAmbig, Undef).
 resolve(+Callable, -File)
Try to find files from which to resolve Callable.
 2266resolve(Callable, File) :-              % Export from module files
 2267    xref_exported(File, Callable),
 2268    atom(File).
 2269resolve(Callable, File) :-              % Non-module files
 2270    defined(File, Callable),
 2271    atom(File),
 2272    \+ xref_module(File, _).
 2273resolve(Callable, File) :-              % The Prolog autoload library
 2274    autoload_predicate(Callable, File).
 merge_by_key(+KeyedList, -ListOfKeyValues) is det
Example: [a-x, a-y, b-z] --> [a-[x,y], b-[z]]
 2281merge_by_key([], []).
 2282merge_by_key([K-V|T0], [K-[V|Vs]|T]) :-
 2283    same_key(K, T0, Vs, T1),
 2284    merge_by_key(T1, T).
 2285
 2286same_key(K, [K-V|T0], [V|VT], T) :-
 2287    !,
 2288    same_key(K, T0, VT, T).
 2289same_key(_, L, [], L).
 make_import(+RefFile, +ImportList, -UseModules)
Glues it all together to make a list of directives.
 2296make_import(RefFile, File-Imports, (:-use_module(ShortPath, PIs))) :-
 2297    local_filename(File, RefFile, ShortPath),
 2298    sort_callables(Imports, SortedImports),
 2299    maplist(predicate_indicator, SortedImports, PIs).
 2300
 2301local_filename(File, RefFile, ShortPath) :-
 2302    atom(RefFile),
 2303    file_directory_name(File, Dir),
 2304    file_directory_name(RefFile, Dir),     % i.e. same dir
 2305    !,
 2306    file_base_name(File, Base),
 2307    remove_extension(Base, ShortPath).
 2308local_filename(File, _RefFile, ShortPath) :-
 2309    file_name_on_path(File, ShortPath0),
 2310    remove_extension(ShortPath0, ShortPath).
 2311
 2312
 2313remove_extension(Term0, Term) :-
 2314    Term0 =.. [Alias,ShortPath0],
 2315    file_name_extension(ShortPath, pl, ShortPath0),
 2316    !,
 2317    Term  =.. [Alias,ShortPath].
 2318remove_extension(ShortPath0, ShortPath) :-
 2319    atom(ShortPath0),
 2320    file_name_extension(ShortPath, pl, ShortPath0),
 2321    !.
 2322remove_extension(Path, Path).
 2323
 2324:- pce_begin_class(xref_disambiguate_import_dialog, auto_sized_dialog,
 2325                   "Prompt for alternative sources").
 2326
 2327initialise(D, File:name, Ambig:prolog) :->
 2328    send_super(D, initialise, string('Disambiguate calls for %s', File)),
 2329    forall(member(Files-Callable, Ambig),
 2330           send(D, append_row, File, Callable, Files)),
 2331    send(D, append, button(ok)),
 2332    send(D, append, button(cancel)).
 2333
 2334append_row(D, File:name, Callable:prolog, Files:prolog) :->
 2335    send(D, append, xref_predicate_text(Callable, @default, File)),
 2336    send(D, append, new(FM, menu(file, cycle)), right),
 2337    send(FM, append, menu_item(@nil, @default, '-- Select --')),
 2338    forall(member(Path, Files),
 2339           (   file_name_on_path(Path, ShortId),
 2340               short_file_name_to_atom(ShortId, Label),
 2341               send(FM, append, menu_item(Path, @default, Label))
 2342           )).
 2343
 2344result(D, Disam:prolog) :<-
 2345    "Get disambiguated files"::
 2346    get_chain(D, graphicals, Grs),
 2347    selected_files(Grs, Disam).
 2348
 2349selected_files([], []).
 2350selected_files([PreText,Menu|T0], [File-Callable|T]) :-
 2351    send(PreText, instance_of, xref_predicate_text),
 2352    send(Menu, instance_of, menu),
 2353    get(Menu, selection, File),
 2354    atom(File),
 2355    !,
 2356    get(PreText, callable, Callable),
 2357    selected_files(T0, T).
 2358selected_files([_|T0], T) :-
 2359    selected_files(T0, T).
 2360
 2361
 2362ok(D) :->
 2363    send(D, return, ok).
 2364
 2365cancel(D) :->
 2366    send(D, destroy).
 2367
 2368:- pce_end_class(xref_disambiguate_import_dialog).
 xref_file_exports(+File, -Exports)
Produce the export-header for non-module files. Fails if the file is already a module file.
 2375xref_file_exports(FileSpec, (:- module(Module, Exports))) :-
 2376    canonical_filename(FileSpec, File),
 2377    \+ xref_module(File, _),
 2378    findall(C, export_link_1(File, _, C), Cs),
 2379    sort_callables(Cs, Sorted),
 2380    file_base_name(File, Base),
 2381    file_name_extension(Module, _, Base),
 2382    maplist(predicate_indicator, Sorted, Exports)