1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 1998-2025, 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).
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 predicate_location/2. % :Pred, -Location
78edit(Spec) :- 79 notrace(edit_no_trace(Spec)). 80 81edit_no_trace(Spec) :- 82 var(Spec), 83 !, 84 throw(error(instantiation_error, _)). 85edit_no_trace(Spec) :- 86 load_extensions, 87 findall(Location-FullSpec, 88 locate(Spec, FullSpec, Location), 89 Pairs0), 90 sort(Pairs0, Pairs1), 91 merge_locations(Pairs1, Pairs), 92 do_select_location(Pairs, Spec, Location), 93 do_edit_source(Location).
% swipl [-s] file.pl
104edit :- 105 current_prolog_flag(associated_file, File), 106 !, 107 edit(file(File)). 108edit :- 109 '$cmd_option_val'(script_file, OsFiles), 110 OsFiles = [OsFile], 111 !, 112 prolog_to_os_filename(File, OsFile), 113 edit(file(File)). 114edit :- 115 throw(error(context_error(edit, no_default_file), _)). 116 117 118 /******************************* 119 * LOCATE * 120 *******************************/
124locate(FileSpec:Line, file(Path, line(Line)), #{file:Path, line:Line}) :- 125 integer(Line), Line >= 1, 126 ground(FileSpec), % so specific; do not try alts 127 !, 128 locate(FileSpec, _, #{file:Path}). 129locate(FileSpec:Line:LinePos, 130 file(Path, line(Line), linepos(LinePos)), 131 #{file:Path, line:Line, linepos:LinePos}) :- 132 integer(Line), Line >= 1, 133 integer(LinePos), LinePos >= 1, 134 ground(FileSpec), % so specific; do not try alts 135 !, 136 locate(FileSpec, _, #{file:Path}). 137locate(Path, file(Path), #{file:Path}) :- 138 atom(Path), 139 exists_file(Path). 140locate(Pattern, file(Path), #{file:Path}) :- 141 atom(Pattern), 142 catch(expand_file_name(Pattern, Files), error(_,_), fail), 143 member(Path, Files), 144 exists_file(Path). 145locate(FileBase, file(File), #{file:File}) :- 146 atom(FileBase), 147 find_source(FileBase, File). 148locate(FileSpec, file(File), #{file:File}) :- 149 is_file_search_spec(FileSpec), 150 find_source(FileSpec, File). 151locate(FileBase, source_file(Path), #{file:Path}) :- 152 atom(FileBase), 153 source_file(Path), 154 file_base_name(Path, File), 155 ( File == FileBase 156 -> true 157 ; file_name_extension(FileBase, _, File) 158 ). 159locate(FileBase, include_file(Path), #{file:Path}) :- 160 atom(FileBase), 161 setof(Path, include_file(Path), Paths), 162 member(Path, Paths), 163 file_base_name(Path, File), 164 ( File == FileBase 165 -> true 166 ; file_name_extension(FileBase, _, File) 167 ). 168locate(Name, FullSpec, Location) :- 169 atom(Name), 170 locate(Name/_, FullSpec, Location). 171locate(Name/Arity, Module:Name/Arity, Location) :- 172 locate(Module:Name/Arity, Location). 173locate(Name//DCGArity, FullSpec, Location) :- 174 ( integer(DCGArity) 175 -> Arity is DCGArity+2, 176 locate(Name/Arity, FullSpec, Location) 177 ; locate(Name/_, FullSpec, Location) % demand arity >= 2 178 ). 179locate(Name/Arity, library(File), #{file:PlPath}) :- 180 atom(Name), 181 '$in_library'(Name, Arity, Path), 182 ( absolute_file_name(library(.), Dir, 183 [ file_type(directory), 184 solutions(all) 185 ]), 186 atom_concat(Dir, File0, Path), 187 atom_concat(/, File, File0) 188 -> find_source(Path, PlPath) 189 ; fail 190 ). 191locate(Module:Name, Module:Name/Arity, Location) :- 192 locate(Module:Name/Arity, Location). 193locate(Module:Head, Module:Name/Arity, Location) :- 194 callable(Head), 195 \+ ( Head = (PName/_), 196 atom(PName) 197 ), 198 functor(Head, Name, Arity), 199 locate(Module:Name/Arity, Location). 200locate(Spec, module(Spec), Location) :- 201 locate(module(Spec), Location). 202locate(Spec, Spec, Location) :- 203 locate(Spec, Location). 204 205include_file(Path) :- 206 source_file_property(Path, included_in(_,_)).
212is_file_search_spec(Spec) :- 213 compound(Spec), 214 compound_name_arguments(Spec, Alias, [Arg]), 215 is_file_spec(Arg), 216 user:file_search_path(Alias, _), 217 !. 218 219is_file_spec(Name), atom(Name) => true. 220is_file_spec(Name), string(Name) => true. 221is_file_spec(Term), cyclic_term(Term) => fail. 222is_file_spec(A/B) => is_file_spec(A), is_file_spec(B). 223is_file_spec(_) => fail.
230find_source(FileSpec, File) :- 231 catch(absolute_file_name(FileSpec, File0, 232 [ file_type(prolog), 233 access(read), 234 file_errors(fail) 235 ]), 236 error(_,_), fail), 237 prolog_source(File0, File). 238 239prolog_source(File0, File) :- 240 file_name_extension(_, Ext, File0), 241 user:prolog_file_type(Ext, qlf), 242 !, 243 '$qlf_module'(File0, Info), 244 File = Info.get(file). 245prolog_source(File, File).
252locate(file(File, line(Line)), #{file:File, line:Line}). 253locate(file(File), #{file:File}). 254locate(Module:Name/Arity, Location) :- 255 ( atom(Name), integer(Arity) 256 -> functor(Head, Name, Arity) 257 ; Head = _ % leave unbound 258 ), 259 ( ( var(Module) 260 ; var(Name) 261 ) 262 -> NonImport = true 263 ; NonImport = false 264 ), 265 current_predicate(Name, Module:Head), 266 \+ ( NonImport == true, 267 Module \== system, 268 predicate_property(Module:Head, imported_from(_)) 269 ), 270 functor(Head, Name, Arity), % bind arity 271 predicate_location(Module:Head, Location). 272locate(module(Module), Location) :- 273 atom(Module), 274 module_property(Module, file(Path)), 275 ( module_property(Module, line_count(Line)) 276 -> Location = #{file:Path, line:Line} 277 ; Location = #{file:Path} 278 ). 279locate(breakpoint(Id), Location) :- 280 integer(Id), 281 breakpoint_property(Id, clause(Ref)), 282 ( breakpoint_property(Id, file(File)), 283 breakpoint_property(Id, line_count(Line)) 284 -> Location = #{file:File, line:Line} 285 ; locate(clause(Ref), Location) 286 ). 287locate(clause(Ref), #{file:File, line:Line}) :- 288 clause_property(Ref, file(File)), 289 clause_property(Ref, line_count(Line)). 290locate(clause(Ref, _PC), #{file:File, line:Line}) :- % TBD: use clause 291 clause_property(Ref, file(File)), 292 clause_property(Ref, line_count(Line)).
301predicate_location(Pred, #{file:File, line:Line}) :- 302 copy_term(Pred, Pred2), 303 distinct(Primary, primary_predicate(Pred2, Primary)), 304 ignore(Pred = Primary), 305 ( predicate_property(Primary, file(File)), 306 predicate_property(Primary, line_count(Line)) 307 -> true 308 ; '$foreign_predicate_source'(Primary, Source), 309 string_codes(Source, Codes), 310 phrase(addr2line_output(File, Line), Codes) 311 ). 312 313primary_predicate(Pred, Primary) :- 314 ( predicate_property(Pred, imported_from(Source)) 315 -> strip_module(Pred, _, Head), 316 Primary = Source:Head 317 ; Primary = Pred 318 ).
addr2line utility. This implementation
works for Linux. Additional lines may be needed for other
environments.327addr2line_output(File, Line) --> 328 string(_), " at ", string(FileCodes), ":", integer(Line), 329 !, 330 remainder(_), 331 { atom_codes(File, FileCodes) }. 332 333 334 /******************************* 335 * EDIT * 336 *******************************/
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.
350do_edit_source(Location) :- % hook 351 edit_source(Location), 352 !. 353do_edit_source(Location) :- % PceEmacs 354 current_prolog_flag(editor, Editor), 355 is_pceemacs(Editor), 356 current_prolog_flag(gui, true), 357 !, 358 location_url(Location, URL), % File[:Line[:LinePos]] 359 run_pce_emacs(URL). 360do_edit_source(Location) :- % External editor 361 external_edit_command(Location, Command), 362 print_message(informational, edit(waiting_for_editor)), 363 ( catch(shell(Command), E, 364 (print_message(warning, E), 365 fail)) 366 -> print_message(informational, edit(make)), 367 make 368 ; print_message(informational, edit(canceled)) 369 ). 370 371external_edit_command(Location, Command) :- 372 #{file:File, line:Line} :< Location, 373 editor(Editor), 374 file_base_name(Editor, EditorFile), 375 file_name_extension(Base, _, EditorFile), 376 edit_command(Base, Cmd), 377 prolog_to_os_filename(File, OsFile), 378 atom_codes(Cmd, S0), 379 substitute('%e', Editor, S0, S1), 380 substitute('%f', OsFile, S1, S2), 381 substitute('%d', Line, S2, S), 382 !, 383 atom_codes(Command, S). 384external_edit_command(Location, Command) :- 385 #{file:File} :< Location, 386 editor(Editor), 387 file_base_name(Editor, EditorFile), 388 file_name_extension(Base, _, EditorFile), 389 edit_command(Base, Cmd), 390 prolog_to_os_filename(File, OsFile), 391 atom_codes(Cmd, S0), 392 substitute('%e', Editor, S0, S1), 393 substitute('%f', OsFile, S1, S), 394 \+ substitute('%d', 1, S, _), 395 !, 396 atom_codes(Command, S). 397external_edit_command(Location, Command) :- 398 #{file:File} :< Location, 399 editor(Editor), 400 format(string(Command), '"~w" "~w"', [Editor, File]). 401 402is_pceemacs(pce_emacs). 403is_pceemacs(built_in).
409run_pce_emacs(URL) :-
410 autoload_call(in_pce_thread(autoload_call(emacs(URL)))).416editor(Editor) :- % $EDITOR 417 current_prolog_flag(editor, Editor), 418 ( sub_atom(Editor, 0, _, _, $) 419 -> sub_atom(Editor, 1, _, 0, Var), 420 catch(getenv(Var, Editor), _, fail), ! 421 ; Editor == default 422 -> catch(getenv('EDITOR', Editor), _, fail), ! 423 ; \+ is_pceemacs(Editor) 424 -> ! 425 ). 426editor(Editor) :- % User defaults 427 getenv('EDITOR', Editor), 428 !. 429editor(vi) :- % Platform defaults 430 current_prolog_flag(unix, true), 431 !. 432editor(notepad) :- 433 current_prolog_flag(windows, true), 434 !. 435editor(_) :- % No luck 436 throw(error(existence_error(editor), _)).
| %e | Path name of the editor |
| %f | Path name of the file to be edited |
| %d | Line number of the target |
448edit_command(vi, '%e +%d \'%f\''). 449edit_command(vi, '%e \'%f\''). 450edit_command(emacs, '%e +%d \'%f\''). 451edit_command(emacs, '%e \'%f\''). 452edit_command(notepad, '"%e" "%f"'). 453edit_command(wordpad, '"%e" "%f"'). 454edit_command(uedit32, '%e "%f/%d/0"'). % ultraedit (www.ultraedit.com) 455edit_command(jedit, '%e -wait \'%f\' +line:%d'). 456edit_command(jedit, '%e -wait \'%f\''). 457edit_command(edit, '%e %f:%d'). % PceEmacs client script 458edit_command(edit, '%e %f'). 459 460edit_command(emacsclient, Command) :- edit_command(emacs, Command). 461edit_command(vim, Command) :- edit_command(vi, Command). 462edit_command(nvim, Command) :- edit_command(vi, Command). 463 464substitute(FromAtom, ToAtom, Old, New) :- 465 atom_codes(FromAtom, From), 466 ( atom(ToAtom) 467 -> atom_codes(ToAtom, To) 468 ; number_codes(ToAtom, To) 469 ), 470 append(Pre, S0, Old), 471 append(From, Post, S0) -> 472 append(Pre, To, S1), 473 append(S1, Post, New), 474 !. 475substitute(_, _, Old, Old). 476 477 478 /******************************* 479 * SELECT * 480 *******************************/ 481 482merge_locations(Locations0, Locations) :- 483 append(Before, [L1|Rest], Locations0), 484 select(L2, Rest, Rest1), 485 merge_location(L1, L2, Loc), 486 !, 487 append([Before, [Loc], Rest1], Locations1), 488 merge_locations(Locations1, Locations). 489merge_locations(Locations, Locations). 490 491merge_location(Loc1-Spec1, Loc2-Spec2, Loc1-Spec1) :- 492 same_file_location(Loc1,Loc2), 493 better_spec(Spec1, Spec2). 494merge_location(Loc1-Spec1, Loc2-Spec2, Loc-Spec) :- 495 same_location(Loc1, Loc2, Loc), 496 merge_specs(Spec1, Spec2, Spec). 497 498same_file_location(L1, L2) :- 499 #{file:File} :< L1, 500 #{file:File} :< L2. 501 502same_location(L, L, L). 503same_location(#{file:F1}, #{file:F2}, #{file:F}) :- 504 best_same_file(F1, F2, F). 505same_location(#{file:F1, line:Line}, #{file:F2}, #{file:F, line:Line}) :- 506 best_same_file(F1, F2, F). 507same_location(#{file:F1}, #{file:F2, line:Line}, #{file:F, line:Line}) :- 508 best_same_file(F1, F2, F). 509 510best_same_file(F1, F2, F) :- 511 catch(same_file(F1, F2), _, fail), 512 !, 513 atom_length(F1, L1), 514 atom_length(F2, L2), 515 ( L1 < L2 516 -> F = F1 517 ; F = F2 518 ). 519 520merge_specs(Spec, Spec, Spec) :- 521 !. 522merge_specs(file(F1), file(F2), file(F)) :- 523 best_same_file(F1, F2, F), 524 !. 525merge_specs(Spec1, Spec2, Spec) :- 526 merge_specs_(Spec1, Spec2, Spec), 527 !. 528merge_specs(Spec1, Spec2, Spec) :- 529 merge_specs_(Spec2, Spec1, Spec), 530 !. 531 532merge_specs_(FileSpec, Spec, Spec) :- 533 is_filespec(FileSpec). 534 535is_filespec(file(_)) => true. 536is_filespec(source_file(_)) => true. 537is_filespec(Term), 538 compound(Term), 539 compound_name_arguments(Term, Alias, [_Arg]), 540 user:file_search_path(Alias, _) => true. 541is_filespec(_) => 542 fail. 543 544better_spec(class(_), module(_)). 545better_spec(_, FileSpec) :- 546 is_filespec(FileSpec).
553do_select_location(Pairs, Spec, Location) :- 554 select_location(Pairs, Spec, Location), % HOOK 555 !, 556 Location \== []. 557do_select_location([], Spec, _) :- 558 !, 559 print_message(warning, edit(not_found(Spec))), 560 fail. 561do_select_location([#{file:File}-file(File)], _, Location) :- 562 !, 563 Location = #{file:File}. 564do_select_location([Location-_Spec], _, Location) :- 565 existing_location(Location), 566 !. 567do_select_location(Pairs, _, Location) :- 568 foldl(number_location, Pairs, NPairs, 1, End), 569 print_message(help, edit(select(NPairs))), 570 ( End == 1 571 -> fail 572 ; Max is End - 1, 573 user_selection(Max, I), 574 memberchk(I-(Location-_Spec), NPairs) 575 ).
583existing_location(Location) :- 584 exists_location(Location), 585 !. 586existing_location(Location) :- 587 #{file:File} :< Location, 588 access_file(File, read). 589 590number_location(Pair, N-Pair, N, N1) :- 591 Pair = Location-_Spec, 592 existing_location(Location), 593 !, 594 N1 is N+1. 595number_location(Pair, 0-Pair, N, N). 596 597user_selection(Max, I) :- 598 user_select(Max, I), 599 !. 600user_selection(Max, I) :- 601 print_message(help, edit(choose(Max))), 602 read_number(Max, I).
608read_number(Max, X) :- 609 Max < 10, 610 !, 611 get_single_char(C), 612 put_code(user_error, C), 613 between(0'0, 0'9, C), 614 X is C - 0'0. 615read_number(_, X) :- 616 read_line_to_string(user_input, String), 617 number_string(X, String). 618 619 620 /******************************* 621 * MESSAGES * 622 *******************************/ 623 624:- multifile 625 prolog:message/3. 626 627prologmessage(edit(Msg)) --> 628 message(Msg). 629 630message(not_found(Spec)) --> 631 [ 'Cannot find anything to edit from "~p"'-[Spec] ], 632 ( { atom(Spec) } 633 -> [ nl, ' Use edit(file(~q)) to create a new file'-[Spec] ] 634 ; [] 635 ). 636message(select(NPairs)) --> 637 { \+ (member(N-_, NPairs), N > 0) }, 638 !, 639 [ 'Found the following locations:', nl ], 640 sequence(target, [nl], NPairs). 641message(select(NPairs)) --> 642 [ 'Please select item to edit:', nl ], 643 sequence(target, [nl], NPairs). 644message(choose(_Max)) --> 645 [ nl, 'Your choice? ', flush ]. 646message(waiting_for_editor) --> 647 [ 'Waiting for editor ... ', flush ]. 648message(make) --> 649 [ 'Running make to reload modified files' ]. 650message(canceled) --> 651 [ 'Editor returned failure; skipped make/0 to reload files' ]. 652 653target(0-(Location-Spec)) ==> 654 [ ansi(warning, '~t*~3| ', [])], 655 edit_specifier(Spec), 656 [ '~t~32|' ], 657 edit_location(Location, false), 658 [ ansi(warning, ' (no source available)', [])]. 659target(N-(Location-Spec)) ==> 660 [ ansi(bold, '~t~d~3| ', [N])], 661 edit_specifier(Spec), 662 [ '~t~32|' ], 663 edit_location(Location, true). 664 665edit_specifier(Module:Name/Arity) ==> 666 [ '~w:'-[Module], 667 ansi(code, '~w/~w', [Name, Arity]) ]. 668edit_specifier(file(_Path)) ==> 669 [ '<file>' ]. 670edit_specifier(source_file(_Path)) ==> 671 [ '<loaded file>' ]. 672edit_specifier(include_file(_Path)) ==> 673 [ '<included file>' ]. 674edit_specifier(Term) ==> 675 [ '~p'-[Term] ]. 676 677edit_location(Location, false) ==> 678 { location_label(Location, Label) }, 679 [ ansi(warning, '~s', [Label]) ]. 680edit_location(Location, true) ==> 681 { location_label(Location, Label), 682 location_url(Location, URL) 683 }, 684 [ url(URL, Label) ]. 685 686location_label(Location, Label) :- 687 #{file:File, line:Line} :< Location, 688 !, 689 short_filename(File, ShortFile), 690 format(string(Label), '~w:~d', [ShortFile, Line]). 691location_label(Location, Label) :- 692 #{file:File} :< Location, 693 !, 694 short_filename(File, ShortFile), 695 format(string(Label), '~w', [ShortFile]). 696 697location_url(Location, File:Line:LinePos) :- 698 #{file:File, line:Line, linepos:LinePos} :< Location, 699 !. 700location_url(Location, File:Line) :- 701 #{file:File, line:Line} :< Location, 702 !. 703location_url(Location, File) :- 704 #{file:File} :< Location.
712short_filename(Path, Spec) :- 713 working_directory(Here, Here), 714 atom_concat(Here, Local0, Path), 715 !, 716 remove_leading_slash(Local0, Spec). 717short_filename(Path, Spec) :- 718 findall(LenAlias, aliased_path(Path, LenAlias), Keyed), 719 keysort(Keyed, [_-Spec|_]). 720short_filename(Path, Path). 721 722aliased_path(Path, Len-Spec) :- 723 setof(Alias, file_alias_path(Alias), Aliases), 724 member(Alias, Aliases), 725 Alias \== autoload, % confusing and covered by something else 726 Term =.. [Alias, '.'], 727 absolute_file_name(Term, Prefix, 728 [ file_type(directory), 729 file_errors(fail), 730 solutions(all) 731 ]), 732 atom_concat(Prefix, Local0, Path), 733 remove_leading_slash(Local0, Local1), 734 remove_extension(Local1, Local2), 735 unquote_segments(Local2, Local), 736 atom_length(Local2, Len), 737 Spec =.. [Alias, Local]. 738 739file_alias_path(Alias) :- 740 user:file_search_path(Alias, _). 741 742remove_leading_slash(Path, Local) :- 743 atom_concat(/, Local, Path), 744 !. 745remove_leading_slash(Path, Path). 746 747remove_extension(File0, File) :- 748 file_name_extension(File, Ext, File0), 749 user:prolog_file_type(Ext, source), 750 !. 751remove_extension(File, File). 752 753unquote_segments(File, Segments) :- 754 split_string(File, "/", "/", SegmentStrings), 755 maplist(atom_string, SegmentList, SegmentStrings), 756 maplist(no_quote_needed, SegmentList), 757 !, 758 segments(SegmentList, Segments). 759unquote_segments(File, File). 760 761 762no_quote_needed(A) :- 763 format(atom(Q), '~q', [A]), 764 Q == A. 765 766segments([Segment], Segment) :- 767 !. 768segments(List, A/Segment) :- 769 append(L1, [Segment], List), 770 !, 771 segments(L1, A). 772 773 774 /******************************* 775 * LOAD EXTENSIONS * 776 *******************************/ 777 778load_extensions :- 779 load, 780 fail. 781load_extensions. 782 783:- load_extensions.
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. */