/
StDebugger.class.st
1332 lines (1076 loc) · 36.3 KB
/
StDebugger.class.st
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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"
The StDebugger is a presenter that provides Pharo debugging functionality by using a DebugSession.
The debugging logic is in DebugSession and the corresponding debugging actions.
SpecDebugger only provides a graphical interface to access that functionality.
To interact with the DebugSession, the debugger uses a StDebuggerActionModel, which has responsibility to perform actions to the execution context.
!!Structure of the Pharo debugger
!!!Sub-presenters and layouts
The debugger is composed of default views and extension views.
Default views are the stack, the commands, the status line, the code pane and the inspector.
An extension view automatically appears if any debugging extension is present and if extensions are activated in the settings.
If not, extensions do not appear.
Default and extension views are available in a vertical and an horizontal layout.
It is configureable in the settings under ==Tools >> Debugging >> Debugger layout==.
It is possible to fully customize the debugger layout, and therefore to add or remove any view.
The customized layout must then me selected in the aforementioned setting to be set as default.
To appear in that setting's list of possible values, the layout must be implemented in a method under the ==specs== protocol of the ==StDebugger== class side.
""""The stack.""""
It shows the call stack of the current interrupted context.
Each element in the stack is a context representing a message send.
The context menu (through right click) provides different interactions with the selected stack element: inspection, exploration, exportation, etc.
By default, only a small part of the stack is shown.
More of the stack is observable by either clicking on the bottom context (the last in the stack) or through the context menu.
""""Commands.""""
Commands are actions applicable to the current context
After each debugging action (''e.g.'', a step), the list of commands is being updated.
New commands may appear or disappear.
It depends if they are applicable or not to the current context and debugging session.
For instance, when a ==doesNotUnderstand== exception is raised (''i.e.'', a method does not exist), the command ''createMethod'' will appear.
In that case, it gives users the possibility to create the missing method and continue the execution.
""""The status line.""""
This view provides information about the current context.
Non-critical information, such as the current instruction being stepped, are displayed in gray.
Critical information ,such as halts or breakpoints or DNU exceptions, are displayed in red.
""""The code.""""
The code pane shows the source code of the interrupted context's method.
The source code is annotated with informations about the position of the interrupted execution.
This position corresponds to the next execution that will be executed if a step action is performed.
It is coloured in blue if that instruction is selected in the source code, or in orange and underlined if the selection is empty.
A context menu (through right click) provides inspection, exploration, and stepping actions applicable to the context.
Such actions include, ''e.g.'', browse implementors of a selected message or skip execution of selected parts of the code.
""""The inspector.""""
It provides views on the current context.
It contains tabs with different inspections or views on the current context or on the system.
The raw inspection shows three items: ''Receiver'', ''Temps'' and ''Context''.
The receiver item shows information about the receiver of the message, or the object executing the method from the context.
The temps item shows the temporary variables of the current context.
Finally, the context item shows lower-level details, such as the interrupted context, the debug session or the debugger itself.
Double-clicking on an item will spawn a new view in the same inspector, with an inspection on the selected item.
The breakpoints inspection shows all breakpoints installed for the current receiver.
Those breakpoints are either installed on the receiver's class (''e.g.'', halts) or on that specific receiver (''i.e.'', object-centric breakpoints or halts.)
Ticking or unticking a breakpoint in that list will activate or deactivate that breakpoint.
The meta inspection shows methods of the receiver's class hierarchy.
Methods can be modified and saved in directly in that pane.
"
Class {
#name : #StDebugger,
#superclass : #StPresenter,
#traits : 'TDebugger',
#classTraits : 'TDebugger classTrait',
#instVars : [
'sessionHolder',
'code',
'toolbar',
'stackTable',
'inspector',
'breakpointInspector',
'stackHeader',
'extensionTools',
'extensionToolsNotebook',
'toolbarCommandGroup',
'debuggerActionModel',
'unsavedCodeChanges',
'programmaticallyClosed'
],
#classVars : [
'ActivateDebuggerExtensions',
'ErrorRecursion',
'FastTDD',
'UsingSpecSelector'
],
#category : #'NewTools-Debugger-View'
}
{ #category : #accessing }
StDebugger class >> activateDebuggerExtensions [
^ ActivateDebuggerExtensions
ifNil: [ ActivateDebuggerExtensions := true ]
]
{ #category : #accessing }
StDebugger class >> activateDebuggerExtensions: anObject [
ActivateDebuggerExtensions := anObject
]
{ #category : #specs }
StDebugger class >> availableSpecSelectors [
^ self class allSelectorsInProtocol: 'specs - selectors'
]
{ #category : #commands }
StDebugger class >> buildCommandsGroupWith: stDebuggerInstance forRoot: rootCommandGroup [
StDebuggerStackCommandTreeBuilder
buildCommandsGroupWith: stDebuggerInstance
forRoot: rootCommandGroup.
StDebuggerToolbarCommandTreeBuilder
buildCommandsGroupWith: stDebuggerInstance
forRoot: rootCommandGroup.
StDebuggerCodeCommandTreeBuilder
buildCommandsGroupWith: stDebuggerInstance
forRoot: rootCommandGroup
]
{ #category : #utilities }
StDebugger class >> closeAllDebuggers [
<script>
StDebugger allInstancesDo: [ :dbg | dbg close ]
]
{ #category : #specs }
StDebugger class >> codeLayout [
self flag: #DBG_SPEC.
"Tool bar height is not enough to contain the buttons with icons (issue #94)."
^ SpBoxLayout newVertical
add: #toolbar withConstraints: [ :constraints |
constraints
expand: true;
height: self toolbarHeight + 12];
add: #code;
yourself
]
{ #category : #'instance creation' }
StDebugger class >> debugSession: aDebugSession [
| debugger |
debugger := self on: aDebugSession.
aDebugSession application
ifNotNil: [ :app | debugger application: app ].
debugger openWithFullView.
^ debugger
]
{ #category : #accessing }
StDebugger class >> debuggerContextClass [
^ StDebuggerContext
]
{ #category : #settings }
StDebugger class >> debuggerLayoutSettingsOn: aBuilder [
<systemsettings>
(aBuilder pickOne: #usingSpecSelector)
label: 'Default debugger spec layout';
target: self;
default: #debuggerWithoutExtensionsSpec;
parent: #debugging;
domainValues: self availableSpecSelectors;
description: 'Sets the default spec layout of the debugger.'
]
{ #category : #'specs - selectors' }
StDebugger class >> debuggerWithExtensionsSpec [
^ SpPanedLayout newVertical
positionOfSlider: 65 percent;
add:
(SpPanedLayout newHorizontal
positionOfSlider: 65 percent;
add: self stackAndCodeLayout;
add: #extensionToolsNotebook;
yourself);
add: #inspector;
yourself
]
{ #category : #'specs - selectors' }
StDebugger class >> debuggerWithoutExtensionsSpec [
^ SpPanedLayout newVertical
positionOfSlider: 65 percent;
add: self stackAndCodeLayout;
add: #inspector;
yourself
]
{ #category : #accessing }
StDebugger class >> defaultDebuggerRank [
^ 10
]
{ #category : #specs }
StDebugger class >> defaultSpec [
^ self perform: self usingSpecSelector
]
{ #category : #'tools registry' }
StDebugger class >> extensionToolsClasses [
^TStDebuggerExtension users
]
{ #category : #settings }
StDebugger class >> fastTDD [
^FastTDD ifNil:[FastTDD := false]
]
{ #category : #settings }
StDebugger class >> fastTDD: aBoolean [
FastTDD := aBoolean
]
{ #category : #settings }
StDebugger class >> fastTDDSettingsOn: aBuilder [
<systemsettings>
(aBuilder setting: #fastTDD)
label: 'Fast TDD';
target: self;
default: false;
parent: #debugging;
description: 'Create classes and methods directly in the receiver''s class
without requesting information from the user'
]
{ #category : #'tools registry' }
StDebugger class >> hasAnyActivatedExtension: extensionsClasses [
^ extensionsClasses anySatisfy: [ :toolClass |
toolClass showInDebugger ]
]
{ #category : #'class initialization' }
StDebugger class >> initialize [
self flag: 'Error recursion is used only here?'.
ErrorRecursion := false
]
{ #category : #opening }
StDebugger class >> openOn: aDebugSession withFullView: aBool [
^ self openOn: aDebugSession withFullView: aBool andNotification: nil
]
{ #category : #opening }
StDebugger class >> openOn: aDebugSession withFullView: aBool andNotification: aString [
| debugger |
debugger := self on: aDebugSession.
debugger openWithFullView.
^ debugger
]
{ #category : #accessing }
StDebugger class >> sessionClass [
self flag: 'Should be replaced by Sindarin - should even be removed in that case!'.
^ DebugSession
]
{ #category : #accessing }
StDebugger class >> spanNewSessionFrom: anotherSession [
self flag: 'Where is that used? 2019-06-28'.
^ anotherSession spanNewSession
]
{ #category : #specs }
StDebugger class >> stackAndCodeLayout [
^ SpPanedLayout newVertical
positionOfSlider: 30 percent;
add: self stackLayout;
add: self codeLayout;
yourself
]
{ #category : #specs }
StDebugger class >> stackLayout [
^ SpBoxLayout newVertical
add: #stackHeader
expand: false
fill: false
padding: 5;
add: #stackTable;
yourself
]
{ #category : #deprecated }
StDebugger class >> taskbarIconName [
^#smallDebugIcon
]
{ #category : #'tools registry' }
StDebugger class >> usesExtensions [
^ self activateDebuggerExtensions and: [
self hasAnyActivatedExtension: self extensionToolsClasses ]
]
{ #category : #specs }
StDebugger class >> usingSpecSelector [
^ (UsingSpecSelector
ifNil: [ UsingSpecSelector := StDebuggerSpecSelector defaultSelector ])
selectSpecSelector
]
{ #category : #specs }
StDebugger class >> usingSpecSelector: aSelector [
UsingSpecSelector := StDebuggerSpecSelector on: aSelector
]
{ #category : #'presenter - code' }
StDebugger >> acceptCodeChanges: newSource forContext: aContext [
"It is wrong here that I have to use code adapter widget.
It is expected down in the compilation call chain that the notified requestor answers to textMorph.
This is for modifying the source code when declaring temps"
self
recompileMethodTo: newSource
inContext: aContext
notifying: code adapter widget.
self clearUnsavedCodeChanges
]
{ #category : #'command support' }
StDebugger >> buildContextMenus [
| stackGroup codeCommands debuggerCommandGroup |
debuggerCommandGroup := self rootCommandsGroup.
"Stack"
stackGroup := debuggerCommandGroup / StDebuggerStackCommandTreeBuilder groupName.
stackTable contextMenu: stackGroup beRoot asMenuPresenter.
"Toolbar"
toolbarCommandGroup := debuggerCommandGroup / StDebuggerToolbarCommandTreeBuilder groupName.
SpToolbarPresenterBuilder new
toolbarPresenter: toolbar;
visit: toolbarCommandGroup.
self updateToolbar.
"Code"
codeCommands := debuggerCommandGroup / StDebuggerCodeCommandTreeBuilder groupName.
code contextMenu: codeCommands beRoot asMenuPresenter
]
{ #category : #'command support' }
StDebugger >> canExecuteCreateMissingClassCommand [
^ self debuggerActionModel isInterruptedContextMissingClassException
]
{ #category : #'command support' }
StDebugger >> canExecuteCreateMissingMethodCommand [
^ self debuggerActionModel isInterruptedContextDoesNotUnderstand
]
{ #category : #'command support' }
StDebugger >> canExecuteCreateSubclassResponsibilityCommand [
^ self debuggerActionModel isInterruptedContextSubclassResponsibilityException
]
{ #category : #'command support' }
StDebugger >> canExecuteDebugCommand [
^ self debuggerActionModel isContextSteppable
]
{ #category : #'command support' }
StDebugger >> canExecuteRestartCommand [
^ (self debuggerActionModel isInterruptedContextPostMortem or: [
self debuggerActionModel isInterruptedContextDead ]) not
]
{ #category : #'command support' }
StDebugger >> canExecuteReturnCommand [
^ self debuggerActionModel isInterruptedContextDead not
]
{ #category : #actions }
StDebugger >> clear [
extensionToolsNotebook pages do: [ :page |
page activePresenter windowIsClosing ].
extensionTools := nil.
self unsubscribeFromSystemAnnouncer.
self removeActionsForSession: self session.
"When we programmatically close the window, we do not need to terminate the session as it was already cleared"
programmaticallyClosed ifTrue: [ ^ self ].
debuggerActionModel clearDebugSession
]
{ #category : #'updating widgets' }
StDebugger >> clearCode [
self code
clearInteractionModel;
clearSelection;
clearContent
]
{ #category : #'presenter - code' }
StDebugger >> clearUnsavedCodeChanges [
unsavedCodeChanges := IdentityDictionary new
]
{ #category : #opening }
StDebugger >> close [
programmaticallyClosed := true.
[ self withWindowDo: #close ] ensure: [
debuggerActionModel ifNotNil: [ :actionModel |
actionModel clearDebugSession ] ]
]
{ #category : #'accessing widgets' }
StDebugger >> code [
^ code
]
{ #category : #initialization }
StDebugger >> connectPresenters [
super connectPresenters.
self whenDisplayDo: [
self updateToolbar.
self updateCodeFromContext ]
]
{ #category : #'accessing context' }
StDebugger >> context [
^ self interruptedContext
]
{ #category : #actions }
StDebugger >> copyStackToClipboard [
self debuggerActionModel copyStackToClipboard
]
{ #category : #actions }
StDebugger >> createMissingClass [
| message exception variableNode |
self flag: #DBG_MISSINGTEST.
message := self interruptedContext tempAt: 1.
exception := self interruptedContext tempAt: 2.
variableNode := exception variableNode.
[
OCUndeclaredVariableWarning new
node: variableNode;
defineClass: variableNode name.
self
createMissingMethodFor: message
in: self interruptedContext receiver class ]
on: Abort
do: [ ^ self ].
self debuggerActionModel restartContext: self interruptedContext
]
{ #category : #actions }
StDebugger >> createMissingMethod [
| msg chosenClass |
self flag: #DBG_MISSINGTEST.
msg := self interruptedContext tempAt: 1.
[
chosenClass := self requestClassFrom:
self interruptedContext receiver class.
self createMissingMethodFor: msg in: chosenClass ]
on: Abort
do: [ ^ self ].
code takeKeyboardFocus
]
{ #category : #actions }
StDebugger >> createMissingMethodFor: aMessage in: aClass [
self flag: #DBG_MISSINGTEST.
self debuggerActionModel
implement: aMessage
classified: (self requestProtocolIn: aClass)
inClass: aClass
forContext: self interruptedContext.
self selectTopContext
]
{ #category : #actions }
StDebugger >> createSubclassResponsibility [
| senderContext msg msgCategory chosenClass |
senderContext := self interruptedContext sender.
msg := Message
selector: senderContext selector
arguments: senderContext arguments.
msgCategory := senderContext methodClass organization
categoryOfElement: msg selector.
chosenClass := self requestClassFrom: senderContext receiver class.
chosenClass ifNil: [ ^ self ].
self debuggerActionModel
implement: msg
classified: msgCategory
inClass: chosenClass
forContext: senderContext
]
{ #category : #'accessing context' }
StDebugger >> currentContext [
"I answer the context on which an action should be executed.
This is either the selected context or if nothing is selected the interrupted context."
^ self selectedContext isNil
ifTrue: [ self interruptedContext ]
ifFalse: [ self selectedContext ]
]
{ #category : #accessing }
StDebugger >> debuggerActionModel [
^ debuggerActionModel
ifNil: [ debuggerActionModel := StDebuggerActionModel on: self session]
]
{ #category : #accessing }
StDebugger >> debuggerInspectorClass [
^ StDebuggerInspector
]
{ #category : #accessing }
StDebugger >> debuggerInspectorModelClass [
^ StDebuggerInspectorModel
]
{ #category : #'presenter - code' }
StDebugger >> discardCodeChangesFor: aContext [
self unsavedCodeChanges removeKey: aContext ifAbsent: [ ].
self code text: aContext sourceCode.
self update
]
{ #category : #commands }
StDebugger >> doBrowseClass [
self systemNavigation browse: self stackSelectionMethodContext value
]
{ #category : #commands }
StDebugger >> doBrowseReceiverClass [
self systemNavigation browse: self stackSelectionReceiverClassContext value
]
{ #category : #stack }
StDebugger >> expandStackIfLastItemIsSelected [
| selection |
selection := stackTable selection.
selection selectedItem == stackTable items last ifFalse: [ ^ self ].
stackTable items: (self filterStack:
(self session stackOfSize: 2 * stackTable items size)).
stackTable selectIndex: selection selectedIndex
]
{ #category : #extensions }
StDebugger >> extensionTools [
^ extensionTools ifNil: [ extensionTools := OrderedCollection new ]
]
{ #category : #actions }
StDebugger >> fileOutSelectedContext [
self debuggerActionModel
fileOutMethod: stackTable selection selectedItem method
]
{ #category : #stack }
StDebugger >> filterStack: aStack [
^self debuggerActionModel filterStack: aStack
]
{ #category : #stack }
StDebugger >> findFirstRelevantStackIndexIn: aStack [
| counter |
counter := 1.
aStack do: [ :context |
context method methodClass == TestAsserter ifFalse: [ ^ counter ].
counter := counter + 1 ].
^ 1
]
{ #category : #'updating session' }
StDebugger >> forceSessionUpdate [
self sessionHolder valueChanged
]
{ #category : #api }
StDebugger >> initialExtent [
"This code was taken from the old debugger to get the same behavior. Fell free to refactor"
self flag: 'Do it better'.
"RealEstateAgent standardWindowExtent y < 400"true "a tiny screen"
ifTrue: [ ^ 1000@800 ].
^ [ | w h |
w := Display width.
h := Display height.
(w - (w // 3)) @ (h - (h // 5)) ]
on: Error
do: [ 850@650 ]
]
{ #category : #initialization }
StDebugger >> initialize [
super initialize.
self debuggerActionModel updateContextPredicate.
self setSessionHolderSubscriptions.
self forceSessionUpdate.
self subscribeToMethodAddedAnnouncement.
programmaticallyClosed := false.
]
{ #category : #initialization }
StDebugger >> initializeBreakpointInspector [
breakpointInspector := self
instantiate: StDebuggerBreakpointInspection
on: (StInspectorModel on: nil).
]
{ #category : #'presenter - code' }
StDebugger >> initializeCode [
code := self newCode.
code whenSubmitDo: [ :text |
self acceptCodeChanges: text string forContext: self selectedContext ].
code whenResetDo: [ self discardCodeChangesFor: self selectedContext ]
]
{ #category : #extensions }
StDebugger >> initializeExtensionTools [
| sortedToolClasses pages |
extensionToolsNotebook := self newNotebook.
self class usesExtensions ifFalse: [ ^ self ].
sortedToolClasses := SortedCollection new sortBlock: [ :s1 :s2 | s1 displayOrder <= s2 displayOrder ].
sortedToolClasses addAll: (self class extensionToolsClasses select: [ :s | s showInDebugger ]).
pages := sortedToolClasses collect: [ :toolClass |
self instantiateExtensionToolsPage: toolClass ].
extensionToolsNotebook pages: pages
]
{ #category : #initialization }
StDebugger >> initializeInspector [
inspector := self
instantiate: self debuggerInspectorClass
on:
(self debuggerInspectorModelClass on: self newDebuggerContext).
inspector label: 'Receiver'
]
{ #category : #initialization }
StDebugger >> initializePresenters [
self initializeStack.
self initializeToolbar.
self initializeCode.
self initializeInspector.
self initializeExtensionTools
]
{ #category : #initialization }
StDebugger >> initializeShortcuts: aWindowPresenter [
self flag: #DBG_TempSolvesSpecBug_RemoveWhenFixed.
"This is only because of a problem in presenters, where shortcuts only work in the scope of their presenter.
Consequently, debug shortcuts from the debugger toolbar only work if the mouse is over the toolbar button.
This should be removed once the spec bug is fixed"
aWindowPresenter
bindKeyCombination: StProceedCommand defaultShortcut
toAction: [ self proceedDebugSession ].
aWindowPresenter
bindKeyCombination: StStepIntoCommand defaultShortcut
toAction: [ self stepInto ].
aWindowPresenter
bindKeyCombination: StStepOverCommand defaultShortcut
toAction: [ self stepOver ].
aWindowPresenter
bindKeyCombination: StStepThroughCommand defaultShortcut
toAction: [ self stepThrough ].
aWindowPresenter
bindKeyCombination: StRestartCommand defaultShortcut
toAction: [ self restartCurrentContext ]
]
{ #category : #initialization }
StDebugger >> initializeStack [
stackTable := self newTable.
stackTable
addColumn: ((SpImageTableColumn
title: ''
evaluated: [ :context | self stackIconForContext: context ])
width: 8;
sortFunction: nil;
yourself);
addColumn: ((SpStringTableColumn
title: 'Class'
evaluated: [ :item | self printReceiverClassInContext: item ])
sortFunction: nil;
yourself);
addColumn:
((SpStringTableColumn title: 'Method' evaluated: [ :item |
| method |
method := item method.
method isCompiledBlock
ifTrue: [ method sourceNode sourceCode ]
ifFalse: [ method selector ] ])
sortFunction: nil;
yourself);
addColumn:
((SpStringTableColumn title: 'Package' evaluated: [ :item |
| package |
package := item method package.
package ifNil: [ '-' ] ifNotNil: [ package name asString ] ])
sortFunction: nil;
yourself).
stackTable transmitDo: [ :context |
stackTable selection isEmpty ifFalse: [
self updateInspectorFromContext: context.
self updateCodeFromContext: context.
self expandStackIfLastItemIsSelected.
self updateWindowTitle ] ].
stackHeader := self instantiate: StHeaderBar.
stackHeader label: 'Stack'.
stackHeader toolbarActions: (CmCommandGroup new
register:
((StDebuggerToggleFilterStackCommand forSpecContext: self)
iconName: #changeSorter);
register:
(StDebuggerSettingsCommand forSpecWithIconNamed: #configuration);
asSpecGroup)
]
{ #category : #initialization }
StDebugger >> initializeToolbar [
toolbar := self newToolbar.
toolbar displayMode: self application toolbarDisplayMode
]
{ #category : #initialization }
StDebugger >> initializeWindow: aWindowPresenter [
super initializeWindow: aWindowPresenter.
aWindowPresenter
title: self debuggerActionModel statusStringForContext;
initialExtent: self initialExtent;
whenClosedDo: [ self clear ].
self initializeShortcuts: aWindowPresenter
]
{ #category : #extensions }
StDebugger >> instantiateExtensionToolsPage: aToolClass [
| toolInstance |
toolInstance := self instantiate: aToolClass on: self.
self registerExtensionTool: toolInstance.
^ SpNotebookPage
title: toolInstance debuggerExtensionToolName
provider: [ toolInstance ]
]
{ #category : #'accessing context' }
StDebugger >> interruptedContext [
^ self session interruptedContext
]
{ #category : #accessing }
StDebugger >> interruptedProcess [
^ self session interruptedProcess
]
{ #category : #'instance creation' }
StDebugger >> newDebuggerContext [
^ self class debuggerContextClass new
exception: self session exception;
yourself
]
{ #category : #'instance creation' }
StDebugger >> newDebuggerContextFor: aContext [
^ self newDebuggerContext
context: aContext;
yourself
]
{ #category : #'accessing context' }
StDebugger >> newTitle [
^ self debuggerActionModel statusStringForContext
]
{ #category : #api }
StDebugger >> okToChange [
self code canDiscardEdits
ifTrue: [ ^ true ].
self code changed: #wantToChange.
^ self code canDiscardEdits
]
{ #category : #opening }
StDebugger >> openWithFullView [
"Create and schedule a full debugger with the given label. Do not
terminate the current active process."
self openWithSpec
]
{ #category : #opening }
StDebugger >> openWithNotification: notificationString [
self openWithFullView
]
{ #category : #actions }
StDebugger >> peelToFirstLike: aContext [
self debuggerActionModel peelToFirstLike: aContext.
self clearUnsavedCodeChanges.
self code text: self currentContext sourceCode
]
{ #category : #printing }
StDebugger >> printReceiverClassInContext: aContext [
| receiverClass methodClass |
receiverClass := aContext receiver class.
methodClass := aContext method methodClass.
receiverClass == methodClass ifTrue: [ ^ receiverClass name ].
^ (WriteStream on: String new)
nextPutAll: receiverClass name;
space;
nextPut: $(;
nextPutAll: methodClass name;
nextPut: $);
contents
]
{ #category : #actions }
StDebugger >> proceedDebugSession [
self removeActionsForSession: self session.
self debuggerActionModel proceedDebugSession.
self close
]
{ #category : #actions }
StDebugger >> recompileMethodTo: aString inContext: aContext notifying: aNotifyer [
aContext ifNil: [ ^ self ].
self session
recompileMethodTo: aString
inContext: aContext
notifying: aNotifyer
]
{ #category : #'presenter - code' }
StDebugger >> recordUnsavedCodeChanges [
|codeInteractionModel codeText|
codeInteractionModel := self code interactionModel.
codeInteractionModel hasUnsavedCodeChanges ifFalse:[^self].
codeText := self code text asString.
self unsavedCodeChanges at: codeInteractionModel context put: codeText
]
{ #category : #'updating session' }
StDebugger >> registerActionsForSession: aSession [
self flag: 'Rewrite it'.
aSession
ifNotNil: [ aSession
when: #restart send: #updateRestart to: self;
when: #resume send: #updateResume to: self;
when: #stepInto send: #updateStepInto to: self;
when: #stepOver send: #updateStepOver to: self;
when: #stepThrough send: #updateStepThrough to: self;
when: #contextChanged send: #updateContextChanged to: self ]
]
{ #category : #extensions }
StDebugger >> registerExtensionTool: anExtension [
self extensionTools add: anExtension
]
{ #category : #'updating session' }
StDebugger >> removeActionsForSession: aSession [
self flag: 'Rewrite it'.
aSession
ifNotNil: [ aSession
removeActionsForEvent: #restart;
removeActionsForEvent: #resume;
removeActionsForEvent: #stepInto;
removeActionsForEvent: #stepOver;
removeActionsForEvent: #stepThrough;
removeActionsForEvent: #contextChanged ]
]
{ #category : #'updating session' }
StDebugger >> removeSessionHolderSubscriptions [
self sessionHolder announcer unsubscribe: self sessionHolder.
self removeActionsForSession: self session
]
{ #category : #'ui requests' }
StDebugger >> requestClassFrom: aClass [
self class fastTDD ifTrue: [ ^ aClass ].
^ self requestSuperclassOf: aClass to: ProtoObject
]
{ #category : #'ui requests' }
StDebugger >> requestProtocolIn: aClass [
| entryCompletion applicants choice |
self class fastTDD ifTrue: [ ^ Protocol unclassified ].
applicants := AbstractTool protocolSuggestionsFor: aClass.
entryCompletion := EntryCompletion new
dataSourceBlock: [ :currText | applicants ];
filterBlock: [ :currApplicant :currText |
currText size > 3 and: [
currApplicant asLowercase includesSubstring:
currText asString asLowercase ] ].
choice := (UIManager default
request:
'Start typing for suggestions (3 characters minimum)'
initialAnswer: Protocol unclassified
title: 'Choose a protocol'
entryCompletion: entryCompletion) ifNil: [ Abort signal ].
^ choice ifEmpty: [ Protocol unclassified ]
]
{ #category : #'ui requests' }
StDebugger >> requestSuperclassOf: aClass to: aSuperclass [
| classes |
classes := OrderedCollection with: aClass.
classes addAll: (aClass allSuperclassesIncluding: aSuperclass).
classes addAll:
(aClass traits sort: [ :t1 :t2 | t1 asString < t2 asString ]).
classes size = 1 ifTrue: [ ^ classes first ].
^ (UIManager default
chooseFrom: (classes collect: [ :c | c name ])
values: classes
title: 'Define selector in:') ifNil: [ Abort signal ]
]
{ #category : #actions }
StDebugger >> restartCurrentContext [
self debuggerActionModel restartContext: self currentContext.
self clearUnsavedCodeChanges.
self code text: self currentContext sourceCode.
self updateCodeFromContext
]
{ #category : #actions }
StDebugger >> returnEnteredValue [
| expression |
self flag: #DBG_HowToTestNilIfUserCancels.
expression := (UIManager default
request: 'Enter expression for return value:') ifNil: [^self].
self debuggerActionModel
returnValueFromExpression: expression
fromContext: self currentContext