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:- if(exists_source(library(editline))). 49:- autoload(library(editline), [el_history_events/2]). 50:- endif. 51 52% :- set_prolog_flag(generate_debug_info, false).
62:- multifile 63 locate/3, % +Partial, -FullSpec, -Location 64 locate/2, % +FullSpec, -Location 65 select_location/3, % +Pairs, +Spec, -Location 66 exists_location/1, % +Location 67 user_select/2, % +Max, -I 68 edit_source/1, % +Location 69 edit_command/2, % +Editor, -Command 70 load/0. % provides load-hooks 71 72:- public 73 locations/2, % +Spec, -Locations 74 predicate_location/2, % :Pred, -Location 75 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.86edit(Spec) :- 87 notrace(edit_no_trace(Spec)). 88 89edit_no_trace(Spec) :- 90 var(Spec), 91 !, 92 throw(error(instantiation_error, _)). 93edit_no_trace(Spec) :- 94 locations(Spec, Pairs), 95 do_select_location(Pairs, Spec, Location), 96 do_edit_source(Location).
?- [file]., ?- consult(file). or ?-
use_module(file).
% swipl [-s] file.pl
110edit :- 111 current_prolog_flag(associated_file, File), 112 !, 113 edit(file(File)). 114edit :- 115 '$cmd_option_val'(script_file, OsFiles), 116 OsFiles = [OsFile], 117 !, 118 prolog_to_os_filename(File, OsFile), 119 edit(file(File)). 120:- if(exists_source(library(editline))). 121edit :- 122 session_loaded_file(File), 123 !, 124 edit(file(File)). 125:- endif. 126edit :- 127 throw(error(context_error(edit, no_default_file), _)). 128 129:- if(exists_source(library(editline))). 130session_loaded_file(File) :- 131 catch(el_history_events(user_input, Events), 132 error(_,_), fail), 133 member(_-Event, Events), 134 catch(term_string(Command, Event), error(_,_), fail), 135 load_command(Command, File0), 136 ( is_list(File0) 137 -> member(File1, File0) 138 ; File1 = File0 139 ), 140 catch(absolute_file_name(File1, File, 141 [ file_type(source), 142 file_errors(fail) 143 ]), 144 error(_,_), fail), 145 source_file(File), 146 !. 147 148load_command([File|T], [File|T]). 149load_command(consult(File), File). 150load_command(use_module(File), File). 151:- endif.
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.161locations(Spec, Locations) :- 162 load_extensions, 163 findall(Location-FullSpec, 164 locate(Spec, FullSpec, Location), 165 Pairs0), 166 sort(Pairs0, Pairs1), 167 merge_locations(Pairs1, Locations). 168 169 170 /******************************* 171 * LOCATE * 172 *******************************/
176locate(FileSpec:Line, file(Path, line(Line)), #{file:Path, line:Line}) :- 177 integer(Line), Line >= 1, 178 ground(FileSpec), % so specific; do not try alts 179 !, 180 locate(FileSpec, _, #{file:Path}). 181locate(FileSpec:Line:LinePos, 182 file(Path, line(Line), linepos(LinePos)), 183 #{file:Path, line:Line, linepos:LinePos}) :- 184 integer(Line), Line >= 1, 185 integer(LinePos), LinePos >= 1, 186 ground(FileSpec), % so specific; do not try alts 187 !, 188 locate(FileSpec, _, #{file:Path}). 189locate(Path, file(Path), #{file:Path}) :- 190 atom(Path), 191 exists_file(Path). 192locate(Pattern, file(Path), #{file:Path}) :- 193 atom(Pattern), 194 catch(expand_file_name(Pattern, Files), error(_,_), fail), 195 member(Path, Files), 196 exists_file(Path). 197locate(FileBase, file(File), #{file:File}) :- 198 atom(FileBase), 199 find_source(FileBase, File). 200locate(FileSpec, file(File), #{file:File}) :- 201 is_file_search_spec(FileSpec), 202 find_source(FileSpec, File). 203locate(FileBase, source_file(Path), #{file:Path}) :- 204 atom(FileBase), 205 source_file(Path), 206 file_base_name(Path, File), 207 ( File == FileBase 208 -> true 209 ; file_name_extension(FileBase, _, File) 210 ). 211locate(FileBase, include_file(Path), #{file:Path}) :- 212 atom(FileBase), 213 setof(Path, include_file(Path), Paths), 214 member(Path, Paths), 215 file_base_name(Path, File), 216 ( File == FileBase 217 -> true 218 ; file_name_extension(FileBase, _, File) 219 ). 220locate(Name, FullSpec, Location) :- 221 atom(Name), 222 locate(Name/_, FullSpec, Location). 223locate(Name/Arity, Module:Name/Arity, Location) :- 224 locate(Module:Name/Arity, Location). 225locate(Name//DCGArity, FullSpec, Location) :- 226 ( integer(DCGArity) 227 -> Arity is DCGArity+2, 228 locate(Name/Arity, FullSpec, Location) 229 ; locate(Name/_, FullSpec, Location) % demand arity >= 2 230 ). 231locate(Name/Arity, library(File), #{file:PlPath}) :- 232 atom(Name), 233 '$in_library'(Name, Arity, Path), 234 ( absolute_file_name(library(.), Dir, 235 [ file_type(directory), 236 solutions(all) 237 ]), 238 atom_concat(Dir, File0, Path), 239 atom_concat(/, File, File0) 240 -> find_source(Path, PlPath) 241 ; fail 242 ). 243locate(Module:Name, Module:Name/Arity, Location) :- 244 locate(Module:Name/Arity, Location). 245locate(Module:Head, Module:Name/Arity, Location) :- 246 callable(Head), 247 \+ ( Head = (PName/_), 248 atom(PName) 249 ), 250 functor(Head, Name, Arity), 251 locate(Module:Name/Arity, Location). 252locate(Spec, module(Spec), Location) :- 253 locate(module(Spec), Location). 254locate(Spec, Spec, Location) :- 255 locate(Spec, Location). 256 257include_file(Path) :- 258 source_file_property(Path, included_in(_,_)).
264is_file_search_spec(Spec) :- 265 compound(Spec), 266 compound_name_arguments(Spec, Alias, [Arg]), 267 is_file_spec(Arg), 268 user:file_search_path(Alias, _), 269 !. 270 271is_file_spec(Name), atom(Name) => true. 272is_file_spec(Name), string(Name) => true. 273is_file_spec(Term), cyclic_term(Term) => fail. 274is_file_spec(A/B) => is_file_spec(A), is_file_spec(B). 275is_file_spec(_) => fail.
282find_source(FileSpec, File) :- 283 catch(absolute_file_name(FileSpec, File0, 284 [ file_type(prolog), 285 access(read), 286 file_errors(fail) 287 ]), 288 error(_,_), fail), 289 prolog_source(File0, File). 290 291prolog_source(File0, File) :- 292 file_name_extension(_, Ext, File0), 293 user:prolog_file_type(Ext, qlf), 294 !, 295 '$qlf_module'(File0, Info), 296 File = Info.get(file). 297prolog_source(File, File).
304locate(file(File, line(Line)), #{file:File, line:Line}). 305locate(file(File), #{file:File}). 306locate(Module:Name/Arity, Location) :- 307 ( atom(Name), integer(Arity) 308 -> functor(Head, Name, Arity) 309 ; Head = _ % leave unbound 310 ), 311 ( ( var(Module) 312 ; var(Name) 313 ) 314 -> NonImport = true 315 ; NonImport = false 316 ), 317 current_predicate(Name, Module:Head), 318 \+ ( NonImport == true, 319 Module \== system, 320 predicate_property(Module:Head, imported_from(_)) 321 ), 322 functor(Head, Name, Arity), % bind arity 323 predicate_location(Module:Head, Location). 324locate(module(Module), Location) :- 325 atom(Module), 326 module_property(Module, file(Path)), 327 ( module_property(Module, line_count(Line)) 328 -> Location = #{file:Path, line:Line} 329 ; Location = #{file:Path} 330 ). 331locate(breakpoint(Id), Location) :- 332 integer(Id), 333 breakpoint_property(Id, clause(Ref)), 334 ( breakpoint_property(Id, file(File)), 335 breakpoint_property(Id, line_count(Line)) 336 -> Location = #{file:File, line:Line} 337 ; locate(clause(Ref), Location) 338 ). 339locate(clause(Ref), #{file:File, line:Line}) :- 340 clause_property(Ref, file(File)), 341 clause_property(Ref, line_count(Line)). 342locate(clause(Ref, _PC), #{file:File, line:Line}) :- % TBD: use clause 343 clause_property(Ref, file(File)), 344 clause_property(Ref, line_count(Line)).
353predicate_location(Pred, #{file:File, line:Line}) :- 354 copy_term(Pred, Pred2), 355 distinct(Primary, primary_predicate(Pred2, Primary)), 356 ignore(Pred = Primary), 357 '$predicate_source_location'(Primary, File:Line). 358 359primary_predicate(Pred, Primary) :- 360 ( predicate_property(Pred, imported_from(Source)) 361 -> strip_module(Pred, _, Head), 362 Primary = Source:Head 363 ; Primary = Pred 364 ).
371addr2location(Address, File, Line) :- 372 '$addr2line'(Address, Source), 373 '$addr2line_location'(Source, File, Line). 374 375 376 /******************************* 377 * EDIT * 378 *******************************/
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.
392do_edit_source(Location) :- % hook 393 edit_source(Location), 394 !. 395do_edit_source(Location) :- % PceEmacs 396 current_prolog_flag(editor, Editor), 397 is_pceemacs(Editor), 398 current_prolog_flag(gui, true), 399 !, 400 location_url(Location, URL), % File[:Line[:LinePos]] 401 run_pce_emacs(URL). 402do_edit_source(Location) :- % External editor 403 external_edit_command(Location, Command), 404 print_message(informational, edit(waiting_for_editor)), 405 ( catch(shell(Command), E, 406 (print_message(warning, E), 407 fail)) 408 -> print_message(informational, edit(make)), 409 make 410 ; print_message(informational, edit(canceled)) 411 ). 412 413external_edit_command(Location, Command) :- 414 #{file:File, line:Line} :< Location, 415 editor(Editor), 416 file_base_name(Editor, EditorFile), 417 file_name_extension(Base, _, EditorFile), 418 edit_command(Base, Cmd), 419 prolog_to_os_filename(File, OsFile), 420 atom_codes(Cmd, S0), 421 substitute('%e', Editor, S0, S1), 422 substitute('%f', OsFile, S1, S2), 423 substitute('%d', Line, S2, S), 424 !, 425 atom_codes(Command, S). 426external_edit_command(Location, Command) :- 427 #{file:File} :< Location, 428 editor(Editor), 429 file_base_name(Editor, EditorFile), 430 file_name_extension(Base, _, EditorFile), 431 edit_command(Base, Cmd), 432 prolog_to_os_filename(File, OsFile), 433 atom_codes(Cmd, S0), 434 substitute('%e', Editor, S0, S1), 435 substitute('%f', OsFile, S1, S), 436 \+ substitute('%d', 1, S, _), 437 !, 438 atom_codes(Command, S). 439external_edit_command(Location, Command) :- 440 #{file:File} :< Location, 441 editor(Editor), 442 format(string(Command), '"~w" "~w"', [Editor, File]). 443 444is_pceemacs(pce_emacs). 445is_pceemacs(built_in).
451run_pce_emacs(URL) :-
452 autoload_call(in_pce_thread(autoload_call(emacs(URL)))).458editor(Editor) :- % $EDITOR 459 current_prolog_flag(editor, Editor), 460 ( sub_atom(Editor, 0, _, _, $) 461 -> sub_atom(Editor, 1, _, 0, Var), 462 catch(getenv(Var, Editor), _, fail), ! 463 ; Editor == default 464 -> catch(getenv('EDITOR', Editor), _, fail), ! 465 ; \+ is_pceemacs(Editor) 466 -> ! 467 ). 468editor(Editor) :- % User defaults 469 getenv('EDITOR', Editor), 470 !. 471editor(vi) :- % Platform defaults 472 current_prolog_flag(unix, true), 473 !. 474editor(notepad) :- 475 current_prolog_flag(windows, true), 476 !. 477editor(_) :- % No luck 478 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 |
490edit_command(vi, '%e +%d \'%f\''). 491edit_command(vi, '%e \'%f\''). 492edit_command(emacs, '%e +%d \'%f\''). 493edit_command(emacs, '%e \'%f\''). 494edit_command(notepad, '"%e" "%f"'). 495edit_command(wordpad, '"%e" "%f"'). 496edit_command(uedit32, '%e "%f/%d/0"'). % ultraedit (www.ultraedit.com) 497edit_command(jedit, '%e -wait \'%f\' +line:%d'). 498edit_command(jedit, '%e -wait \'%f\''). 499edit_command(edit, '%e %f:%d'). % PceEmacs client script 500edit_command(edit, '%e %f'). 501 502edit_command(emacsclient, Command) :- edit_command(emacs, Command). 503edit_command(vim, Command) :- edit_command(vi, Command). 504edit_command(nvim, Command) :- edit_command(vi, Command). 505 506substitute(FromAtom, ToAtom, Old, New) :- 507 atom_codes(FromAtom, From), 508 ( atom(ToAtom) 509 -> atom_codes(ToAtom, To) 510 ; number_codes(ToAtom, To) 511 ), 512 append(Pre, S0, Old), 513 append(From, Post, S0) -> 514 append(Pre, To, S1), 515 append(S1, Post, New), 516 !. 517substitute(_, _, Old, Old). 518 519 520 /******************************* 521 * SELECT * 522 *******************************/ 523 524merge_locations(Locations0, Locations) :- 525 append(Before, [L1|Rest], Locations0), 526 select(L2, Rest, Rest1), 527 merge_location(L1, L2, Loc), 528 !, 529 append([Before, [Loc], Rest1], Locations1), 530 merge_locations(Locations1, Locations). 531merge_locations(Locations, Locations). 532 533merge_location(Loc1-Spec1, Loc2-Spec2, Loc1-Spec1) :- 534 same_file_location(Loc1,Loc2), 535 better_spec(Spec1, Spec2). 536merge_location(Loc1-Spec1, Loc2-Spec2, Loc-Spec) :- 537 same_location(Loc1, Loc2, Loc), 538 merge_specs(Spec1, Spec2, Spec). 539 540same_file_location(L1, L2) :- 541 #{file:File} :< L1, 542 #{file:File} :< L2. 543 544same_location(L, L, L). 545same_location(#{file:F1}, #{file:F2}, #{file:F}) :- 546 best_same_file(F1, F2, F). 547same_location(#{file:F1, line:Line}, #{file:F2}, #{file:F, line:Line}) :- 548 best_same_file(F1, F2, F). 549same_location(#{file:F1}, #{file:F2, line:Line}, #{file:F, line:Line}) :- 550 best_same_file(F1, F2, F). 551 552best_same_file(F1, F2, F) :- 553 catch(same_file(F1, F2), _, fail), 554 !, 555 atom_length(F1, L1), 556 atom_length(F2, L2), 557 ( L1 < L2 558 -> F = F1 559 ; F = F2 560 ). 561 562merge_specs(Spec, Spec, Spec) :- 563 !. 564merge_specs(file(F1), file(F2), file(F)) :- 565 best_same_file(F1, F2, F), 566 !. 567merge_specs(Spec1, Spec2, Spec) :- 568 merge_specs_(Spec1, Spec2, Spec), 569 !. 570merge_specs(Spec1, Spec2, Spec) :- 571 merge_specs_(Spec2, Spec1, Spec), 572 !. 573 574merge_specs_(FileSpec, Spec, Spec) :- 575 is_filespec(FileSpec). 576 577is_filespec(file(_)) => true. 578is_filespec(source_file(_)) => true. 579is_filespec(Term), 580 compound(Term), 581 compound_name_arguments(Term, Alias, [_Arg]), 582 user:file_search_path(Alias, _) => true. 583is_filespec(_) => 584 fail. 585 586better_spec(class(_), module(_)). 587better_spec(_, FileSpec) :- 588 is_filespec(FileSpec).
595do_select_location(Pairs, Spec, Location) :- 596 select_location(Pairs, Spec, Location), % HOOK 597 !, 598 Location \== []. 599do_select_location([], Spec, _) :- 600 !, 601 print_message(warning, edit(not_found(Spec))), 602 fail. 603do_select_location([#{file:File}-file(File)], _, Location) :- 604 !, 605 Location = #{file:File}. 606do_select_location([Location-_Spec], _, Location) :- 607 existing_location(Location), 608 !. 609do_select_location(Pairs, _, Location) :- 610 foldl(number_location, Pairs, NPairs, 1, End), 611 print_message(help, edit(select(NPairs))), 612 ( End == 1 613 -> fail 614 ; Max is End - 1, 615 user_selection(Max, I), 616 memberchk(I-(Location-_Spec), NPairs) 617 ).
625existing_location(Location) :- 626 exists_location(Location), 627 !. 628existing_location(Location) :- 629 #{file:File} :< Location, 630 access_file(File, read). 631 632number_location(Pair, N-Pair, N, N1) :- 633 Pair = Location-_Spec, 634 existing_location(Location), 635 !, 636 N1 is N+1. 637number_location(Pair, 0-Pair, N, N). 638 639user_selection(Max, I) :- 640 user_select(Max, I), 641 !. 642user_selection(Max, I) :- 643 print_message(help, edit(choose(Max))), 644 read_number(Max, I).
650read_number(Max, X) :- 651 Max < 10, 652 !, 653 get_single_char(C), 654 put_code(user_error, C), 655 between(0'0, 0'9, C), 656 X is C - 0'0. 657read_number(_, X) :- 658 read_line_to_string(user_input, String), 659 number_string(X, String). 660 661 662 /******************************* 663 * MESSAGES * 664 *******************************/ 665 666:- multifile 667 prolog:message/3. 668 669prologmessage(edit(Msg)) --> 670 message(Msg). 671 672message(not_found(Spec)) --> 673 [ 'Cannot find anything to edit from "~p"'-[Spec] ], 674 ( { atom(Spec) } 675 -> [ nl, ' Use edit(file(~q)) to create a new file'-[Spec] ] 676 ; [] 677 ). 678message(select(NPairs)) --> 679 { \+ (member(N-_, NPairs), N > 0) }, 680 !, 681 [ 'Found the following locations:', nl ], 682 sequence(target, [nl], NPairs). 683message(select(NPairs)) --> 684 [ 'Please select item to edit:', nl ], 685 sequence(target, [nl], NPairs). 686message(target(Location-Spec, N)) --> 687 ['~t~d~3| '-[N]], 688 edit_specifier(Spec), 689 [ '\t' ], 690 edit_location(Location, true). 691message(choose(_Max)) --> 692 [ nl, 'Your choice? ', flush ]. 693message(waiting_for_editor) --> 694 [ 'Waiting for editor ... ', flush ]. 695message(make) --> 696 [ 'Running make to reload modified files' ]. 697message(canceled) --> 698 [ 'Editor returned failure; skipped make/0 to reload files' ]. 699 700target(0-(Location-Spec)) ==> 701 [ ansi(warning, '~t*~3| ', [])], 702 edit_specifier(Spec), 703 [ '~t~32|' ], 704 edit_location(Location, false), 705 [ ansi(warning, ' (no source available)', [])]. 706target(N-(Location-Spec)) ==> 707 [ ansi(bold, '~t~d~3| ', [N])], 708 edit_specifier(Spec), 709 [ '~t~32|' ], 710 edit_location(Location, true). 711 712edit_specifier(Module:Name/Arity) ==> 713 [ '~w:'-[Module], 714 ansi(code, '~w/~w', [Name, Arity]) ]. 715edit_specifier(file(_Path)) ==> 716 [ '<file>' ]. 717edit_specifier(source_file(_Path)) ==> 718 [ '<loaded file>' ]. 719edit_specifier(include_file(_Path)) ==> 720 [ '<included file>' ]. 721edit_specifier(Term) ==> 722 [ '~p'-[Term] ]. 723 724edit_location(Location, false) ==> 725 { location_label(Location, Label) }, 726 [ ansi(warning, '~s', [Label]) ]. 727edit_location(Location, true) ==> 728 { location_label(Location, Label), 729 location_url(Location, URL) 730 }, 731 [ url(URL, Label) ]. 732 733location_label(Location, Label) :- 734 #{file:File, line:Line} :< Location, 735 !, 736 short_filename(File, ShortFile), 737 format(string(Label), '~w:~d', [ShortFile, Line]). 738location_label(Location, Label) :- 739 #{file:File} :< Location, 740 !, 741 short_filename(File, ShortFile), 742 format(string(Label), '~w', [ShortFile]). 743 744location_url(Location, File:Line:LinePos) :- 745 #{file:File, line:Line, linepos:LinePos} :< Location, 746 !. 747location_url(Location, File:Line) :- 748 #{file:File, line:Line} :< Location, 749 !. 750location_url(Location, File) :- 751 #{file:File} :< Location.
759short_filename(Path, Spec) :- 760 working_directory(Here, Here), 761 atom_concat(Here, Local0, Path), 762 !, 763 remove_leading_slash(Local0, Spec). 764short_filename(Path, Spec) :- 765 findall(LenAlias, aliased_path(Path, LenAlias), Keyed), 766 keysort(Keyed, [_-Spec|_]). 767short_filename(Path, Path). 768 769aliased_path(Path, Len-Spec) :- 770 setof(Alias, file_alias_path(Alias), Aliases), 771 member(Alias, Aliases), 772 Alias \== autoload, % confusing and covered by something else 773 Term =.. [Alias, '.'], 774 absolute_file_name(Term, Prefix, 775 [ file_type(directory), 776 file_errors(fail), 777 solutions(all) 778 ]), 779 atom_concat(Prefix, Local0, Path), 780 remove_leading_slash(Local0, Local1), 781 remove_extension(Local1, Local2), 782 unquote_segments(Local2, Local), 783 atom_length(Local2, Len), 784 Spec =.. [Alias, Local]. 785 786file_alias_path(Alias) :- 787 user:file_search_path(Alias, _). 788 789remove_leading_slash(Path, Local) :- 790 atom_concat(/, Local, Path), 791 !. 792remove_leading_slash(Path, Path). 793 794remove_extension(File0, File) :- 795 file_name_extension(File, Ext, File0), 796 user:prolog_file_type(Ext, source), 797 !. 798remove_extension(File, File). 799 800unquote_segments(File, Segments) :- 801 split_string(File, "/", "/", SegmentStrings), 802 maplist(atom_string, SegmentList, SegmentStrings), 803 maplist(no_quote_needed, SegmentList), 804 !, 805 segments(SegmentList, Segments). 806unquote_segments(File, File). 807 808 809no_quote_needed(A) :- 810 format(atom(Q), '~q', [A]), 811 Q == A. 812 813segments([Segment], Segment) :- 814 !. 815segments(List, A/Segment) :- 816 append(L1, [Segment], List), 817 !, 818 segments(L1, A). 819 820 821 /******************************* 822 * LOAD EXTENSIONS * 823 *******************************/ 824 825load_extensions :- 826 load, 827 fail. 828load_extensions. 829 830:- 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.