View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           https://www.swi-prolog.org
    6    Copyright (c)  1998-2026, University of Amsterdam
    7                              VU University Amsterdam
    8                              SWI-Prolog Solutions b.v.
    9    All rights reserved.
   10
   11    Redistribution and use in source and binary forms, with or without
   12    modification, are permitted provided that the following conditions
   13    are met:
   14
   15    1. Redistributions of source code must retain the above copyright
   16       notice, this list of conditions and the following disclaimer.
   17
   18    2. Redistributions in binary form must reproduce the above copyright
   19       notice, this list of conditions and the following disclaimer in
   20       the documentation and/or other materials provided with the
   21       distribution.
   22
   23    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   24    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   25    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   26    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   27    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   28    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   29    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   30    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   31    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   33    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   34    POSSIBILITY OF SUCH DAMAGE.
   35*/
   36
   37:- module(prolog_edit,
   38          [ edit/1,                     % +Spec
   39            edit/0
   40          ]).   41:- autoload(library(lists), [member/2, append/3, select/3, append/2]).   42:- autoload(library(make), [make/0]).   43:- autoload(library(prolog_breakpoints), [breakpoint_property/2]).   44:- autoload(library(apply), [foldl/5, maplist/3, maplist/2]).   45:- use_module(library(dcg/high_order), [sequence/5]).   46:- autoload(library(readutil), [read_line_to_string/2]).   47:- autoload(library(dcg/basics), [string/3, integer/3, remainder/3]).   48:- autoload(library(solution_sequences), [distinct/2]).   49
   50
   51% :- set_prolog_flag(generate_debug_info, false).

Editor interface

This module implements the generic editor interface. It consists of two extensible parts with little in between. The first part deals with translating the input into source-location, and the second with starting an editor. */

   61:- multifile
   62    locate/3,                       % +Partial, -FullSpec, -Location
   63    locate/2,                       % +FullSpec, -Location
   64    select_location/3,              % +Pairs, +Spec, -Location
   65    exists_location/1,              % +Location
   66    user_select/2,                  % +Max, -I
   67    edit_source/1,                  % +Location
   68    edit_command/2,                 % +Editor, -Command
   69    load/0.                         % provides load-hooks
   70
   71:- public
   72    locations/2,                    % +Spec, -Locations
   73    predicate_location/2,           % :Pred, -Location
   74    addr2location/3.                % +Address, -File, -Line
 edit(+Spec)
Edit indicated object.
   80edit(Spec) :-
   81    notrace(edit_no_trace(Spec)).
   82
   83edit_no_trace(Spec) :-
   84    var(Spec),
   85    !,
   86    throw(error(instantiation_error, _)).
   87edit_no_trace(Spec) :-
   88    locations(Spec, Pairs),
   89    do_select_location(Pairs, Spec, Location),
   90    do_edit_source(Location).
 edit
Edit associated or script file. This is the Prolog file opened by double-clicking or the file loaded using
% swipl [-s] file.pl
  101edit :-
  102    current_prolog_flag(associated_file, File),
  103    !,
  104    edit(file(File)).
  105edit :-
  106    '$cmd_option_val'(script_file, OsFiles),
  107    OsFiles = [OsFile],
  108    !,
  109    prolog_to_os_filename(File, OsFile),
  110    edit(file(File)).
  111edit :-
  112    throw(error(context_error(edit, no_default_file), _)).
 locations(+Spec, -Locations) is det
Locate entities matching Spec. Locations is a list of pairs Location-FullSpec, where Location is a dict holding a file, optional line and optional linepos keys. FullSpec is the disambiguated specification, e.g., member expands to member/2 for the predicate.
  122locations(Spec, Locations) :-
  123    load_extensions,
  124    findall(Location-FullSpec,
  125            locate(Spec, FullSpec, Location),
  126            Pairs0),
  127    sort(Pairs0, Pairs1),
  128    merge_locations(Pairs1, Locations).
  129
  130
  131                 /*******************************
  132                 *            LOCATE            *
  133                 *******************************/
 locate(+Spec, -FullSpec, -Location:dict)
  137locate(FileSpec:Line, file(Path, line(Line)), #{file:Path, line:Line}) :-
  138    integer(Line), Line >= 1,
  139    ground(FileSpec),                      % so specific; do not try alts
  140    !,
  141    locate(FileSpec, _, #{file:Path}).
  142locate(FileSpec:Line:LinePos,
  143       file(Path, line(Line), linepos(LinePos)),
  144       #{file:Path, line:Line, linepos:LinePos}) :-
  145    integer(Line), Line >= 1,
  146    integer(LinePos), LinePos >= 1,
  147    ground(FileSpec),                      % so specific; do not try alts
  148    !,
  149    locate(FileSpec, _, #{file:Path}).
  150locate(Path, file(Path), #{file:Path}) :-
  151    atom(Path),
  152    exists_file(Path).
  153locate(Pattern, file(Path), #{file:Path}) :-
  154    atom(Pattern),
  155    catch(expand_file_name(Pattern, Files), error(_,_), fail),
  156    member(Path, Files),
  157    exists_file(Path).
  158locate(FileBase, file(File), #{file:File}) :-
  159    atom(FileBase),
  160    find_source(FileBase, File).
  161locate(FileSpec, file(File), #{file:File}) :-
  162    is_file_search_spec(FileSpec),
  163    find_source(FileSpec, File).
  164locate(FileBase, source_file(Path),  #{file:Path}) :-
  165    atom(FileBase),
  166    source_file(Path),
  167    file_base_name(Path, File),
  168    (   File == FileBase
  169    ->  true
  170    ;   file_name_extension(FileBase, _, File)
  171    ).
  172locate(FileBase, include_file(Path),  #{file:Path}) :-
  173    atom(FileBase),
  174    setof(Path, include_file(Path), Paths),
  175    member(Path, Paths),
  176    file_base_name(Path, File),
  177    (   File == FileBase
  178    ->  true
  179    ;   file_name_extension(FileBase, _, File)
  180    ).
  181locate(Name, FullSpec, Location) :-
  182    atom(Name),
  183    locate(Name/_, FullSpec, Location).
  184locate(Name/Arity, Module:Name/Arity, Location) :-
  185    locate(Module:Name/Arity, Location).
  186locate(Name//DCGArity, FullSpec, Location) :-
  187    (   integer(DCGArity)
  188    ->  Arity is DCGArity+2,
  189        locate(Name/Arity, FullSpec, Location)
  190    ;   locate(Name/_, FullSpec, Location) % demand arity >= 2
  191    ).
  192locate(Name/Arity, library(File),  #{file:PlPath}) :-
  193    atom(Name),
  194    '$in_library'(Name, Arity, Path),
  195    (   absolute_file_name(library(.), Dir,
  196                           [ file_type(directory),
  197                             solutions(all)
  198                           ]),
  199        atom_concat(Dir, File0, Path),
  200        atom_concat(/, File, File0)
  201    ->  find_source(Path, PlPath)
  202    ;   fail
  203    ).
  204locate(Module:Name, Module:Name/Arity, Location) :-
  205    locate(Module:Name/Arity, Location).
  206locate(Module:Head, Module:Name/Arity, Location) :-
  207    callable(Head),
  208    \+ ( Head = (PName/_),
  209         atom(PName)
  210       ),
  211    functor(Head, Name, Arity),
  212    locate(Module:Name/Arity, Location).
  213locate(Spec, module(Spec), Location) :-
  214    locate(module(Spec), Location).
  215locate(Spec, Spec, Location) :-
  216    locate(Spec, Location).
  217
  218include_file(Path) :-
  219    source_file_property(Path, included_in(_,_)).
 is_file_search_spec(@Spec) is semidet
True if Spec is valid pattern for absolute_file_name/3.
  225is_file_search_spec(Spec) :-
  226    compound(Spec),
  227    compound_name_arguments(Spec, Alias, [Arg]),
  228    is_file_spec(Arg),
  229    user:file_search_path(Alias, _),
  230    !.
  231
  232is_file_spec(Name), atom(Name) => true.
  233is_file_spec(Name), string(Name) => true.
  234is_file_spec(Term), cyclic_term(Term) => fail.
  235is_file_spec(A/B) => is_file_spec(A), is_file_spec(B).
  236is_file_spec(_) => fail.
 find_source(++FileSpec, =File) is semidet
Find a source file from FileSpec. If FileSpec resolves to a .qlf file, File is the embedded `.pl` file (which may not exist).
  243find_source(FileSpec, File) :-
  244    catch(absolute_file_name(FileSpec, File0,
  245                             [ file_type(prolog),
  246                               access(read),
  247                               file_errors(fail)
  248                             ]),
  249          error(_,_), fail),
  250    prolog_source(File0, File).
  251
  252prolog_source(File0, File) :-
  253    file_name_extension(_, Ext, File0),
  254    user:prolog_file_type(Ext, qlf),
  255    !,
  256    '$qlf_module'(File0, Info),
  257    File = Info.get(file).
  258prolog_source(File, File).
 locate(+Spec, -Location)
Locate object from the specified location.
  265locate(file(File, line(Line)), #{file:File, line:Line}).
  266locate(file(File), #{file:File}).
  267locate(Module:Name/Arity, Location) :-
  268    (   atom(Name), integer(Arity)
  269    ->  functor(Head, Name, Arity)
  270    ;   Head = _                    % leave unbound
  271    ),
  272    (   (   var(Module)
  273        ;   var(Name)
  274        )
  275    ->  NonImport = true
  276    ;   NonImport = false
  277    ),
  278    current_predicate(Name, Module:Head),
  279    \+ (   NonImport == true,
  280           Module \== system,
  281           predicate_property(Module:Head, imported_from(_))
  282       ),
  283    functor(Head, Name, Arity),     % bind arity
  284    predicate_location(Module:Head, Location).
  285locate(module(Module), Location) :-
  286    atom(Module),
  287    module_property(Module, file(Path)),
  288    (   module_property(Module, line_count(Line))
  289    ->  Location = #{file:Path, line:Line}
  290    ;   Location = #{file:Path}
  291    ).
  292locate(breakpoint(Id), Location) :-
  293    integer(Id),
  294    breakpoint_property(Id, clause(Ref)),
  295    (   breakpoint_property(Id, file(File)),
  296        breakpoint_property(Id, line_count(Line))
  297    ->  Location =  #{file:File, line:Line}
  298    ;   locate(clause(Ref), Location)
  299    ).
  300locate(clause(Ref), #{file:File, line:Line}) :-
  301    clause_property(Ref, file(File)),
  302    clause_property(Ref, line_count(Line)).
  303locate(clause(Ref, _PC), #{file:File, line:Line}) :- % TBD: use clause
  304    clause_property(Ref, file(File)),
  305    clause_property(Ref, line_count(Line)).
 predicate_location(:Predicate, -Location) is nondet
Find the source location of a predicate.
Arguments:
Predicate- is a qualified head. The module may be unbound at entry. It will be bound to the actual implementation module.
  314predicate_location(Pred, #{file:File, line:Line}) :-
  315    copy_term(Pred, Pred2),
  316    distinct(Primary, primary_predicate(Pred2, Primary)),
  317    ignore(Pred = Primary),
  318    (   predicate_property(Primary, file(File)),
  319        predicate_property(Primary, line_count(Line))
  320    ->  true
  321    ;   '$foreign_predicate_source'(Primary, Source),
  322        string_codes(Source, Codes),
  323        phrase(addr2line_output(File, Line), Codes)
  324    ).
  325
  326primary_predicate(Pred, Primary) :-
  327    (   predicate_property(Pred, imported_from(Source))
  328    ->  strip_module(Pred, _, Head),
  329        Primary = Source:Head
  330    ;   Primary = Pred
  331    ).
 addr2location(+Address, -File, -Line) is semidet
Get the File and Line for a C address.
  338addr2location(Address, File, Line) :-
  339    '$addr2line'(Address, Source),
  340    string_codes(Source, Codes),
  341    phrase(addr2line_output(File, Line), Codes).
 addr2line_output(-File, -Line)// is semidet
Process the output of the addr2line utility. This implementation works for Linux. Additional lines may be needed for other environments.
  349addr2line_output(File, Line) -->
  350    string(_), " at ", string(FileCodes), ":", integer(Line),
  351    !,
  352    remainder(_),
  353    { atom_codes(File, FileCodes) }.
  354
  355
  356                 /*******************************
  357                 *             EDIT             *
  358                 *******************************/
 do_edit_source(+Location)
Actually call the editor to edit Location, a list of Name(Value) that contains file(File) and may contain line(Line). First the multifile hook edit_source/1 is called. If this fails the system checks for XPCE and the prolog-flag editor. If the latter is built_in or pce_emacs, it will start PceEmacs.

Finally, it will get the editor to use from the prolog-flag editor and use edit_command/2 to determine how this editor should be called.

  372do_edit_source(Location) :-             % hook
  373    edit_source(Location),
  374    !.
  375do_edit_source(Location) :-             % PceEmacs
  376    current_prolog_flag(editor, Editor),
  377    is_pceemacs(Editor),
  378    current_prolog_flag(gui, true),
  379    !,
  380    location_url(Location, URL),        % File[:Line[:LinePos]]
  381    run_pce_emacs(URL).
  382do_edit_source(Location) :-             % External editor
  383    external_edit_command(Location, Command),
  384    print_message(informational, edit(waiting_for_editor)),
  385    (   catch(shell(Command), E,
  386              (print_message(warning, E),
  387               fail))
  388    ->  print_message(informational, edit(make)),
  389        make
  390    ;   print_message(informational, edit(canceled))
  391    ).
  392
  393external_edit_command(Location, Command) :-
  394    #{file:File, line:Line} :< Location,
  395    editor(Editor),
  396    file_base_name(Editor, EditorFile),
  397    file_name_extension(Base, _, EditorFile),
  398    edit_command(Base, Cmd),
  399    prolog_to_os_filename(File, OsFile),
  400    atom_codes(Cmd, S0),
  401    substitute('%e', Editor, S0, S1),
  402    substitute('%f', OsFile, S1, S2),
  403    substitute('%d', Line,   S2, S),
  404    !,
  405    atom_codes(Command, S).
  406external_edit_command(Location, Command) :-
  407    #{file:File} :< Location,
  408    editor(Editor),
  409    file_base_name(Editor, EditorFile),
  410    file_name_extension(Base, _, EditorFile),
  411    edit_command(Base, Cmd),
  412    prolog_to_os_filename(File, OsFile),
  413    atom_codes(Cmd, S0),
  414    substitute('%e', Editor, S0, S1),
  415    substitute('%f', OsFile, S1, S),
  416    \+ substitute('%d', 1, S, _),
  417    !,
  418    atom_codes(Command, S).
  419external_edit_command(Location, Command) :-
  420    #{file:File} :< Location,
  421    editor(Editor),
  422    format(string(Command), '"~w" "~w"', [Editor, File]).
  423
  424is_pceemacs(pce_emacs).
  425is_pceemacs(built_in).
 run_pce_emacs(+URL) is semidet
Dynamically load and run emacs/1.
  431run_pce_emacs(URL) :-
  432    autoload_call(in_pce_thread(autoload_call(emacs(URL)))).
 editor(-Editor)
Determine the external editor to run.
  438editor(Editor) :-                       % $EDITOR
  439    current_prolog_flag(editor, Editor),
  440    (   sub_atom(Editor, 0, _, _, $)
  441    ->  sub_atom(Editor, 1, _, 0, Var),
  442        catch(getenv(Var, Editor), _, fail), !
  443    ;   Editor == default
  444    ->  catch(getenv('EDITOR', Editor), _, fail), !
  445    ;   \+ is_pceemacs(Editor)
  446    ->  !
  447    ).
  448editor(Editor) :-                       % User defaults
  449    getenv('EDITOR', Editor),
  450    !.
  451editor(vi) :-                           % Platform defaults
  452    current_prolog_flag(unix, true),
  453    !.
  454editor(notepad) :-
  455    current_prolog_flag(windows, true),
  456    !.
  457editor(_) :-                            % No luck
  458    throw(error(existence_error(editor), _)).
 edit_command(+Editor, -Command)
This predicate should specify the shell-command called to invoke the user's editor. The following substitutions will be made:
%ePath name of the editor
%fPath name of the file to be edited
%dLine number of the target
  470edit_command(vi,          '%e +%d \'%f\'').
  471edit_command(vi,          '%e \'%f\'').
  472edit_command(emacs,       '%e +%d \'%f\'').
  473edit_command(emacs,       '%e \'%f\'').
  474edit_command(notepad,     '"%e" "%f"').
  475edit_command(wordpad,     '"%e" "%f"').
  476edit_command(uedit32,     '%e "%f/%d/0"').      % ultraedit (www.ultraedit.com)
  477edit_command(jedit,       '%e -wait \'%f\' +line:%d').
  478edit_command(jedit,       '%e -wait \'%f\'').
  479edit_command(edit,        '%e %f:%d').          % PceEmacs client script
  480edit_command(edit,        '%e %f').
  481
  482edit_command(emacsclient, Command) :- edit_command(emacs, Command).
  483edit_command(vim,         Command) :- edit_command(vi,    Command).
  484edit_command(nvim,        Command) :- edit_command(vi,    Command).
  485
  486substitute(FromAtom, ToAtom, Old, New) :-
  487    atom_codes(FromAtom, From),
  488    (   atom(ToAtom)
  489    ->  atom_codes(ToAtom, To)
  490    ;   number_codes(ToAtom, To)
  491    ),
  492    append(Pre, S0, Old),
  493    append(From, Post, S0) ->
  494    append(Pre, To, S1),
  495    append(S1, Post, New),
  496    !.
  497substitute(_, _, Old, Old).
  498
  499
  500                 /*******************************
  501                 *            SELECT            *
  502                 *******************************/
  503
  504merge_locations(Locations0, Locations) :-
  505    append(Before, [L1|Rest], Locations0),
  506    select(L2, Rest, Rest1),
  507    merge_location(L1, L2, Loc),
  508    !,
  509    append([Before, [Loc], Rest1], Locations1),
  510    merge_locations(Locations1, Locations).
  511merge_locations(Locations, Locations).
  512
  513merge_location(Loc1-Spec1, Loc2-Spec2, Loc1-Spec1) :-
  514    same_file_location(Loc1,Loc2),
  515    better_spec(Spec1, Spec2).
  516merge_location(Loc1-Spec1, Loc2-Spec2, Loc-Spec) :-
  517    same_location(Loc1, Loc2, Loc),
  518    merge_specs(Spec1, Spec2, Spec).
  519
  520same_file_location(L1, L2) :-
  521    #{file:File} :< L1,
  522    #{file:File} :< L2.
  523
  524same_location(L, L, L).
  525same_location(#{file:F1}, #{file:F2}, #{file:F}) :-
  526    best_same_file(F1, F2, F).
  527same_location(#{file:F1, line:Line}, #{file:F2}, #{file:F, line:Line}) :-
  528    best_same_file(F1, F2, F).
  529same_location(#{file:F1}, #{file:F2, line:Line}, #{file:F, line:Line}) :-
  530    best_same_file(F1, F2, F).
  531
  532best_same_file(F1, F2, F) :-
  533    catch(same_file(F1, F2), _, fail),
  534    !,
  535    atom_length(F1, L1),
  536    atom_length(F2, L2),
  537    (   L1 < L2
  538    ->  F = F1
  539    ;   F = F2
  540    ).
  541
  542merge_specs(Spec, Spec, Spec) :-
  543    !.
  544merge_specs(file(F1), file(F2), file(F)) :-
  545    best_same_file(F1, F2, F),
  546    !.
  547merge_specs(Spec1, Spec2, Spec) :-
  548    merge_specs_(Spec1, Spec2, Spec),
  549    !.
  550merge_specs(Spec1, Spec2, Spec) :-
  551    merge_specs_(Spec2, Spec1, Spec),
  552    !.
  553
  554merge_specs_(FileSpec, Spec, Spec) :-
  555    is_filespec(FileSpec).
  556
  557is_filespec(file(_)) => true.
  558is_filespec(source_file(_)) => true.
  559is_filespec(Term),
  560    compound(Term),
  561    compound_name_arguments(Term, Alias, [_Arg]),
  562    user:file_search_path(Alias, _) => true.
  563is_filespec(_) =>
  564    fail.
  565
  566better_spec(class(_), module(_)).
  567better_spec(_, FileSpec) :-
  568    is_filespec(FileSpec).
 select_location(+Pairs, +UserSpec, -Location) is semidet
Arguments:
Pairs- is a list of Location-Spec pairs
Location- is a list of properties
  575do_select_location(Pairs, Spec, Location) :-
  576    select_location(Pairs, Spec, Location),                % HOOK
  577    !,
  578    Location \== [].
  579do_select_location([], Spec, _) :-
  580    !,
  581    print_message(warning, edit(not_found(Spec))),
  582    fail.
  583do_select_location([#{file:File}-file(File)], _, Location) :-
  584    !,
  585    Location = #{file:File}.
  586do_select_location([Location-_Spec], _, Location) :-
  587    existing_location(Location),
  588    !.
  589do_select_location(Pairs, _, Location) :-
  590    foldl(number_location, Pairs, NPairs, 1, End),
  591    print_message(help, edit(select(NPairs))),
  592    (   End == 1
  593    ->  fail
  594    ;   Max is End - 1,
  595        user_selection(Max, I),
  596        memberchk(I-(Location-_Spec), NPairs)
  597    ).
 existing_location(+Location) is semidet
True when Location can be edited. By default that means that the file exists. This facility is hooked to allow for alternative ways to reach the source, e.g., by lazily downloading it.
  605existing_location(Location) :-
  606    exists_location(Location),
  607    !.
  608existing_location(Location) :-
  609    #{file:File} :< Location,
  610    access_file(File, read).
  611
  612number_location(Pair, N-Pair, N, N1) :-
  613    Pair = Location-_Spec,
  614    existing_location(Location),
  615    !,
  616    N1 is N+1.
  617number_location(Pair, 0-Pair, N, N).
  618
  619user_selection(Max, I) :-
  620    user_select(Max, I),
  621    !.
  622user_selection(Max, I) :-
  623    print_message(help, edit(choose(Max))),
  624    read_number(Max, I).
 read_number(+Max, -X) is semidet
Read a number between 1 and Max. If Max < 10, use get_single_char/1.
  630read_number(Max, X) :-
  631    Max < 10,
  632    !,
  633    get_single_char(C),
  634    put_code(user_error, C),
  635    between(0'0, 0'9, C),
  636    X is C - 0'0.
  637read_number(_, X) :-
  638    read_line_to_string(user_input, String),
  639    number_string(X, String).
  640
  641
  642                 /*******************************
  643                 *             MESSAGES         *
  644                 *******************************/
  645
  646:- multifile
  647    prolog:message/3.  648
  649prolog:message(edit(Msg)) -->
  650    message(Msg).
  651
  652message(not_found(Spec)) -->
  653    [ 'Cannot find anything to edit from "~p"'-[Spec] ],
  654    (   { atom(Spec) }
  655    ->  [ nl, '    Use edit(file(~q)) to create a new file'-[Spec] ]
  656    ;   []
  657    ).
  658message(select(NPairs)) -->
  659    { \+ (member(N-_, NPairs), N > 0) },
  660    !,
  661    [ 'Found the following locations:', nl ],
  662    sequence(target, [nl], NPairs).
  663message(select(NPairs)) -->
  664    [ 'Please select item to edit:', nl ],
  665    sequence(target, [nl], NPairs).
  666message(choose(_Max)) -->
  667    [ nl, 'Your choice? ', flush ].
  668message(waiting_for_editor) -->
  669    [ 'Waiting for editor ... ', flush ].
  670message(make) -->
  671    [ 'Running make to reload modified files' ].
  672message(canceled) -->
  673    [ 'Editor returned failure; skipped make/0 to reload files' ].
  674
  675target(0-(Location-Spec)) ==>
  676    [ ansi(warning, '~t*~3| ', [])],
  677    edit_specifier(Spec),
  678    [ '~t~32|' ],
  679    edit_location(Location, false),
  680    [ ansi(warning, ' (no source available)', [])].
  681target(N-(Location-Spec)) ==>
  682    [ ansi(bold, '~t~d~3| ', [N])],
  683    edit_specifier(Spec),
  684    [ '~t~32|' ],
  685    edit_location(Location, true).
  686
  687edit_specifier(Module:Name/Arity) ==>
  688    [ '~w:'-[Module],
  689      ansi(code, '~w/~w', [Name, Arity]) ].
  690edit_specifier(file(_Path)) ==>
  691    [ '<file>' ].
  692edit_specifier(source_file(_Path)) ==>
  693    [ '<loaded file>' ].
  694edit_specifier(include_file(_Path)) ==>
  695    [ '<included file>' ].
  696edit_specifier(Term) ==>
  697    [ '~p'-[Term] ].
  698
  699edit_location(Location, false) ==>
  700    { location_label(Location, Label) },
  701    [ ansi(warning, '~s', [Label]) ].
  702edit_location(Location, true) ==>
  703    { location_label(Location, Label),
  704      location_url(Location, URL)
  705    },
  706    [ url(URL, Label) ].
  707
  708location_label(Location, Label) :-
  709    #{file:File, line:Line} :< Location,
  710    !,
  711    short_filename(File, ShortFile),
  712    format(string(Label), '~w:~d', [ShortFile, Line]).
  713location_label(Location, Label) :-
  714    #{file:File} :< Location,
  715    !,
  716    short_filename(File, ShortFile),
  717    format(string(Label), '~w', [ShortFile]).
  718
  719location_url(Location, File:Line:LinePos) :-
  720    #{file:File, line:Line, linepos:LinePos} :< Location,
  721    !.
  722location_url(Location, File:Line) :-
  723    #{file:File, line:Line} :< Location,
  724    !.
  725location_url(Location, File) :-
  726    #{file:File} :< Location.
 short_filename(+Path, -Spec) is det
Spec is a way to refer to the file Path that is shorter. The path is shortened by either taking it relative to the current working directory or use one of the Prolog path aliases.
  734short_filename(Path, Spec) :-
  735    working_directory(Here, Here),
  736    atom_concat(Here, Local0, Path),
  737    !,
  738    remove_leading_slash(Local0, Spec).
  739short_filename(Path, Spec) :-
  740    findall(LenAlias, aliased_path(Path, LenAlias), Keyed),
  741    keysort(Keyed, [_-Spec|_]).
  742short_filename(Path, Path).
  743
  744aliased_path(Path, Len-Spec) :-
  745    setof(Alias, file_alias_path(Alias), Aliases),
  746    member(Alias, Aliases),
  747    Alias \== autoload,             % confusing and covered by something else
  748    Term =.. [Alias, '.'],
  749    absolute_file_name(Term, Prefix,
  750                       [ file_type(directory),
  751                         file_errors(fail),
  752                         solutions(all)
  753                       ]),
  754    atom_concat(Prefix, Local0, Path),
  755    remove_leading_slash(Local0, Local1),
  756    remove_extension(Local1, Local2),
  757    unquote_segments(Local2, Local),
  758    atom_length(Local2, Len),
  759    Spec =.. [Alias, Local].
  760
  761file_alias_path(Alias) :-
  762    user:file_search_path(Alias, _).
  763
  764remove_leading_slash(Path, Local) :-
  765    atom_concat(/, Local, Path),
  766    !.
  767remove_leading_slash(Path, Path).
  768
  769remove_extension(File0, File) :-
  770    file_name_extension(File, Ext, File0),
  771    user:prolog_file_type(Ext, source),
  772    !.
  773remove_extension(File, File).
  774
  775unquote_segments(File, Segments) :-
  776    split_string(File, "/", "/", SegmentStrings),
  777    maplist(atom_string, SegmentList, SegmentStrings),
  778    maplist(no_quote_needed, SegmentList),
  779    !,
  780    segments(SegmentList, Segments).
  781unquote_segments(File, File).
  782
  783
  784no_quote_needed(A) :-
  785    format(atom(Q), '~q', [A]),
  786    Q == A.
  787
  788segments([Segment], Segment) :-
  789    !.
  790segments(List, A/Segment) :-
  791    append(L1, [Segment], List),
  792    !,
  793    segments(L1, A).
  794
  795
  796                 /*******************************
  797                 *        LOAD EXTENSIONS       *
  798                 *******************************/
  799
  800load_extensions :-
  801    load,
  802    fail.
  803load_extensions.
  804
  805:- load_extensions.