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