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