1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
|
{
***************************************************************************
* *
* 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.
|