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 locations/2, % +Spec, -Locations 73 predicate_location/2, % :Pred, -Location 74 addr2location/3. % +Address, -File, -Line
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).
% 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), _)).
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 *******************************/
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(_,_)).
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.
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).
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)).
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 ).
338addr2location(Address, File, Line) :-
339 '$addr2line'(Address, Source),
340 string_codes(Source, Codes),
341 phrase(addr2line_output(File, Line), Codes).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 *******************************/
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).
431run_pce_emacs(URL) :-
432 autoload_call(in_pce_thread(autoload_call(emacs(URL)))).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), _)).
| %e | Path name of the editor |
| %f | Path name of the file to be edited |
| %d | Line 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).
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 ).
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).
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 649prologmessage(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.
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.
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. */