<commit_before>"
A DoesNotUnderstandDebugAction is a debugging action that can create a method if the debugger was opened as a result of a #doesNotUnderstand mesage send.

"
Class {
	#name : #DoesNotUnderstandDebugAction,
	#superclass : #DebugAction,
	#category : #'DebuggerActions-Actions'
}

{ #category : #registration }
DoesNotUnderstandDebugAction class >> actionType [
	<debuggingAction>
]

{ #category : #testing }
DoesNotUnderstandDebugAction >> appliesToDebugger: aDebugger [

	^ aDebugger session isInterruptedContextDoesNotUnderstand 
]

{ #category : #private }
DoesNotUnderstandDebugAction >> askForCategoryIn: aClass default: aString [
	| categoryName |
	categoryName := AbstractTool requestProtocolNameFor: aClass initialAnswer: aString.
	categoryName ifNil: [^aString].
	^ categoryName isEmptyOrNil 
		ifTrue: [^ aString] 
		ifFalse: [ categoryName ]
]

{ #category : #private }
DoesNotUnderstandDebugAction >> askForSuperclassOf: aClass toImplement: aSelector ifCancel: cancelBlock [
	| classes chosenClassIndex |
	classes := aClass withAllSuperclasses addAll: (aClass traits sort: [ :t1 :t2 | t1 asString < t2 asString ]); yourself.
	chosenClassIndex := UIManager default 
		chooseFrom: (classes collect: [:c | c name])
		title: 'Define #', aSelector, ' in which class?'.
	chosenClassIndex = 0 ifTrue: [^ cancelBlock value].
	^ classes at: chosenClassIndex
]

{ #category : #private }
DoesNotUnderstandDebugAction >> closeWindow [
	"Safely close the debugger window without destroying the debugger context and stack"
	| modelWindow |
	modelWindow := self debugger window.
	modelWindow
		model: nil;
		close
]

{ #category : #private }
DoesNotUnderstandDebugAction >> createMissingClassIn: aContext [
	| senderContext newClassVar variableNode previousContext errorMsgNode |
	
	variableNode := nil.
	
	[ senderContext := aContext sender.
	errorMsgNode := senderContext method sourceNodeExecutedForPC: senderContext pc.
	variableNode := self findUndeclaredVariableIn: errorMsgNode ]
		on: Error
		do: [ self error: 'Unable to create missing class' ].
		
	newClassVar := OCUndeclaredVariableWarning new
		node: variableNode;
		defineClass: variableNode name.
		
	previousContext := aContext stack second.
	
	self closeWindow.
	
	previousContext restart
]

{ #category : #accessing }
DoesNotUnderstandDebugAction >> defaultLabel [

	^  'Create'
]

{ #category : #accessing }
DoesNotUnderstandDebugAction >> defaultOrder [

	^ 45
]

{ #category : #actions }
DoesNotUnderstandDebugAction >> executeAction [
	"Should only be called when the debugger was created in response to a
	MessageNotUnderstood exception. Create a stub for the method that was
	missing and proceed into it, or create a class if it was missing instead"
	
	| msg msgCategory chosenClass |
	
	msg := self interruptedContext tempAt: 1.
	(msg lookupClass == UndefinedObject ) ifTrue: [ 
		^self createMissingClassIn: self interruptedContext ].
	
	chosenClass := self 
		askForSuperclassOf: self interruptedContext receiver class
		toImplement: msg selector
		ifCancel: [^self].
	msgCategory := (self askForCategoryIn: chosenClass default: 'as yet unclassified').
	self  session
		implement: msg 
		classified: msgCategory 
		inClass: chosenClass 
		forContext: self interruptedContext.
	self debugger selectTopContext
]

{ #category : #private }<commit_msg>22380 DNU "create method" for nil should not raise error but create a method https://pharo.fogbugz.com/f/cases/22380/DNU-create-method-for-nil-should-not-raise-error-but-create-a-method<commit_after>"
A DoesNotUnderstandDebugAction is a debugging action that can create a method if the debugger was opened as a result of a #doesNotUnderstand mesage send.

"
Class {
	#name : #DoesNotUnderstandDebugAction,
	#superclass : #DebugAction,
	#category : #'DebuggerActions-Actions'
}

{ #category : #registration }
DoesNotUnderstandDebugAction class >> actionType [
	<debuggingAction>
]

{ #category : #testing }
DoesNotUnderstandDebugAction >> appliesToDebugger: aDebugger [

	^ aDebugger session isInterruptedContextDoesNotUnderstand 
]

{ #category : #private }
DoesNotUnderstandDebugAction >> askForCategoryIn: aClass default: aString [
	| categoryName |
	categoryName := AbstractTool requestProtocolNameFor: aClass initialAnswer: aString.
	categoryName ifNil: [^aString].
	^ categoryName isEmptyOrNil 
		ifTrue: [^ aString] 
		ifFalse: [ categoryName ]
]

{ #category : #private }
DoesNotUnderstandDebugAction >> askForSuperclassOf: aClass toImplement: aSelector ifCancel: cancelBlock [
	| classes chosenClassIndex |
	classes := aClass withAllSuperclasses addAll: (aClass traits sort: [ :t1 :t2 | t1 asString < t2 asString ]); yourself.
	chosenClassIndex := UIManager default 
		chooseFrom: (classes collect: [:c | c name])
		title: 'Define #', aSelector, ' in which class?'.
	chosenClassIndex = 0 ifTrue: [^ cancelBlock value].
	^ classes at: chosenClassIndex
]

{ #category : #private }
DoesNotUnderstandDebugAction >> closeWindow [
	"Safely close the debugger window without destroying the debugger context and stack"
	| modelWindow |
	modelWindow := self debugger window.
	modelWindow
		model: nil;
		close
]

{ #category : #private }
DoesNotUnderstandDebugAction >> createMissingClassIn: aContext [
	| senderContext variableNode previousContext errorMsgNode |
	
	variableNode := nil.
	
	[ senderContext := aContext sender.
	errorMsgNode := senderContext method sourceNodeExecutedForPC: senderContext pc.
	variableNode := self findUndeclaredVariableIn: errorMsgNode ]
		on: Error
		do: [ ^self ].
		
	OCUndeclaredVariableWarning new
		node: variableNode;
		defineClass: variableNode name.
		
	previousContext := aContext stack second.
	
	self closeWindow.
	
	previousContext restart
]

{ #category : #accessing }
DoesNotUnderstandDebugAction >> defaultLabel [

	^  'Create'
]

{ #category : #accessing }
DoesNotUnderstandDebugAction >> defaultOrder [

	^ 45
]

{ #category : #actions }
DoesNotUnderstandDebugAction >> executeAction [
	"Should only be called when the debugger was created in response to a
	MessageNotUnderstood exception. Create a stub for the method that was
	missing and proceed into it, or create a class if it was missing instead"
	
	| msg msgCategory chosenClass |
	
	msg := self interruptedContext tempAt: 1.
	(msg lookupClass == UndefinedObject ) ifTrue: [ 
		self createMissingClassIn: self interruptedContext ].
	
	chosenClass := self 
		askForSuperclassOf: self interruptedContext receiver class
		toImplement: msg selector
		ifCancel: [^self].
	msgCategory := (self askForCategoryIn: chosenClass default: 'as yet unclassified').
	self  session
		implement: msg 
		classified: msgCategory 
		inClass: chosenClass 
		forContext: self interruptedContext.
	self debugger selectTopContext
]

{ #category : #private }
{'fraction_non_alphanumeric': 0.08354243542435424, 'fraction_numerical': 0.003837638376383764, 'mean_word_length': 5.05, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 41, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4667113', 'n_tokens_mistral': 2053, 'n_tokens_neox': 1938, 'n_words': 598}