{ *************************************************************************** * * * 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 . 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: An IDE dialog to show various internals about a TCodeTreeNode at cursor. } unit CodyNodeInfoDlg; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Laz_AVL_Tree, // LCL Forms, Controls, Graphics, Dialogs, ButtonPanel, ComCtrls, StdCtrls, // IDEIntf SrcEditorIntf, // CodeTools CodeToolManager, CodeTree, FindDeclarationCache, PascalParserTool, LinkScanner, CodeCache, BasicCodeTools, FindDeclarationTool, SourceLog, CodyStrConsts, FileProcs, LazFileUtils; type { TCodyNodeInfoDialog } TCodyNodeInfoDialog = class(TForm) ButtonPanel1: TButtonPanel; CodeBufferMemo: TMemo; CodeBuffersComboBox: TComboBox; LinksMemo: TMemo; PageControl1: TPageControl; ReportMemo: TMemo; ReportTabSheet: TTabSheet; CodeBuffersTabSheet: TTabSheet; LinksTabSheet: TTabSheet; procedure CodeBuffersComboBoxSelect(Sender: TObject); procedure FormCreate(Sender: TObject); private procedure ReportBaseTypeCache(Report: TStringList; Tool: TFindDeclarationTool; Node: TCodeTreeNode); procedure ReportAllNodes(Report: TStringList; Tool: TFindDeclarationTool); procedure ShowCodeBuffer(Filename: string); public procedure UpdateReport; end; procedure ShowCodeNodeInfoDialog(Sender: TObject); implementation procedure ShowCodeNodeInfoDialog(Sender: TObject); var CodyNodeInfoDialog: TCodyNodeInfoDialog; begin CodyNodeInfoDialog:=TCodyNodeInfoDialog.Create(nil); try CodyNodeInfoDialog.ShowModal; finally CodyNodeInfoDialog.Free; end; end; { TCodyNodeInfoDialog } procedure TCodyNodeInfoDialog.FormCreate(Sender: TObject); begin Caption:=crsCodeNodeInformation; ReportTabSheet.Caption:=crsReport; CodeBuffersTabSheet.Caption:=crsCodeBuffers; LinksTabSheet.Caption:=crsLinks; ButtonPanel1.CloseButton.Caption:=crsClose; PageControl1.PageIndex:=0; UpdateReport; end; procedure TCodyNodeInfoDialog.CodeBuffersComboBoxSelect(Sender: TObject); begin ShowCodeBuffer(CodeBuffersComboBox.Text); end; procedure TCodyNodeInfoDialog.ReportBaseTypeCache(Report: TStringList; Tool: TFindDeclarationTool; Node: TCodeTreeNode); procedure ReportNode(Prefix: string; ATool: TFindDeclarationTool; ANode: TCodeTreeNode); var s: String; begin if (ATool=nil) or (ANode=nil) then begin Report.Add(Prefix+'Tool='+DbgSName(ATool)+' Node='+DbgSName(ANode)); exit; end; Report.Add(Prefix+'Node.Desc='+ANode.DescAsString+' Start='+ATool.CleanPosToStr(ANode.StartPos,true)); s:=dbgstr(ATool.Src,ANode.StartPos,ANode.EndPos-ANode.StartPos); if length(s)>100 then s:=copy(s,1,48)+'...'+copy(s,length(s)-47,48); Report.Add(GetIndentStr(length(Prefix)+2)+'Src="'+s+'"'); end; var Cache: TBaseTypeCache; BaseContext: TFindContext; LastBaseContext: TFindContext; i: Integer; Visited: TFPList; NextTool: TFindDeclarationTool; NextNode: TCodeTreeNode; begin LastBaseContext:=CleanFindContext; i:=0; Visited:=TFPList.Create; try while Node<>nil do begin inc(i); ReportNode(dbgs(i)+': ',Tool,Node); if not (Node.Cache is TBaseTypeCache) then begin Report.Add('Node.Cache='+DbgSName(Node.Cache)); exit; end; if Visited.IndexOf(Node)>=0 then begin Report.Add('ERROR: CIRCLE'); exit; end; Visited.Add(Node); Cache:=TBaseTypeCache(Node.Cache); BaseContext:=CreateFindContext(TFindDeclarationTool(Cache.BaseTool),Cache.BaseNode); if CompareFindContexts(@LastBaseContext,@BaseContext)<>0 then begin ReportNode(' Base: ',BaseContext.Tool,BaseContext.Node); LastBaseContext:=BaseContext; end; NextTool:=TFindDeclarationTool(Cache.NextTool); NextNode:=Cache.NextNode; if (NextNode<>nil) and (NextTool=nil) then begin Report.Add('Error: node without tool: Cache.NextTool=nil, Cache.NextNode='+NextNode.DescAsString); exit; end; if NextNode=Node then begin // base node reached exit; end; Node:=NextNode; Tool:=NextTool; end; finally Visited.Free; end; end; procedure TCodyNodeInfoDialog.ReportAllNodes(Report: TStringList; Tool: TFindDeclarationTool); var Node: TCodeTreeNode; s: String; begin Report.Add(''); Report.Add('Nodes:'); if Tool=nil then exit; if Tool.Tree=nil then exit; Node:=Tool.Tree.Root; while Node<>nil do begin s:=GetIndentStr(Node.GetLevel*2); s:=s+Node.DescAsString; s:=s+',Start='+IntToStr(Node.StartPos)+'='+Tool.CleanPosToStr(Node.StartPos); s:=s+',End='+IntToStr(Node.EndPos)+'='+Tool.CleanPosToStr(Node.EndPos); Report.Add(s); Node:=Node.Next; end; end; procedure TCodyNodeInfoDialog.ShowCodeBuffer(Filename: string); var Code: TCodeBuffer; i: Integer; sl: TStringList; begin Code:=CodeToolBoss.LoadFile(Filename,false,false); sl:=TStringList.Create; if Code<>nil then begin for i:=0 to Code.LineCount-1 do begin sl.Add('Line='+dbgs(i+1)+' Start='+dbgs(Code.GetLineStart(i))+' ="'+dbgstr(Code.GetLine(i,true))+'"'); end; end; CodeBufferMemo.Lines.Assign(sl); sl.Free; end; procedure TCodyNodeInfoDialog.UpdateReport; var SrcEdit: TSourceEditorInterface; CursorPos: TCodeXYPosition; sl: TStringList; Tool: TCodeTool; CleanPos: integer; Node: TCodeTreeNode; SrcCode: TSourceLog; i: Integer; UsedCodeBuffers: TAVLTree; AVLNode: TAVLTreeNode; Filenames: TStringList; CodeBuf: TCodeBuffer; Scanner: TLinkScanner; Link: TSourceLink; LinkSize: Integer; s: String; begin Tool:=nil; sl:=TStringList.Create; try SrcEdit:=SourceEditorManagerIntf.ActiveEditor; if SrcEdit=nil then begin sl.Add('no source editor'); exit; end; sl.Add('File='+SrcEdit.FileName); CursorPos.X:=SrcEdit.CursorTextXY.X; CursorPos.Y:=SrcEdit.CursorTextXY.Y; sl.Add('Line='+dbgs(CursorPos.Y)); sl.Add('Column='+dbgs(CursorPos.X)); CursorPos.Code:=SrcEdit.CodeToolsBuffer as TCodeBuffer; if not CodeToolBoss.InitCurCodeTool(CursorPos.Code) then begin sl.Add('CodeToolBoss.InitCurCodeTool failed. Maybe unit of include file not found.'); exit; end; try Tool:=CodeToolBoss.CurCodeTool; if CompareFilenames(Tool.MainFilename,SrcEdit.FileName)<>0 then sl.Add('Unit='+Tool.MainFilename); Tool.BuildTreeAndGetCleanPos(trTillCursor,lsrEnd,CursorPos,CleanPos, [btSetIgnoreErrorPos]); // nodes sl.Add('Scanner.ScannedRange='+dbgs(Tool.Scanner.ScannedRange)); sl.Add('Tool.ScannedRange='+dbgs(Tool.ScannedRange)); Node:=Tool.FindDeepestNodeAtPos(CleanPos,false); if Node=nil then begin try Tool.RaiseCursorOutsideCode(CursorPos); except on E: Exception do begin sl.Add('Error: '+E.Message); end; end; end else begin sl.Add('Node.Desc='+Node.DescAsString); sl.Add('Node.SubDesc=%'+binStr(Node.SubDesc,16)); sl.Add('Node.StartPos='+dbgs(Node.StartPos)+'='+Tool.CleanPosToStr(Node.StartPos)); sl.Add('Node.EndPos='+dbgs(Node.EndPos)+'='+Tool.CleanPosToStr(Node.EndPos)); sl.Add('Node Src>>>>>>>>>>>>>>>>>>'); SrcCode:=TSourceLog.Create(copy(Tool.Src,Node.StartPos,Node.EndPos-Node.StartPos)); for i:=0 to SrcCode.LineCount-1 do begin sl.Add('Line='+dbgs(i)+',CleanPos='+dbgs(Node.StartPos+SrcCode.GetLineStart(i)-1)+'="'+dbgstr(SrcCode.GetLine(i,true))+'"'); end; SrcCode.Free; sl.Add('Node Src<<<<<<<<<<<<<<<<<<'); ReportBaseTypeCache(sl,Tool,Node); ReportAllNodes(sl,Tool); end; // codebuffers Filenames:=TStringList.Create; if Tool.Scanner<>nil then begin UsedCodeBuffers:=Tool.Scanner.CreateTreeOfSourceCodes; AVLNode:=UsedCodeBuffers.FindLowest; while AVLNode<>nil do begin CodeBuf:=TCodeBuffer(AVLNode.Data); Filenames.Add(CodeBuf.Filename); AVLNode:=UsedCodeBuffers.FindSuccessor(AVLNode); end; UsedCodeBuffers.Free; end; CodeBuffersComboBox.Items.Assign(Filenames); Filenames.Free; if CodeBuffersComboBox.Items.Count>0 then begin CodeBuffersComboBox.Text:=CodeBuffersComboBox.Items[0]; ShowCodeBuffer(CodeBuffersComboBox.Text); end else begin CodeBuffersComboBox.Text:=''; end; except on e: Exception do CodeToolBoss.HandleException(e); end; finally if CodeToolBoss.ErrorMessage<>'' then begin sl.Add('Error: '+CodeToolBoss.ErrorMessage); if CodeToolBoss.ErrorCode<>nil then begin sl.Add('Error: '+CodeToolBoss.ErrorCode.Filename); if CodeToolBoss.ErrorLine>0 then sl.Add('Error line='+dbgs(CodeToolBoss.ErrorLine)+' column='+dbgs(CodeToolBoss.ErrorColumn)); end; end; ReportMemo.Lines.Assign(sl); sl.Free; end; // links sl:=TStringList.Create; try sl.Clear; if Tool.Scanner<>nil then begin Scanner:=Tool.Scanner; sl.Add('MainFilename:'+Scanner.MainFilename); sl.Add('ScannedRange='+dbgs(Scanner.ScannedRange)); sl.Add('==================================='); sl.Add('InitialValues:'); sl.Add(Scanner.InitialValues.AsString); sl.Add('==================================='); sl.Add('Values:'); sl.Add(Scanner.Values.AsString); sl.Add('==================================='); sl.Add('IsUnit='+dbgs(Scanner.IsUnit)); sl.Add('SourceName='+Scanner.SourceName); sl.Add('==================================='); sl.Add('Links:'); for i:=0 to Scanner.LinkCount-1 do begin Link:=Scanner.Links[i]; s:=dbgs(i)+'/'+dbgs(Scanner.LinkCount)+': Kind='+dbgs(Link.Kind); if Link.Code<>nil then s+=',File='+ExtractFileName(TCodeBuffer(Link.Code).Filename); s+=',CleanedPos='+dbgs(Link.CleanedPos)+',SrcPos='+dbgs(Link.SrcPos); LinkSize:=Scanner.LinkSize(i); s+=',Size='+dbgs(LinkSize); if LinkSize>60 then s+=',Code="'+dbgstr(Scanner.CleanedSrc,Link.CleanedPos,30)+'...'+dbgstr(Scanner.CleanedSrc,Link.CleanedPos+LinkSize-30,30)+'"' else s+=',Code="'+dbgstr(Scanner.CleanedSrc,Link.CleanedPos,LinkSize)+'"'; sl.Add(s); end; sl.Add('==================================='); end; LinksMemo.Lines.Assign(sl); finally sl.Free; end; end; {$R *.lfm} end.