1/*****************************************************************************
    2 * This file is part of the Prolog Development Tool (PDT)
    3 * 
    4 * Author: G´┐Żnter Kniesel
    5 * WWW: http://sewiki.iai.uni-bonn.de/research/pdt/start
    6 * Mail: pdt@lists.iai.uni-bonn.de
    7 * Copyright (C): 2013, CS Dept. III, University of Bonn
    8 * 
    9 * All rights reserved. This program is  made available under the terms
   10 * of the Eclipse Public License v1.0 which accompanies this distribution,
   11 * and is available at http://www.eclipse.org/legal/epl-v10.html
   12 * 
   13 ****************************************************************************/
   14
   15:- module( pdt_search,
   16         [ find_reference_to/12                  % (+Functor,+Arity,?DefFile,?DefModule,?RefModule,?RefName,?RefArity,?RefFile,?RefLine,?Nth,?Kind)
   17         , find_definitions_categorized/12       % (+EnclFile,+SelectionLine, +Term, -Functor, -Arity, -This, -DeclOrDef, -DefiningEntity, -FullPath, -Line, -Properties,-Visibility)
   18         , find_primary_definition_visible_in/6  % (EnclFile,TermString,ReferencedModule,MainFile,FirstLine,MultifileResult)
   19         , find_definition_contained_in/8
   20         , find_pred/8
   21         ]).   22
   23:- use_module( split_file_path,
   24             [ split_file_path/4                % (File,Folder,FileName,BaseName,Extension)
   25             ] ).   26:- use_module( pdt_xref, 
   27             [ find_reference_to/12             % ...
   28             ] ).   29:- use_module( properties, 
   30             [ properties_for_predicate/4
   31             ] ).   32:- use_module( pdt_prolog_library(utils4modules),
   33             [ module_of_file/2                 % (File,FileModule)
   34             , defined_in/4             % (SubModule,Name,Arity,DeclModule),
   35             , defined_in_module/3              % (Module,Name,Arity),
   36             , declared_in_file/4               % (Module,Name,Arity,Location)
   37             , defined_in_files/4               % (Module,Name,Arity,Locations)
   38             ] ).   39
   40
   41% TODO: Why this import?
   42:- user:consult(pdt_runtime_builder_analyzer('meta_pred_toplevel.pl')).   43
   44
   45:- op(600, xfy, ::).   % Logtalk message sending operator
   46
   47        /***********************************************************************
   48         * Find Definitions and Declarations and categorize them by visibility *
   49         * --------------------------------------------------------------------*
   50         * for "Find All Declarations" (Ctrl+G) action                         *
   51         ***********************************************************************/ 
   52
   53
   54% find_definitions_categorized(+ReferencingFile,+-ReferencingLine,+ReferencingTerm,-Name,-Arity, 
   55%                               ???ReferencingModule, -DefiningModule, -DeclOrDef, -Visibility, -File,-Line)
   56%                                                      ^^^^^^^^^^^^^^ TODO: moved to this place (two arguments forward)
   57% Logtalk
   58find_definitions_categorized(EnclFile,SelectionLine, Term, Functor, Arity, This, DeclOrDef, DefiningEntity, FullPath, Line, Properties, Visibility):-
   59    Term \= _:_,
   60    split_file_path(EnclFile, _Directory,_FileName,_,lgt),
   61    !,
   62    logtalk_adapter::find_definitions_categorized(EnclFile,SelectionLine, Term, Functor, Arity, This, DeclOrDef, DefiningEntity, FullPath, Line, Properties, Visibility).
   63    
   64find_definitions_categorized(EnclFile,_SelectionLine,Term,Functor,Arity, ReferencingModule, DeclOrDef, DefiningModule, File,Line, PropertyList, Visibility):-
   65    search_term_to_predicate_indicator(Term, Functor/Arity),
   66    module_of_file(EnclFile,FileModule),
   67    (  atom(ReferencingModule)
   68    -> true                            % Explicit entity reference ReferencedModule:Term (or ReferencedModule::Term
   69    ;  ReferencingModule = FileModule   % Implicit module reference
   70    ),    
   71    find_definition(ReferencingModule,Functor,Arity,Sources),
   72    member(DeclOrDef-Visibility-DefiningModule-Location,Sources),
   73    member(File-Lines,Location),
   74    member(Line,Lines),
   75    properties_for_predicate(ReferencingModule,Functor,Arity,PropertyList).
   76
   77search_term_to_predicate_indicator(_:Term, Functor/Arity) :- !, functor(Term, Functor, Arity).
   78search_term_to_predicate_indicator(  Term, Functor/Arity) :- functor(Term, Functor, Arity).
 find_definition(+ReferencingModule, +Name, ?Arity, -Visibility, -Sources)
   83find_definition(ReferencingModule,Name,Arity,Sources) :-
   84    var(Name),
   85    throw( input_argument_free(find_definition(ReferencingModule,Name,Arity,Sources)) ).
   86
   87find_definition(Name,Arity,ReferencingModule,Module,Visibility,File,LineNr,N,Case) :-	
   88%	(  pdt_option(search, restrict_arity(true))
   89%	->
   90%	), 
   91%	(  pdt_option( search, restrict_module(true) 
   92%	-> 
   93%	),
   94	defined_in(Module,Name,Arity),
   95    visibility(Module,Name,Arity,ReferencingModule,Visibility),
   96    location(Module,Name,Arity,File,LineNr,N,Case).
   97%    locations_by_file(Module,Name,Arity,File,Lines).
   98%
   99%locations_by_file(Module,Name,Arity,File,Line) :-    
  100%    setof( LineNr-N-Case, 
  101%           Module^Name^Arity^ location(Module,Name,Arity,File,LineNr,N,Case),
  102%           Lines
  103%    ),
  104%    member(Line,Lines)
  105%    .
 location(+Module, +Name, +Arity, ?File, ?Line, ?N, ?Case)
Arguments:
File- The full path of the file containing the N-th clause
Case- = clause|declaration|foreign|dynamic
  • Case==clause: a clause of the predicate Module:Name/Arity starts at line Line in File.
  • Case==declaration: a declaration for the predicate Module:Name/Arity is somewhere in file. Lacking information about positions of declarations, we guess the Line to be 1.
  • Case==foreign There is no source location for Module:Name/Arity since it is defined by foreign language code. In this case File==none and Line==0.
  • Case==dynamic There is no source location for Module:Name/Arity since it is defined only by dynamic code. In this case File==none and Line==0.
  128location(Module,Name,Arity, File,Line,N,clause) :-      % Clause
  129    clause_location(Module,Name,Arity,File,Line,N).
  130    
  131location(Module,Name,Arity,File,Line,N,declaration) :-  % Declaration
  132    \+ clause_location(Module,Name,Arity,_,_,_),
  133    module_property(Module, file(File)),  
  134    !,
  135    Line=1,
  136    N=0.
  137	
  138location(Module,Name,Arity,File,Line,N,Prop) :-         % No source code 
  139    \+ clause_location(Module,Name,Arity,_,_,_),
  140    functor(Head,Name,Arity),
  141    ( (Prop = foreign, predicate_property(Module:Head,Prop))
  142    ; (Prop = (dynamic), predicate_property(Module:Head,Prop))
  143    ),
  144    !,
  145    File=none,
  146    Line=0,
  147    N=0.
 clause_location(+Module, +Name, +Arity, ?N, ?File, ?Line) is nondet
The N-th clause of the predicate Module:Name/Arity starts at line Line in File.
Arguments:
File- The full path of the file containing the N-th clause
  157clause_location(Module,Name,Arity,N,File,Line) :-
  158	functor(Head,Name,Arity),
  159	nth_clause(Module:Head,N,Ref), 
  160	clause_property(Ref, file(File)),
  161	clause_property(Ref, line_count(Line)).
  162
  163
  164
  165imports_pred_from(Sub,Head,Super) :-
  166    predicate_property(Sub:Head, imported_from(Super)).
 visibility(+ContextModule, +Name, +Arity, ?DeclModule)
  172visibility(ContextModule,Name,Arity,DeclModule, supermodule) :-
  173    defined_in(ContextModule,Name,Arity,DeclModule),
  174    ContextModule \== DeclModule. 
  175
  176visibility(ContextModule,Name,Arity,DeclModule, local) :-
  177    defined_in(ContextModule,Name,Arity,DeclModule),
  178    ContextModule == DeclModule.
  179
  180visibility(ContextModule,Name,Arity,DeclModule, submodule) :-
  181    defined_in(DeclModule,Name,Arity,DeclModule),
  182    % DeclModule is a submodule of ContextModule
  183    defined_in(DeclModule,_,_,ContextModule), % submodule
  184    ContextModule \== DeclModule. 
  185visibility(ContextModule,Name,Arity,DeclModule, invisible) :-    
  186    % There is some DeclaringModule 
  187    defined_in(DeclModule,Name,Arity,DeclModule),
  188    DeclModule \== ContextModule,
  189    % ... but the ContextModule neither is imported to it
  190    % nor imports from it:
  191    functor(Head,Name,Arity),
  192    \+ imports_pred_from(DeclModule,Head,ContextModule),
  193    \+ imports_pred_from(ContextModule,Head,DeclModule).
  194
  195
  196   
  197   
  198        /***********************************************************************
  199         * Find Primary Definition                                             *
  200         * --------------------------------------------------------------------*
  201         * for "Open Primary Declaration" (F3) action                          *
  202         ***********************************************************************/ 
 find_primary_definition_visible_in(+EnclFile, +Name, +Arity, ?ReferencedModule, ?MainFile, ?FirstLine, ?MultifileResult)
Find first line of first clause in the primary file defining the predicate Name/Arity visible in ReferencedModule. In case of multifile predicates, the primary file is either the file whose module is the DefiningModule or otherwise (this case only occurs for "magic" system modules, (e.g. 'system')) the file containing most clauses.

Used for the open declaration action (F3) in pdt/src/org/cs3/pdt/internal/actions/FindPredicateActionDelegate.java

  215find_primary_definition_visible_in(EnclFile,TermString,ReferencedModule,MainFile,FirstLine,MultifileResult) :-
  216    split_file_path(EnclFile, _Directory,_FileName,_,lgt),
  217    !,
  218    logtalk_adapter::find_primary_definition_visible_in(EnclFile,TermString,ReferencedModule,MainFile,FirstLine,MultifileResult).
  219
  220
  221% The second argument is just an atom contianing the string representation of the term:     
  222find_primary_definition_visible_in(EnclFile,TermString,ReferencedModule,MainFile,FirstLine,MultifileResult) :-
  223	retrieve_term_from_atom(EnclFile, TermString, Term),
  224    extract_name_arity(Term,Head,Name,Arity),
  225    find_primary_definition_visible_in__(EnclFile,Head,Name,Arity,ReferencedModule,MainFile,FirstLine,MultifileResult).
  226
  227retrieve_term_from_atom(EnclFile, TermString, Term) :-
  228	(	module_property(Module, file(EnclFile))
  229	->	atom_concat(TermString, '.', TermStringWithDot),
  230		open_chars_stream(TermStringWithDot, Stream),
  231		read_term(Stream, Term, [module(Module)])
  232	;	atom_to_term(TermString, Term, _)
  233	).
  234
  235extract_name_arity(Term,Head,Name,Arity) :-
  236    (  var(Term) 
  237    -> throw( 'Cannot display the definition of a variable. Please select a predicate name.' )
  238     ; true
  239    ),
  240    % Special treatment of Name/Arity terms:
  241    (  Term = Name/Arity
  242    -> true
  243     ; (  Term = _Module:Term2
  244       -> functor(Term2, Name, Arity)
  245       ;  functor(Term,Name,Arity)
  246       )
  247    ),
  248    % Create most general head
  249    functor(Head,Name,Arity).
  250
  251% Now the second argument is a real term that is 
  252%  a) a file loading directive:     
  253find_primary_definition_visible_in__(_,Term,_,_,_,File,Line,no):-
  254    find_file(Term,File,Line).
  255
  256%  b) a literal (call or clause head):    
  257find_primary_definition_visible_in__(EnclFile,Term,Name,Arity,ReferencedModule,MainFile,FirstLine,MultifileResult) :-
  258	find_definition_visible_in(EnclFile,Term,Name,Arity,ReferencedModule,DefiningModule,Locations),
  259	(	Locations = [_,_|_]
  260	->	MultifileResult = yes
  261	;	MultifileResult = no
  262	),
  263	primary_location(Locations,DefiningModule,MainFile,FirstLine).
  264
  265
  266% If Term is a loading directive, find the related file,
  267% eventually interpreting a FileSPec that contains an alias
  268find_file(Term,File,Line) :-
  269    extract_file_spec(Term,FileSpec),
  270    catch( absolute_file_name(FileSpec,[solutions(all),extensions(['.pl', '.lgt', '.ct', '.ctc'])], File),
  271           _,
  272           fail
  273    ),
  274    access_file(File, read),
  275    !,
  276    Line=1.
  277    
  278% Work regardelessly whether the user selected the entire consult/use_module
  279% statement or just the file spec. Does NOT work if he only selected a file
  280% name within an alias but not the complete alias.
  281extract_file_spec(consult(FileSpec),FileSpec) :- !.
  282extract_file_spec(use_module(FileSpec),FileSpec) :- !.
  283extract_file_spec(ensure_loaded(FileSpec),FileSpec) :- !.
  284extract_file_spec(Term,Term).
  285    
  286find_definition_visible_in(EnclFile,_Term,Name,Arity,ReferencedModule,DefiningModule,Locations) :-
  287    module_of_file(EnclFile,FileModule),
  288    (  atom(ReferencedModule)
  289    -> true                            % Explicit module reference
  290    ;  ReferencedModule = FileModule   % Implicit module reference
  291    ),
  292    (  defined_in_module(ReferencedModule,Name,Arity,DefiningModule)
  293    -> defined_in_files(DefiningModule,Name,Arity,Locations)
  294    ;  ( defined_in(ReferencedModule,Name,Arity,DeclaringModule),
  295    defined_in_files(DeclaringModule,Name,Arity,Locations)
  296       )
  297    ).
  298
  299primary_location(Locations,DefiningModule,File,FirstLine) :-
  300    member(File-Lines,Locations),
  301    module_of_file(File,DefiningModule),
  302    !,
  303    Lines = [FirstLine|_].
  304primary_location(Locations,_,File,FirstLine) :-
  305    findall( NrOfClauses-File-FirstLine,
  306             ( member(File-Lines,Locations),
  307               length(Lines,NrOfClauses),
  308               Lines=[FirstLine|_]
  309             ),
  310             All
  311    ),
  312    sort(All, Sorted),
  313    Sorted = [ NrOfClauses-File-FirstLine |_ ].
  314    
  315
  316        /***********************************************************************
  317         * Find Definitions in File                                            *
  318         * --------------------------------------------------------------------*
  319         * for Outline                                                         *
  320         ***********************************************************************/ 
  321         
  322% TODO: This is meanwhile subsumed by other predicates. Integrate!
 find_definition_contained_in(+File, -Name, -Arity, -Line, -PropertyList) is nondet
Looks up the starting line of each clause of each predicate Name/Arity defined in File. Core properties of the predicate are contained in the PropertyList.

Called from PDTOutlineQuery.java

  332find_definition_contained_in(File, Entity, EntityKind, Functor, Arity, SearchCategory, Line, PropertyList) :-
  333    split_file_path(File, _Directory,_FileName,_,lgt),
  334    !,
  335    logtalk_adapter::find_definition_contained_in(File, Entity, EntityKind, Functor, Arity, SearchCategory, Line, PropertyList).
  336
  337find_definition_contained_in(File, Module, module, Functor, Arity, SearchCategory, Line, PropertyList) :-
  338    % Backtrack over all predicates defined in File:
  339    source_file(ModuleCandidate:Head, File), 
  340	% strip_module(ModuleHead,ModuleCandidate,Head),
  341	(	module_property(ModuleCandidate, file(File))
  342	->	Module = ModuleCandidate
  343	;	Module = user
  344	),
  345    functor(Head, Functor, Arity),
  346    properties_for_predicate(ModuleCandidate,Functor, Arity, PropertyList0),
  347    % In the case of a multifile predicate, we want to find all clauses for this 
  348    % predicate, even when they occur in other files
  349    (	member(multifile, PropertyList0)
  350    -> (	defined_in_file(ModuleCandidate, Functor, Arity, _, DeclFile, Line),
  351    		(	DeclFile \= File
  352    		-> 	(	module_property(MultiModule, file(DeclFile)),
  353    				append([for(MultiModule), defining_file(DeclFile)], PropertyList0, PropertyList),
  354    				SearchCategory = multifile
  355    			)
  356    		;	(	PropertyList = PropertyList0,
  357    				SearchCategory = definition
  358    			)
  359    		)
  360    	)
  361    ;	(	PropertyList = PropertyList0,
  362    		SearchCategory = definition,
  363    % The following backtracks over each clause of each predicate.
  364    % Do this at the end, after the things that are deterministic: 
  365    		(	defined_in_file(ModuleCandidate, Functor, Arity, _, File, _)
  366    		->	Module2 = ModuleCandidate
  367    		;	Module2 = Module
  368    		),
  369    		defined_in_file(Module2, Functor, Arity, _, File, Line)
  370    	)
  371    ),
  372    \+find_blacklist(Functor,Arity,Module).
  373    
  374        
  375% The following clause searches for clauses inside the given file, which contribute to multifile 
  376% predicates, defined in foreign modules.
  377find_definition_contained_in(File, Module, module, Functor, Arity, multifile, Line, PropertyList):-
  378    module_property(FileModule, file(File)),
  379    declared_in_module(Module,Head),
  380    Module \= FileModule,
  381    predicate_property(Module:Head, multifile),
  382    nth_clause(Module:Head,_,Ref),
  383    clause_property(Ref,file(File)),     
  384    clause_property(Ref,line_count(Line)),
  385    functor(Head, Functor, Arity),
  386    properties_for_predicate(Module, Functor, Arity, PropertyList0),
  387    append([from(Module)], PropertyList0, PropertyList),
  388    \+find_blacklist(Functor,Arity,Module).
 find_blacklist(?Functor, ?Arity, ?Module) is nondet
Used to remove (internal) predicates from the results of find_definition_contained_in/8.
  396find_blacklist('$load_context_module',2,_).
  397find_blacklist('$mode',2,_).
  398find_blacklist('$pldoc',4,_).
  399
  400    
  401    
  402
  403
  404               /***********************************************
  405                * FIND VISIBLE PREDICATE (FOR AUTOCOMPLETION) *
  406                ***********************************************/
 find_pred(+EnclFile, +Prefix, -EnclModule, -Name, -Arity, -Exported, -Builtin, -Help) is nondet
Looks up all predicates with prefix Prefix defined or imported in file EnclFile.

Used by the PLEditor content assist.

The meaning of Arity is overloaded: -2: atom, -1 : module, >= 0 : predicate

For performance reasons an empty prefix with an unspecified module will only bind predicates if EnclFile is specified.

<EnclFile> specifies the file in which this query is triggered <Prefix> specifies the prefix of the predicate <Module> specifies the module associated to the file.

  423find_pred(EnclFile,Prefix,Module,Name,Arity,Exported,Builtin,Help) :-
  424    split_file_path(EnclFile, _Directory,_FileName,_,lgt),
  425    !,
  426    logtalk_adapter::find_pred(EnclFile,Prefix,Module,Name,Arity,Exported,Builtin,Help).
  427
  428
  429find_pred(EnclFile,Prefix,Module,Name,Arity,Exported,Builtin,Help) :-
  430    \+ atom(EnclFile),
  431    throw( first_argument_free_in_call_to(find_pred(EnclFile,Prefix,Module,Name,Arity,Exported,Builtin,Help))).
  432
  433find_pred(EnclFile,Prefix,Module,Name,Arity,Exported,Builtin,Help) :-
  434	setof(
  435	   (Name,Arity),
  436	   Prefix^Module^
  437	   ( my_module_of_file(EnclFile,Module),
  438	     find_pred_(Prefix,Module,Name,Arity,true)
  439	   ),
  440	   All
  441	),
  442	member((Name,Arity),All),
  443	
  444	% no enclosing module specified in the code via modulename:..
  445	get_declaring_module(EnclFile,Module,Name,Arity),
  446	functor(Term,Name,Arity),
  447	( predicate_property(Module:Term,exported)->
  448	  Exported=true
  449	; Exported=false
  450	),
  451	( predicate_property(Module:Term,built_in)->
  452	  Builtin=true
  453	; Builtin=false
  454	),
  455	predicate_manual_entry(Module,Name,Arity,Help).
  456
  457find_pred_(Prefix,Module,Name,Arity,true) :-
  458    ( var(Module)->
  459    	Prefix \== ''
  460    ; true
  461    ), % performance tweak:
  462    current_predicate(Module:Name/Arity),
  463    atom_concat(Prefix,_,Name),
  464    % rule out used built-ins, like =../2, in case the enclosing module is given (in this case the prefix might be empty):   
  465    ( nonvar(Module) ->
  466      ( functor(Term,Name,Arity),
  467    	(Prefix \== ''; \+ predicate_property(Term, built_in)) )
  468      ; true
  469    ).
  470
  471
  472get_declaring_module(EnclFile,Module,Name,Arity) :-
  473	var(Module),
  474     my_module_of_file(EnclFile,ContainingModule),
  475     current_predicate(ContainingModule:Name/Arity),
  476     functor(Head,Name,Arity),
  477     ( predicate_property(ContainingModule:Head,imported_from(Module))
  478     ; Module = ContainingModule
  479     ),
  480     !.
  481
  482get_declaring_module(_EnclFile,Module,_Name,_Arity) :-
  483	nonvar(Module),
  484	!.
 find_pred(+EnclFile, +Prefix, -EnclModule, -Name, -Arity, -Exported, -Builtin, -Help, -Kind) is nondet
  489find_pred_for_editor_completion(EnclFile,Prefix,Module,Name,Arity,Exported,Builtin,Help,Kind) :-
  490    \+ atom(EnclFile),
  491    throw( first_argument_free_in_call_to(find_pred_for_editor_completion(EnclFile,Prefix,Module,Name,Arity,Exported,Builtin,Help,Kind))).
  492
  493find_pred_for_editor_completion(EnclFile,Prefix,Module,Name,Arity,Exported,Builtin,Help,predicate) :-
  494	find_pred(EnclFile,Prefix,Module,Name,Arity,Exported,Builtin,Help).
  495
  496find_pred_for_editor_completion(_EnclFile,Prefix,EnclModule,Name,-1,true,false,'nodoc', module) :-
  497    var(EnclModule),
  498	current_module(Name),
  499    atom_concat(Prefix,_,Name).
  500
  501% TODO: Improvement Idea: use "string" Prefix instead 
  502%  of atom to avoid Prefix to be added to the set of atoms
  503find_pred_for_editor_completion(_EnclFile,Prefix,'',Atom,-1,fail,true,'nodoc', atom) :-
  504	'$atom_completions'(Prefix, Atoms),
  505	member(Atom,Atoms), 
  506	Atom \= Prefix,
  507	garbage_collect_atoms,
  508	\+ current_predicate(Atom/_Arity).
  509
  510my_module_of_file(_File, Module) :-
  511	atom(Module),
  512	current_module(Module),
  513	!.
  514
  515my_module_of_file(File,Module):-
  516    module_property(Module2,file(File)),
  517	(	Module = Module2
  518	;	Module = user
  519	).
  520                                       
  521my_module_of_file(File,Module):-
  522    atom(File),                           
  523    \+ module_property(Module,file(File)),
  524    ( Module=user                         
  525    ; Module=system                       
  526    )