mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 13:49:34 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			257 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			257 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
{
 | 
						|
 ***************************************************************************
 | 
						|
 *                                                                         *
 | 
						|
 *   This source is free software; you can redistribute it and/or modify   *
 | 
						|
 *   it under the terms of the GNU General Public License as published by  *
 | 
						|
 *   the Free Software Foundation; either version 2 of the License, or     *
 | 
						|
 *   (at your option) any later version.                                   *
 | 
						|
 *                                                                         *
 | 
						|
 *   This code is distributed in the hope that it will be useful, but      *
 | 
						|
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 | 
						|
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 | 
						|
 *   General Public License for more details.                              *
 | 
						|
 *                                                                         *
 | 
						|
 *   A copy of the GNU General Public License is available on the World    *
 | 
						|
 *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
 | 
						|
 *   obtain it by writing to the Free Software Foundation,                 *
 | 
						|
 *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
 | 
						|
 *                                                                         *
 | 
						|
 ***************************************************************************
 | 
						|
 | 
						|
  Author: Mattias Gaertner
 | 
						|
 | 
						|
  Abstract:
 | 
						|
    High level caches.
 | 
						|
}
 | 
						|
unit CacheCodeTools;
 | 
						|
 | 
						|
{$mode objfpc}{$H+}
 | 
						|
 | 
						|
interface
 | 
						|
 | 
						|
uses
 | 
						|
  Classes, SysUtils, Laz_AVL_Tree,
 | 
						|
  // Codetools
 | 
						|
  FileProcs, CodeCache, KeywordFuncLists, CustomCodeTool,
 | 
						|
  BasicCodeTools, FindDeclarationTool;
 | 
						|
 | 
						|
type
 | 
						|
 | 
						|
  { TDeclarationInheritanceCacheItem }
 | 
						|
 | 
						|
  TDeclarationInheritanceCacheItem = class
 | 
						|
  public
 | 
						|
    CodePos: TCodePosition;
 | 
						|
    ListOfPCodeXYPosition: TFPList;
 | 
						|
    destructor Destroy; override;
 | 
						|
  end;
 | 
						|
 | 
						|
  { TDeclarationInheritanceCacheTree
 | 
						|
    Tree of TDeclarationInheritanceCacheItem sorted by CompareDeclInhCacheItems }
 | 
						|
 | 
						|
  TDeclarationInheritanceCacheTree = class(TAVLTree)
 | 
						|
  public
 | 
						|
    CodeToolsChangeStep: integer;
 | 
						|
    constructor CreateDeclInhTree;
 | 
						|
    destructor Destroy; override;
 | 
						|
  end;
 | 
						|
  
 | 
						|
  TOnFindDeclarations = function(Code: TCodeBuffer; X,Y: integer;
 | 
						|
          out ListOfPCodeXYPosition: TFPList;
 | 
						|
          Flags: TFindDeclarationListFlags): boolean of object;
 | 
						|
 | 
						|
  TDeclarationInheritanceCache = class
 | 
						|
  private
 | 
						|
    FCurrent: TDeclarationInheritanceCacheTree;
 | 
						|
    FOldTrees: TFPList; // list of TDeclarationInheritanceCacheTree
 | 
						|
    FOnFindDeclarations: TOnFindDeclarations;
 | 
						|
    FOnGetNodesDeletedStep: TGetChangeStepEvent;
 | 
						|
    procedure CheckCurrentIsValid;
 | 
						|
    procedure CleanCache(FreeItemCount: integer);
 | 
						|
  public
 | 
						|
    constructor Create(const TheOnFindDeclarations: TOnFindDeclarations;
 | 
						|
                       const TheOnGetNodesDeletedStep: TGetChangeStepEvent);
 | 
						|
    destructor Destroy; override;
 | 
						|
    procedure Clear;
 | 
						|
    function FindDeclarations(Code: TCodeBuffer; X,Y: integer;
 | 
						|
          out ListOfPCodeXYPosition: TFPList;
 | 
						|
          out CacheWasUsed: boolean): boolean;
 | 
						|
    property OnFindDeclarations: TOnFindDeclarations read FOnFindDeclarations
 | 
						|
                                                     write FOnFindDeclarations;
 | 
						|
    property OnGetNodesDeletedStep: TGetChangeStepEvent read FOnGetNodesDeletedStep
 | 
						|
                                                   write FOnGetNodesDeletedStep;
 | 
						|
  end;
 | 
						|
 | 
						|
function CompareDeclInhCacheItems(Data1, Data2: Pointer): integer;
 | 
						|
function ComparePCodePosWithDeclInhCacheItem(CodePosition, DeclInhItem: Pointer): integer;
 | 
						|
 | 
						|
implementation
 | 
						|
 | 
						|
function CompareDeclInhCacheItems(Data1, Data2: Pointer): integer;
 | 
						|
var
 | 
						|
  Item1: TDeclarationInheritanceCacheItem;
 | 
						|
  Item2: TDeclarationInheritanceCacheItem;
 | 
						|
begin
 | 
						|
  Item1:=TDeclarationInheritanceCacheItem(Data1);
 | 
						|
  Item2:=TDeclarationInheritanceCacheItem(Data2);
 | 
						|
  Result:=CompareCodePositions(@Item1.CodePos,@Item2.CodePos);
 | 
						|
end;
 | 
						|
 | 
						|
function ComparePCodePosWithDeclInhCacheItem(CodePosition, DeclInhItem: Pointer): integer;
 | 
						|
begin
 | 
						|
  Result:=CompareCodePositions(PCodePosition(CodePosition),
 | 
						|
                               @TDeclarationInheritanceCacheItem(DeclInhItem).CodePos);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDeclarationInheritanceCache.CheckCurrentIsValid;
 | 
						|
var
 | 
						|
  NodesDeletedStep: integer;
 | 
						|
begin
 | 
						|
  if FCurrent=nil then exit;
 | 
						|
  OnGetNodesDeletedStep(NodesDeletedStep);
 | 
						|
  if (FCurrent.CodeToolsChangeStep=NodesDeletedStep) then exit;
 | 
						|
  // the current cache is invalid => move to old
 | 
						|
  if FOldTrees=nil then FOldTrees:=TFPList.Create;
 | 
						|
  FOldTrees.Add(FCurrent);
 | 
						|
  FCurrent:=nil;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDeclarationInheritanceCache.CleanCache(FreeItemCount: integer);
 | 
						|
// free some old cache items
 | 
						|
var
 | 
						|
  i: Integer;
 | 
						|
  OldTree: TDeclarationInheritanceCacheTree;
 | 
						|
begin
 | 
						|
  for i:=1 to FreeItemCount do begin
 | 
						|
    if FOldTrees=nil then exit;
 | 
						|
    if FOldTrees.Count=0 then begin
 | 
						|
      FreeAndNil(FOldTrees);
 | 
						|
    end else begin
 | 
						|
      OldTree:=TDeclarationInheritanceCacheTree(FOldTrees[FOldTrees.Count-1]);
 | 
						|
      if OldTree.Count=0 then begin
 | 
						|
        OldTree.Free;
 | 
						|
        FOldTrees.Delete(FOldTrees.Count-1);
 | 
						|
      end else begin
 | 
						|
        OldTree.FreeAndDelete(OldTree.Root);
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
constructor TDeclarationInheritanceCache.Create(
 | 
						|
  const TheOnFindDeclarations: TOnFindDeclarations;
 | 
						|
  const TheOnGetNodesDeletedStep: TGetChangeStepEvent);
 | 
						|
begin
 | 
						|
  OnFindDeclarations:=TheOnFindDeclarations;
 | 
						|
  OnGetNodesDeletedStep:=TheOnGetNodesDeletedStep;
 | 
						|
end;
 | 
						|
 | 
						|
destructor TDeclarationInheritanceCache.Destroy;
 | 
						|
begin
 | 
						|
  Clear;
 | 
						|
  FreeAndNil(FCurrent);
 | 
						|
  FreeAndNil(FOldTrees);
 | 
						|
  inherited Destroy;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TDeclarationInheritanceCache.Clear;
 | 
						|
var
 | 
						|
  i: LongInt;
 | 
						|
begin
 | 
						|
  if FOldTrees<>nil then begin
 | 
						|
    for i:=FOldTrees.Count-1 downto 0 do
 | 
						|
      TDeclarationInheritanceCacheTree(FOldTrees[i]).Free;
 | 
						|
    FreeAndNil(FOldTrees);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
function TDeclarationInheritanceCache.FindDeclarations(Code: TCodeBuffer; X,
 | 
						|
  Y: integer; out ListOfPCodeXYPosition: TFPList; out CacheWasUsed: boolean
 | 
						|
  ): boolean;
 | 
						|
var
 | 
						|
  CodePos: TCodePosition;
 | 
						|
  AVLNode: TAVLTreeNode;
 | 
						|
  Item: TDeclarationInheritanceCacheItem;
 | 
						|
begin
 | 
						|
  Result:=false;
 | 
						|
  ListOfPCodeXYPosition:=nil;
 | 
						|
  CacheWasUsed:=true;
 | 
						|
  if Code=nil then exit;
 | 
						|
  CodePos.Code:=Code;
 | 
						|
  Code.LineColToPosition(Y,X,CodePos.P);
 | 
						|
  if (CodePos.P<1) or (CodePos.P>Code.SourceLength) then exit;
 | 
						|
 | 
						|
  // move cursor to start of atom (needed to find CodePos in cache)
 | 
						|
  CodePos.P:=FindStartOfAtom(Code.Source,CodePos.P);
 | 
						|
 | 
						|
  // search in cache
 | 
						|
  CheckCurrentIsValid;
 | 
						|
  if FCurrent<>nil then begin
 | 
						|
    // the current cache is valid
 | 
						|
    AVLNode:=FCurrent.FindKey(@CodePos,@ComparePCodePosWithDeclInhCacheItem);
 | 
						|
    if AVLNode<>nil then begin
 | 
						|
      Item:=TDeclarationInheritanceCacheItem(AVLNode.Data);
 | 
						|
      ListOfPCodeXYPosition:=Item.ListOfPCodeXYPosition;
 | 
						|
      Result:=ListOfPCodeXYPosition<>nil;
 | 
						|
      exit;
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
 | 
						|
  CacheWasUsed:=false;
 | 
						|
 | 
						|
  //DebugLn(['TDeclarationInheritanceCache.FindDeclarations searching ',Code.Filename,'(X=',X,',Y=',Y,')']);
 | 
						|
 | 
						|
  // ask the codetools
 | 
						|
  if OnFindDeclarations(Code,X,Y,ListOfPCodeXYPosition,[])
 | 
						|
  and (ListOfPCodeXYPosition<>nil)
 | 
						|
  and (ListOfPCodeXYPosition.Count>0) then begin
 | 
						|
    Result:=true;
 | 
						|
  end else begin
 | 
						|
    FreeAndNil(ListOfPCodeXYPosition);
 | 
						|
    Result:=false;
 | 
						|
  end;
 | 
						|
 | 
						|
  // save to cache
 | 
						|
  Item:=TDeclarationInheritanceCacheItem.Create;
 | 
						|
  Item.CodePos:=CodePos;
 | 
						|
  Item.ListOfPCodeXYPosition:=ListOfPCodeXYPosition;
 | 
						|
  CheckCurrentIsValid;
 | 
						|
  if FCurrent=nil then begin
 | 
						|
    FCurrent:=TDeclarationInheritanceCacheTree.CreateDeclInhTree;
 | 
						|
    OnGetNodesDeletedStep(FCurrent.CodeToolsChangeStep);
 | 
						|
  end;
 | 
						|
  FCurrent.Add(Item);
 | 
						|
  
 | 
						|
  //if ListOfPCodeXYPosition<>nil then DebugLn(['TDeclarationInheritanceCache.FindDeclarations ',ListOfPCodeXYPositionToStr(ListOfPCodeXYPosition)]);
 | 
						|
 | 
						|
  // clean up cache a bit
 | 
						|
  CleanCache(5);
 | 
						|
  
 | 
						|
  // consistency check
 | 
						|
  AVLNode:=FCurrent.FindKey(@CodePos,@ComparePCodePosWithDeclInhCacheItem);
 | 
						|
  if Item<>TDeclarationInheritanceCacheItem(AVLNode.Data) then raise Exception.Create('');
 | 
						|
end;
 | 
						|
 | 
						|
constructor TDeclarationInheritanceCacheTree.CreateDeclInhTree;
 | 
						|
begin
 | 
						|
  Create(@CompareDeclInhCacheItems);
 | 
						|
end;
 | 
						|
 | 
						|
destructor TDeclarationInheritanceCacheTree.Destroy;
 | 
						|
begin
 | 
						|
  FreeAndClear;
 | 
						|
  inherited Destroy;
 | 
						|
end;
 | 
						|
 | 
						|
{ TDeclarationInheritanceCacheItem }
 | 
						|
 | 
						|
destructor TDeclarationInheritanceCacheItem.Destroy;
 | 
						|
begin
 | 
						|
  FreeListOfPCodeXYPosition(ListOfPCodeXYPosition);
 | 
						|
  ListOfPCodeXYPosition:=nil;
 | 
						|
  inherited Destroy;
 | 
						|
end;
 | 
						|
 | 
						|
end.
 | 
						|
 |