Debugger: Editor-Hint for debug-value, use Watch object to retrieve value via new API. Enables FpDebug-value-converter

This commit is contained in:
Martin 2022-06-30 16:41:20 +02:00
parent 6476a2ed2b
commit bf6baffcfe
5 changed files with 138 additions and 143 deletions

View File

@ -125,6 +125,7 @@ type
FRegisters: TIdeRegistersMonitor;
FSnapshots: TSnapshotManager;
FManagerStates: TDebugManagerStates;
FCurrentWatches: TCurrentWatches;
function GetState: TDBGState; virtual; abstract;
function GetCommands: TDBGCommands; virtual; abstract;
function GetPseudoTerminal: TPseudoTerminal; virtual; abstract;
@ -245,6 +246,7 @@ type
{$IFDEF DBG_WITH_DEBUGGER_DEBUG}
property Debugger: TDebuggerIntf read GetDebugger;
{$ENDIF}
property CurrentWatches: TCurrentWatches read FCurrentWatches; // for the hint
end;

View File

@ -1299,6 +1299,9 @@ begin
if FDialogs[DialogType] <> nil then
FDialogs[DialogType].BeginUpdate;
if FDebugger.State <> dsPause then
FCurrentWatches.Clear;
if FDebugger.State = dsInternalPause then exit; // set debug windows to ignore / no updating
end;
@ -1966,6 +1969,8 @@ begin
FWatches.OnModified := @DoProjectModified;
FCurrentWatches := TCurrentWatches.Create(FWatches);
FIsInitializingDebugger:= False;
inherited Create(TheOwner);
@ -1996,6 +2001,7 @@ begin
SetDebugger(nil);
FreeAndNil(FCurrentWatches);
FreeAndNil(FEventLogManager);
FreeAndNil(FSnapshots);
FreeAndNil(FWatches);

View File

@ -101,8 +101,9 @@ uses
Translations,
// debugger
LazDebuggerGdbmi, GDBMIDebugger, RunParamsOpts, BaseDebugManager,
DebugManager, debugger, DebuggerDlg, DebugAttachDialog, DbgIntfBaseTypes,
DbgIntfDebuggerBase, LazDebuggerIntf, FpDebugValueConvertors,
DebugManager, debugger, DebuggerDlg, DebugAttachDialog, DebuggerStrConst,
DbgIntfBaseTypes, DbgIntfDebuggerBase, LazDebuggerIntf,
LazDebuggerIntfBaseTypes, FpDebugValueConvertors,
// packager
PackageSystem, PkgManager, BasePkgManager, LPKCache,
// source editing
@ -145,20 +146,20 @@ uses
// rest of the ide
Splash, IDEDefs, LazarusIDEStrConsts, LazConf, SearchResultView,
CodeTemplatesDlg, CodeBrowser, FindUnitDlg, InspectChksumChangedDlg,
IdeOptionsDlg, EditDefineTree, EnvironmentOpts, TransferMacros,
KeyMapping, IDETranslations, IDEProcs, ExtToolDialog, ExtToolEditDlg,
JumpHistoryView, DesktopManager, DiskDiffsDialog,
BuildLazDialog, BuildProfileManager, BuildManager, CheckCompOptsForNewUnitDlg,
MiscOptions, InputHistory, InputhistoryWithSearchOpt, UnitDependencies,
IDEFPCInfo, IDEInfoDlg, IDEInfoNeedBuild, ProcessList, IdeDebuggerOpts, InitialSetupDlgs,
InitialSetupProc, NewDialog, MakeResStrDlg, DialogProcs, FindReplaceDialog,
FindInFilesDlg, CodeExplorer, BuildFileDlg, ProcedureList, ExtractProcDlg,
FindRenameIdentifier, AbstractsMethodsDlg, EmptyMethodsDlg, UnusedUnitsDlg,
UseUnitDlg, FindOverloadsDlg, EditorFileManager,
CleanDirDlg, CodeContextForm, AboutFrm, CompatibilityRestrictions,
RestrictionBrowser, ProjectWizardDlg, IDECmdLine, IDEGuiCmdLine, CodeExplOpts,
EditorMacroListViewer, SourceFileManager, EditorToolbarStatic,
IDEInstances, NotifyProcessEnd, WordCompletion,
IdeOptionsDlg, EditDefineTree, EnvironmentOpts, TransferMacros, KeyMapping,
IDETranslations, IDEProcs, ExtToolDialog, ExtToolEditDlg, JumpHistoryView,
DesktopManager, DiskDiffsDialog, BuildLazDialog, BuildProfileManager,
BuildManager, CheckCompOptsForNewUnitDlg, MiscOptions, InputHistory,
InputhistoryWithSearchOpt, UnitDependencies, IDEFPCInfo, IDEInfoDlg,
IDEInfoNeedBuild, ProcessList, IdeDebuggerOpts, IdeDebuggerWatchResPrinter,
IdeDebuggerWatchResult, InitialSetupDlgs, InitialSetupProc, NewDialog,
MakeResStrDlg, DialogProcs, FindReplaceDialog, FindInFilesDlg, CodeExplorer,
BuildFileDlg, ProcedureList, ExtractProcDlg, FindRenameIdentifier,
AbstractsMethodsDlg, EmptyMethodsDlg, UnusedUnitsDlg, UseUnitDlg,
FindOverloadsDlg, EditorFileManager, CleanDirDlg, CodeContextForm, AboutFrm,
CompatibilityRestrictions, RestrictionBrowser, ProjectWizardDlg, IDECmdLine,
IDEGuiCmdLine, CodeExplOpts, EditorMacroListViewer, SourceFileManager,
EditorToolbarStatic, IDEInstances, NotifyProcessEnd, WordCompletion,
// main ide
MainBar, MainIntf, MainBase;
@ -187,6 +188,7 @@ type
procedure LazInstancesStartNewInstance(const aFiles: TStrings;
var Result: TStartNewInstanceResult; var outSourceWindowHandle: HWND);
procedure LazInstancesGetOpenedProjectFileName(var outProjectFileName: string);
procedure OnHintWatchValidityChanged(Sender: TObject);
public
// file menu
procedure mnuNewUnitClicked(Sender: TObject);
@ -651,6 +653,13 @@ type
OldCompilerFilename, OldLanguage: String;
OIChangedTimer: TIdleTimer;
FEnvOptsCfgExisted: boolean; // tracks if a local or user specific environment options configuration file existed
FHintWatchData: record
WatchValue: TCurrentWatchValue;
HintPos: TRect;
BaseURL, SmartHintStr, Expression: String;
AutoShown: Boolean;
SrcEdit: TSourceEditor;
end;
FIdentifierWordCompletion: TSourceEditorWordCompletion;
FIdentifierWordCompletionWordList: TStringList;
@ -11348,123 +11357,59 @@ begin
CodeExplorerView.CurrentCodeBufferChanged;
end;
type
{ TSrcNotebookHintCallback
ONLY used by SrcNotebookShowHintForSource
}
TSrcNotebookHintCallback = class
private
FExpression, FBaseURL, FSmartHintStr, FDebugResText: string;
FAutoShown: Boolean;
FSrcEdit: TSourceEditor;
FCaretPos: TPoint;
procedure ShowHint;
public
constructor Create(SrcEdit: TSourceEditor; CaretPos: TPoint; AnExpression, ABaseURL, ASmartHintStr: string; AAutoShown: Boolean);
procedure AddDebuggerResult(Sender: TObject; ASuccess: Boolean; ResultText: String; ResultDBGType: TDBGType);
procedure AddDebuggerResultDeref(Sender: TObject; ASuccess: Boolean; ResultText: String; ResultDBGType: TDBGType);
end;
{ TSrcNotebookHintCallback }
procedure TSrcNotebookHintCallback.ShowHint;
procedure TMainIDE.OnHintWatchValidityChanged(Sender: TObject);
var
AtomStartPos, AtomEndPos: integer;
p: SizeInt;
AtomRect: TRect;
WatchPrinter: TWatchResultPrinter;
ResultText: String;
begin
FExpression := FExpression + ' = ' + FDebugResText;
if FSmartHintStr<>'' then
begin
p:=PosI('<body>',FSmartHintStr);
if p>0 then
Insert('<div class="debuggerhint">'+CodeHelpBoss.TextToHTML(FExpression)+'</div><br>',
FSmartHintStr, p+length('<body>'))
else
FSmartHintStr:=FExpression+LineEnding+LineEnding+FSmartHintStr;
end else
FSmartHintStr:=FExpression;
AtomRect := Rect(-1,-1,-1,-1);
FSrcEdit.EditorComponent.GetWordBoundsAtRowCol(FCaretPos, AtomStartPos, AtomEndPos);
AtomRect.TopLeft := FSrcEdit.EditorComponent.RowColumnToPixels(Point(AtomStartPos, FCaretPos.y));
AtomRect.BottomRight := FSrcEdit.EditorComponent.RowColumnToPixels(Point(AtomEndPos, FCaretPos.y+1));
FSrcEdit.ActivateHint(AtomRect, FBaseURL, FSmartHintStr, FAutoShown, False);
Destroy;
end;
constructor TSrcNotebookHintCallback.Create(SrcEdit: TSourceEditor;
CaretPos: TPoint; AnExpression, ABaseURL, ASmartHintStr: string;
AAutoShown: Boolean);
begin
FExpression := AnExpression;
FSrcEdit := SrcEdit;
FCaretPos := CaretPos;
FBaseURL := ABaseURL;
FSmartHintStr := ASmartHintStr;
FAutoShown := AAutoShown;
end;
procedure TSrcNotebookHintCallback.AddDebuggerResult(Sender: TObject;
ASuccess: Boolean; ResultText: String; ResultDBGType: TDBGType);
var
Opts: TWatcheEvaluateFlags;
begin
try
if not ASuccess then begin
FDebugResText := '???';
end
else begin
// deference a pointer - maybe it is a class
if ASuccess and Assigned(ResultDBGType) and (ResultDBGType.Kind in [skPointer])
and not ( StringCase(ResultDBGType.TypeName,
['char', 'character', 'ansistring'], True, False) in [0..2] )
then
begin
if ResultDBGType.Value.AsPointer <> nil then
begin
Opts := [];
if EditorOpts.DbgHintAutoTypeCastClass
then Opts := [defClassAutoCast];
FDebugResText := ResultText;
if DebugBoss.Evaluate('('+FExpression + ')^', @AddDebuggerResultDeref, Opts) then begin
FreeAndNil(ResultDBGType);
exit;
end;
end;
end else
FDebugResText := DebugBoss.FormatValue(ResultDBGType, ResultText);
FreeAndNil(ResultDBGType);
end;
ShowHint;
except
on E: Exception do
try
IDEMessageDialog('Error',E.Message,mtError,[mbCancel]);
except
end;
end;
end;
procedure TSrcNotebookHintCallback.AddDebuggerResultDeref(Sender: TObject;
ASuccess: Boolean; ResultText: String; ResultDBGType: TDBGType);
begin
if ASuccess and Assigned(ResultDBGType) and
( (ResultDBGType.Kind <> skPointer) or
(StringCase(ResultDBGType.TypeName,
['char', 'character', 'ansistring'], True, False) in [0..2])
)
if (Sender <> FHintWatchData.WatchValue) or (FHintWatchData.WatchValue = nil) or
(SourceEditorManager = nil) or
(SourceEditorManager.FirstIndexOfSourceEditor(FHintWatchData.SrcEdit) < 0)
then
FDebugResText := FDebugResText + LineEnding + LineEnding + '(' + FExpression + ')^ = ' + DebugBoss.FormatValue(ResultDBGType, ResultText);
exit;
FreeAndNil(ResultDBGType);
ShowHint;
if not(FHintWatchData.WatchValue.Validity in [ddsValid, ddsInvalid, ddsError]) then
exit;
ResultText := '';
WatchPrinter := TWatchResultPrinter.Create;
try
if FHintWatchData.WatchValue.Validity = ddsValid then begin
ResultText := WatchPrinter.PrintWatchValue(FHintWatchData.WatchValue.ResultData, wdfStructure);
if (FHintWatchData.WatchValue.ResultData <> nil) and
(FHintWatchData.WatchValue.ResultData.ValueKind = rdkArray) and (FHintWatchData.WatchValue.ResultData.ArrayLength > 0)
then
ResultText := Format(drsLen, [FHintWatchData.WatchValue.ResultData.ArrayLength]) + ResultText
else
if (FHintWatchData.WatchValue.TypeInfo <> nil) and
(FHintWatchData.WatchValue.TypeInfo.Attributes * [saArray, saDynArray] <> []) and
(FHintWatchData.WatchValue.TypeInfo.Len >= 0)
then
ResultText := Format(drsLen, [FHintWatchData.WatchValue.TypeInfo.Len]) + ResultText;
end
else
ResultText := FHintWatchData.WatchValue.Value;
FHintWatchData.Expression := FHintWatchData.Expression + ' = ' + ResultText;
if FHintWatchData.SmartHintStr<>'' then
begin
p:=PosI('<body>',FHintWatchData.SmartHintStr);
if p>0 then
Insert('<div class="debuggerhint">'+CodeHelpBoss.TextToHTML(FHintWatchData.Expression)+'</div><br>',
FHintWatchData.SmartHintStr, p+length('<body>'))
else
FHintWatchData.SmartHintStr:=FHintWatchData.Expression+LineEnding+LineEnding+FHintWatchData.SmartHintStr;
end else
FHintWatchData.SmartHintStr:=FHintWatchData.Expression;
FHintWatchData.SrcEdit.ActivateHint(FHintWatchData.HintPos, FHintWatchData.BaseURL, FHintWatchData.SmartHintStr, FHintWatchData.AutoShown, False);
finally
WatchPrinter.Free;
end;
end;
procedure TMainIDE.SrcNotebookShowHintForSource(SrcEdit: TSourceEditor;
@ -11498,9 +11443,8 @@ var
BaseURL, SmartHintStr, Expression: String;
HasHint: Boolean;
Opts: TWatcheEvaluateFlags;
AtomStartPos, AtomEndPos: integer;
AtomRect: TRect;
DebugHint: TSrcNotebookHintCallback;
AtomStartPos, AtomEndPos, tid: integer;
aWatch: TCurrentWatch;
begin
//DebugLn(['TMainIDE.SrcNotebookShowHintForSource START']);
if (SrcEdit=nil) then exit;
@ -11525,8 +11469,20 @@ begin
HasHint:=true;
{$IFDEF IDE_MEM_CHECK}CheckHeapWrtMemCnt('TMainIDE.SrcNotebookShowHintForSource B');{$ENDIF}
end;
if (ToolStatus = itDebugger) and EditorOpts.AutoToolTipExprEval then
if HasHint then
begin
//Find start of identifier
FHintWatchData.HintPos := Rect(-1,-1,-1,-1);
SrcEdit.EditorComponent.GetWordBoundsAtRowCol(CaretPos, AtomStartPos, AtomEndPos);
FHintWatchData.HintPos.TopLeft := SrcEdit.EditorComponent.RowColumnToPixels(Point(AtomStartPos, CaretPos.y));
FHintWatchData.HintPos.BottomRight := SrcEdit.EditorComponent.RowColumnToPixels(Point(AtomEndPos, CaretPos.y+1));
end;
if (ToolStatus = itDebugger) and EditorOpts.AutoToolTipExprEval and
(DebugBoss.State = dsPause)
then begin
if SrcEdit.SelectionAvailable and SrcEdit.CaretInSelection(CaretPos) then
begin
Expression := SrcEdit.GetText(True);
@ -11538,28 +11494,35 @@ begin
//DebugLn(['TMainIDE.SrcNotebookShowHintForSource Expression="',Expression,'"']);
if Expression <> '' then begin
FHintWatchData.BaseURL := BaseURL;
FHintWatchData.SmartHintStr := SmartHintStr;
FHintWatchData.Expression := Expression;
FHintWatchData.AutoShown := AutoShown;
FHintWatchData.SrcEdit := SrcEdit;
Opts := [];
if EditorOpts.DbgHintAutoTypeCastClass
then Opts := [defClassAutoCast];
DebugHint := TSrcNotebookHintCallback.Create(SrcEdit, CaretPos, Expression, BaseURL, SmartHintStr, AutoShown);
if DebugBoss.Evaluate(Expression, @DebugHint.AddDebuggerResult, Opts) then
exit;
DebugBoss.CurrentWatches.BeginUpdate;
aWatch := DebugBoss.CurrentWatches.Add(Expression);
aWatch.EvaluateFlags := Opts;
aWatch.Enabled := True;
DebugBoss.CurrentWatches.EndUpdate;
DebugHint.Free; // eval not available
// Add note to SmartHintStr: no debug result for expression
tid := DebugBoss.Threads.CurrentThreads.CurrentThreadId;
FHintWatchData.WatchValue := aWatch.Values[tid, 0] as TCurrentWatchValue;
FHintWatchData.WatchValue.OnValidityChanged := @OnHintWatchValidityChanged;
FHintWatchData.WatchValue.Value;
OnHintWatchValidityChanged(FHintWatchData.WatchValue);
exit;
end;
end;
if HasHint then
begin
//Find start of identifier
AtomRect := Rect(-1,-1,-1,-1);
SrcEdit.EditorComponent.GetWordBoundsAtRowCol(CaretPos, AtomStartPos, AtomEndPos);
AtomRect.TopLeft := SrcEdit.EditorComponent.RowColumnToPixels(Point(AtomStartPos, CaretPos.y));
AtomRect.BottomRight := SrcEdit.EditorComponent.RowColumnToPixels(Point(AtomEndPos, CaretPos.y+1));
SrcEdit.ActivateHint(AtomRect, BaseURL, SmartHintStr, AutoShown, False);
SrcEdit.ActivateHint(FHintWatchData.HintPos, BaseURL, SmartHintStr, AutoShown, False);
end;
end;

View File

@ -752,6 +752,7 @@ type
function ResData: TLzDbgWatchDataIntf;
function GetFpDbgConverter: TObject;
private
FOnValidityChanged: TNotifyEvent;
FSnapShot: TIdeWatchValue;
procedure SetSnapShot(const AValue: TIdeWatchValue);
protected
@ -765,6 +766,7 @@ type
public
destructor Destroy; override;
property SnapShot: TIdeWatchValue read FSnapShot write SetSnapShot;
property OnValidityChanged: TNotifyEvent read FOnValidityChanged write FOnValidityChanged;
end;
{ TCurrentWatchValueList }
@ -4001,6 +4003,9 @@ begin
TCurrentWatches(TCurrentWatch(Watch).Collection).Update(Watch);
if FSnapShot <> nil
then FSnapShot.Assign(self);
if FOnValidityChanged <> nil then
FOnValidityChanged(Self);
end;
destructor TCurrentWatchValue.Destroy;

View File

@ -1100,6 +1100,7 @@ type
function SourceWindowWithID(const AnID: Integer): TSourceNotebook;
// Editors
function SourceEditorCount: integer; override;
function FirstIndexOfSourceEditor(AnSrcEditor: TSourceEditor): Integer;
function GetActiveSE: TSourceEditor; { $note deprecate and use ActiveEditor}
property ActiveEditor: TSourceEditor read GetActiveSrcEditor write SetActiveSrcEditor; // reintroduced
property SourceEditors[Index: integer]: TSourceEditor read GetSrcEditors; // reintroduced
@ -10357,6 +10358,24 @@ begin
Result := Result + SourceWindows[i].Count;
end;
function TSourceEditorManager.FirstIndexOfSourceEditor(
AnSrcEditor: TSourceEditor): Integer;
var
i, c: Integer;
begin
i := 0;
c := 0;
while (i < SourceWindowCount) do begin
Result := SourceWindows[i].IndexOfEditor(AnSrcEditor);
if Result >= 0 then begin
Result := Result + c;
exit;
end;
c := c + SourceWindows[i].Count;
inc(i);
end;
end;
function TSourceEditorManager.GetActiveSE: TSourceEditor;
begin
Result := TSourceEditor(ActiveEditor);