1 {
2  ***************************************************************************
3  *                                                                         *
4  *   This source is free software; you can redistribute it and/or modify   *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This code is distributed in the hope that it will be useful, but      *
10  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
12  *   General Public License for more details.                              *
13  *                                                                         *
14  *   A copy of the GNU General Public License is available on the World    *
15  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
16  *   obtain it by writing to the Free Software Foundation,                 *
17  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
18  *                                                                         *
19  ***************************************************************************
20 
21   Author: Mattias Gaertner
22 
23   Abstract:
24     An IDE dialog to show various internals about a TCodeTreeNode at cursor.
25 }
26 unit CodyNodeInfoDlg;
27 
28 {$mode objfpc}{$H+}
29 
30 interface
31 
32 uses
33   Classes, SysUtils, Laz_AVL_Tree,
34   // LCL
35   Forms, Controls, Graphics, Dialogs, ButtonPanel, ComCtrls, StdCtrls,
36   // IDEIntf
37   SrcEditorIntf,
38   // CodeTools
39   CodeToolManager, CodeTree, FindDeclarationCache, PascalParserTool,
40   LinkScanner, CodeCache, BasicCodeTools, FindDeclarationTool, SourceLog,
41   CodyStrConsts, FileProcs, LazFileUtils;
42 
43 type
44 
45   { TCodyNodeInfoDialog }
46 
47   TCodyNodeInfoDialog = class(TForm)
48     ButtonPanel1: TButtonPanel;
49     CodeBufferMemo: TMemo;
50     CodeBuffersComboBox: TComboBox;
51     LinksMemo: TMemo;
52     PageControl1: TPageControl;
53     ReportMemo: TMemo;
54     ReportTabSheet: TTabSheet;
55     CodeBuffersTabSheet: TTabSheet;
56     LinksTabSheet: TTabSheet;
57     procedure CodeBuffersComboBoxSelect(Sender: TObject);
58     procedure FormCreate(Sender: TObject);
59   private
60     procedure ReportBaseTypeCache(Report: TStringList;
61       Tool: TFindDeclarationTool; Node: TCodeTreeNode);
62     procedure ReportAllNodes(Report: TStringList;
63       Tool: TFindDeclarationTool);
64     procedure ShowCodeBuffer(Filename: string);
65   public
66     procedure UpdateReport;
67   end;
68 
69 procedure ShowCodeNodeInfoDialog(Sender: TObject);
70 
71 implementation
72 
73 procedure ShowCodeNodeInfoDialog(Sender: TObject);
74 var
75   CodyNodeInfoDialog: TCodyNodeInfoDialog;
76 begin
77   CodyNodeInfoDialog:=TCodyNodeInfoDialog.Create(nil);
78   try
79     CodyNodeInfoDialog.ShowModal;
80   finally
81     CodyNodeInfoDialog.Free;
82   end;
83 end;
84 
85 { TCodyNodeInfoDialog }
86 
87 procedure TCodyNodeInfoDialog.FormCreate(Sender: TObject);
88 begin
89   Caption:=crsCodeNodeInformation;
90   ReportTabSheet.Caption:=crsReport;
91   CodeBuffersTabSheet.Caption:=crsCodeBuffers;
92   LinksTabSheet.Caption:=crsLinks;
93   ButtonPanel1.CloseButton.Caption:=crsClose;
94   PageControl1.PageIndex:=0;
95   UpdateReport;
96 end;
97 
98 procedure TCodyNodeInfoDialog.CodeBuffersComboBoxSelect(Sender: TObject);
99 begin
100   ShowCodeBuffer(CodeBuffersComboBox.Text);
101 end;
102 
103 procedure TCodyNodeInfoDialog.ReportBaseTypeCache(Report: TStringList;
104   Tool: TFindDeclarationTool; Node: TCodeTreeNode);
105 
106   procedure ReportNode(Prefix: string; ATool: TFindDeclarationTool;
107     ANode: TCodeTreeNode);
108   var
109     s: String;
110   begin
111     if (ATool=nil) or (ANode=nil) then begin
112       Report.Add(Prefix+'Tool='+DbgSName(ATool)+' Node='+DbgSName(ANode));
113       exit;
114     end;
115     Report.Add(Prefix+'Node.Desc='+ANode.DescAsString+' Start='+ATool.CleanPosToStr(ANode.StartPos,true));
116     s:=dbgstr(ATool.Src,ANode.StartPos,ANode.EndPos-ANode.StartPos);
117     if length(s)>100 then
118       s:=copy(s,1,48)+'...'+copy(s,length(s)-47,48);
119     Report.Add(GetIndentStr(length(Prefix)+2)+'Src="'+s+'"');
120   end;
121 
122 var
123   Cache: TBaseTypeCache;
124   BaseContext: TFindContext;
125   LastBaseContext: TFindContext;
126   i: Integer;
127   Visited: TFPList;
128   NextTool: TFindDeclarationTool;
129   NextNode: TCodeTreeNode;
130 begin
131   LastBaseContext:=CleanFindContext;
132   i:=0;
133   Visited:=TFPList.Create;
134   try
135     while Node<>nil do begin
136       inc(i);
137       ReportNode(dbgs(i)+': ',Tool,Node);
138       if not (Node.Cache is TBaseTypeCache) then begin
139         Report.Add('Node.Cache='+DbgSName(Node.Cache));
140         exit;
141       end;
142       if Visited.IndexOf(Node)>=0 then begin
143         Report.Add('ERROR: CIRCLE');
144         exit;
145       end;
146       Visited.Add(Node);
147       Cache:=TBaseTypeCache(Node.Cache);
148       BaseContext:=CreateFindContext(TFindDeclarationTool(Cache.BaseTool),Cache.BaseNode);
149       if CompareFindContexts(@LastBaseContext,@BaseContext)<>0 then begin
150         ReportNode('  Base: ',BaseContext.Tool,BaseContext.Node);
151         LastBaseContext:=BaseContext;
152       end;
153 
154       NextTool:=TFindDeclarationTool(Cache.NextTool);
155       NextNode:=Cache.NextNode;
156       if (NextNode<>nil) and (NextTool=nil) then begin
157         Report.Add('Error: node without tool: Cache.NextTool=nil, Cache.NextNode='+NextNode.DescAsString);
158         exit;
159       end;
160       if NextNode=Node then begin
161         // base node reached
162         exit;
163       end;
164       Node:=NextNode;
165       Tool:=NextTool;
166     end;
167   finally
168     Visited.Free;
169   end;
170 end;
171 
172 procedure TCodyNodeInfoDialog.ReportAllNodes(Report: TStringList;
173   Tool: TFindDeclarationTool);
174 var
175   Node: TCodeTreeNode;
176   s: String;
177 begin
178   Report.Add('');
179   Report.Add('Nodes:');
180   if Tool=nil then exit;
181   if Tool.Tree=nil then exit;
182   Node:=Tool.Tree.Root;
183   while Node<>nil do begin
184     s:=GetIndentStr(Node.GetLevel*2);
185     s:=s+Node.DescAsString;
186     s:=s+',Start='+IntToStr(Node.StartPos)+'='+Tool.CleanPosToStr(Node.StartPos);
187     s:=s+',End='+IntToStr(Node.EndPos)+'='+Tool.CleanPosToStr(Node.EndPos);
188     Report.Add(s);
189     Node:=Node.Next;
190   end;
191 end;
192 
193 procedure TCodyNodeInfoDialog.ShowCodeBuffer(Filename: string);
194 var
195   Code: TCodeBuffer;
196   i: Integer;
197   sl: TStringList;
198 begin
199   Code:=CodeToolBoss.LoadFile(Filename,false,false);
200   sl:=TStringList.Create;
201   if Code<>nil then begin
202     for i:=0 to Code.LineCount-1 do begin
203       sl.Add('Line='+dbgs(i+1)+' Start='+dbgs(Code.GetLineStart(i))+' ="'+dbgstr(Code.GetLine(i,true))+'"');
204     end;
205   end;
206   CodeBufferMemo.Lines.Assign(sl);
207   sl.Free;
208 end;
209 
210 procedure TCodyNodeInfoDialog.UpdateReport;
211 var
212   SrcEdit: TSourceEditorInterface;
213   CursorPos: TCodeXYPosition;
214   sl: TStringList;
215   Tool: TCodeTool;
216   CleanPos: integer;
217   Node: TCodeTreeNode;
218   SrcCode: TSourceLog;
219   i: Integer;
220   UsedCodeBuffers: TAVLTree;
221   AVLNode: TAVLTreeNode;
222   Filenames: TStringList;
223   CodeBuf: TCodeBuffer;
224   Scanner: TLinkScanner;
225   Link: TSourceLink;
226   LinkSize: Integer;
227   s: String;
228 begin
229   Tool:=nil;
230   sl:=TStringList.Create;
231   try
232     SrcEdit:=SourceEditorManagerIntf.ActiveEditor;
233     if SrcEdit=nil then begin
234       sl.Add('no source editor');
235       exit;
236     end;
237     sl.Add('File='+SrcEdit.FileName);
238     CursorPos.X:=SrcEdit.CursorTextXY.X;
239     CursorPos.Y:=SrcEdit.CursorTextXY.Y;
240     sl.Add('Line='+dbgs(CursorPos.Y));
241     sl.Add('Column='+dbgs(CursorPos.X));
242     CursorPos.Code:=SrcEdit.CodeToolsBuffer as TCodeBuffer;
243     if not CodeToolBoss.InitCurCodeTool(CursorPos.Code) then begin
244       sl.Add('CodeToolBoss.InitCurCodeTool failed. Maybe unit of include file not found.');
245       exit;
246     end;
247     try
248       Tool:=CodeToolBoss.CurCodeTool;
249       if CompareFilenames(Tool.MainFilename,SrcEdit.FileName)<>0 then
250         sl.Add('Unit='+Tool.MainFilename);
251       Tool.BuildTreeAndGetCleanPos(trTillCursor,lsrEnd,CursorPos,CleanPos,
252                                    [btSetIgnoreErrorPos]);
253 
254       // nodes
255       sl.Add('Scanner.ScannedRange='+dbgs(Tool.Scanner.ScannedRange));
256       sl.Add('Tool.ScannedRange='+dbgs(Tool.ScannedRange));
257       Node:=Tool.FindDeepestNodeAtPos(CleanPos,false);
258       if Node=nil then begin
259         try
260           Tool.RaiseCursorOutsideCode(CursorPos);
261         except
262           on E: Exception do begin
263             sl.Add('Error: '+E.Message);
264           end;
265         end;
266       end else begin
267         sl.Add('Node.Desc='+Node.DescAsString);
268         sl.Add('Node.SubDesc=%'+binStr(Node.SubDesc,16));
269         sl.Add('Node.StartPos='+dbgs(Node.StartPos)+'='+Tool.CleanPosToStr(Node.StartPos));
270         sl.Add('Node.EndPos='+dbgs(Node.EndPos)+'='+Tool.CleanPosToStr(Node.EndPos));
271         sl.Add('Node Src>>>>>>>>>>>>>>>>>>');
272         SrcCode:=TSourceLog.Create(copy(Tool.Src,Node.StartPos,Node.EndPos-Node.StartPos));
273         for i:=0 to SrcCode.LineCount-1 do begin
274           sl.Add('Line='+dbgs(i)+',CleanPos='+dbgs(Node.StartPos+SrcCode.GetLineStart(i)-1)+'="'+dbgstr(SrcCode.GetLine(i,true))+'"');
275         end;
276         SrcCode.Free;
277         sl.Add('Node Src<<<<<<<<<<<<<<<<<<');
278 
279         ReportBaseTypeCache(sl,Tool,Node);
280         ReportAllNodes(sl,Tool);
281       end;
282 
283       // codebuffers
284       Filenames:=TStringList.Create;
285       if Tool.Scanner<>nil then begin
286         UsedCodeBuffers:=Tool.Scanner.CreateTreeOfSourceCodes;
287         AVLNode:=UsedCodeBuffers.FindLowest;
288         while AVLNode<>nil do begin
289           CodeBuf:=TCodeBuffer(AVLNode.Data);
290           Filenames.Add(CodeBuf.Filename);
291           AVLNode:=UsedCodeBuffers.FindSuccessor(AVLNode);
292         end;
293         UsedCodeBuffers.Free;
294       end;
295       CodeBuffersComboBox.Items.Assign(Filenames);
296       Filenames.Free;
297       if CodeBuffersComboBox.Items.Count>0 then begin
298         CodeBuffersComboBox.Text:=CodeBuffersComboBox.Items[0];
299         ShowCodeBuffer(CodeBuffersComboBox.Text);
300       end else begin
301         CodeBuffersComboBox.Text:='';
302       end;
303 
304     except
305       on e: Exception do CodeToolBoss.HandleException(e);
306     end;
307   finally
308     if CodeToolBoss.ErrorMessage<>'' then begin
309       sl.Add('Error: '+CodeToolBoss.ErrorMessage);
310       if CodeToolBoss.ErrorCode<>nil then begin
311         sl.Add('Error: '+CodeToolBoss.ErrorCode.Filename);
312         if CodeToolBoss.ErrorLine>0 then
313           sl.Add('Error line='+dbgs(CodeToolBoss.ErrorLine)+' column='+dbgs(CodeToolBoss.ErrorColumn));
314       end;
315     end;
316     ReportMemo.Lines.Assign(sl);
317     sl.Free;
318   end;
319   // links
320   sl:=TStringList.Create;
321   try
322     sl.Clear;
323     if Tool.Scanner<>nil then begin
324       Scanner:=Tool.Scanner;
325       sl.Add('MainFilename:'+Scanner.MainFilename);
326       sl.Add('ScannedRange='+dbgs(Scanner.ScannedRange));
327       sl.Add('===================================');
328       sl.Add('InitialValues:');
329       sl.Add(Scanner.InitialValues.AsString);
330       sl.Add('===================================');
331       sl.Add('Values:');
332       sl.Add(Scanner.Values.AsString);
333       sl.Add('===================================');
334       sl.Add('IsUnit='+dbgs(Scanner.IsUnit));
335       sl.Add('SourceName='+Scanner.SourceName);
336       sl.Add('===================================');
337       sl.Add('Links:');
338       for i:=0 to Scanner.LinkCount-1 do begin
339         Link:=Scanner.Links[i];
340         s:=dbgs(i)+'/'+dbgs(Scanner.LinkCount)+': Kind='+dbgs(Link.Kind);
341         if Link.Code<>nil then
342           s+=',File='+ExtractFileName(TCodeBuffer(Link.Code).Filename);
343         s+=',CleanedPos='+dbgs(Link.CleanedPos)+',SrcPos='+dbgs(Link.SrcPos);
344         LinkSize:=Scanner.LinkSize(i);
345         s+=',Size='+dbgs(LinkSize);
346         if LinkSize>60 then
347           s+=',Code="'+dbgstr(Scanner.CleanedSrc,Link.CleanedPos,30)+'...'+dbgstr(Scanner.CleanedSrc,Link.CleanedPos+LinkSize-30,30)+'"'
348         else
349           s+=',Code="'+dbgstr(Scanner.CleanedSrc,Link.CleanedPos,LinkSize)+'"';
350         sl.Add(s);
351       end;
352       sl.Add('===================================');
353     end;
354     LinksMemo.Lines.Assign(sl);
355   finally
356     sl.Free;
357   end;
358 end;
359 
360 {$R *.lfm}
361 
362 end.
363 
364